Managing multiple Times Series with Crosstalk and plotly - r

I am trying to have a dynamic graph using plotly with crosstalk to show multiple times series. Depending on if a checkbox if checked or not, I would like the time series to be display on the graph or not.
In the code below, I created two times series 1 and 2 in the same dataframe (df_final).
I have two issues so far:
When none of the checkbox is checked, there is still a time series on
the graph
When both of the checkboxes are checked, instead of showing two times
series, the code considers that only 1 unique time series.
I don’t know how to improve any of the above, any ideas?
Also, I am not sure I am using the right tools (crosstalk, etc). Any other suggestions are welcomed.
library(plotly)
library(crosstalk)
#Create two identical dataframe
df_1 <- economics
df_2 <- economics
#Add a key to each dataframe to dissociate the two time series
df_1$ts <- 1
df_2$ts <- 2
#Modify the first dataframe
df_2$psavert <- economics$psavert + 1
df_final <- rbind(df_1, df_2)
shared_df_final <- SharedData$new(df_final)
bscols(
list(filter_checkbox("Time series", "Time series", shared_df_final, ~ts, inline = TRUE)),
plot_ly(data = shared_df_final, x = ~date, height = 700) %>%
add_lines(y = ~df_final$psavert, line = list(color = "#00526d", width = 1),
hoverinfo = "text", text = "ts")
)

Are you looking for something like that instead:
plot_ly() %>%
add_lines(data = df_1, x= ~date, y = ~psavert, name = "First") %>%
add_lines(data = df_2, x= ~date, y = ~psavert, name = "Second") %>%
layout(
updatemenus = list(
list(
y = 0.8,
type= 'buttons',
buttons = list(
list(method = "restyle",
args = list("visible", list(TRUE, TRUE)),
label = "Both"),
list(method = "restyle",
args = list("visible", list(TRUE, FALSE)),
label = "First"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE)),
label = "Second")))
)
)

Related

Is there a reason my plotly dropdown menu doesn't update data appropriately?

I'm working with plotly for the first time to create an interactive chart that can plot any variable from the R tidykids package as a time series grouped by state. Here's my code to format the data frame object as I have it in my plotly chain.
kids <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-09-15/kids.csv')
kids_my_variables <- kids %>% pivot_wider(id_cols = c('state','year') , names_from = 'variable' , values_from = 'inf_adj') %>% mutate(fips = fips(state), total = rowSums(.[3:25], na.rm = TRUE), education_agg = PK12ed + highered + edsubs + edservs + pell + HeadStartPriv, education_pcnt = (round(education_agg / total, 2)*100) , lib_pcnt = ((lib / total)*100) , park_pcnt = (round(parkrec / total,2))*100) %>% select(fips, state, year, education = education_pcnt, libraries = lib_pcnt, `parks & rec` = park_pcnt, total) %>% filter(year != 1997)
Here's my plotly chain.
plot_ly(kids_my_variables, x = ~year) %>%
add_lines(y = ~education, color = ~state) %>%
add_lines(y = ~`parks & rec`, color = ~state, visible = FALSE) %>%
layout(
title = "% of total child spending",
showlegend = TRUE,
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list(list(visible = list(TRUE, FALSE)),
list(yaxis = list(title = "Education"))),
label = "Education"),
list(method = "restyle",
args = list(list(visible = list(FALSE, TRUE)),
list(yaxis = list(title = "Parks & Rec"))),
label = "Parks & Rec")))
)
)
When I use the dropdown menu to change my plotted data, it retains data from the original plot afterwards. The range of the y-axis for education s/b from 25-69. Parks and rec on the other hand s/ from 1-7, but the using the dropdown filter to update the plot to parks and rec give me this.
link to image cause I'm not cool enough to embed yet
It's a quirk in plotly that it expects you to set visible to TRUE or FALSE for each level of your grouping variable (i.e. state).
Concatenating two rep()s (one for true, one for false) for the number of states seems to be the only workaround I can find for now.
You also need to use method = "update" instead of restyle as you're changing both data and layout (y-axis title).
This should work:
plot_ly(kids_my_variables, x = ~year) %>%
add_lines(y = ~education, color = ~state) %>%
add_lines(y = ~`parks & rec`, color = ~state, visible = FALSE) %>%
layout(
title = "% of total child spending",
showlegend = TRUE,
updatemenus = list(
list(
buttons = list(
list(method = "update",
args = list(list(visible = c(rep(TRUE, length(unique(kids_my_variables$state))), rep(FALSE, length(unique(kids_my_variables$state))))),
list(yaxis = list(title = "Education"))),
label = "Education"),
list(method = "update",
args = list(list(visible = c(rep(FALSE, length(unique(kids_my_variables$state))), rep(TRUE, length(unique(kids_my_variables$state))))),
list(yaxis = list(title = "Parks & Rec"))),
label = "Parks & Rec")))
)
)

