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?
Related
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")
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")))
)
I want to create an interactive plotly 3d scatter with markers and lines in R. The graphic should be able to highlight individual traces, which is working. It should also be able to change the color according to other variables.
Here is an example of what I want to do, the highlighting works fine, changing the color does not work:
library(plotly)
irs <- data.table(iris)
setkey(irs, `Species`)
p <- plot_ly(type = "scatter3d", mode = "lines+markers")
for (i in levels(irs$Species)) {
xx <- irs[i]$Sepal.Length
yy <- irs[i]$Sepal.Width
zz <- irs[i]$Petal.Length
cc <- irs[i]$Petal.Width
p <- p %>% add_trace(x = xx, y = yy, z = zz, color = cc)
}
p <- p %>%
layout(
updatemenus = list(
## set opacity per trace to highlight a single trace
list(y = 0.6,
buttons = lapply(
levels(irs$Species),
function (x) {
list(method = "restyle",
args = list("opacity",
ifelse(levels(irs$Species) == x,
1, 0.1)),
label = x)
})),
## try to set different colors for points inside traces
## NOT WORKING
list(y = 0.4,
buttons = lapply(
names(irs),
function(x) {
list(
method = "restyle",
args = list(
"color",
split(irs[[x]], irs$Species)
),
label = x
)
}))
)
)
p
The following code solves only a small part of your problem: how to change colors of markers and lines for a single series.
I did not find a solution for the case with multiple series.
In addition, I did not find a way for an automatic rescaling of the color map after changing the color variable from the menu. Hence, I rescaled "by hand" all variables between 1 and 3.
I realise that this is a small contribution to the solution of the problem. Anyway, I hope it can help you.
library(plotly)
library(scales)
irs <- iris
irs[,1:4] <- apply(irs[,1:4],2, rescale, to=c(1,3))
p <- plot_ly(data = irs, type = "scatter3d", mode = "lines+markers")
p <- p %>% add_trace(x=~Sepal.Length, y=~Sepal.Width,
z=~Petal.Length, color=~Petal.Width)
p <- p %>%
layout(
updatemenus = list(
list(y = 0.4,
buttons = lapply(
names(irs),
function(x) {
cols <- as.numeric(irs[,x])
list(
method = "restyle",
label = x,
args = list(
list(marker.color=list(cols),
line.color=list(cols), autocolorscale=TRUE)
)
)
}))
)
)
p
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")
)
)))
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.