Removing Unused Subplot in R Plotly - r

When using plotly (in R), after combining subplots there remains an unused and blank subplot. I've recreated the issue using the ggplot2 dataset mpg below.
library(dplyr)
library(ggplot2)
library(plotly)
audi <- mpg %>%
filter(manufacturer == "audi")
chevy <- mpg %>%
filter(manufacturer == "chevrolet")
fig1 <- plot_ly(audi, x = ~hwy, y = ~year, name = "", type = 'scatter',
mode = "markers", marker = list(color = "blue", symbol = 'x-dot'))
fig2 <- plot_ly(chevy, x = ~hwy, y = ~year, name = "", type = 'scatter',
mode = "markers", marker = list(color = "red", symbol = 'circle'))
fig <- subplot(fig1, fig2)
fig <- fig %>% subplot(shareX = TRUE,shareY = TRUE,which_layout = "merge")
fig <- fig %>% layout(
title = "Audi and Chevy",
xaxis = list(title = "Highway MPG"),
yaxis = list(title = "Year"),
margin = list(l = 100)
)
The only solution I've been able to find is tinkering with the width of the used subplot, but this leaves quite a bit of unused white space on the right and causes the title to be far off to the right (as it adjusts into the center of the used and unused subplots).
Is there a way to remove the unused subplot? If not, is there a way to organize/subset the dataframe such that only one plot needs to be used in the first place?
Thanks!

You can assign the colours based on the manufacturer column:
data.subs <- mpg %>%
filter(manufacturer == "audi" | manufacturer == "chevrolet")
fig <- plot_ly(data.subs, x = ~hwy, y = ~year, name = "",
type = 'scatter', mode = "markers",
marker = list(color = factor(data.subs$manufacturer,
labels = c("red", "blue")),
symbol = 'circle'),
text = factor(data.subs$manufacturer,
labels = c("audi", "chevy")), hoverinfo = 'text'))
fig <- fig %>% layout(
title = "Audi and Chevy",
xaxis = list(title = "Highway MPG"),
yaxis = list(title = "Year"),
margin = list(l = 100)
)
fig
This makes generating multiple subplots unnecessary.

Related

Create a stacked bar chart with 3 traces for 2 bars

I am trying to replicate the following stacked bar chart with plotly. I attach one screenshot for every hover text I get when hovering on a bar. As you will see there are 2 issues. First I cannot achieve 3 colors, besides the fact that I create them in the legend and secondly I cannot put First dose as top bar besides the fact that I use factor() based on the levels. Maybe there is an issue with the way I have created my dataset. I have no problem if you have to reform it instead of fix the plotly code to replicate the chart.
library(plotly)
Category<-c("First dose","Full vaccination")
`Uptake first dose`<-c(19.8,7.6)
`Uptake full vaccination`<-c(0,0)
`Not vaccinated`<-c(80.2,92.4)
ch5<-data.frame(Category,`Uptake first dose`,`Uptake full vaccination`,`Not vaccinated`)
ch5$Category <- factor(ch5$Category, levels = ch5[["Category"]])
ax <- list(
title = "",
showticklabels = FALSE,
showgrid = FALSE
)
fig <- plot_ly(ch5, y = ~Category, x = ~`Uptake first dose`,
type = 'bar', name = 'Uptake first dose',marker = list(color = 'lightgreen'))
fig <- fig %>% add_trace(x = ~`Uptake full vaccination`, name = 'Uptake full vaccination',marker = list(color = 'green'))
fig <- fig %>% add_trace(x = ~`Not vaccinated`, name = 'Not vaccinated',marker = list(color = 'gray'))
fig <- fig %>% layout(yaxis = ax,xaxis=list(title="",showgrid=F), barmode = 'stack')
fig
There might be a problem with your dataset. The 7.6% of full vaccination is listed under first doese. Therefore your coloring might not work.
Furthermore I transformed the data into a long format for an easy way to create hovertemplates.
library(plotly)
library(tidyverse)
# data
Category<-c("First dose","Full vaccination")
`Uptake first dose`<-c(19.8,0)
`Uptake full vaccination`<-c(0,7.6)
`Not vaccinated`<-c(80.2,92.4)
ch5<-data.frame(Category,`Uptake first dose`,`Uptake full vaccination`,`Not vaccinated`)
# transform data
data.long <- ch5 %>%
pivot_longer(cols = -Category,
names_to = "vac",
values_to = "percent") %>%
mutate(vac = str_replace_all(vac, "\\.", " "),
vac = fct_rev(factor(vac)))
# add plot
plot_ly(data.long) %>%
add_bars(y = ~Category,
x = ~percent,
color = ~vac,
text = ~vac,
colors = c("darkgreen", "green", "gray"),
hovertemplate = paste('<b>%{y}</b>',
'<br>%{text}: %{x} ',
'<extra></extra>')) %>%
layout(barmode = "stack",
yaxis = list(autorange="reversed"),
hoverlabel = list(bgcolor = "black",
bordercolor = "black",
font = list(color = "white")),
shapes = list(type = "line",
y0 = 0, y1 = 1, yref = "paper",
x0 = 70, x1 = 70),
annotations = list(text = "Target (70.0%)",
showarrow = FALSE,
x = 70,
y = 1.05,
yref = "paper"))

