I am working with the R programming language. I am trying to replicate this tutorial over here for my own data: https://plotly.com/r/dropdowns/
I created some fake data and made 4 plots:
#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)
date= seq(as.Date("2014/1/1"), as.Date("2016/1/1"),by="day")
df <- data.frame(x,y, z, date)
df$x = as.factor(df$x)
# plot 1 : time series
aggregate = df %>%
mutate(date = as.Date(date)) %>%
group_by(month = format(date, "%Y-%m")) %>%
summarise( mean = mean(y))
ts_1 <- ggplot(aggregate) + geom_line(aes(x = month, y = mean, group = 1)) + theme(axis.text.x = element_text(angle = 90)) + ggtitle("time series 1")
plot_1 = ggplotly(ts_1)
#plot 2 : box plot
plot_2 <- plot_ly(df, y = ~y, color = ~x, type = "box") %>% layout(title = "boxplot")
#plot 3, 4 : scatter plots
df_1 <- df[which(df$x == "A"),]
df_2 <- df[which(df$x == "B"),]
plot_3 <- plot_ly( data = df_1, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 3")
plot_4 <- plot_ly( data = df_2, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 4")
Once these 4 plots have been created, I know how to save them together:
sub = subplot(plot_1, plot_2, plot_3, plot_4, nrows = 2)
#view result
sub
Now what I am trying to do, is have the user "toggle" (switch) between these graphs (as seen here: https://plotly.com/r/dropdowns/)
In a previous post (R: Switching Between Graphs ), I learned how to "glue" similar graphs together (e.g. 4 scatter plots). Now, I am trying to do so with different graphs (2 scatter plots, 1 time series and 1 box plot). I tried to adapt the code from the previous post to suit my example:
fig <- df %>%
add_trace(name = "A", plot_1) %>%
add_trace (name = "B" , df, y = ~y, color = ~x, type = "box") %>% layout(title = "boxplot")
add_trace (name = "C" , data = df_1, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 3") %>%
add_trace( name = "D", data = df_2, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 4") %>%
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(TRUE, FALSE, FALSE, FALSE)),
label = "A"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE, FALSE, 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")))))
But this produces the following errors:
Error: $ operator is invalid for atomic vectors
Error in add_data(p, data) : argument "p" is missing, with no default
Can someone please show me if it is possible to fix this problem? Instead of using the "add_trace" approach, is it somehow possible to individually call each plotly graph object by its name (e.g. subplot(plot_1, plot_2, plot_3, plot_4, nrows = 2)), "glue" all the graphs together, and then add a "toggle button" that lets the user switch between them?
(note: I need to be able to save the final result as a "html" file)
Thanks
First of all, you should take care about plots which add multiple traces (see nTracesA etc.)
Besides changing the trace visibility you'll need to seperate categorial and numerical data onto separate x and y-axes and manage their visibility, too (see xaxis2, xaxis3, xaxis4 - this also works with a single y-axis but in this case the grid isn't displayed properly)
As described in the docs:
The updatemenu method determines which plotly.js function will be used
to modify the chart. There are 4 possible methods:
"restyle": modify data or data attributes
"relayout": modify layout attributes
"update": modify data and layout attributes
"animate": start or pause an animation (only available offline)
Accordingly the following, is using the update method (a lot of repition here - needs some cleanup, but I think it's better to understand this way):
# load libraries
library(dplyr)
library(plotly)
# 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)
date <- seq(as.Date("2014/1/1"), as.Date("2016/1/1"), by = "day")
df <- data.frame(x, y, z, date)
df$x = as.factor(df$x)
nTracesA <- nTracesC <- nTracesD <- 1
nTracesB <- length(unique(df$x))
plotA <- plot_ly(data = df %>%
mutate(date = as.Date(date)) %>%
group_by(month = format(date, "%Y-%m")) %>%
summarise(mean = mean(y)),
type = 'scatter', mode = 'lines', x= ~ month, y= ~ mean, name = "plotA", visible = TRUE, xaxis = "x", yaxis = "y")
plotAB <- add_trace(plotA, data = df, x = ~x, y = ~y, color = ~ x, name = ~ paste0("plotB_", x),
type = "box", xaxis = "x2", yaxis = "y2", visible = FALSE, inherit = FALSE)
plotABC <- add_trace(plotAB, data = df[which(df$x == "A"),],
type = "scatter", mode = "markers", x = ~ y, y = ~ z,
name = "plotC", xaxis = "x3", yaxis = "y3", visible = FALSE, inherit = FALSE)
plotABCD <- add_trace(plotABC, data = df[which(df$x == "B"),], x = ~ y, y = ~ z,
type = "scatter", mode = "markers", name = "plotD", xaxis = "x4", yaxis = "y4", visible = FALSE, inherit = FALSE)
fig <- layout(plotABCD, title = "Initial Title",
xaxis = list(domain = c(0.1, 1), visible = TRUE, type = "date"),
xaxis2 = list(overlaying = "x", visible = FALSE),
xaxis3 = list(overlaying = "x", visible = FALSE),
xaxis4 = list(overlaying = "x", visible = FALSE),
yaxis = list(title = "y"),
yaxis2 = list(overlaying = "y", visible = FALSE),
yaxis3 = list(overlaying = "y", visible = FALSE),
yaxis4 = list(overlaying = "y", visible = FALSE),
updatemenus = list(
list(
y = 0.7,
buttons = list(
list(label = "A",
method = "update",
args = list(list(name = paste0("new_trace_name_", 1:7), visible = unlist(Map(rep, x = c(TRUE, FALSE, FALSE, FALSE), each = c(nTracesA, nTracesB, nTracesC, nTracesD)))),
list(title = "title A",
xaxis = list(visible = TRUE),
xaxis2 = list(overlaying = "x", visible = FALSE),
xaxis3 = list(overlaying = "x", visible = FALSE),
xaxis4 = list(overlaying = "x", visible = FALSE),
yaxis = list(visible = TRUE),
yaxis2 = list(overlaying = "y", visible = FALSE),
yaxis3 = list(overlaying = "y", visible = FALSE),
yaxis4 = list(overlaying = "y", visible = FALSE)))
),
list(label = "B",
method = "update",
args = list(list(visible = unlist(Map(rep, x = c(FALSE, TRUE, FALSE, FALSE), each = c(nTracesA, nTracesB, nTracesC, nTracesD)))),
list(title = "title B",
xaxis = list(visible = FALSE),
xaxis2 = list(overlaying = "x", visible = TRUE),
xaxis3 = list(overlaying = "x", visible = FALSE),
xaxis4 = list(overlaying = "x", visible = FALSE),
yaxis = list(visible = FALSE),
yaxis2 = list(overlaying = "y", visible = TRUE),
yaxis3 = list(overlaying = "y", visible = FALSE),
yaxis4 = list(overlaying = "y", visible = FALSE)))),
list(label = "C",
method = "update",
args = list(list(visible = unlist(Map(rep, x = c(FALSE, FALSE, TRUE, FALSE), each = c(nTracesA, nTracesB, nTracesC, nTracesD)))),
list(title = "title C",
xaxis = list(visible = FALSE),
xaxis2 = list(overlaying = "x", visible = FALSE),
xaxis3 = list(overlaying = "x", visible = TRUE),
xaxis4 = list(overlaying = "x", visible = FALSE),
yaxis = list(visible = FALSE),
yaxis2 = list(overlaying = "y", visible = FALSE),
yaxis3 = list(overlaying = "y", visible = TRUE),
yaxis4 = list(overlaying = "y", visible = FALSE)))),
list(label = "D",
method = "update",
args = list(list(visible = unlist(Map(rep, x = c(FALSE, FALSE, FALSE, TRUE), each = c(nTracesA, nTracesB, nTracesC, nTracesD)))),
list(title = "title D",
xaxis = list(visible = FALSE),
xaxis2 = list(overlaying = "x", visible = FALSE),
xaxis3 = list(overlaying = "x", visible = FALSE),
xaxis4 = list(overlaying = "x", visible = TRUE),
yaxis = list(visible = FALSE),
yaxis2 = list(overlaying = "y", visible = FALSE),
yaxis3 = list(overlaying = "y", visible = FALSE),
yaxis4 = list(overlaying = "y", visible = TRUE))))
))))
print(fig)
# htmlwidgets::saveWidget(partial_bundle(fig), file = "fig.html", selfcontained = TRUE)
# utils::browseURL("fig.html")
Some related info:
https://plotly.com/r/custom-buttons/
https://plotly.com/r/multiple-axes/
This is just a guess from the documentation but there is no add_data() call so maybe try this for your first line:
fig <- plot_ly() %>% add_data(df) %>%
See docs example:
plot_ly() %>% add_data(economics) %>% add_trace(x = ~date, y = ~pce)
A user on the Rstudio community forum provided an answer : https://community.rstudio.com/t/gluing-graphs-together-switch-toggle-between-graphs-in-r-plotly/95891/3
I am still trying to figure out how to format the axis - maybe someone could take a look at this?
#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)
date= seq(as.Date("2014/1/1"), as.Date("2016/1/1"),by="day")
df <- data.frame(x,y, z, date)
df$x = as.factor(df$x)
# plot 1 : time series
aggregate = df %>%
mutate(date = as.Date(date)) %>%
group_by(month = format(date, "%Y-%m")) %>%
summarise( mean = mean(y))
ts_1 <- ggplot(aggregate) + geom_line(aes(x = month, y = mean, group = 1)) + theme(axis.text.x = element_text(angle = 90)) + ggtitle("time series 1")
plot_1 = ggplotly(ts_1)
#plot 2 : box plot
plot_2 <- plot_ly(df, y = ~y, color = ~x, type = "box") %>% layout(title = "boxplot")
#plot 3, 4 : scatter plots
df_1 <- df[which(df$x == "A"),]
df_2 <- df[which(df$x == "B"),]
plot_3 <- plot_ly( data = df_1, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 3")
plot_4 <- plot_ly( data = df_2, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 4")
fig = plot_ly()
fig = fig %>% add_trace(data = df %>%
mutate(date = as.Date(date)) %>%
group_by(month = format(date, "%Y-%m")) %>%
summarise( mean = mean(y)), type = 'scatter', mode = 'lines', x= ~month, y= ~mean,
name = "timeseries")
fig = fig %>% add_trace(data = df[which(df$x == "A"),], y = ~y, color = ~x,
type = "box", name = "boxplot")
fig = fig %>% add_trace( data = df[which(df$x == "B"),],
type = "scatter", mode = "markers", x = ~ y, y = ~z,
name= "graph2")
fig = fig %>% add_trace(data = df[which(df$x == "A"),], y = ~y, color = ~x,
type = "box", name = "boxplot2")
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(TRUE, FALSE, FALSE, FALSE)),
label = "A"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE, FALSE, 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")))))
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'm trying to make a dual axis plot of rainfall and temperature. I have ordered the months on the bottom, but that causes my line graph to screw up. How do I make sure the added line uses the same x axis?
temprain<-data.frame(month = c(1:12),
Train = c(250,220, 180,97,38,27,31,47,70,140,200,250),
Tair = c(17,16, 15,13,9,6,5,9,12,13,14,16))
tempseq<-seq(0,20,by=0.5)
rainseq<-seq(0,260,by=1)
xlab<-list(type = "category",
categoryorder = "array",
categoryarray = month.name,
showgrid = TRUE,
showline = TRUE,
autorange = TRUE,
showticklabels = TRUE,
ticks = "outside",
tickangle = 0
)
plot_ly(temprain) %>%
add_bars(x = ~MonthName, y = ~Train, type = "bar", name = "Rain") %>%
add_lines(x = ~MonthName, y = ~Tair, yaxis = "y2", name = "Temp") %>%
layout(xaxis = xlab,
yaxis = list(showline = TRUE, side = "left",
title = "Rainfall (mm)Temp", range = tempseq),
yaxis2 = list(showline = TRUE, side = "right",
overlaying = "y", title = "Air Temp (C)", range = rainseq),
showlegend = FALSE,
margin = list(pad = 0, b = 50, l = 50, r = 50))
I tried this as well, and it doesn't work, the temp graph disappears
plot_ly(temprain, x = ~MonthName, y = ~Tair, name = "Temp") %>%
add_bars(x = ~MonthName, y = ~Train, yaxis = "y2", type = "bar", name = "Rain") %>%
layout(xaxis = xlab,
yaxis = list(showline = TRUE, side = "left",
title = "Air Temp (C)", range = tempseq),
yaxis2 = list(showline = TRUE, side = "right",
overlaying = "y",
title = "Rainfall (mm)", range = rainseq),
showlegend = FALSE,
margin = list(pad = 0, b = 50, l = 50, r = 50))
Below is the solution:
Your data:
temprain<-data.frame(month = c(1:12),
Train = c(250,220, 180,97,38,27,31,47,70,140,200,250),
Tair = c(17,16, 15,13,9,6,5,9,12,13,14,16))
Generate a column for month abbreviations from month:
mymonths <- c("Jan","Feb","Mar",
"Apr","May","Jun",
"Jul","Aug","Sep",
"Oct","Nov","Dec")
# match the month numbers against abbreviations:
temprain$MonthAbb = mymonths[ temprain$month ]
# This is the code to archieving a consistent combined graph:
temprain$MonthAbb <- factor(temprain$MonthAbb, levels = c(as.character(temprain$MonthAbb)))
Now plot your data:
fig <- plot_ly(temprain)
# Add the Train trace:
fig <- fig %>% add_trace(x = ~MonthAbb, y = ~Train, name = "Train", type = "bar")
ay <- list(
tickfont = list(color = "red"),
overlaying = "y",
side = "right",
title = "<b>Tair</b>")
# Add the Tair trace:
fig <- fig %>% add_trace(x = ~MonthAbb, y = ~Tair, name = "Tair", yaxis = "y2", mode = "lines+markers", type = "scatter")
fig <- fig %>% layout(yaxis2 = ay,
xaxis = list(title="Month"),
yaxis = list(title="<b>Train</b>"))%>%
layout(xaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'),
yaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff')
)
fig
Output:
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 am using the R programming language. I am trying to follow the tutorial here on "switching between graphs" : https://plotly.com/r/dropdowns/ (first example).
First, I generated some data in R:
library(plotly)
library(MASS)
x <- sample( LETTERS[1:4], 1000, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25) )
y <- rnorm(1000,10,10)
z <- rnorm(1000,5,5)
df <- data.frame(x,y, z)
df$x = as.factor(df$x)
colnames(df) <- c("x", "y", "z")
I tried to modify the code from this tutorial to make the final result:
fig <- plot_ly(df, x = ~x, y = ~y, z = ~z alpha = 0.3)
fig <- fig %>% add_markers(marker = list(line = list(color = "black", width = 1)))
fig <- fig %>% layout(
title = "Drop down menus - Plot type",
xaxis = list(domain = c(0.1, 1)),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.8,
buttons = list(
list(method = "restyle",
args = list("type", "scatter"),
label = "Scatter A"),
list(method = "restyle",
args = list("type", "scatter"),
label = "Scatter B"),
list(method = "restyle",
args = list("type", "scatter"),
label = "Scatter C"),
list(method = "restyle",
args = list("type", "scatter"),
label = "Scatter D")
))
But this does not seem to be working.
Instead, I had a different idea: Perhaps I could create a series of graphs that I want to be able to "switch" between:
df_1 <- df[which(df$x == "A"),]
df_2 <- df[which(df$x == "B"),]
df_3 <- df[which(df$x == "C"),]
df_4 <- df[which(df$x == "D"),]
graph_1 <- plot_ly( data = df_1, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 1")
graph_2 <- plot_ly( data = df_2, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 2")
graph_3 <- plot_ly( data = df_3, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 3")
graph_4 <- plot_ly( data = df_4, type = "scatter", mode = "markers", x = ~ y, y = ~z) %>% layout(title = "graph 4")
graph_5 <- plot_ly(df, y = ~y, color = ~x, type = "box") %>% layout(title = "boxplot")
Now, is it possible to modify the plotly code to "tie" all these graphs (graph_1, graph_2, graph_3, graph_4, graph_5) together, so that the user can click the tab on the left and switch between these graphs?
Thanks
The example you should look at the tutorial is the last one (with the sine waves). It hides and shows different traces of the plot depending on the value of the selection in the dropdown menu.
You just need to change the format of your dataframe to wide.
df.wide <- df %>% tidyr::pivot_wider(names_from = x, values_from=z)
df.wide
## A tibble: 1,000 x 5
# y D B A C
# <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 6.48 6.21 NA NA NA
# 2 23.6 NA 15.3 NA NA
# 3 -9.99 -2.16 NA NA NA
# 4 19.6 NA NA 0.0683 NA
# 5 18.8 -1.40 NA NA NA
# 6 -2.71 9.80 NA NA NA
# 7 2.32 NA NA NA 3.77
# 8 11.9 NA 4.35 NA NA
# 9 21.4 NA NA NA 13.9
#10 2.34 NA 2.10 NA NA
## … with 990 more rows
Then add a separate scatter trace for each column. In the arguments of the dropdown menu you can set which traces are going to be visible when each option is selected. For example args = list("visible", list(TRUE, FALSE, FALSE, FALSE)) means that only the first trace added (in this case column A) is going to be visible.
fig <- plot_ly(df.wide, x = ~y)
fig <- fig %>%
add_trace(y = ~A, name = "A", type='scatter', mode='markers') %>%
add_trace(y = ~B, name = "B", type='scatter', mode='markers', visible = F) %>%
add_trace(y = ~C, name = "C", type='scatter', mode='markers', visible = F) %>%
add_trace(y = ~D, name = "D", type='scatter', mode='markers', visible = F) %>%
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(TRUE, FALSE, FALSE, FALSE)),
label = "A"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE, FALSE, 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")))))
EDIT: Adding also a box plot
Adding an option for a different type of plot (like a box plot) is a little bit harder. The issue now is that x axis in the box plot and scatter plot are different. So you can't use the same axis. Fortunately, plotly lets you map different traces to different axis. Then you can set the position of this new axis within the complete plot using the domain attribute.
My solution is a little bit hacky because I used the domain attribute to "hide" the "plot" I did not want to use by making it very small (I also made the corresponding data invisible by setting visible = FALSE). This is because hiding the axis only hides the lines. You are still left with the background of the plot.
Note that now I use the method update (instead of restyle) because it allows you to change also the layout of the plot (https://plotly.com/r/custom-buttons/).
But it seemed to work very well!
# I had to reorder the dataframe because the boxplot was not following the order of the factors. Apparently it follows the orders that the letter appear.
df <- df %>% dplyr::arrange(x)
df.wide <- df %>% tidyr::pivot_wider(names_from = x, values_from=z)
# this is a list with axis config for scatter plot (define here to avoid repetition)
axis.config.scatter <- list(xaxis = list(title = "x", domain = c(0.1, 1), visible=T),
yaxis = list(title = "y", domain = c(0, 1), visible=T),
xaxis2 = list(title = "group", domain = c(0.99, 1), visible=F),
yaxis2 = list(title = "y", domain = c(0,99, 1), visible=F))
# this is a list with axis config for box plot (define here to avoid repetition)
axis.config.box <- list(xaxis = list(title = "x", domain = c(0.99, 1), visible=F),
yaxis = list(title = "y", domain = c(0.99, 1), visible=F),
xaxis2 = list(title = "group", domain = c(0.1, 1), visible=T, anchor='free'),
yaxis2 = list(title = "y", domain = c(0, 1), visible=T, anchor='free'))
fig <- plot_ly(df.wide)
fig <- fig %>%
add_trace(x = ~y, y = ~A, name = "A", type='scatter', mode='markers') %>%
add_trace(x = ~y, y = ~B, name = "B", type='scatter', mode='markers', visible = F) %>%
add_trace(x = ~y, y = ~C, name = "C", type='scatter', mode='markers', visible = F) %>%
add_trace(x = ~y, y = ~D, name = "D", type='scatter', mode='markers', visible = F) %>%
add_trace(data=df, x=~x, y=~z, name='box', type='box', visible=F, xaxis='x2', yaxis='y2') %>%
layout(xaxis = list(title = "x", domain = c(0.1, 1)),
yaxis = list(title = "y"),
xaxis2 = list(title = "group", domain = c(0.99, 1), visible=F),
yaxis2 = list(title = "y", domain = c(0.99, 1), visible=F),
updatemenus = list(
list(
y = 0.7,
buttons = list(
list(method = "update",
args = list(list(visible = c(TRUE, FALSE, FALSE, FALSE, FALSE)),
axis.config.scatter),
label = "A"),
list(method = "update",
args = list(list(visible = c(FALSE, TRUE, FALSE, FALSE, FALSE)),
axis.config.scatter),
label = "B"),
list(method = "update",
args = list(list(visible = c(FALSE, FALSE, TRUE, FALSE, FALSE)),
axis.config.scatter),
label = "C"),
list(method = "update",
args = list(list(visible = c(FALSE, FALSE, FALSE, TRUE, FALSE)),
axis.config.scatter),
label = "D"),
list(method = "update",
args = list(list(visible = c(FALSE, FALSE, FALSE, FALSE, TRUE)),
axis.config.box),
label = "box")
))))
I am trying to add custom button to update the y axis, here is a small example that is similar.
When I click on “frq” or “count” the correct bar chart appears, but the values are wrong (its take only one value for each city and put it in all gender categories bars)
library(plotly)
library(dplyr)
library(tidyr)
Occupation = c("Tel Aviv", "Paris", "Amsterdam", "Kyoto")
Gender = c("F", "M", "[missing]")
df <- crossing(Occupation, Gender) %>%
mutate(n = row_number()) %>%
group_by(.data[["Occupation"]]) %>%
mutate(frq = round(100 * (n / sum(n)), 1))
chart_type <- list(
type = "buttons",
direction = "right",
xanchor = 'center',
yanchor = "top",
pad = list('r' = 0, 't' = 10, 'b' = 10),
x = 0.1,
y = 1.20,
buttons = list(
list(
method = "update",
args = list(list(y = list(df$n))),
label = "count"
),
list(
method = "update",
args = list(list(y = list(df$frq))),
label = "frq"
)
)
)
df %>%
plot_ly(
x = ~ Occupation,
y = ~ n,
color = ~ Gender,
text = ~ n,
textposition = 'auto',
type = "bar"
) %>%
layout(updatemenus = list(chart_type))
Another version:
chart_type <- list(
type = "buttons",
direction = "right",
xanchor = 'center',
yanchor = "top",
pad = list('r'= 0, 't'= 10, 'b' = 10),
x = 0.1,
y = 1.20,
buttons = list(
list(method = "update",
args = list(list(visible = c(TRUE, FALSE)
)),
label = "count"),
list(method = "update",
args = list(list(visible = c(FALSE, TRUE)
)),
label = "frq")
))
df %>%
plot_ly(mode = 'markers',type = "bar") %>%
add_trace(x = ~Occupation, y = ~n, color = ~Gender,
text=~n, textposition = 'auto', visible = FALSE,
colorbar = list()) %>%
add_trace(x = ~Occupation, y = ~frq, color = ~Gender,
text=~frq, textposition = 'auto', visible = TRUE,
colorbar = list()) %>%
layout(updatemenus = list(chart_type))
After the click: