I am trying to create a plot in R similar to this:
with up to 6 variables and it has to be reactive.
I tried plotly, however in plotly I would get axis ticks on the plot, and it gets messy, and I managed to get only one y axis out.
Is there a way to recreate the plot in plotly or any other interactive library?
My plotly code:
library(dplyr)
library(plotly)
library(tidyr)
data <- cbind(
seq(from = 1, to = 30, by = 1),
sample(seq(from = 100, to = 300, by = 10), size = 30, replace = TRUE),
sample(seq(from = 1, to = 100, by = 9), size = 30, replace = TRUE),
sample(seq(from = 50, to = 60, by = 2), size = 30, replace = TRUE),
sample(seq(from = 100, to = 130, by = 1), size = 30, replace = TRUE)
) %>%
as.data.frame()
names(data) <- c("date", "a", "b", "x", "y")
plot_ly(x = ~data$date) %>%
add_lines(y = ~data[, 2], name = "a", line = list(color = "red")) %>%
add_lines(y = ~data[, 3], name = "b", line = list(color = "blue"), yaxis = "y2") %>%
add_lines(y = ~data[, 4], name = "x", line = list(color = "green"), yaxis = "y3") %>%
add_lines(y = ~data[, 5], name = "y", line = list(color = "pink"), yaxis = "y4") %>%
layout(
yaxis = list(
side = "left",
title = list("")
),
yaxis2 = list(
side = "left",
overlaying = "y",
anchor = "free"
),
yaxis3 = list(
side = "left",
overlaying = "y",
anchor = "free",
position = 0.04
),
yaxis4 = list(
side = "left",
overlaying = "y",
anchor = "free",
position = 0.08
),
margin = list(pad = 30)
)
With the highcharter package one can generate nice time series plots with multiple y-axes:
library(highcharter)
set.seed(1)
n <- 100
x1 <- cumsum(rnorm(n))
x2 <- cumsum(runif(n)-0.5)+10
x3 <- cumsum(rnorm(n,0,20))+100
x4 <- cumsum(rnorm(n,0,20))+1000
highchart() %>%
hc_add_series(data = x1) %>%
hc_add_series(data = x2, yAxis = 1) %>%
hc_add_series(data = x3, yAxis = 2) %>%
hc_add_series(data = x4, yAxis = 3) %>%
hc_yAxis_multiples(
list(lineWidth = 3, lineColor='#7cb5ec', title=list(text="First y-axis")),
list(lineWidth = 3, lineColor="#434348", title=list(text="Second y-axis")),
list(lineWidth = 3, lineColor="#90ed7d", title=list(text="Third y-axis")),
list(lineWidth = 3, lineColor="#f7a35c", title=list(text="Fourth y-axis"))
)
I just tweaked your code. Hope this helps!
plot_ly(x = ~data$date) %>%
add_lines(y = ~data[, 2], name = "a", line = list(color = "red")) %>%
add_lines(y = ~data[, 3], name = "b", line = list(color = "blue"), yaxis = "y2") %>%
add_lines(y = ~data[, 4], name = "x", line = list(color = "green"), yaxis = "y3") %>%
add_lines(y = ~data[, 5], name = "y", line = list(color = "pink"), yaxis = "y4") %>%
layout(
yaxis = list(
side = "left",
color="red",
title = ""
),
yaxis2 = list(
overlaying = "y",
anchor = "free",
color="blue",
title= ""
),
yaxis3 = list(
side = "right",
overlaying = "y",
color="green",
title= ""
),
yaxis4 = list(
side = "right",
overlaying = "y",
anchor = "free",
position = 1,
color="pink",
title= ""
),
xaxis = list(
title = ""
),
margin = list(pad = 25)
)
To add to and modify #Prem's answer, you can avoid overlap of the y-axes with the graph itself by tweaking the x-axis portion of the plot with the domain parameter of the x-axis.
plot_ly(data, x = ~time, type = 'scatter', mode = 'lines') %>%
add_lines(y = ~y1, name='y1', line = list(color = "red")) %>%
add_lines(y = ~y2, name='y2', yaxis='y2', line = list(color = "orange")) %>%
add_lines(y = ~y3, name='y3', yaxis='y3', line = list(color = "darkgreen")) %>%
add_lines(y = ~y4, name='y4', yaxis='y4', line = list(color = "purple")) %>%
add_lines(y = ~y5, name='y5', yaxis='y5', line = list(color = "brown")) %>%
layout(
xaxis = list(title = "time", domain = c(0.5,1)),
yaxis = list(title = 'Y-axis 1', side = "left", color = "red", position = 0,
anchor = 'free'),
yaxis2 = list(title = 'Y-axis 2', side = "left", color = "orange",
overlaying = "y", anchor = 'free', position = 0.1),
yaxis3 = list(title = 'Y-axis 3', side = "left",
overlaying = "y", anchor = 'free', color = "darkgreen", position = 0.2),
yaxis4 = list(title = 'Y-axis 4', side = "left",
overlaying = "y", anchor = 'free', color = "purple", position = 0.3),
yaxis5 = list(title = 'Y-axis 5',side = "left",
overlaying = "y", anchor = 'free', color = "brown", position = 0.4),
showlegend = T
)
The position of the single y-axes with respect to the plot size in percent can be set with the position parameter.
In the above example, each y-axis gets 10% of the total plot size while the graph is reserved for the right half.
Related
I am trying to reproduce a graph generated via ggplotly with plot_ly. I am struggling however with the colorbar.
This is the ggploty plot that I would like to reproduce, and in particular the colorbar:
library(plotly)
X <- data.frame(w = rep(c("a", "b"), each = 8),
x = 1:16,
y = 1:16,
z = c(1, 1:13, 13, 13))
X$z_scaled <- (X$z-min(X$z))/(max(X$z)-min(X$z)) # scale to 0-1
# ggplot
gg <- ggplot(X) +
geom_point(aes(x, y, color = z_scaled, alpha = w, text = paste0(x, ", ", y))) +
scale_color_gradient2(low = '#0d71db', mid = "#dbc00d", high = '#db220d',
midpoint = .5, breaks = 0:1, limits = 0:1) +
scale_alpha_manual(name = " ", values = rep(1, nrow(X))) +
labs(color = "Z", x = "", y = "")
ggplotly(gg, type = "scattergl", tooltip = "text") %>% toWebGL()
This is what I have with plot_ly:
length_unique_vals <- length(unique(X$z))
.colors <- colorRampPalette(c('#0d71db', "#dbc00d", "#db220d"))(length_unique_vals)
.colors <- .colors[factor(X$z)]
plot_ly() %>%
add_markers(
data = X, x = ~x, y = ~y,
split = ~w,
text = ~paste0(x, ", ", y),
hoverinfo = "text",
type = "scattergl",
mode = "markers",
marker = list(
# color = ~z_scaled,
color = .colors,
# colorscale = list(c(0, .5, 1), c("#0d71db", "#dbc00d", "#db220d")),
colorscale = .colors,
hoverlabel = list(bgcolor = .colors),
colorbar = list(
title = list(text = "Z"),
len = .5,
x = 1,
y = .7
)
)
) %>%
layout(
legend = list(x = 1, y = .4, bgcolor = 'rgba(255,255,255,0.6)')
) %>% toWebGL() %>% partial_bundle(local = FALSE)
As you can see, the colorbar is not displaying correctly. I have tried multiple possibilities (commented above) without success. What am I missing?
Edit
#Kat's answer solves the colorbar issue. However, if you want to use scattergl or toWebGl you will need to fix the hoverlabel background so it remains dynamic. Here is a solution for that below building on her answer.
length_unique_vals <- length(unique(X$z))
.colors <- colorRampPalette(c('#0d71db', "#dbc00d", "#db220d"))(length_unique_vals)
.colors <- .colors[factor(X$z)]
plot_ly() %>%
add_trace(x = ~x,
y = ~y,
split = ~w, # instead of alpha or opacity
data = X,
type = "scattergl",
mode = "markers",
color = ~z_scaled, # color = var and colors = literal colors
colors = c('#0d71db', "#dbc00d", '#db220d'),
hoverlabel = list(bgcolor = .colors)) %>% # Fix hovercolor bg
layout(xaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
yaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
plot_bgcolor = "#eeeeee") %>% # gray background
colorbar(title = "Z", # colorbar title
dtick = c(0, 1), # colorbar ticks
thickness = 25) %>% # width
toWebGL() %>% partial_bundle(local = FALSE)
Edit 2
The hoverlabel bgcolor breaks down then the split factor is not ordered. This is why it needs to be ordered first.
library(plotly)
library(data.table)
X <- data.table(w = rep(c("a", "b"), 8), #not ordered
x = 1:16,
y = 1:16,
z = c(1, 1:13, 13, 13))
X[, z_scaled := (X$z-min(X$z))/(max(X$z)-min(X$z))] # scale to 0-1
# Get colors for hoverlabel bgcolor
X <- X[order(w)]
length_unique_vals <- length(unique(X$z))
.colors <- colorRampPalette(c('#0d71db', "#dbc00d", "#db220d"))(length_unique_vals)
.colors <- .colors[factor(X$z)]
plot_ly() %>%
add_trace(x = ~x,
y = ~y,
split = ~w, # instead of alpha or opacity
data = X,
type = "scattergl",
mode = "markers",
color = ~z_scaled, # color = var and colors = literal colors
colors = c('#0d71db', "#dbc00d", '#db220d'),
hoverlabel = list(bgcolor = .colors),
marker = list(size = 10)) %>% # Fix hovercolor bg
layout(xaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
yaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
plot_bgcolor = "#eeeeee") %>% # gray background
colorbar(title = "Z", # colorbar title
dtick = c(0, 1), # colorbar ticks
thickness = 25) %>% # width
toWebGL() %>% partial_bundle(local = FALSE)
Assuming you even wanted the gray background, this should work. If something isn't clear or not what you were looking for, let me know.
You don't even need the text, because you specified the default hover content.
plot_ly() %>%
add_trace(x = ~x,
y = ~y,
split = ~w, # instead of alpha or opacity
data = X,
type = "scatter",
mode = "markers",
color = ~z_scaled, # color = var and colors = literal colors
colors = c('#0d71db', "#dbc00d", '#db220d')) %>%
layout(xaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
yaxis = list(title = "",
dtick = 4,
zeroline = F,
gridcolor = "white"), # white on gray
plot_bgcolor = "#eeeeee") %>% # gray background
colorbar(title = "Z", # colorbar title
dtick = c(0, 1), # colorbar ticks
thickness = 25) # width
I have created the following bullet chart in R
library(plotly)
fig<-plot_ly()
fig <- fig %>%
add_trace(
type = "indicator",
mode = "number+gauge+delta",
value = 35,
delta = list(reference = 100),
domain = list(x = c(0.25, 1), y = c(0.4, 0.6)),
title = list(text = "Avalue"),
gauge = list(shape = "bullet",axis = list(range = list(NULL, 100)),threshold = list(
line = list(color = "black", width= 2), thickness = 0.75,value = 55),steps = list(
list(range = c(0, 25), color = "red"),
list(range = c(25, 50), color = "blue"),
list(range = c(50, 75), color = "green"),
list(range = c(75, 100), color = "grey")),
bar = list(color = "grey")))
How do I remove the whitespace around the chart. And is there a way I can adjust the length of the chart
You should change your values for your domain in the add_trace. I changed the y to FALSE and x to a different range, which results in the following plot:
library(plotly)
fig<-plot_ly()
fig <- fig %>%
add_trace(
type = "indicator",
mode = "number+gauge+delta",
value = 35,
delta = list(reference = 100),
domain = list(x = c(0.1, 1), y = FALSE),
title = list(text = "Avalue"),
gauge = list(shape = "bullet",axis = list(range = list(NULL, 100)),threshold = list(
line = list(color = "black", width= 2), thickness = 0.75,value = 55),steps = list(
list(range = c(0, 25), color = "red"),
list(range = c(25, 50), color = "blue"),
list(range = c(50, 75), color = "green"),
list(range = c(75, 100), color = "grey")),
bar = list(color = "grey")))
fig
Output:
As you can see, the whitespace is way less.
I have a plot with multiple lines and would like to initialize it with just some layers active. Is it possible? Here is an example of what the data looks like:
set.seed(101)
df <- data.frame(x = seq(1,20,1),
y1 = runif(20),
y2 = runif(20),
y3 = runif(20),
y4 = runif(20))
plot_ly(df, x = ~x) %>%
add_lines(y = ~y1, name='y1', line = list(color = 'rgba(255,0,0,1')) %>%
add_lines(y = ~y2, name='y2', line = list(color = 'rgba(0,255,0,1')) %>%
add_lines(y = ~y3, name='y3', line = list(color = 'rgba(106,90,205,1')) %>%
add_lines(y = ~y4, name='y4', line = list(color = 'rgba(0,0,255,1')) %>%
layout(
xaxis = list(title = "x", domain = c(0,1), tickmode = 'auto'),
yaxis = list(title = 'y', side = "left", color = "black", position = 0,
anchor = 'free', range = c(0,1), dtick = 1),
showlegend = T
)
What I want is to generate a plot with all lines, but e.g. with y3 and y4 off and still allow the user to turn it on (this plot is part of a shiny app).
Is it even possible?
Thanks!
You need to set visible = "legendonly" for the traces you want to hide:
library(plotly)
set.seed(101)
df <- data.frame(x = seq(1,20,1),
y1 = runif(20),
y2 = runif(20),
y3 = runif(20),
y4 = runif(20))
plot_ly(df, x = ~x) %>%
add_lines(y = ~y1, name='y1', line = list(color = 'rgba(255,0,0,1')) %>%
add_lines(y = ~y2, name='y2', line = list(color = 'rgba(0,255,0,1')) %>%
add_lines(y = ~y3, name='y3', line = list(color = 'rgba(106,90,205,1'), visible = "legendonly") %>%
add_lines(y = ~y4, name='y4', line = list(color = 'rgba(0,0,255,1'), visible = "legendonly") %>%
layout(
xaxis = list(title = "x", domain = c(0,1), tickmode = 'auto'),
yaxis = list(title = 'y', side = "left", color = "black", position = 0,
anchor = 'free', range = c(0,1), dtick = 1),
showlegend = TRUE
)
To get more information regarding the available trace attributes use:
schema()
and navigate as follows:
object ► traces ► scatter ► attributes
I have a magneta colored graph made in plotly with certain levels which I have added via vertical lines. Currently I have color filled these areas between the vertical lines.
However I would like to only have a color between the vertical line and the magneta graph (I have circled the aereas as a reference.
Is this possible in plotly?
plot <- plot %>% add_lines(type = 'scatter', mode = "lines",
name = "test", yaxis = 'y2',
x = ~t, y = ~v.x,
line = list(color = '#CC79A7'),
hoverinfo = "text",
text = ~paste(round(v.x, 1), t))
plot <- plot %>% add_segments(x = ~min(t), xend = ~max(t),
y = round(quantNUPL[1], 1), yend = round(quantNUPL[1], 1), yaxis = 'y2',
line= list(color = "#0072B0",
dash = "dash",
width = 1),
showlegend = F)
plot <- plot %>% add_segments(x = ~min(t), xend = ~max(t),
y = round(quantNUPL[2], 1), yend = round(quantNUPL[2], 1), yaxis = 'y2',
line= list(color = "#0072B0",
dash = "dash",
width = 1),
fill = 'tonexty',
fillcolor = '#56B4E940',
name = "90% UPSIDE")
plot <- plot %>% add_segments(x = ~min(t), xend = ~max(t),
y = round(quantNUPL[6], 1), yend = round(quantNUPL[6], 1), yaxis = 'y2',
line= list(color = "#999999",
dash = "dash",
width = 1),
name = "50% UPSIDE")
plot <- plot %>% add_segments(x = ~min(t), xend = ~max(t),
y = round(quantNUPL[9], 1), yend = round(quantNUPL[9], 1), yaxis = 'y2',
line= list(color = "#E69F00",
dash = "dot",
width = 1),
name = "20% UPSIDE",
showlegend = F)
plot <- plot %>% add_segments(x = ~min(t), xend = ~max(t),
y = round(quantNUPL[10], 1), yend = round(quantNUPL[10], 1), yaxis = 'y2',
line= list(color = "#E69F00",
dash = "dash",
width = 1),
fill = 'tonexty',
fillcolor = "#E69F0020",
name = "20% UPSIDE")
plot <- plot %>% add_segments(x = ~min(t), xend = ~max(t),
y = round(quantNUPL[11], 1), yend = round(quantNUPL[11], 1), yaxis = 'y2',
line= list(color = "#E69F00",
dash = "solid",
width = 1),
fill = 'tonexty',
fillcolor = "#E69F0040",
name = "10% UPSIDE")
You can try setting a trace and defining the max y-values with an ifelse-function.
Example Data
df <- data.frame(x = 1:50,
y = c(20:5,6:10,9:3,4:20, 19:15))
Code
plot_ly(df) %>%
add_lines(type = 'scatter', mode = "lines",
name = "test", yaxis = 'y2',
x = ~x, y = ~y,
line = list(color = '#CC79A7'),
hoverinfo = "text",
text = ~paste(round(y, 1), x)) %>%
#color space between y = 8 and line
add_trace(x = ~x, y = ~ifelse(y <=8, y, 8),
type = "scatter", fill = "toself",
fillcolor = "blue", mode = "none",
yaxis = 'y2') %>%
#background boxes
layout(shapes = list(
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.3,
x0 = ~min(x), x1 = ~max(x),
y0 = 0, y1 = 8, yref = "y2"),
list(type = "rect",
fillcolor = "orange", line = list(color = "orange"), opacity = 0.3,
x0 = ~min(x), x1 = ~max(x),
y0 = 18, y1 = 20, yref = "y2"),
list(type = "rect",
fillcolor = "yellow", line = list(color = "yellow"), opacity = 0.3,
x0 = ~min(x), x1 = ~max(x),
y0 = 16, y1 = 20, yref = "y2")))
Plot
Second idea
df <- data.frame(x = 1:50,
y = c(20:5,6:10,9:3,4:8,1,10:20, 5:1))
plot_ly(df) %>%
# add darkblue background
add_trace(x = ~x, y = 8,
type = "scatter", fill = "tozeroy",
fillcolor = "blue", mode = "none",
yaxis = 'y2', hoverinfo = "none") %>%
# set area underneath red line to white
add_lines(type = 'scatter', mode = "lines",
yaxis = 'y2',
x = ~x, y = ~y,
line = list(width = 0),
hoverinfo = "none",
fillcolor = "white",
fill = "tozeroy",
showlegend = FALSE) %>%
# add red line graph
add_lines(type = 'scatter', mode = "lines",
name = "test", yaxis = 'y2',
x = ~x, y = ~y,
line = list(color = '#CC79A7'),
hoverinfo = "text",
text = ~paste(round(y, 1), x)) %>%
layout(shapes = list(
# add lightblue background
list(type = "rect",
fillcolor = "blue", line = list(color = "blue"), opacity = 0.3,
x0 = ~min(x), x1 = ~max(x),
y0 = 0, y1 = 8, yref = "y2"),
# add orange background
list(type = "rect",
fillcolor = "orange", line = list(color = "orange"), opacity = 0.3,
x0 = ~min(x), x1 = ~max(x),
y0 = 18, y1 = 20, yref = "y2"),
#add yellow backround
list(type = "rect",
fillcolor = "yellow", line = list(color = "yellow"), opacity = 0.3,
x0 = ~min(x), x1 = ~max(x),
y0 = 16, y1 = 20, yref = "y2")),
#remove grid
xaxis = list(showgrid = F),
yaxis2 = list(showgrid = F)
)
I have a bar chart that is horizontally oriented and it's flipped (autorange = 'reversed') so its base is on the right side. My problem is that ticktexts for Y-axis stay on the left side, where the Y-axis had been before being flipped. That means ticktexts are not at the base of the chart bars, but at their ends.
How do I change their position and move them to the right?
Image of the chart result
library(plotly)
my_data <- data.frame(Value = c(3, 6, 7, 4, 9), Type = c("Red", "Orange", "White", "Black", "Green"))
my_chart <- function(my_data){
chart <- plot_ly(my_data) %>%
plotly::add_trace(x = ~Value,
y = ~Type,
name = ~Type,
marker = list(color = "#4369ab"),
type = "bar",
orientation = 'h')
chart %>% layout(yaxis = list(title = ""),
xaxis = list(title = "",
autorange = 'reversed'),
margin = list(pad = 6),
title = "",
showlegend = F)
}
my_chart(my_data)
You've to use the paramater side = "right"
library(plotly)
my_data <- data.frame(Value = c(3, 6, 7, 4, 9), Type = c("Red", "Orange", "White", "Black", "Green"))
my_chart <- function(my_data){
chart <- plot_ly(my_data) %>%
plotly::add_trace(x = ~Value,
y = ~Type,
name = ~Type,
marker = list(color = "#4369ab"),
type = "bar",
orientation = 'h'
)
chart %>% layout(yaxis = list(title = "", side = "right"),
xaxis = list(title = "", autorange ='reversed'),
margin = list(pad = 6),
title = "",
showlegend = F)
}
my_chart(my_data)