Mesclando legendas na subtrama plotada
Tenho vários grupos onde para cada um tenho várias classes para as quais medi valores contínuos:
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"))
Nem cada grupo nos dados tem as mesmas classes ou, em outras palavras, cada grupo tem um subconjunto de todas as classes.
Estou tentando gerar R plotlycurvas de densidade para cada grupo, codificadas por cores por classe e, em seguida, combiná-las em um único gráfico usando plotlya subplotfunção de.
Isso é o que estou fazendo:
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)
Que dá:
Os problemas que eu gostaria de corrigir são:
- Faça a legenda aparecer apenas uma vez (agora ela se repete para cada grupo) mesclando todas as classes
- Faça com que o título apareça em cada uma das subtramas em vez de apenas na última parcela em que é agora. (Eu sei que poderia simplesmente ter o nome do grupo como os títulos do eixo x, mas prefiro guardar esse espaço porque na realidade tenho mais de 3 grupos)
Respostas
Você pode usar o seguinte código
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')
)
o que me dá o seguinte enredo
Usar plot_ly()é um pouco complicado, pelo menos se você quiser continuar usando o colorargumento para gerar vários rastreamentos a partir dos dados.
Você precisa definir um legendgrouplevando em consideração sua variável de classe. esta legendgroup, porém, não mesclar os itens de legenda em um (ele apenas grupos deles).
Da mesma forma, para evitar entradas duplicadas na legenda, você precisa definir showlegend = FALSEos traços que deseja ocultar em relação à legenda.
Devido ao fato de que você não usa um loop (ou lapply) para criar os traços únicos de uma subtrama, não podemos controlar a visibilidade de cada traço ao gerar o gráfico (através do colorargumento mencionado acima - podemos ocultar ou mostrar todos os rastros de uma plot_lychamada - por meio de add_tracepodemos controlá-lo para cada rastreamento individualmente). Portanto, configurei showlegend = TRUEapenas para o primeiro gráfico e forcei-o a exibir todas as classes disponíveis por meio de dados fictícios. Por favor, veja o seguinte:
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)
Outra abordagem (evitar a solução alternativa de dados fictícios) seria criar cada traço em um loop (ou via lapply) e controlar sua visibilidade de legenda de acordo com a primeira ocorrência do item.
Além disso, acho que deve ser possível controlar a visibilidade dos itens de legenda usando ?plotly::style. No entanto, não posso controlá-lo para rastros únicos atualmente. Eu registrei um problema aqui .
Em relação aos títulos das subtramas, consulte isto .