R 巧妙地分离功能图例

R plotly separate functional legends

我想通过 R plotly 制作一个带有独立图例的绘图,同时尊重色阶。

这是我的:

library(plotly)

X <- data.frame(xcoord = 1:6,
                ycoord = 1:6,
                score  = 1:6,
                gender = c("M", "M", "M", "F", "F", "F"),
                age = c("young", "old", "old", "old", "young", "young"))

plot_ly(data = X, x = ~xcoord, y = ~ycoord, split = ~interaction(age, gender),
        type = "scatter", mode = "markers",
        marker = list(color = ~score,
                      colorbar = list(len = .5, y = .3)))

这是结果:

如你所见,colorbar乱了,两个类别纠缠在一起

我需要为 age(年轻与年老)和 gender(男与女)分别创建图例,它们可以相互独立地单击。这将是预期的结果:


编辑 1
这相当于 ggplot2:

gg <- ggplot(X, aes(x = xcoord, y = ycoord)) +
  geom_point(aes(color = score, shape = gender, alpha = age), size = 5) +
  scale_shape_manual(values = c("M" = 19, "F" = 19)) +
  scale_alpha_manual(values = c("young" = 1, "old" = 1))

ggplotly(gg)

它在 ggplot 中显示正确,但在应用时中断 ggplotly()

请注意,我更喜欢使用原生 plotly 图的解决方案,而不是 post hoc ggplotly() 修复已在其他 post 中提出。


编辑 2
尽管当前的答案确实解开了两个图例(agegender),但它们不起作用。例如,如果您单击 young 级别,整个 age 图例将切换为 on/off。这里的 objective 是每个图例的每个子级别都可以独立切换,通过点击图例的级别,点将相应地 show/hide。

这不是您要找的。不过,我能够创建一个有意义的颜色条。

我删除了组间交互的调用并创建了一个单独的跟踪。然后我创建了图例组并命名它们,为 genderage 创建单独的图例。当我从调用中拉出 color = 以创建颜色栏时,这会同步色标。

但是,它为年龄和性别标签分配颜色,这没有意义!有一些内容与您的要求不符,但有人可以根据此信息进行构建。

plot_ly(data = X, x = ~xcoord, y = ~ycoord, 
        split = ~age,
        legendgroup = 'age', # create first split and name it
        legendgrouptitle = list(text = "Age"),
        type = "scatter", mode = "markers",
        color = ~score,
        marker = list(colorbar = list(len = .5, y = .3))) %>% 
  add_trace(split = ~gender,
            legendgroup = 'gender', # create second split and name it
            color = ~score,
            legendgrouptitle = list(text = "Gender")) %>% 
    colorbar(title = 'Score')

我不确定这是否正是您想要的。我尝试使用两个 markers 制作年龄和性别的图例。图例可独立点击,但我不确定这是否是您希望它们可点击的方式。也可以单击颜色条。您可以使用此代码:

library(tidyverse)
library(plotly)
plot_ly() %>%
  add_markers(data = X,
            x = ~xcoord, 
            y = ~ycoord, 
            type = "scatter", 
            mode = "markers",
            #name = "M",
            color = I("grey"),
            split = ~gender,
            legendgroup = 'gender', 
            legendgrouptitle = list(text = "Gender")) %>%
  add_markers(data = X,
              x = ~xcoord, 
              y = ~ycoord, 
              type = "scatter", 
              mode = "markers",
              #name = "M",
              color = I("grey"),
              split = ~age,
              legendgroup = 'age', 
              legendgrouptitle = list(text = "Age")) %>%
  add_trace(data = X,
            x = ~xcoord, 
            y = ~ycoord, 
            type = "scatter", 
            mode = "markers",
            name = "",
            marker = list(color = ~score,
                          colorbar = list(len = .5, y = .3)))

输出如下所示:

Plotly 似乎并不容易支持这一点,因为不同的指南链接到多个跟踪。所以取消选择例如“年龄”轨迹上的“旧”不会从“性别”轨迹的单独点集中删除任何内容。

这是使用 crosstalkSharedData 数据对象的解决方法。这不是(取消)选择 plotly traces,而是在 plotly 使用的数据集上使用过滤器。它在技术上实现了所请求的选择行为,但它是否是一个可行的解决方案取决于最终的应用程序。如果该机制适合您,可能有一些方法可以调整样式和布局以使其更加 plotly-ish。

library(crosstalk)

#SharedData object used for filters and plot
shared <- SharedData$new(X) 

crosstalk::bscols(
  widths = c(2, 10),
   list(
     crosstalk::filter_checkbox("Age", 
                                label = "Age",
                                sharedData = shared, 
                                group = ~age),
     crosstalk::filter_checkbox("Gender", 
                                label = "Gender",
                                sharedData = shared, 
                                group = ~gender)
   ),
   plot_ly(data = shared, x = ~xcoord, y = ~ycoord,
           type = "scatter", mode = "markers",
           marker = list(color = ~score,
                         colorbar = list(len = .5, y = .3),
                         cmin = 0, cmax = 6)) %>%
    layout(
      xaxis = list(range=c(.5,6.5)),
      yaxis = list(range=c(.5,6.5))
    )
   )

编辑:将所有复选框初始化为“选中”

我只能通过修改输出 HTML 标签来做到这一点。这会产生相同的图,但会在开始时选中所有框。

out <- crosstalk::bscols(...) #previous output object

library(htmltools)
out_tags <- htmltools::renderTags(out)

#check all Age and Gender checkboxes
out_tags$html <- stringr::str_replace_all(
  out_tags$html, 
  '(<input type="checkbox" name="(Age|Gender)" value=".*")/>',
  '\1 checked="checked"/>'
)
out_tags$html <- HTML(out_tags$html)
# view in RStudio Viewer
browsable(as.tags(out_tags))
#or from Rmd chunk
as.tags(out_tags)