Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Type: Package
Package: thisplot
Title: Utility Functions for Plotting
Version: 0.3.8
Date: 2026-04-23
Version: 0.3.10
Date: 2026-05-28
Authors@R:
c(
person("Meng", "Xu", email = "mengxu98@qq.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-8300-1054")),
Expand Down Expand Up @@ -50,7 +50,8 @@ Suggests:
scales,
stringr,
testthat (>= 3.0.0),
tidyr
tidyr,
venny
Config/Needs/website: mengxu98/thistemplate
Config/testthat/edition: 3
Encoding: UTF-8
Expand Down
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# thisplot 0.3.9
# thisplot 0.3.10

* **feat**:
* `DimDataPlot()`: New lightweight helper for plotting supplied 2D coordinates from data frames, with grouping, splitting, shape mapping, labels, palettes, theme controls, and optional group marks.
* `StatPlot()`: Added configurable major panel grid lines via `grid_major`, `grid_major_colour`, `grid_major_linetype`, and `grid_major_linewidth`.
* `StatPlot()`: Added optional `venn_engine = "venny"` support for more flexible 2-4 set Venn diagrams while keeping `ggVennDiagram` as the default.

* **docs**:
* Added documentation and examples for `DimDataPlot()`.
* Updated `StatPlot()` documentation for the new major grid controls.
* Added `StatPlot()` documentation and examples for the optional `venny` Venn backend.

# thisplot 0.3.8

