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.
Related
this is my dataframe :
artists <- c("Black Waitress", "Black Pumas")
tee_x<- c(20, 0)
tee_y <- c(3, 18)
tee_z <- c (30,0)
tee_t <- c(0,35)
data2 <- data.frame(artists, tee_x, tee_y,tee_t)
And this is what I am trying to create :
fig <- plot_ly(data=data2, x = ~artists, y = ~tee_x, type = 'bar', name = 'tee_x')
fig <- fig %>% add_trace(y = ~tee_y, name = 'tee_y')
fig <- fig %>% add_trace(y = ~tee_t, name = 'tee_t')
fig <- fig %>% layout(yaxis = list(title = 'Count'), barmode = 'group',
updatemenus = list(
list(
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("x", list(data2[c(1),(2:4)])),
label = "Black Waitress"),
list(method = "restyle",
args = list("x", list(data2[c(2),(2:4)])),
label = "Black Pumas")))
))
fig
I am trying to create a grouper barplot in plotly which shows, for each artist the number of tees they sold and their type. I am also trying to create buttons so that you can look at individual artists instead of both of them. However it is not working and I have no clue how to solve the problem.
Thank you
EDIT :
I have been also trying this way
product <- c("tee_X","tee_y","tee_t")
artists <- c("Black Waitress", "Black Pumas")
Black_Waitress<- c(20, 0, 0)
Black_Pumas <- c(3, 18, 0)
tee_z <- c (30,0)
tee_t <- c(0,35)
data2 <- data.frame(product, Black_Waitress, Black_Pumas)
show_vec = c()
for (i in 1:length(artists)){
show_vec = c(show_vec,FALSE)
}
get_menu_list <- function(artists){
n_names = length(artists)
buttons = vector("list",n_names)
for(i in seq_along(buttons)){
show_vec[i] = TRUE
buttons[i] = list(list(method = "restyle",
args = list("visible", show_vec),
label = artists[i]))
print(list(show_vec))
show_vec[i] = FALSE
}
return_list = list(
list(
type = 'dropdown',
active = 0,
buttons = buttons
)
)
return(return_list)
}
print(get_menu_list(artists))
fig <- plot_ly(data=data2, x = ~product, y = ~Black_Waitress, type = 'bar')
fig <- fig %>% add_trace(y = ~Black_Pumas)
fig <- fig %>% layout(showlegend = F,yaxis = list(title = 'Count'), barmode = 'group',
updatemenus = get_menu_list(artists))
fig
However the problem is that when I choose an artist in the dropdown menu I want to be shown ONLY his/her products (in other words I would like to get rid of the 0 values dynamically) Is this possible?
Without the 0 Values and Initially Blank Plot
Essentially, you need to add a trace with no data. Additionally, since visibility settings were defined, all of that requires updating (because there are more traces now).
This can be further customized, of course. Here's a basic version of what I think you're looking for.
plot_ly(data = data3, x = ~artists, y = 0, type = "bar", color = ~tees,
visible = c(T, T, T)) %>%
add_bars(y = ~values, split = ~artists, # visibility F for all here
legendgroup = ~tees, name = ~tees, visible = rep(F, times = 4),
color = ~tees) %>%
layout(
yaxis = list(title = "Count"), barmode = "group",
updatemenus = list(
list(y = .8,
buttons = list(
list(method = "restyle", # there are 7 traces now; 3 blank
args = list(list(visible = c(F, F, F, F, T, F, T))),
label = "Black Waitress"),
list(method = "restyle",
args = list(list(visible = c(F, F, F, T, F, T, F))),
label = "Black Pumas")))))
Without the 0 Values
By your request, here is a version where the zero values are removed. First, I filtered the data for the non-zero values. This changed the number of traces from 6 to 4, so that needed to be accounted for in the areas where visibility is declared.
In this version, I only commented where there was something that changed from my original answer.
library(plotly)
library(tidyverse)
artists <- c("Black Waitress", "Black Pumas")
tee_x <- c(20, 0)
tee_y <- c(3, 18)
# tee_z <- c(30,0)
tee_t <- c(0,35)
data2 <- data.frame(artists, tee_x, tee_y, tee_t)
data3 <- pivot_longer(data2, col = starts_with("tee"),
names_to = "tees", values_to = "values") %>%
filter(values != 0) # <----- filter for non-zeros
plot_ly(data = data3, x = ~artists, y = ~values, split = ~artists,
legendgroup = ~tees, name = ~tees,
visible = rep(c(F, T), times = 2), # <---- 4 traces
color = ~tees, type = "bar") %>%
layout(
yaxis = list(title = "Count"), barmode = "group",
updatemenus = list(
list(y = .8,
buttons = list(
list(method = "restyle", # 4 traces
args = list(list(visible = c(F, T, F, T))),
label = "Black Waitress"),
list(method = "restyle", # 4 traces
args = list(list(visible = c(T, F, T, F))),
label = "Black Pumas")))))
With the 0 Values
I think it will be a lot easier to use visibility than trying to change out the data. If you wanted to see one artist at a time and use the dropdown to switch between the groups, this works.
First, I rearranged the data to make this easier. When I plotted it, I used split, so that the traces were split by the values on the x-axis, along with the colors. The traces are ordered artist 1, tee_t, artist 2, tee_t... and so on. When using visibility, you need the method restyle (because it's a trace attribute) and a declaration of true or false for each trace.
library(tidyverse)
library(plotly)
data3 <- pivot_longer(data2, col = starts_with("tee"),
names_to = "tees", values_to = "values")
plot_ly(data = data3, x = ~artists, y = ~values, split = ~artists,
legendgroup = ~tees, name = ~tees, visible = rep(c(F, T), times = 3),
color = ~tees, type = "bar") %>%
layout(
yaxis = list(title = "Count"), barmode = "group",
updatemenus = list(
list(y = .8,
buttons = list(
list(method = "restyle",
args = list(list(visible = c(F, T, F, T, F, T))),
label = "Black Waitress"),
list(method = "restyle",
args = list(list(visible = c(T, F, T, F, T, F))),
label = "Black Pumas")))))
I am working with the R programming language. I am trying to replicate the following tutorial with some fake data that I generated: https://plotly.com/r/dropdowns/.
That is, I generated some fake data and made 4 scatter plots. Using the "plotly" library, I then want to "attach" these 4 plots together and let the user "toggle" (switch, shuffle) between these graphs.
I have attached the code below:
#load libraries
library(plotly)
library(MASS)
library(dplyr)
# create data
x <- sample( LETTERS[1:4], 731, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25) )
y <- rnorm(731,10,10)
z <- rnorm(731,5,5)
df <- data.frame(x,y, z)
df$x = as.factor(df$x)
fig = plot_ly()
fig = fig %>% add_trace( data = df[which(df$x == "A"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph1") %>% layout(title = list(text = "graph 1"))
fig = fig %>% add_trace( data = df[which(df$x == "B"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph2") %>% layout(title = list(text = "graph 2"))
fig = fig %>% add_trace( data = df[which(df$x == "C"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph2") %>% layout(title = list(text = "graph 3"))
fig = fig %>% add_trace( data = df[which(df$x == "D"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph2") %>% layout(title = list(text = "graph 4"))
final = fig %>% layout(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(FALSE, FALSE, TRUE, FALSE)),
label = "A"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, TRUE, FALSE)),
label = "B"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, TRUE, FALSE)),
label = "C"),
list(method = "restyle",
args = list("visible", list(FALSE, FALSE, FALSE, TRUE)),
label = "D")))))
#view final file
final
#save
library(htmlwidgets)
saveWidget( final, "file.html")
The code seems to run successfully. However, there seems to be a few problems.
Problem 1) When the user "views" the plotly object for the first time:
#view final file
final
Even though the drop down menu is selected as "A", observations corresponding to all 4 groups (4 different colors) all appear on the graph. Also, the title "graph 4" appears instead of "graph 1".
Problem 2) When you try to toggle between all 4 graphs:
None of the titles change at all. Also, the first three graphs appear to be identical.
Can someone please show me what am I doing wrong? Is there a way to fix this?
Thanks
I think this tutorial from plotly explains some of the issues in your code. Probably worth having a look!
The title is an attribute of the whole plot and not of each individual trace of the plot. So when you are setting layout(title='graph #') when you add each trace to the plot, you are setting it for the whole plot and overriding the value set in the previous trace. Since the last one you set is layout(title='graph 4'), this is the one you see when the plot is created.
You need to set the initial value of the visible attribute when you create each trace, so when the plot is created you see only one trace. In this case, when you create the traces, you need to set visible=TRUE for A and visible=FALSE for the rest.
Because you want to update the title of the plot, you cannot use the restyle method (take a look at the tutorial link above). You have to use the update method, which is used to change both the attributes of each trace and also to change the layout of the plot. With the update method, the value of arg is a list of lists. The first element of the main list is a list with the named values for updating the traces (just as you did before with restyle). The second element of the main list is another list with the named values for changing the layout.
Here is my code.
fig = plot_ly() %>%
# for each trace set the initial visibility
add_trace(data = df[which(df$x == "A"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph1", visible=T) %>%
add_trace(data = df[which(df$x == "B"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph2", visible=F) %>%
add_trace(data = df[which(df$x == "C"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph3", visible=F) %>%
add_trace(data = df[which(df$x == "D"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph4", visible=F) %>%
# set initial value of title in the layout
layout(title="Graph 1",
xaxis = list(domain = c(0.1, 1)),
yaxis = list(title = "y"),
updatemenus = list(
list(y = 0.7,
buttons = list(
list(method = "update",
args = list(list(visible =c(TRUE, FALSE, FALSE, FALSE)),
list(title='Graph 1')),
label = "A"),
list(method = "update",
args = list(list(visible =c(FALSE, TRUE, FALSE, FALSE)),
list(title='Graph 2')),
label = "B"),
list(method = "update",
args = list(list(visible =c(FALSE, FALSE, TRUE, FALSE)),
list(title='Graph 3')),
label = "C"),
list(method = "update",
args = list(list(visible =c(FALSE, FALSE, FALSE, TRUE)),
list(title='Graph 4')),
label = "D")))))
#view final file
fig
There were other issues that iI fixed like all traces had the same name ('graph2') and the value of visible was always list(FALSE, FALSE, TRUE, FALSE) (which would result in always the same trace being shown). But I believe these were typos.
I've created a graph that lets you pick which group's data to plot. I'd like to change the title when you pick the group, but I'm not sure how or if its possible. I'm having trouble learning which way to structure lists for certain plotly parameters. Even if I could add custom text to graph would probably work.
#Working Example so Far
library(plotly)
x <- c(1:100)
random_y <- rnorm(100, mean = 0)
random_y_prim <- rnorm(100, mean = 50)
mydata <- data.frame(x, random_y, random_y_prim, group = rep(letters[1:4], 25))
# Make Group List Button
groupList <- unique(mydata$group)
groupLoop <- list()
for (iter in 1:length(groupList)) {
groupLoop[[iter]] <- list(method = "restyle",
args = list("transforms[0].value", groupList[iter]),
label = groupList[iter])
}
# Set up Axis labeling
f <- list(
family = "Verdana",
size = 18,
color = "#7f7f7f"
)
xLab <- list(
title = "x Axis",
titlefont = f
)
yLab <- list(
title = "y Axis",
titlefont = f
)
fig <- plot_ly(mydata, x = ~x, y = ~random_y
, type = 'scatter', mode = 'lines',
transforms = list(
list(
type = 'filter',
target = ~mydata$group,
operation = '=',
value = groupList[1]
)
)
)
fig <- fig %>%
layout(
title = "Updating Practice",
xaxis = xLab,
yaxis = yLab,
updatemenus = list(
list(
type = 'dropdown',xanchor = 'center',
yanchor = "top",
active = 1,
buttons = groupLoop
)
)
)
fig
I am trying to map color over two variables using Restyle Buttons of plotly keeping the y and x-axis dynamics when changing the colours. When I add color=~fruit in the main plot it gives the result I am looking for, but I lose the dynamics of the axes when changing the variables. I basically want to change the color of the lines referent to the fruits. Below are data and the code I am using for playing with that. Thanks for any help or hints!
libraries
library(dplyr); library(plotly);
data
dfake <- tibble(days = seq(1,100, by=1),
bask = seq(1,500, by=5),
fruit = c(rep("grape", 50),
rep("apple", 50)));
plotting code
plot <- dfake %>%
plot_ly(x = ~days, y = ~bask, text = ~fruit,
type = 'scatter',
mode = 'lines',
hoverinfo = 'text',
transforms = list(
list(type = 'filter',
target = ~fruit,
operation = '=',
value = unique(dfake$fruit)[1]))) %>%
layout(updatemenus = list(
list(type = 'dropdown',
active = 1,
buttons = list(
list(method = "restyle",
args = list("transforms[0].value",
unique(dfake$fruit)[1]),
label = unique(dfake$fruit)[1]),
list(method = "restyle",
args = list("transforms[0].value",
unique(dfake$fruit)[2]),
label = unique(dfake$fruit)[2])))));
plot;
Yes, wasn't sure of the data was in the best possible format. So, I was fiddling with this in the following manner:
Make a dataframe, where each Y-axis variable goes into individual column (refer to the tidyverse philosopy).
Appending the lines layer-by-layer into plotly.
Using updatemenus to get the interactive buttons & desired visibility.
#Converting into a dataframe, mutating new columns for each fruit and getting their name:
df_dfake <- as.data.frame(dfake)
df_dfake <- df_dfake %>% mutate(fruit1_bask = case_when(fruit == "grape" ~ bask),
fruit2_bask = case_when(fruit == "apple" ~ bask))
fruit1 <- unique(dfake$fruit)[1]; fruit2 <- unique(dfake$fruit)[2];
#Plotly, adding layer by layer:
fig <- df_dfake %>% plot_ly(type = 'scatter',
mode = 'lines',
hoverinfo = 'text');
fig1 <- fig %>% add_lines(x = ~days , y = ~fruit1_bask, text = ~fruit,
line=list(color="#33CFA5"));
fig2 <- fig1 %>% add_lines(x = ~days, y = ~fruit2_bask, text = ~fruit,
line=list(color="#F06A6A"));
fig2;
fig2
Now, updatemenus component, to make the interactive buttons
updatemenus <- list(
list(
active = -1,
type= 'buttons',
buttons = list(
list(
label = unique(dfake$fruit)[1],
method = "update",
args = list(list(visible = c(FALSE, TRUE)),# this defines visibility on click
list(title = "fruit1",
annotations = list(c(), df_dfake$fruit1_bask)))),
list(
label = unique(dfake$fruit)[2],
method = "update",
args = list(list(visible = c(T, F)),# this defines visibility on click
list(title = "fruit2",
annotations = list(c(), df_dfake$fruit2_bask))))
)
)
)
fig3 <- fig2 %>% layout(title = "Apples & Oranges", showlegend=FALSE,
xaxis=list(title="Days"),
yaxis=list(title="Basket"),
updatemenus=updatemenus); fig
Which results in the following graphs with interactive buttons:
fig3
Check Update Button to learn more :)
I finally got what I was looking for,
very easy actually, I just needed to have my grape and apple not inside column fruit but as different columns and treat them with add_trace. Setting one as invisible. In each add_trace I was free to play with color, width etc.
After this organisation it was easier to work with button .
I hope this simple coding can help someone. If not sorry.
dfake <- tibble(days = seq(1,100, by=1),
grape = seq(1,500, by=5),
apple = seq(501,1000, by=5))
fig <- plot_ly(dfake, x = ~days) %>%
add_trace(y = ~grape, name = 'Bask',
mode = 'lines+markers', type = "scatter",
marker = list(color = 'blue'),
line = list(color = 'blue', width = 4)) %>%
add_trace(y = ~apple, name = 'New', visible = F,
mode = 'lines+markers', type = "scatter",
marker = list(color = 'red'),
line = list(color = 'red', width = 4)) %>%
layout(
title = "Corona global Cases and Depths",
xaxis = list(domain = c(0.1)),
yaxis = list(title = "yaxis"),
updatemenus = list(
list(y = 0.9,
buttons = list(
list(method = "restyle",
args = list("visible", list(TRUE, FALSE)),
label = "Grape"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE)),
label = "Apple")))))
fig
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")))
)