How to apply subplot to a list of plots with secondary y axis

I want to prepare a subplot where each facet is a separate dual y-axis plot of one variable against the others. So I make a base plot p and add secondary y-axis variable in a loop:
library(rlang)
library(plotly)
library(tibble)
dual_axis_lines <- function(data, x, y_left, ..., facets = FALSE, axes = NULL){
x <- rlang::enquo(x)
y_left <- rlang::enquo(y_left)
y_right <- rlang::enquos(...)
y_left_axparms <- list(
title = FALSE,
tickfont = list(color = "#1f77b4"),
side = "left")
y_right_axparms <- list(
title = FALSE,
overlaying = "y",
side = "right",
zeroline = FALSE)
p <- plotly::plot_ly(data , x = x) %>%
plotly::add_trace(y = y_left, name = quo_name(y_left),
yaxis = "y1", type = 'scatter', mode = 'lines',
line = list(color = "#1f77b4"))
p_facets <- list()
for(v in y_right){
p_facets[[quo_name(v)]] <- p %>%
plotly::add_trace(y = v, name = quo_name(v),
yaxis = "y2", type = 'scatter', mode = 'lines') %>%
plotly::layout(yaxis = y_left_axparms,
yaxis2 = y_right_axparms)
}
p <- subplot(p_facets, nrows = length(y_right), shareX = TRUE)
return(p)
}
mtcars %>%
rowid_to_column() %>%
dual_axis_lines(rowid, mpg, cyl, disp, hp, facets = TRUE)
However, the resulting plots have all the secondary y-axis variables cluttered in the first facet.
The issue seems to be absent when I return p_facets lists that goes into subplot as each plot looks like below:
How can I fix this issue?
Okay, I followed the ideas given in this github issue about your bug.
library(rlang)
library(plotly)
library(tibble)
dual_axis_lines <- function(data, x, y_left, ..., facets = FALSE, axes = NULL){
x <- rlang::enquo(x)
y_left <- rlang::enquo(y_left)
y_right <- rlang::enquos(...)
## I removed some things here for simplicity, and because we want overlaying to vary between subplots.
y_left_axparms <- list(
tickfont = list(color = "#1f77b4"),
side = "left")
y_right_axparms <- list(
side = "right")
p <- plotly::plot_ly(data , x = x) %>%
plotly::add_trace(y = y_left, name = quo_name(y_left),
yaxis = "y", type = 'scatter', mode = 'lines',
line = list(color = "#1f77b4"))
p_facets <- list()
## I needed to change the for loop so that i can have which plot index we are working with
for(v in 1:length(y_right)){
p_facets[[quo_name(y_right[[v]])]] <- p %>%
plotly::add_trace(y = y_right[[v]], x = x, name = quo_name(y_right[[v]]),
yaxis = "y2", type = 'scatter', mode = 'lines') %>%
plotly::layout(yaxis = y_left_axparms,
## here is where you can assign each extra line to a particular subplot.
## you want overlaying to be: "y", "y3", "y5"... for each subplot
yaxis2 = append(y_right_axparms, c(overlaying = paste0(
"y", c("", as.character(seq(3,100,by = 2)))[v]))))
}
p <- subplot(p_facets, nrows = length(y_right), shareX = TRUE)
return(p)
}
mtcars %>%
rowid_to_column() %>%
dual_axis_lines(rowid, mpg, cyl, disp, hp, facets = TRUE)
Axis text the same color as the lines.
For this you would need two things. You would need to give a palette to your function outside of your for-loop:
color_palette <- colorRampPalette(RColorBrewer::brewer.pal(10,"Spectral"))(length(y_right))
If you don't like the color palette, you'd change it!
I've cleaned up the for-loop so it's easier to look at. This is what it would now look like now so that lines and axis text share the same color:
for(v in 1:length(y_right)){
## here is where you can assign each extra line to a particular subplot.
## you want overlaying to be: "y", "y3", "y5"... for each subplot
overlaying_location = paste0("y", c("", as.character(seq(3,100,by = 2)))[v])
trace_name = quo_name(y_right[[v]])
trace_value = y_right[[v]]
trace_color = color_palette[v]
p_facets[[trace_name]] <- p %>%
plotly::add_trace(y = trace_value,
x = x,
name = trace_name,
yaxis = "y2",
type = 'scatter',
mode = 'lines',
line = list(color = trace_color)) %>%
plotly::layout(yaxis = y_left_axparms,
## We can build the yaxis2 right here.
yaxis2 = eval(
parse(
text = "list(side = 'right',
overlaying = overlaying_location,
tickfont = list(color = trace_color))")
)
)
}

