Updating multiple data attributes with plotly - r

I am trying to update multiple data attributes in a figure generated via plotly.
This is my script based on this post:
library(plotly)
library(magrittr)
library(data.table)
X <- data.table(x = c(1, 2, 1, 2), y = c(1, 1, 2, 2), z = rep(c("a", "b"), each = 2))
gg <- ggplot() + geom_point(data = X, aes(x = x, y = y, color = z))
ggplotly(gg) %>%
layout(
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list(
list(x = list(X[z == "a", x])),
list(y = list(X[z == "a", y]))
),
label = "a"),
list(method = "restyle",
args = list(
list(x = list(X[z == "b", x])),
list(y = list(X[z == "b", y]))
),
label = "b")
)
)
)
)
However, as can be seen, the update menu does not work as intended. Not sure what the problem is.

There are too many lists in your above args parameter.
Furthermore, when calling restyle without specifying the trace number you are restyling all traces.
If you want to restyle a single trace you need to provide its trace index:
library(plotly)
library(magrittr)
library(data.table)
DT <- data.table(x = c(1, 2, 10, 20), y = c(1, 1, 20, 20), z = rep(c("a", "b"), each = 2))
gg <- ggplot() + geom_point(data = DT, aes(x = x, y = y, color = z))
ggplotly(gg) %>%
layout(
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list(list(x = DT[z == "a", x], y = DT[z == "a", y]), 0L),
label = "a"),
list(method = "restyle",
args = list(list(x = DT[z == "b", x], y = DT[z == "b", y]), 1L),
label = "b")
)
)
)
)
I'm not sure what you are trying to achive.
If you want to show trace "a" when "a" is selected and trace "b" when "b" is selected this filter approach is problematic, as it completly removes traces (reduces the dataset) from the plot and once the trace is removed you can no longer restyle it.
If you just want to toggle the trace visibility you should use the visible parameter instead of modifying the data:
DT <- data.table(x = c(1, 2, 10, 20), y = c(1, 1, 20, 20), z = rep(c("a", "b"), each = 2))
gg <- ggplot() + geom_point(data = DT, aes(x = x, y = y, color = z))
ggplotly(gg) %>% style(visible = FALSE, traces = 2) %>%
layout(
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list(list(visible = c(TRUE, FALSE)), c(0, 1)),
label = "a"),
list(method = "restyle",
args = list(list(visible = c(FALSE, TRUE)), c(0, 1)),
label = "b")
)
)
)
)
PS: Plotly.restyle expects the trace count starting from 0 (JS) and style() from 1 (R)
Using addTraces/deleteTraces as done here would be another approach. However, if the data stays the same I guess toggling the visibility is less computationally intensive.

Related

Plotly update nested argument via updatemenus

I would like to create a dropdown menu using updatemenus from R Plotly to be able to change the source image in a plot.
This is what I have done so far, without success:
library(plotly)
library(magrittr)
img1 <- "https://www.gravatar.com/avatar/dc04a25fbabbffc1f1b7c6a3362bfae4?s=256&d=identicon&r=PG"
img2 <- "https://www.gravatar.com/avatar/bc680ed8d2555accacebc0fe2d8c9691?s=256&d=identicon&r=PG"
plot_ly(x = 1:5, y = 1:5, type = 'scatter', mode = 'markers') %>%
layout(
images = list(
list(
source = img1,
xref = "x",
yref = "y",
x = 3,
y = 4,
sizex = 2,
sizey = 2
)
),
updatemenus = list(
list(
y = 1,
buttons = list(
list(method = "restyle",
args = list(list(images = list(list(source = img1)))),
label = "img1"),
list(method = "restyle",
args = list(list(images = list(list(source = img2)))),
label = "img2")
)
)
)
)
What am I missing?
There is probably more than one way to do this, but this works. For layout updates, you're usually going to use the method relayout. You need to provide all of the information, as well (i.e., x, y, xref, etc.).
library(plotly)
img1 <- "https://www.gravatar.com/avatar/dc04a25fbabbffc1f1b7c6a3362bfae4?s=256&d=identicon&r=PG"
img2 <- "https://www.gravatar.com/avatar/bc680ed8d2555accacebc0fe2d8c9691?s=256&d=identicon&r=PG"
imgOne = list( # create image expectations
list(
source = img1,
xref = "x", yref = "y",
x = 3, y = 4,
sizex = 2, sizey = 2))
imgTwo = list( # create image expectations
list(
source = img2,
xref = "x", yref = "y",
x = 3, y = 4,
sizex = 2, sizey = 2))
plot_ly(x = 1:5, y = 1:5, type = 'scatter', mode = 'markers') %>%
layout(
images = imgOne,
updatemenus = list(
list(
y = 1,
active = 0,
buttons = list(
list(label = "None",
method = "relayout",
args = list(list(images = c()))),
list(label = "img1",
method = "relayout",
args = list(list(images = imgOne))),
list(label = "img2",
method = "relayout",
args = list(list(images = imgTwo)))
))))
There are a few method options for updatemenus. You can read more about that here.

