在情节子图中合并图例
Posted
技术标签:
【中文标题】在情节子图中合并图例【英文标题】:Merging legends in plotly subplot 【发布时间】:2020-12-17 00:29:39 【问题描述】:我有几个组,每个组都有几个类,我测量了连续值:
set.seed(1)
df <- data.frame(value = c(rnorm(100,1,1), rnorm(100,2,1), rnorm(100,3,1),
rnorm(100,3,1), rnorm(100,1,1), rnorm(100,2,1),
rnorm(100,2,1), rnorm(100,3,1), rnorm(100,1,1)),
class = c(rep("c1",100), rep("c2",100), rep("c3",100),
rep("c2",100), rep("c4",100), rep("c1",100),
rep("c4",100), rep("c3",100), rep("c2",100)),
group = c(rep("g1",300), rep("g2",300), rep("g3",300)))
df$class <- factor(df$class, levels =c("c1","c2","c3","c4"))
df$group <- factor(df$group, levels =c("g1","g2","g3"))
并非数据中的每个组都具有相同的类,或者换句话说,每个组都具有所有类的子集。
我正在尝试为每个组生成R
plotly
密度曲线,按类别进行颜色编码,然后使用plotly
的subplot
函数将它们全部组合到一个图上。
这就是我正在做的:
library(dplyr)
library(ggplot2)
library(plotly)
set.seed(1)
df <- data.frame(value = c(rnorm(100,1,1), rnorm(100,2,1), rnorm(100,3,1),
rnorm(100,3,1), rnorm(100,1,1), rnorm(100,2,1),
rnorm(100,2,1), rnorm(100,3,1), rnorm(100,1,1)),
class = c(rep("c1",100), rep("c2",100), rep("c3",100),
rep("c2",100), rep("c4",100), rep("c1",100),
rep("c4",100), rep("c3",100), rep("c2",100)),
group = c(rep("g1",300), rep("g2",300), rep("g3",300)))
df$class <- factor(df$class, levels =c("c1","c2","c3","c4"))
df$group <- factor(df$group, levels =c("g1","g2","g3"))
plot.list <- lapply(c("g1","g2","g3"), function(g)
density.df <- do.call(rbind,lapply(unique(dplyr::filter(df, group == g)$class),function(l)
ggplot_build(ggplot(dplyr::filter(df, group == g & class == l),aes(x=value))+geom_density(adjust=1,colour="#A9A9A9"))$data[[1]] %>%
dplyr::select(x,y) %>% dplyr::mutate(class = l)))
plot_ly(x = density.df$x, y = density.df$y, type = 'scatter', mode = 'lines',color = density.df$class) %>%
layout(title=g,xaxis = list(zeroline = F), yaxis = list(zeroline = F))
)
subplot(plot.list,nrows=length(plot.list),shareX=T)
这给出了:
我想解决的问题是:
-
让图例只出现一次(现在它对每个组重复)合并所有类
让标题出现在每个子图中,而不是只出现在最后一个图上。 (我知道我可以简单地将组名作为 x 轴标题,但我宁愿节省空间,因为实际上我有超过 3 个组)
【问题讨论】:
Here plotly.js 的相关 FR 和 here r-plotly 的相关问题可以找到。 【参考方案1】:使用plot_ly()
有点棘手,至少如果您想坚持使用color
参数从数据中生成多个跟踪。
您需要定义一个legendgroup
并考虑您的类变量。
这 legendgroup
但是不会将图例项合并为一个(它只是将它们分组)。
因此,为避免图例中出现重复条目,您需要为要隐藏的有关图例的那些痕迹设置 showlegend = FALSE
。
由于您不使用循环(或 lapply)来创建子图的单个轨迹,因此在生成图时我们无法控制每个轨迹的可见性(通过上面提到的 color
参数 - 我们可以隐藏或显示 plot_ly
调用的所有痕迹 - 通过 add_trace
我们可以单独控制每个痕迹)。因此我只为第一个图设置了showlegend = TRUE
,并强制它通过虚拟数据显示所有可用的类。请参阅以下内容:
set.seed(1)
df <- data.frame(value = c(rnorm(100,1,1), rnorm(100,2,1), rnorm(100,3,1),
rnorm(100,3,1), rnorm(100,1,1), rnorm(100,2,1),
rnorm(100,2,1), rnorm(100,3,1), rnorm(100,1,1)),
class = c(rep("c1",100), rep("c2",100), rep("c3",100),
rep("c2",100), rep("c4",100), rep("c1",100),
rep("c4",100), rep("c3",100), rep("c2",100)),
group = c(rep("g1",300), rep("g2",300), rep("g3",300)))
df$class <- factor(df$class, levels =c("c1","c2","c3","c4"))
df$group <- factor(df$group, levels =c("g1","g2","g3"))
library(dplyr)
library(ggplot2)
library(plotly)
plot.list <- lapply(c("g1","g2","g3"), function(g)
density.df <- do.call(rbind,lapply(unique(dplyr::filter(df, group == g)$class),function(l)
ggplot_build(ggplot(dplyr::filter(df, group == g & class == l),aes(x=value))+geom_density(adjust=1,colour="#A9A9A9"))$data[[1]] %>%
dplyr::select(x,y) %>% dplyr::mutate(class = l)))
p <- plot_ly(data = density.df, x = ~x, y = ~y, type = 'scatter', mode = 'lines', color = ~class, legendgroup = ~class, showlegend = FALSE) %>%
layout(xaxis = list(zeroline = F), yaxis = list(zeroline = FALSE)) %>%
add_annotations(
text = g,
x = 0.5,
y = 1.1,
yref = "paper",
xref = "paper",
xanchor = "middle",
yanchor = "top",
showarrow = FALSE,
font = list(size = 15)
)
if(g == "g1")
dummy_df <- data.frame(class = unique(df$class))
dummy_df$x <- density.df$x[1]
dummy_df$y <- density.df$y[1]
p <- add_trace(p, data = dummy_df, x = ~x, y = ~y, color = ~class, type = "scatter", mode = "lines", showlegend = TRUE, legendgroup = ~class, hoverinfo = 'none')
p
)
subplot(plot.list, nrows = length(plot.list), shareX = TRUE)
另一种方法(避免使用虚拟数据解决方法)是在循环中(或通过 lapply)创建每个跟踪,并根据项目的第一次出现来控制它的图例可见性。
此外,我认为应该可以使用?plotly::style
控制图例项目的可见性。但是,我目前无法控制单个跟踪。我提交了一个问题here。
关于子图的标题,请参阅this。
【讨论】:
【参考方案2】:您可以使用以下代码
library(tidyverse)
library(plotly)
ggplotly(
ggplot(df, aes(x=value, col = class)) +
geom_density(adjust=1) +
facet_wrap(~group, ncol = 1) +
theme_minimal() +
theme(legend.position = 'top')
)
这给了我以下情节
【讨论】:
以上是关于在情节子图中合并图例的主要内容,如果未能解决你的问题,请参考以下文章
如何在情节中摆脱 scatter_matrix 的所有子图中的网格线?