Creating a line plot using plot_ly (R) with two y-axes

I am a newcomer to the plot_ly package and am trying to produce a time series line plot with two variables on the y axis.
In my dataframe 'baro' I have 'DateTime' variable in POSIXct format, and 'Pressure' and 'Temperature' in numeric format.
I am basing my code off the example given here: https://plot.ly/r/multiple-axes/
p <- plot_ly(baro)
add_trace(p, x = ~DateTime, y = ~Pressure, type = "scatter",
mode = "lines", name = "Pressure")
add_trace(p, x = ~DateTime, y = ~Temperature, type = "scatter",
mode = "lines", name = "Temperature", yaxis = "y2")
layout(p,
title = "Pressure & Temperature", yaxis2 = ay,
xaxis = list(title="x")
)
This outputs a set of axes labelled -1 to 6 on the x axis and -1 to 4 on the y axis with no data plotted.
I prefer use pipes %>% rather than attribute an object to a plot.
When you have 2 Y-axis it's nice to set the layout of every one explicitly.
This should do what you want:
# Build randon data
set.seed(123)
baro = data.frame(DateTime = as.POSIXct(1:10,origin = "2019-01-01"),
Pressure = sample(1000:2000,10),
Temperature = sample(20:60,10)
)
# Build plot
baro %>%
plot_ly(type = "scatter", mode = "lines") %>%
add_trace(x = ~DateTime, y = ~Pressure, name = "Pressure")%>%
add_trace(x = ~DateTime, y = ~Temperature, name = "Temperature", yaxis = "y2") %>%
layout(title = "Pressure & Temperature",
yaxis = list(title = "Pressure"),
yaxis2 = list(title = "Temperature",
overlaying = "y",
side = "right"
)
)
Here the output:
Best regards.

Ordered bars on plotly

I'm trying to make a CPU Usage graph with Plotly and R, I want the max usage (100%, the blue bar) on top, and the other one on bottom, but when I try this code
plot_ly( x = cores, y = max, type = 'bar', name = 'Free') %>%
add_trace( y = data, name = 'Used') %>%
layout(
title = "CPU Usage",
font = list(family = 'Comic Sans MS'),
yaxis = list(title = 'Percent'),
xaxis = list(title = '', tickangle = -45),
barmode = 'stack')
})
It gives me the reverse, ordering the greater bar on bottom, and the orange one on top. I want to invert it.
I searched on some references but nothing was found about that...
We do not know your data but:
cores <- c("Average", "Core1", "Core2", "Core5")
Free <- c(65, 60, 80,50)
Used <- c(100-65, 40, 20,50)
data <- data.frame(cores, Free, Used)
plot_ly(data, x = ~cores, y = ~Used, type = 'bar', name = 'Used') %>%
add_trace(y = ~Free, name = 'Free') %>%
layout(yaxis = list(title = '%'), barmode = 'stack')

Overlaying two histograms in R Plotly

