drop-down menu and check-boxes in r - r

I have a dataframe that has different columns. The first one is the year, the rest are different brands. I would like to plot a graph showing how those different brands performed throughout the years in terms of profits. This graph should have a dropdown menu that allows you to choose which company you would like to see, that is a dropdown checkbox with all the brands. The checkbox should also allow you to see all of them at the same time, or just some.
#Here is my go at it.
library(plotly)
x <- seq(-2 * pi, 2 * pi, length.out = 1000)
df <- data.frame(x, y1 = sin(x), y2 = cos(x), y3=cos(2*x), y4=sin(3*x))
p <- plot_ly(df, x = ~x) %>%
add_lines(y = ~y1, name = "Sin") %>%
add_lines(y = ~y2, name = "Cos", visible = F) %>%
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, FALSE)),
label = "Sinx"),
list(method = "restyle",
args = list("visible", list(FALSE, TRUE)),
label = "Cosx")))
)
)
p
In the example above, I was able to create a drop-down menu but it is not close to what I want. Also, I couldn’t grasp the answer given in this question since it uses so much html (I suck at html). Any help is greatly appreciated.

The question you linked uses shiny, but you do not seem to be using shiny.
Here is a non-shiny approach that is pretty close to what you want. It uses crosstalk::SharedData and crosstalk::filter_select() on long format data to give you a drop-down type input permitting multiple selections.
library(crosstalk)
library(dplyr)
library(plotly)
library(tidyr)
data_wide <- tibble(x = seq(-2 * pi, 2 * pi, length.out = 1000),
y0 = 0,
y1 = sin(x),
y2 = cos(x),
y3 = cos(2 * x),
y4 = sin(3 * x))
data_long <- data_wide %>%
gather("series", "y", -x) %>%
mutate(series = case_when(series == "y0" ~ "0",
series == "y1" ~ "sin(x)",
series == "y2" ~ "cos(x)",
series == "y3" ~ "cos(2x)",
series == "y4" ~ "sin(3x)"))
data_shared <- SharedData$new(data_long, key = ~series)
p <- data_shared %>%
plot_ly(x = ~x, y = ~y, color = ~series) %>%
add_lines()
bscols(widths = c(3, NA),
filter_select(id = "fsid",
label = "Series",
sharedData = data_shared,
group = ~series),
p)
If you are not aware, clicking series in the legend of a plotly plot will toggle visibility of that series. So you may not need a drop-down menu at all and could instead just do something like:
library(plotly)
x <- seq(-2 * pi, 2 * pi, length.out = 1000)
df <- data.frame(x, y = 0, y1 = sin(x), y2 = cos(x), y3 = cos(2 * x), y4 = sin(3 * x))
plot_ly(df, type = "scatter", mode = "lines") %>%
add_lines(x= ~x, y= ~y, name = "0", line=list(color="red")) %>%
add_lines(x= ~x, y= ~y1, name = "sin(x)", line=list(color="orange")) %>%
add_lines(x= ~x, y= ~y2, name = "cos(x)", line=list(color="yellow")) %>%
add_lines(x= ~x, y= ~y3, name = "cos(2x)", line=list(color="green")) %>%
add_lines(x= ~x, y= ~y4, name = "sin(3x)", line=list(color="blue")) %>%
layout(title = "Choose Your Own Brands")

Related

Switching between two plots with groups in Plotly

