我的“最近”线条被隐藏,图表没有恢复到原始状态。这是 bscol 的错误吗?

发布于 2025-01-10 00:09:15 字数 2513 浏览 5 评论 0原文

我正在尝试使用串扰(特别是使用filter_checkbox 和filter_select)向我的绘图添加一些交互性,但我遇到了一些障碍。我首先通过 ggplot 生成绘图,然后使用 ggplot 函数将其转换为绘图。

虽然我可以很好地生成图表(并且降价有很多交互性),但我有几个问题。首先,当我希望过滤(通过filter_select或filter_checkbox)时,“最近”数据会从图表中完全消失,并且在不刷新html的情况下无法恢复。我正在过滤的实际数据也会发生类似的情况;如果不刷新页面,我无法将图表恢复到原始状态。

有谁知道为什么会这样?我的代码+数据的副本如下。

下面是我的数据片段(数据=历史):

structure(list(date = c("23-03-2019", "23-03-2019", "23-03-2019", 
"23-03-2019", "05-05-2020", "05-05-2020", "05-05-2020", "05-05-2020", 
"17-06-2021", "17-06-2021", "17-06-2021", "17-06-2021"), cumvol = c(0.004, 
0.034, 0.054, 0.057, 0.005, 0.048, 0.068, 0.075, 2.009, 2.029, 
2.049, 2.064), time = structure(c(26457, 26636, 26658, 27216, 
25152, 25614, 25667, 25668, 56966, 57268, 57303, 58986), units = "secs", class = c("hms", 
"difftime")), Year = c("2019", "2019", "2019", "2019", "2020", 
"2020", "2020", "2020", "2021", "2021", "2021", "2021"))

在此之上,我从单独的 df 绘制另一条线(数据=最近)。

structure(list(date = structure(c(19038, 19038, 19038, 19038), class = "Date"), 
cumvol = c(0.029, 0.034, 0.07, 0.075), time = structure(c(29674, 29674, 29691, 29719), 
class = c("hms", "difftime"), units = "secs")), Year = c("2022", "2022", "2022", "2022"))

然后,我将数据转换为共享数据,使用该数据创建一个 ggplot,然后将该图转换为 ggplot,如下所示(变量“most_recent”指的是“最近”数据框中的最新条目,由最近[nrow(生成)最近),]):

sharedhistoric <- SharedData$new(historic, key = ~Date)
sharedrecent <- SharedData$new(recent, key = ~Date)

plot <- ggplot()+geom_line(data=sharedhistoric,aes(x=time, y=cumvol, group=date),color='#BAB0AC', alpha=0.5)+
      geom_line(data=sharedrecent ,aes(x=time, y=cumvol, group=date),size=1.2,color='#E15758')+
      geom_point(data=most_recent, aes(x=time,y=cumvol), color='#E15759',size=3)+geom_hline(yintercept = 0)+  theme(title=element_text(size=12),panel.background = element_rect(fill='white',color='black'),legend.position='right')+
        labs(title = "Vol",subtitle = "Cum Vol so far", x = "Time", y = "Vol")

最后,我将图表转换为绘图并使用以下 bcol:

chartyplot <- plotly::ggplotly(plot)
bscols(widths = c(4, 9),
       list(
         crosstalk::filter_checkbox("Year", 
                         label = "Select Year",
                       sharedhistoric, 
                        group = ~Year),
         crosstalk::filter_select("Date", 
                       label = "Date",
                      sharedhistoric, 
                       group = ~Date)
         ), chartyplot)

感谢您的任何帮助/建议。

I'm trying to add some interactivity to my plotly charts using crosstalk (specifically using filter_checkbox and filter_select) and I've run into a bit of snag. I produce my plots firstly through ggplot then I convert it to plotly using ggplot function.