Plotly multiple updatemenus

Building on this post, I am trying to create two updatemenus in R plotly that would allow to select every possible combination of two factors.
This is what I have done so far:
library(plotly)
X <- data.frame(x = 1:6,
y = 1:6,
z = 1:6,
gender = rep(c("M", "F"), each = 3),
eyes = rep(c("B", "G"), 3))
gg <- ggplot(data = X, aes(x = x, y = y)) +
geom_point(aes(color = z, alpha = interaction(gender, eyes))) +
scale_alpha_manual(values = rep(1, 4))
ggplotly(gg) %>%
layout(
updatemenus = list(
list(y = 1,
buttons = list(
list(method = "restyle",
args = list(list(visible = c(TRUE, TRUE, TRUE, TRUE)), 0:3),
label = "F&M"),
list(method = "restyle",
args = list(list(visible = c(TRUE, TRUE)), c(0, 2)),
label = "F"),
list(method = "restyle",
args = list(list(visible = c(TRUE, TRUE)), c(1, 3)),
label = "M"))),
list(y = .8,
buttons = list(
list(method = "restyle",
args = list(list(visible = c(TRUE, TRUE, TRUE, TRUE)), 0:3),
label = "B&G"),
list(method = "restyle",
args = list(list(visible = c(TRUE, TRUE)), 0:1),
label = "B"),
list(method = "restyle",
args = list(list(visible = c(TRUE, TRUE)), 2:3),
label = "G")))
)
)
)
The data:
# x y z gender eyes
# 1 1 1 M B
# 2 2 2 M G
# 3 3 3 M B
# 4 4 4 F G
# 5 5 5 F B
# 6 6 6 F G
This is the output, which does not work at all:
Edit
For instance, if F and B are selected, I would expect only a single data point to be displayed, namely (5, 5).
When you created this plotly object, plotly divided it into five traces. So when you wanted to use visibility, you needed to address each of these traces. If you have any questions, let me know!
I used gg1 to find five traces and what was in each of these traces. You could look at gg1 in the source pane or plotly_json().
Typically (not always!), you'll find the traces at gg1$x$data where gg1 is the plotly object.
There are five traces. For each args, each trace needs a T or F for visibility.
The first trace only contains (5, 5), that's F B
The second trace contains (1, 1) and (3, 3); those are both M B
The third trace contains (4, 4) and (6, 6); those are both F G
The fourth trace contains (2, 2); that's M G
The fifth trace contains the color gradient that all traces use
Here's the code:
gg1 = plotly_build(gg)
ggplotly(gg) %>%
layout(
updatemenus = list(
list(y = 1,
buttons = list(
list(method = "restyle",
args = list("visible", as.list(unlist(rep(T, 5)))),
label = "F&M"),
list(method = "restyle",
args = list("visible", list(T, F, T, F, T)),
label = "F"),
list(method = "restyle",
args = list("visible", list(F, T, F, T, T)),
label = "M")
) # end button
), # end first button list
list(y = .8,
buttons = list(
list(method = "restyle",
args = list("visible", as.list(unlist(rep(T, 5)))),
label = "B&G"),
list(method = "restyle",
args = list("visible", list(T, T, F, F, T)),
label = "B"),
list(method = "restyle",
args = list("visible", list(F, F, T, T, T)),
label = "G")
) # end button
) # end second menu list
) # end updatemenus
) # end layout
Update
From our comments, I do not believe that it's possible at this time to use updatemenus style buttons that interact with each other.

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