Expand Down
274 changes: 181 additions & 93 deletions R/StatPlot.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
#' Can be one of `"bar"`, `"rose"`, `"ring"`, `"pie"`, `"trend"`,
#' `"trend_alluvial"`, `"area"`, `"dot"`, `"sankey"`, `"chord"`,
#' `"venn"`, or `"upset"`.
#' @param venn_engine The engine used when `plot_type = "venn"`.
#' Can be one of `"ggVennDiagram"` or `"venny"`.
#' Default is `"ggVennDiagram"`.
#' The `"venny"` engine supports 2 to 4 sets.
#' @param venn_args A list of additional arguments passed to
#' [venny::venny()] when `plot_type = "venn"` and
#' `venn_engine = "venny"`.
#' The `data` argument is generated internally and cannot be supplied here.
#' @param stat_type The type of statistic to compute for the plot.
#' Can be one of `"percent"` or `"count"`.
#' @param position The position adjustment for the plot.
Expand Down Expand Up @@ -164,6 +172,18 @@
#' group.by = "Group",
#' plot_type = "trend_alluvial"
#' )
#'
#' meta_data$A <- meta_data$Type == "A"
#' meta_data$B <- meta_data$Group == "X"
#' meta_data$C <- meta_data$Batch == "B1"
#' StatPlot(
#' meta_data,
#' stat.by = c("A", "B", "C"),
#' stat_level = TRUE,
#' plot_type = "venn",
#' venn_engine = "venny",
#' venn_args = list(detail = TRUE)
#' )
StatPlot <- function(
meta.data,
stat.by,
Expand All @@ -190,6 +210,8 @@ StatPlot <- function(
"venn",
"upset"
),
venn_engine = c("ggVennDiagram", "venny"),
venn_args = list(),
stat_type = c("percent", "count"),
position = c("stack", "dodge"),
palette = "Chinese",
Expand Down Expand Up @@ -227,7 +249,9 @@ StatPlot <- function(

stat_type <- match.arg(stat_type)
plot_type <- match.arg(plot_type)
venn_engine <- match.arg(venn_engine)
position <- match.arg(position)
venn_args <- venn_args %||% list()

if (identical(plot_type, "trend_alluvial")) {
check_r("ggalluvial", verbose = FALSE)
Expand Down Expand Up @@ -343,6 +367,27 @@ StatPlot <- function(
)
}
}
if (identical(plot_type, "venn") && identical(venn_engine, "venny")) {
if (!is.list(venn_args)) {
log_message(
"{.arg venn_args} must be a list",
message_type = "error"
)
}
if ("data" %in% names(venn_args)) {
log_message(
"{.arg venn_args} cannot contain {.arg data}; it is generated internally",
message_type = "error"
)
}
if (length(stat.by) < 2 || length(stat.by) > 4) {
log_message(
"{.arg stat.by} must contain 2 to 4 sets when {.arg venn_engine = 'venny'}",
message_type = "error"
)
}
check_r("venny", verbose = FALSE)
}

levels <- unique(
unlist(
Expand Down Expand Up @@ -982,6 +1027,7 @@ StatPlot <- function(
colors <- palette_colors(stat.by, palette = palette, palcolor = palcolor)
nlev <- nlevels(dat_all[[split.by]])
chord_use_temp <- plot_type == "chord" && isTRUE(combine) && nlev > 1L
venny_details <- list()
if (plot_type == "chord" && isTRUE(combine)) {
if (chord_use_temp) {
temp <- tempfile(fileext = "png")
Expand Down Expand Up @@ -1019,101 +1065,136 @@ StatPlot <- function(
cellkeep
}
)
venn <- ggVennDiagram::Venn(dat_list)
data <- ggVennDiagram::process_data(venn)
dat_venn_region <- ggVennDiagram::venn_region(data)
idname <- dat_venn_region[["name"]][
dat_venn_region[["name"]] %in% stat.by
]
names(idname) <- dat_venn_region[["id"]][
dat_venn_region[["name"]] %in% stat.by
]
idcomb <- strsplit(dat_venn_region[["id"]], split = "")
colorcomb <- lapply(idcomb, function(x) colors[idname[as.character(x)]])
dat_venn_region[["colors"]] <- sapply(
colorcomb,
function(x) blendcolors(x, mode = "blend")
)
dat_venn_region[["label"]] <- paste0(
dat_venn_region[["count"]],
"\n",
round(
dat_venn_region[["count"]] / sum(dat_venn_region[["count"]]) * 100,
1
),
"%"
)
dat_venn_setedge <- ggVennDiagram::venn_setedge(data)
dat_venn_setedge[["colors"]] <- colors[stat.by[as.numeric(
dat_venn_setedge[["id"]]
)]]

venn_regionedge_data <- ggVennDiagram::venn_regionedge(data)
venn_regionedge_data[["colors"]] <- dat_venn_region[["colors"]][match(
venn_regionedge_data[["id"]],
dat_venn_region[["id"]]
)]

p <- ggplot() +
geom_polygon(
data = venn_regionedge_data,
aes(X, Y, fill = colors, group = id),
alpha = alpha
) +
geom_path(
data = dat_venn_setedge,
aes(X, Y, group = id),
color = "black",
linewidth = 1,
show.legend = FALSE
) +
ggrepel::geom_text_repel(
data = ggVennDiagram::venn_setlabel(data),
aes(
X,
Y,
label = paste0(
name,
"\n(",
count,
")"
)
if (identical(venn_engine, "venny")) {
venny_result <- do.call(
venny::venny,
c(list(data = dat_list), venn_args)
)
venny_detail <- NULL
if (
is.list(venny_result) &&
!inherits(venny_result, c("gg", "ggplot")) &&
!is.null(venny_result[["venn"]])
) {
venny_detail <- venny_result
p <- venny_result[["venn"]]
} else {
p <- venny_result
}
if (!inherits(p, c("gg", "ggplot"))) {
log_message(
"{.fun venny::venny} must return a ggplot object or a list containing {.arg venn}",
message_type = "error"
)
}
p <- p + labs(x = sp, title = title, subtitle = subtitle)
if (!is.null(venny_detail)) {
attr(p, "venny_detail") <- venny_detail
venny_details[[sp]] <- venny_detail
}
} else {
venn <- ggVennDiagram::Venn(dat_list)
data <- ggVennDiagram::process_data(venn)
dat_venn_region <- ggVennDiagram::venn_region(data)
idname <- dat_venn_region[["name"]][
dat_venn_region[["name"]] %in% stat.by
]
names(idname) <- dat_venn_region[["id"]][
dat_venn_region[["name"]] %in% stat.by
]
idcomb <- strsplit(dat_venn_region[["id"]], split = "")
colorcomb <- lapply(
idcomb,
function(x) colors[idname[as.character(x)]]
)
dat_venn_region[["colors"]] <- sapply(
colorcomb,
function(x) blendcolors(x, mode = "blend")
)
dat_venn_region[["label"]] <- paste0(
dat_venn_region[["count"]],
"\n",
round(
dat_venn_region[["count"]] /
sum(dat_venn_region[["count"]]) * 100,
1
),
fontface = "bold",
colour = label.fg,
size = label.size + 0.5,
bg.color = label.bg,
bg.r = label.bg.r,
point.size = NA,
max.overlaps = 100,
force = 0,
min.segment.length = 0,
segment.colour = "black"
) +
ggrepel::geom_text_repel(
data = ggVennDiagram::venn_regionlabel(data),
aes(X, Y, label = count),
colour = label.fg,
size = label.size,
bg.color = label.bg,
bg.r = label.bg.r,
point.size = NA,
max.overlaps = 100,
force = 0,
min.segment.length = 0,
segment.colour = "black"
) +
scale_fill_identity() +
coord_fixed(ratio = 1, clip = "off") +
theme(
plot.title = element_text(hjust = 0.5),
plot.background = element_blank(),
panel.background = element_blank(),
axis.title.y = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
"%"
)
p <- p + labs(x = sp, title = title, subtitle = subtitle)
dat_venn_setedge <- ggVennDiagram::venn_setedge(data)
dat_venn_setedge[["colors"]] <- colors[stat.by[as.numeric(
dat_venn_setedge[["id"]]
)]]

venn_regionedge_data <- ggVennDiagram::venn_regionedge(data)
venn_regionedge_data[["colors"]] <- dat_venn_region[["colors"]][
match(
venn_regionedge_data[["id"]],
dat_venn_region[["id"]]
)
]

p <- ggplot() +
geom_polygon(
data = venn_regionedge_data,
aes(X, Y, fill = colors, group = id),
alpha = alpha
) +
geom_path(
data = dat_venn_setedge,
aes(X, Y, group = id),
color = "black",
linewidth = 1,
show.legend = FALSE
) +
ggrepel::geom_text_repel(
data = ggVennDiagram::venn_setlabel(data),
aes(
X,
Y,
label = paste0(
name,
"\n(",
count,
")"
)
),
fontface = "bold",
colour = label.fg,
size = label.size + 0.5,
bg.color = label.bg,
bg.r = label.bg.r,
point.size = NA,
max.overlaps = 100,
force = 0,
min.segment.length = 0,
segment.colour = "black"
) +
ggrepel::geom_text_repel(
data = ggVennDiagram::venn_regionlabel(data),
aes(X, Y, label = count),
colour = label.fg,
size = label.size,
bg.color = label.bg,
bg.r = label.bg.r,
point.size = NA,
max.overlaps = 100,
force = 0,
min.segment.length = 0,
segment.colour = "black"
) +
scale_fill_identity() +
coord_fixed(ratio = 1, clip = "off") +
theme(
plot.title = element_text(hjust = 0.5),
plot.background = element_blank(),
panel.background = element_blank(),
axis.title.y = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
)
p <- p + labs(x = sp, title = title, subtitle = subtitle)
}
}

if (plot_type == "upset") {
Expand Down Expand Up @@ -1329,6 +1410,13 @@ StatPlot <- function(
ncol = ncol,
byrow = byrow
)
if (
identical(plot_type, "venn") &&
identical(venn_engine, "venny") &&
length(venny_details) > 0
) {
attr(plot, "venny_detail") <- venny_details
}
} else {
plot <- plist[[1]]
}
Expand Down
Loading