R 绘制独立的函数图例

发布于 2025-01-14 14:04:36 字数 1677 浏览 4 评论 0原文

我想通过 R 绘制具有独立图例的图,同时尊重色阶。

这就是我所拥有的:

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)))

这就是结果:
结果

如您所见,颜色条混乱,两个类别纠缠在一起。

我需要为age(年轻与年老)和gender(M vs F)提供单独的图例,可以相互独立地单击。这将是预期的结果:
预期结果


编辑 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 图的解决方案,而不是像其他方案中提出的事后 ggplotly() 修复方案帖子。


编辑2
尽管当前的答案确实解开了两个图例(agegender),但它们不起作用。例如,如果您单击young级别,整个age图例将打开/关闭。这里的目标是每个图例的每个子级别都可以独立于其他级别进行切换,并且通过单击图例的级别,点将相应地显示/隐藏。

I want to produce a plot via R plotly with independent legends while respecting the colorscale.

This is what I have:

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)))

This is the outcome:
outcome

As you can see, the colorbar is messed up and the two categories are entangled.

I need to have separate legends for age (young vs old) and gender (M vs F), that can be clicked independently from one another. This would be the expected outcome:
expected outcome


Edit 1
This is the equivalent with 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)

It does display correctly in ggplot, but breaks when applying ggplotly().

Please note that I would favor a solution with the native plotly plot, rather than a post hoc ggplotly() fix as has been proposed in other posts.


Edit 2
Although the current answers do disentangle the two legends (age and gender), they are not functional. For instance, if you click on the young level, the whole age legend will be toggled on/off. The objective here is that each sub level of each legend can be toggled independently from the others, and that by clicking on the legend's levels, the dot will show/hide accordingly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我做我的改变 2025-01-21 14:04:36

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

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

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)

Plotly does not seem to easily support this, since different guides are linked to multiple traces. So deselecting e.g. "old" on an "Age" trace will not remove anything from the separate set of points from the "Gender" trace.

This is a workaround using crosstalk and a SharedData data object. Instead of (de)selecting plotly traces, this uses filters on the dataset that is used by plotly. It technically achieves the selection behaviour that is requested, but whether or not it is a working solution depends on the final application. There are likely ways to adjust the styling and layout to make it more plotly-ish, if the mechanism works for you.

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))
    )
   )

enter image description here

Edit: initialize all checkboxes as "checked"

I only managed to do this by modifying the output HTML tags. This produces the same plot, but has all boxes checked at the beginning.

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)
久隐师 2025-01-21 14:04:36

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

我删除了对组之间交互的调用并创建了一个单独的跟踪。然后,我创建了图例组并命名它们,以便为 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')

输入图片此处描述

This isn't exactly what you're looking for. I was able to create a meaningful color bar, though.

I removed the call for interaction between the groups and created a separate trace. Then I created legend groups and named them to create separate legends for gender and age. When I pull color = out of the call to create a colorbar, this synced the color scales.

However, it assigns colors to the labels for age and gender and that's not meaningful! There are a few things that don't line up with your request, but someone may be able to build on this information.

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')

enter image description here

遗心遗梦遗幸福 2025-01-21 14:04:36

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

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)))

输出如下所示:

在此处输入图像描述

I am not sure if this is exactly what you want. I tried to made the legends for age and gender using two markers. The legends are independently clickable, but I am not sure if this is the way you want them to have clickable. It is also possible to click on the colorbar. You can use this code:

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)))

The output looks like this:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文