I'm trying to overlay two histogram plots in R plotly. However only one of them shows up. Here's the code I'm using with some random data:
myDF <- cbind.data.frame(Income = sample(1:9, size = 1000, replace= TRUE),
AgeInTwoYearIncrements = sample(seq(from = 2, to = 70, by = 2), size = 1000, replace = TRUE))
plot_ly(data = myDF, alpha = 0.6) %>%
add_histogram(x = ~Income, yaxis = "y1") %>%
add_histogram(x = ~AgeInTwoYearIncrements, yaxis = "y2") %>%
layout(
title = "Salary vs Age",
yaxis = list(
tickfont = list(color = "blue"),
overlaying = "y",
side = "left",
title = "Income"
),
yaxis2 = list(
tickfont = list(color = "red"),
overlaying = "y",
side = "right",
title = "Age"
),
xaxis = list(title = "count")
)
Any help would be much appreciated!
It is the main cause to give the 1st yaxis overlaying. And because xaxis is count, Income and Age is y.
plot_ly(data = myDF, alpha = 0.6) %>%
add_histogram(y = ~Income, yaxis = "y1") %>% # not `x =`
add_histogram(y = ~AgeInTwoYearIncrements, yaxis = "y2") %>%
layout(
title = "Salary vs Age",
yaxis = list(
tickfont = list(color = "blue"),
# overlaying = "y", # the main cause is this line.
side = "left",
title = "Income"
),
yaxis2 = list(
tickfont = list(color = "red"),
overlaying = "y",
side = "right",
title = "Age"
),
xaxis = list(title = "count")
)
[Edited: just flip]
plot_ly(data = myDF, alpha = 0.6) %>%
add_histogram(x = ~ Income, xaxis = "x1") %>%
add_histogram(x = ~ AgeInTwoYearIncrements, xaxis = "x2") %>%
layout(
margin = list(t = 60),
title = "Salary vs Age",
xaxis = list(
tickfont = list(color = "blue"),
side = "left",
title = "Income"
),
xaxis2 = list(
tickfont = list(color = "red"),
overlaying = "x",
side = "top",
position = 0.95,
title = "<br>Age"
),
yaxis = list(title = "count")
)
You can mix histograms:
plot_ly(data = myDF, alpha = 0.6) %>%
add_histogram(x = ~Income) %>%
add_histogram(x = ~AgeInTwoYearIncrements) %>%
layout(
title = "Salary and Age",
yaxis = list(
tickfont = list(color = "blue"),
overlaying = "y",
side = "left",
title = "count"
),
xaxis = list(title = "Salary and Age value")
)
A histogram has normally on the y-axis the frequency / count and not on the x-axis. We can produce a diagram like you want but I'm not sure if it is still a histogram.
Also, like you see in my picture you the frequency/count for salary (here blue) is more high and the variability is less then age. That make it difficult for a good looking diagram. Maybe this is just a problem of your sample data...
So When you like to go with the histogram function, you have to invert the meaning of the frequency and the value on the x-axis.
But anyway, I think a scaternplot would be a better solution to show the relation between salary and age.
edit:
This is the result I get when I run your code:
Like this I don't see the sense in the plot and what you want. The meaning of the first orange colum is that a age of 59 occurs between 0 and 5 times in your dataset. The third colum means a age of 88 ocours between 10 and 15 times in your dataset.
To present this information in a barplot don't work. Because you can have several Age-values in on categorie of counts...I hope this is clear.
Anyway, to answer your question I need more clarification.
Following the responses here, I wanted to answer this with an example that others can easily use when for instance plotting two overlapping histograms.
# Add required packages
library(plotly)
# Make some sample data
a = rnorm(1000,4)
b = rnorm(1000,6)
# Make your histogram plot with binsize set automatically
fig <- plot_ly(alpha = 0.6) # don't need "nbinsx = 30"
fig <- fig %>% add_histogram(a, name = "first")
fig <- fig %>% add_histogram(b, name = "second")
fig <- fig %>% layout(barmode = "overlay",
yaxis = list(title = "Frequency"),
xaxis = list(title = "Values"))
# Print your histogram
fig
And here is the result of the code:
Easy way to handle any number of dimensions without repetition
TL;DR: You can rearrange your data to long-form before passing it to plot_ly().
df |>
mutate(row_number = row_number()) |>
pivot_longer(!row_number) |>
plot_ly() |>
add_histogram(x = ~ value,
color = ~ name,
opacity = 0.5) |>
layout(barmode = 'overlay')
Explanation
Given a DF with multiple columns, like the one the OP posted:
df = cbind.data.frame(Income = sample(1:9, size = 1000, replace= TRUE),
AgeInTwoYearIncrements = sample(seq(from = 2, to = 70, by = 2), size = 1000, replace = TRUE))
Then, using tidyr::pivot_longer():
df |> mutate(row_number = row_number()) |> pivot_longer(!row_number)
This gives:
# A tibble: 2,000 × 3
row_number name value
<int> <chr> <dbl>
1 1 Income 1
2 1 AgeInTwoYearIncrements 20
3 2 Income 1
4 2 AgeInTwoYearIncrements 48
5 3 Income 3
6 3 AgeInTwoYearIncrements 26
7 4 Income 4
8 4 AgeInTwoYearIncrements 30
9 5 Income 4
10 5 AgeInTwoYearIncrements 60
# … with 1,990 more rows
Finally, just pipe this to plot_ly(), so the full command is:
df |>
# Add a column to keep track of the row numbers
mutate(row_number = row_number()) |>
# Squash and lengthen the df with one row per row per column (in this case, double its length)
pivot_longer(!row_number) |>
plot_ly() |>
# The magic is here. We set color to track the name variable, which will
# add a separate series per column.
# We set the opacity so we can see where our plots overlap.
add_histogram(x = ~ value,
color = ~ name,
opacity = 0.5) |>
# Without setting this, bars will be plotted side by side for the same x value
# rather than overlapping.
layout(barmode = 'overlay')
Output

Resources