Menu or buttons to select by which variable style the plot in R plotly

I would like to produce a plotly graph in which it is possible to click on a button to select which variable is used to color the markers.
Now, the following example produces a plotly plot with two buttons, except that they don't work:
df <- data.frame(
x=rnorm(10),
y=rnorm(10))
group1 <- sample(c("A", "B"), 10, replace=TRUE)
group2 <- sample(c("X", "Y"), 10, replace=TRUE)
p <- plot_ly(data=df, type="scatter", mode="markers", x=~ x, y=~ y)
p %>% layout(
updatemenus=list(list(
type="buttons",
y=.8,
buttons=list(
list(method="restyle",
args=list("color", group1),
label="Group 1"),
list(method="restyle",
args=list("color", group2),
label="Group 2")
)
)))
What I could probably do is to add different markers with different colors and use buttons to toggle their visibility only. However, I already use that trick for showing different data sets on the same plot and adding each data set with all the different options would unnecessarily duplicate the data.
Is there a simpler solution?
It took me some time to figure it out, but I think your color argument is wrong. Instead you should use marker.color. Also the group variable should contain colours (like "red" or RGB) ... When I tried to use a color variable with multiple colors, only one of them was displayed. So I think I can only give you some hints here ... maybe I find some more time to experiment later :)
dat <- dplyr::tibble(X=rnorm(10),
Y=rnorm(10))
plotly::plot_ly() %>%
plotly::add_trace(data=dat,
x=~X,
y=~Y,
type="scatter",
mode="markers",
color="yellow") %>%
plotly::layout(
updatemenus=list(
list(
type = "buttons",
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("marker.color","blue"),
label = "Blue"),
list(method = "restyle",
args = list("marker.color","red"),
label = "Red")))))
EDIT: I found some time to experiment a little more and think I found a not-so-hacky solution adding the color as list
"%>%" <- magrittr::"%>%"
dat <- dplyr::tibble(X=rnorm(10),
Y=rnorm(10),
COL1=rep(c("1","2"),5),
COL2=c(rep("1",8),rep("2",2)))
plotly::plot_ly() %>%
plotly::add_trace(data=dat,
x=~X,
y=~Y,
type="scatter",
mode="markers") %>%
plotly::layout(
updatemenus=list(
list(
type = "buttons",
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("marker.color",list(~COL1)),
label = "COL1"),
list(method = "restyle",
args = list("marker.color",list(~COL2)),
label = "COL2")))))

Plotly - Changing dropdowns disables groupings for boxplot