I have a data frame with 2 columns, one that I want to use as a toggle (so display grp1 or grp2) and another where I want to split the data into different lines. I can't seem to figure out how to get it to work properly with plotly, I think there should be a simple straightforward way to do it but for the life of me I can't get it to stop mixing up the groups.
library(tidyverse)
library(plotly)
library(ggplot2)
# example data my group 1 would be social, group 2 would be grp
df1 = data.frame(grp = "A", social = "Facebook",
days = c("2020-01-01","2020-01-02","2020-01-03","2020-01-04"),
yval = c(0.1, 0.2, 0.3, 0.4))
df2 = df1
df2$grp = "B"
df2$yval = df2$yval + 0.2
df3 = df1
df3$grp = "C"
df3$yval = df3$yval + 0.4
df = rbind(df1, df2, df3)
aux = df
aux$social = "Twitter"
aux$yval = aux$yval + 0.1
df = rbind(aux, df)
rm(aux, df1, df2, df3)
df$days = as.Date(df$days)
df$social_group = paste(df$social, df$grp)
ggplot(data = df, mapping = aes(x = days, y = yval, color = social)) + geom_point() + geom_line() + facet_wrap(facets = ~social)
So what I'm trying to do is to create a plotly that lets me switch between the ggplot facets, by toggling a Facebook or Twitter button.
This is what I currently got, which starts well, but as soon as I toggle the buttons the groups seem to mix, which shouldn't be happening when I consider I'm filtering on another column...
facebook_annotations <- list(
data=df %>% filter(social=="Facebook"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days
)
twitter_annotations <- list(
data=df %>% filter(social=="Twitter"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days
)
# updatemenus component
updatemenus <- list(
list(
active = 0,
type = "buttons",
buttons = list(
list(
label = "Facebook",
method = "update",
args = list(list(visible = c(TRUE, FALSE)),
list(title = "Facebook",
annotations = list(facebook_annotations, c())))),
list(
label = "Twitter",
method = "update",
args = list(list(visible = c(FALSE, TRUE)),
list(title = "Twitter",
annotations = list(c(), twitter_annotations)))))
)
)
fig <- df %>% plot_ly(type="scatter", mode="lines")
fig <- fig %>% add_lines(
data=df %>% filter(social=="Facebook"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days
)
fig <- fig %>% add_lines(
data=df %>% filter(social=="Twitter"),
x=~days,
y=~yval,
color = ~grp,
hovertemplate = paste('%{x}', '<br>Hover text: %{text}<br>'),
text=~days,
visible=FALSE
)
fig <- fig %>% layout(title="Facebook",
xaxis=list(title=""),
yaxis = list(range = c(0, 1), title = "My Title"),
updatemenus=updatemenus)
fig
What am I missing? It's driving me crazy, I'm even considering just adding each group as an individual trace, but that's not really practical when my actual case study has 8 groups...

Multiple lines/traces for each button in a Plotly drop down menu in R

I am trying to generate multiple graphs in Plotly for 30 different sales offices. Each graph would have 3 lines: sales, COGS, and inventory. I would like to keep this on one graph with 30 buttons for the different offices. This is the closest solution I could find on SO:
## Create random data. cols holds the parameter that should be switched
l <- lapply(1:100, function(i) rnorm(100))
df <- as.data.frame(l)
cols <- paste0(letters, 1:100)
colnames(df) <- cols
df[["c"]] <- 1:100
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly(df,
type = "scatter",
mode = "lines",
x = ~c,
y= ~df[[cols[[1]]]],
name = cols[[1]])
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[-1]) {
p <- p %>% add_lines(x = ~c, y = df[[col]], name = col, visible = FALSE)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(cols, function(col) {
list(method="restyle",
args = list("visible", cols == col),
label = col)
})
)
)
)
print(p)
It works but only on graphs with single lines/traces. How can I modify this code to do the same thing but with graphs with 2 or more traces? or is there a better solution? Any help would be appreciated!
### EXAMPLE 2
#create fake time series data
library(plotly)
set.seed(1)
df <- data.frame(replicate(31,sample(200:500,24,rep=TRUE)))
cols <- paste0(letters, 1:31)
colnames(df) <- cols
#create time series
timeseries <- ts(df[[1]], start = c(2018,1), end = c(2019,12), frequency = 12)
fit <- auto.arima(timeseries, d=1, D=1, stepwise =FALSE, approximation = FALSE)
fore <- forecast(fit, h = 12, level = c(80, 95))
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly() %>%
add_lines(x = time(timeseries), y = timeseries,
color = I("black"), name = "observed") %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 2], ymax = fore$upper[, 2],
color = I("gray95"), name = "95% confidence") %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 1], ymax = fore$upper[, 1],
color = I("gray80"), name = "80% confidence") %>%
add_lines(x = time(fore$mean), y = fore$mean, color = I("blue"), name = "prediction")
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[2:31]) {
timeseries <- ts(df[[col]], start = c(2018,1), end = c(2019,12), frequency = 12)
fit <- auto.arima(timeseries, d=1, D=1, stepwise =FALSE, approximation = FALSE)
fore <- forecast(fit, h = 12, level = c(80, 95))
p <- p %>%
add_lines(x = time(timeseries), y = timeseries,
color = I("black"), name = "observed", visible = FALSE) %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 2], ymax = fore$upper[, 2],
color = I("gray95"), name = "95% confidence", visible = FALSE) %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 1], ymax = fore$upper[, 1],
color = I("gray80"), name = "80% confidence", visible = FALSE) %>%
add_lines(x = time(fore$mean), y = fore$mean, color = I("blue"), name = "prediction", visible = FALSE)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(cols, function(col) {
list(method="restyle",
args = list("visible", cols == col),
label = col)
})
)
)
)
p
You were very close!
If for example you want graphs with 3 traces,
You only need to tweak two things:
Set visible the three first traces,
Modify buttons to show traces in groups of three.
My code:
## Create random data. cols holds the parameter that should be switched
library(plotly)
l <- lapply(1:99, function(i) rnorm(100))
df <- as.data.frame(l)
cols <- paste0(letters, 1:99)
colnames(df) <- cols
df[["c"]] <- 1:100
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly(df,
type = "scatter",
mode = "lines",
x = ~c,
y= ~df[[cols[[1]]]],
name = cols[[1]])
p <- p %>% add_lines(x = ~c, y = df[[2]], name = cols[[2]], visible = T)
p <- p %>% add_lines(x = ~c, y = df[[3]], name = cols[[3]], visible = T)
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[4:99]) {
print(col)
p <- p %>% add_lines(x = ~c, y = df[[col]], name = col, visible = F)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(0:32, function(col) {
list(method="restyle",
args = list("visible", cols == c(cols[col*3+1],cols[col*3+2],cols[col*3+3])),
label = paste0(cols[col*3+1], " ",cols[col*3+2], " ",cols[col*3+3] ))
})
)
)
)
print(p)
PD: I only use 99 cols because I want 33 groups of 3 graphs