While I can generate the chart fine (and there's plenty of interactivity on the markdown), I have a couple of problems. Firstly, when I wish to filter (either via filter_select or filter_checkbox), the 'recent' data disappears from the chart entirely, and cannot be recovered without refreshing the html. A similar thing happens with the actual data I'm filtering; I cannot revert the chart to its original state without refreshing the page.

Does anyone know why this might be? Copy of my code + data is below.

Below is a snippet of my data (data=historic):

structure(list(date = c("23-03-2019", "23-03-2019", "23-03-2019", 
"23-03-2019", "05-05-2020", "05-05-2020", "05-05-2020", "05-05-2020", 
"17-06-2021", "17-06-2021", "17-06-2021", "17-06-2021"), cumvol = c(0.004, 
0.034, 0.054, 0.057, 0.005, 0.048, 0.068, 0.075, 2.009, 2.029, 
2.049, 2.064), time = structure(c(26457, 26636, 26658, 27216, 
25152, 25614, 25667, 25668, 56966, 57268, 57303, 58986), units = "secs", class = c("hms", 
"difftime")), Year = c("2019", "2019", "2019", "2019", "2020", 
"2020", "2020", "2020", "2021", "2021", "2021", "2021"))

On top of this I plot another line from a separate df (data=recent).

structure(list(date = structure(c(19038, 19038, 19038, 19038), class = "Date"), 
cumvol = c(0.029, 0.034, 0.07, 0.075), time = structure(c(29674, 29674, 29691, 29719), 
class = c("hms", "difftime"), units = "secs")), Year = c("2022", "2022", "2022", "2022"))

I then convert the data to Shared data, create a ggplot using that data, then covert that plot to ggplot as follows (the variable "most_recent" refers the to the most recent entry in the 'recent' dataframe, produced by recent[nrow(recent),]):

sharedhistoric <- SharedData$new(historic, key = ~Date)
sharedrecent <- SharedData$new(recent, key = ~Date)

plot <- ggplot()+geom_line(data=sharedhistoric,aes(x=time, y=cumvol, group=date),color='#BAB0AC', alpha=0.5)+
      geom_line(data=sharedrecent ,aes(x=time, y=cumvol, group=date),size=1.2,color='#E15758')+
      geom_point(data=most_recent, aes(x=time,y=cumvol), color='#E15759',size=3)+geom_hline(yintercept = 0)+  theme(title=element_text(size=12),panel.background = element_rect(fill='white',color='black'),legend.position='right')+
        labs(title = "Vol",subtitle = "Cum Vol so far", x = "Time", y = "Vol")

Finally, I convert the chart to plotly and use the following bcols:

chartyplot <- plotly::ggplotly(plot)
bscols(widths = c(4, 9),
       list(
         crosstalk::filter_checkbox("Year", 
                         label = "Select Year",
                       sharedhistoric, 
                        group = ~Year),
         crosstalk::filter_select("Date", 
                       label = "Date",
                      sharedhistoric, 
                       group = ~Date)
         ), chartyplot)

Thanks for any assistance/advice.

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

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

发布评论

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

评论(1

巾帼英雄 2025-01-17 00:09:15

据我所知,有两种影响导致了

  1. SharedData 对象的这种行为(非)唯一键,
  2. 任何未从 crosstalk::filter_* 选择的内容都会从图中删除

TL;DR:实现此目的的方法是确保唯一的键,并将不同的数据集分配给同一组。一旦应用任何过滤器,任何不属于 SharedData 对象的数据都会丢失。我们可以通过 HTML 标签修​​复一些数据,从而欺骗一些数据始终保留在绘图上。

1 键

查看 crosstalk 文档 的键部分,键在数据集中应该是唯一的。因此,在给定的数据集中,日期可能不是一个好的选择。相反,我们可以简单地根据行号创建键(这也是未提供键时的默认行为)

sharedhistoric <- SharedData$new(historic %>% mutate(key = as.character(row_number())), key = ~key)
sharedrecent <- SharedData$new(recent %>% mutate(key = as.character(row_number())), key = ~key)

...但现在显示“最近”数据(2019 年,它应该选择“历史”数据的第 1-4 行)(左图)。切换 geom_line 语句的顺序(首先是“最近”,然后是“历史”)会导致“历史”数据的正确行为,但“最近”数据消失了(右图)。这本质上意味着,过滤后的键仅应用于添加到 ggplot 的最后一个 SharedData 对象。

输入图片此处的描述

交互式使用两个数据集的下一步是将它们分配到同一组,每个文档可用于链接数据集的多个实例。

sharedhistoric <- SharedData$new(..., key = ~key, group = "mydata")
sharedrecent <- SharedData$new(..., key = ~key, group = "mydata")

这看起来已经更好了,我们不断过滤和恢复“历史”和“最近”数据 - 但 2019 年现在与两个数据集的前 4 行相关联(因为重复的键):

输入图像描述这里

一种可能的解决方法是全局定义唯一键并将子数据集分配到同一组:

historic <- as.data.frame(historic) %>%
  dplyr::mutate(date = as.character(date), 
                set = "historic")
recent <- as.data.frame(recent) %>%
  dplyr::mutate(date = as.character(date), 
                set = "recent")
all <- bind_rows(historic, recent) %>%
  dplyr::mutate(key = as.character(row_number()))

sharedall <- SharedData$new(all, key = ~key, group = "mydata")
sharedhistoric <- SharedData$new(all %>% dplyr::filter(set == "historic"), key = ~key, group = "mydata")
sharedrecent <- SharedData$new(all %>% dplyr::filter(set == "recent"), key = ~key, group = "mydata")

#--- no changes to the plot ---
# but choose filter options from full dataset

bscols(widths = c(4, 8),
       list(
         crosstalk::filter_checkbox("id_year", 
                                    label = "Select Year",
                                    sharedall, 
                                    group = ~Year),
         crosstalk::filter_select("id_date", 
                                  label = "Date",
                                  sharedall, 
                                  group = ~date)
       ),
       chartyplot)

现在我们可以正确(取消)选择历史数据和最近数据。

输入图片此处描述

2 个“丢失”数据点

如果前面的示例仅基于“历史”数据进行过滤器选择,则“最近”数据(来自不同年份)在第一过滤器被选中。对于“most_recent”数据点也是如此,它不是 SharedData 对象。该数据点最初是绘制的,但在设置第一个过滤器后立即被删除。

但是,可以通过定义属于同一组的“最新”SharedData 对象来以相同的方式解决此问题:

sharedmostrecent <- SharedData$new(all %>% tail(1), key = ~key, group = "mydata")

#plot adjustment
...
geom_line(...) +
geom_point(data=sharedmostrecent, aes(x=time,y=cumvol), color='#E15759',size=3) +
...

在此处输入图像描述

这样,我们可以选择和取消选择图中的所有数据,而不会丢失任何数据。

3 修复一些数据

为了确保“最新”数据无论(手动过滤器值)如何都能保留,我们可以操纵 HTML 输出。首先,我们不按年份过滤,而是按最近/历史和过去的过滤来分离数据集。年(或任何其他合适的数据子集):

all <- bind_rows(historic, recent) %>%
  dplyr::mutate(key = as.character(row_number()),
                dataset = paste0(set, " ", Year))
...
out <- bscols(widths = c(4, 8),
       list(
         crosstalk::filter_checkbox("id_year", 
                                    label = "Select dataset",
                                    sharedall, 
                                    group = ~dataset)
       ),
       chartyplot) 

然后我们检查并禁用最近数据集的复选框 - 这可能可以更优雅地完成,但它在我的 Rmd 中有效

library(htmltools)
out_tags <- htmltools::renderTags(out)
out_tags$html <- stringr::str_replace(
  out_tags$html, 
  '<input type="checkbox" name="id_year" value="recent 2022"/>',
  '<input type="checkbox" name="id_year" value="recent 2022" disabled="disabled" checked="checked"/>'
  )
out_tags$html <- HTML(out_tags$html)
as.tags(out_tags)

: sstatic.net/LeALa.png" rel="nofollow noreferrer">在此处输入图像描述

As far as I can tell, there are two effects that contribute to this behavior

  1. (non-)unique keys of the SharedData objects
  2. anything not selected from crosstalk::filter_* is dropped from the plot

TL;DR: The way to make this work, is by ensuring unique keys, and assigning the different datasets to the same group. Any data not part of a SharedData object is lost as soon as any filter is applied. And we can cheat some data to always remain on the plot by fixing some data via HTML tags.

1 Keys

Looking at the keys section of the crosstalk documentation, the keys should be unique within the dataset. Therefore date may not be a good choice for this in the given dataset. Instead, we can simply create keys based on the row number (which is also the default behavior when no key is supplied)

sharedhistoric <- SharedData$new(historic %>% mutate(key = as.character(row_number())), key = ~key)
sharedrecent <- SharedData$new(recent %>% mutate(key = as.character(row_number())), key = ~key)

...but now only the "recent" data shows up (for year 2019, which should select rows 1-4 of the "historic" data) (left plot). Switching the order of the geom_line statements (first "recent", then "historic") leads to correct behavior for the "historic" data, but the "recent" is gone (right plot). This essentially means, that the filtered keys are only applied to the last SharedData object added to ggplot.

enter image description here

The next step to making interactive use of both datasets, is assigning them to the same group, which per documentation can be used to link multiple instances of datasets.

sharedhistoric <- SharedData$new(..., key = ~key, group = "mydata")
sharedrecent <- SharedData$new(..., key = ~key, group = "mydata")

This already looks better and we keep filtering and recovering both "historic", and "recent" data - but 2019 is now associated to the first 4 rows of both datasets (because of duplicate keys):

enter image description here

A possible workaround is to globally define unique keys and assign the sub-datasets to the same group:

historic <- as.data.frame(historic) %>%
  dplyr::mutate(date = as.character(date), 
                set = "historic")
recent <- as.data.frame(recent) %>%
  dplyr::mutate(date = as.character(date), 
                set = "recent")
all <- bind_rows(historic, recent) %>%
  dplyr::mutate(key = as.character(row_number()))

sharedall <- SharedData$new(all, key = ~key, group = "mydata")
sharedhistoric <- SharedData$new(all %>% dplyr::filter(set == "historic"), key = ~key, group = "mydata")
sharedrecent <- SharedData$new(all %>% dplyr::filter(set == "recent"), key = ~key, group = "mydata")

#--- no changes to the plot ---
# but choose filter options from full dataset

bscols(widths = c(4, 8),
       list(
         crosstalk::filter_checkbox("id_year", 
                                    label = "Select Year",
                                    sharedall, 
                                    group = ~Year),
         crosstalk::filter_select("id_date", 
                                  label = "Date",
                                  sharedall, 
                                  group = ~date)
       ),
       chartyplot)

Now we can correctly (de)select the historic and recent data.

enter image description here

2 "Lost" Data points

If the previous example had worked with filter selection only based on "historic" data, then the "recent" data (from a different year) appears to not be recoverable after the first filter is selected. Similarly for the "most_recent" data point, which is not a SharedData object. This data point is plotted initially, but is dropped as soon as the first filter is set.

However, this can be adressed the same way, by defining a "most recent" SharedData object belonging to the same group:

sharedmostrecent <- SharedData$new(all %>% tail(1), key = ~key, group = "mydata")

#plot adjustment
...
geom_line(...) +
geom_point(data=sharedmostrecent, aes(x=time,y=cumvol), color='#E15759',size=3) +
...

enter image description here

With this, we can select and deselect all data in the plot, without loosing any.

3 Fix some data

In order to ensure the "recent" data to stick around regardless of (manual filter values), we can manipulate the HTML output. First we separate the datasets by not filtering by year, but by recent/historic & year (or any other suitable subsets of the data):

all <- bind_rows(historic, recent) %>%
  dplyr::mutate(key = as.character(row_number()),
                dataset = paste0(set, " ", Year))
...
out <- bscols(widths = c(4, 8),
       list(
         crosstalk::filter_checkbox("id_year", 
                                    label = "Select dataset",
                                    sharedall, 
                                    group = ~dataset)
       ),
       chartyplot) 

Then we check and disable the checkbox for our recent dataset - this can probably be done more elegantly, but it works in my Rmd:

library(htmltools)
out_tags <- htmltools::renderTags(out)
out_tags$html <- stringr::str_replace(
  out_tags$html, 
  '<input type="checkbox" name="id_year" value="recent 2022"/>',
  '<input type="checkbox" name="id_year" value="recent 2022" disabled="disabled" checked="checked"/>'
  )
out_tags$html <- HTML(out_tags$html)
as.tags(out_tags)

enter image description here

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