I'm creating a boxplot with dropdown options for the variable that gets plotted on the y axis. The data has 4 "people", and each observation has a type A or B. For each person 1:4, the chart plots bars for the observations A and B (example: https://plot.ly/r/box-plots/#grouped-box-plots).
I'm able to create it, but once I change the dropdown the groupings all get messed up. Here's example code:
library(plotly)
set.seed(123)
x <- rep(1:4, 6)
y1 <- rnorm(24, 5, 2)
y2 <- rnorm(24, 2, 5)
type <- rep(c("A", "A", "B", "B", "B", "B", "A", "A"), 3)
df <- data.frame(x, y1, y2, type)
p <- plot_ly(df, x = ~x) %>%
add_boxplot(y = ~y1, color = ~type, name = "First") %>%
add_boxplot(y = ~y2, color = ~type, name = "Second", visible = F) %>%
layout(
boxmode = "group",
title = "On/Off Box Plot",
xaxis = list(domain = c(0.1, 1)),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("visible", list(TRUE, FALSE)),
label = "y1"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE)),
label = "y2")))
)
)
p
The dropdown starts with y1 and looks exactly how I'd like, but changing the dropdown regroups the data and changing back to y1 doesn't get back to the original graph.
Here's what's happening to the groupings: After changing the dropdown, y1 groups all type "A" with y1 and y2 plotted next to each other. Option y2 does the same but uses type "B" data. I only want y1 data for both types A/B (like the original graph).
I'm guessing the ' boxmode = "group" ' line is getting lost during the switches, but I can't get it to work. Does anyone know how to maintain the groupings based on type? Thank you.
updatemenus should be length 4, because there are actually 4 traces (2 visible at a time). Note that for groups, each legend item has a corresponding trace. Unfortunately R's automatic repeating of vectors obscures this.
ie, the value for updatemenus should be:
updatemenus = list(
list(
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("visible", list(TRUE, TRUE, FALSE, FALSE)),
label = "y1"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, TRUE, TRUE)),
label = "y2")))
)

R interactive graph - remove a factor level\element and have axes adapt

I want to be able to present an interactive graph in R, which presents lines/bars/whatever of a measure grouped by a certain factor, and be able to make one or more of the factor's levels (groups) disappear AND have the x and y axes adapt to that, be responsive to this choice.
Example:
df <- data.frame(factor1 = c(rep("a", 3), rep("b", 3), rep("c", 3)),
xAxisVar = c(1:7, 5, 9),
yAxisVar = c(1:7, 5, 25))
ggplot(df, aes(xAxisVar, yAxisVar, group = factor1, color = factor1)) +
geom_line() +
geom_point()
In this graph the y axis is extended to reach 25 because of 1 "large" observation in factor level "c". I want to be able to press on "c" or filter it out AND have the y axis respond to that, re-rendering the plot reaching 6 or so.
I have tried plotly's ggplotly, you can automatically make group "c" disappear, but not have the plot re-rendering to account for that. Suggestions?
You mentioned ggplotly, so if you are open to plotly, you could try:
library(plotly)
library(reshape2)
#from long to wide
df_wide <- dcast(df, xAxisVar ~ factor1, value.var="yAxisVar")
plot_ly(df_wide, x = ~xAxisVar, y = ~a, name='a', type='scatter', mode='lines+markers') %>%
add_trace(y = ~b, name = 'b', type='scatter', mode='lines+markers') %>%
add_trace(y = ~c, name = 'c', type='scatter', mode='lines+markers', connectgaps = TRUE) %>%
layout(
updatemenus = list(
list(
type = "buttons",
x = -0.1,
y = 0.7,
label = 'Category',
buttons = list(
list(method = "restyle",
args = list('visible', c(TRUE, FALSE, FALSE)),
label = "a"),
list(method = "restyle",
args = list('visible', c(FALSE, TRUE, FALSE)),
label = "b"),
list(method = "restyle",
args = list('visible', c(FALSE, FALSE, TRUE)),
label = "c")
)
)))

plotly: Updating data with dropdown selection

I am not sure if this is possible, but here is what I would like to do. I would like to update the data in a plotly plot by selecting from a dropdown menu.
As a simple example, let's assume I have a data frame
df <- data.frame(x = runif(200), y = runif(200), z = runif(200))
from which I use df$x and df$y in a scatter plot. Two scenarios of data manipulation I would like to achieve using a dropdown:
Replace df$y with df$z
Plot only the first n values of df$x and df$y
I looked at the following two examples, which I can easily reproduce:
https://plot.ly/r/dropdowns/
However, I have no idea how to pass the information regarding the data to be plotted based on the dropdown selection. For scenario 2 e.g. I have tried it with args = list("data", df[1:n,]) which did not work.
For scenario 1 the (only?) way to go (according to the examples) seems to be hiding/showing the traces respectively. Is that the only way for scenario 2 as well?
Any alternative ideas?
Update 1: Add reproducible example
So here is an example which achieve what I would like in scenario 1.
require(plotly)
df <- data.frame(x = runif(200), y = runif(200), z = runif(200))
Sys.setenv("plotly_username"="xxx") #actual credentials replaced
Sys.setenv("plotly_api_key"="xxx") #actual credentials replaced
p <- plot_ly(df, x = df$x, y = df$y, mode = "markers", name = "A", visible = T) %>%
add_trace(mode = "markers", y = df$z, name = "B", visible = T) %>%
layout(
title = "Drop down menus - Styling",
xaxis = list(domain = c(0.1, 1)),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
buttons = list(
list(method = "restyle",
args = list("visible", list(TRUE, TRUE)),
label = "Show All"),
list(method = "restyle",
args = list("visible", list(TRUE, FALSE)),
label = "Show A"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE)),
label = "Show B")))
))
plotly_POST(p)
Result here: https://plot.ly/~spietrzyk/96/drop-down-menus-styling/
This is based on the example from https://plot.ly/r/dropdowns/
However, I am wondering if one could pass the data to be plotted instead of triggering changes to the visible property of individual traces.
The one thing I tried was the following:
p <- plot_ly(df, x = df$x, y = df$y, mode = "markers", name = "A", visible = T) %>%
layout(
title = "Drop down menus - Styling",
xaxis = list(domain = c(0.1, 1)),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
buttons = list(
list(method = "restyle",
args = list("y", df$y),
label = "Show A"),
list(method = "restyle",
args = list("y", df$z),
label = "Show B")))
))
Result here: https://plot.ly/~spietrzyk/98/drop-down-menus-styling/
This approach cannot work, as the data from df$z is not posted to the grid (https://plot.ly/~spietrzyk/99/).
So I was wondering is there anyway to manipulate the data to be plotted based on dropdown selection, beyond plotting all traces and than switching the visible property by dropdown selections.
Is this what you were after?
require(plotly)
df <- data.frame(x = runif(200), y = runif(200), z = runif(200))
p <- plot_ly(df, x = ~x, y = ~y, mode = "markers", name = "A", visible = T) %>%
layout(
title = "Drop down menus - Styling",
xaxis = list(domain = c(0.1, 1)),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
buttons = list(
list(method = "restyle",
args = list("y", list(df$y)), # put it in a list
label = "Show A"),
list(method = "restyle",
args = list("y", list(df$z)), # put it in a list
label = "Show B")))
))
p
On the top of #jimmy G's answer.
You can automatically create the buttons so that you don't have to manually specify every single variable you want in the plot.
library(plotly)
df <- data.frame(x = runif(200), y = runif(200), z = runif(200), j = runif(200), k = rep(0.7, 200), i = rnorm(200,0.6,0.05))
create_buttons <- function(df, y_axis_var_names) {
lapply(
y_axis_var_names,
FUN = function(var_name, df) {
button <- list(
method = 'restyle',
args = list('y', list(df[, var_name])),
label = sprintf('Show %s', var_name)
)
},
df
)
}
y_axis_var_names <- c('y', 'z', 'j', 'k', 'i')
p <- plot_ly(df, x = ~x, y = ~y, mode = "markers", name = "A", visible = T) %>%
layout(
title = "Drop down menus - Styling",
xaxis = list(domain = c(0.1, 1)),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
buttons = create_buttons(df, y_axis_var_names)
)
))
p
Hope you find it useful.

Resources