Interactively select a grouping variable in plotly

How can I create a grouped bar chart in plotly that has a dropdown (or something else), so a viewer can select the grouping variable?
Working example:
library(dplyr)
library(plotly)
library(reshape2)
iris$Sepal.L <- iris$Sepal.Length %>%
cut(breaks = c(4,5,7,8),
labels = c("Length.a","Length.b","Length.c"))
iris$Sepal.W <- iris$Sepal.Width %>%
cut(breaks = c(1,3,5),
labels = c("Width.a","Width.b"))
# Get percentages
data1 <- table(iris$Species, iris$Sepal.L) %>%
prop.table(margin = 1)
data2 <- table(iris$Species, iris$Sepal.W) %>%
prop.table(margin = 1)
# Convert to df
data1 <- data.frame(Var1=row.names(data1), cbind(data1))
row.names(data1) <- NULL
data2 <- data.frame(Var1=row.names(data2), cbind(data2))
row.names(data2) <- NULL
plot_ly(
data = data1,
name = "Length.a",
x = ~Var1, y = ~Length.a,
type = "bar") %>%
add_trace(y=~Length.b, name = "Length.b") %>%
add_trace(y=~Length.c, name = "Length.c")
plot_ly(
data = data2,
name = "Width.a",
x = ~Var1, y = ~Width.a,
type = "bar") %>%
add_trace(y=~Width.b, name = "Width.b")
For example if I would like to select between viewing a plot with table(iris$Species, iris$Sepal.Length) and a plot with table(iris$Species, iris$Sepal.Width)
Bonus:
If it's easy; being able to interactively select the x variable as well would be cool, but not necessary.
You can find a solution here.
The idea is to plot your bar charts (with data1 and data2) all together and to make visible only one at a time.
items <- list(
list(label="Var1",
args=list(list(visible=c(T,T,T,F,F)))),
list(label="Var2",
args=list(list(visible=c(F,F,F,T,T))))
)
plot_ly(data=data1) %>%
add_bars(name = "Length.a",
x = ~Var1, y = ~Length.a, visible=T) %>%
add_bars(name = "Length.b",
x = ~Var1, y = ~Length.b, visible=T) %>%
add_bars(name = "Length.c",
x = ~Var1, y = ~Length.c, visible=T) %>%
add_bars(name = "Width.a",
x = ~Var1, y = ~Width.a, visible=F, data=data2, marker=list(color="#377EB8")) %>%
add_bars(name = "Width.b",
x = ~Var1, y = ~Width.b, visible=F, data=data2, marker=list(color="#FF7F00")) %>%
layout(
title = "Bar chart with drop down menu",
xaxis = list(title="x"),
yaxis = list(title = "y"),
showlegend = T,
updatemenus = list(
list(y = 0.9,
buttons = items)
))

Plotly - Dynamically Choose X and Y Variables for Scatter Plot

I'm trying to create a scatter plot where the user can dynamically change the X and Y axis. I have it mostly completed, but I'm running into this weird hiccup.
The first time I change the X dropdown, the scatter plot will be incorrect. However, once I change the Y dropdown the scatter plot corrects itself, but it loses it's color-groupings. Example code:
# Get libraries
library(plotly)
set.seed(123)
index <- 1:24
x1 <- rnorm(24, 4, 3)
x2 <- rnorm(24, 3, 4)
y1 <- rnorm(24, 5, 2)
y2 <- rnorm(24, 2, 5)
type <- rep(c("A", "B", "C"), 8)
df <- data.frame(x1, x2, y1, y2, type)
p <- plot_ly(df, x = ~x1, y = ~y1, color = ~type, type = "scatter", mode = "markers",
text = ~paste("Index: ", index)) %>%
layout(
updatemenus = list(
## X Axis ##
list(
y = 0.6,
buttons = list(
list(method = "restyle",
args = list("x", list(df$x1)), # put it in a list
label = "x1"),
list(method = "restyle",
args = list("x", list(df$x2)), # put it in a list
label = "x2"))),
## Y Axis ##
list(
y = 0.5,
buttons = list(
list(method = "restyle",
args = list("y", list(df$y1)), # put it in a list
label = "y1"),
list(method = "restyle",
args = list("y", list(df$y2)), # put it in a list
label = "y2")))
))
p
After it pops up in your viewer, click the "X1" dropdown and select "X1" again: notice how the graph updates incorrectly. Next, click the "Y1" dropdown and select "Y1": notice how the graph looks the same as the original, but the color-groupings no longer exist and I believe the indexes got mixed up.
Is there an easy fix to what I'm doing wrong?

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