How can I create a subplot grid with Plotly in R?
The official site has this nice Python example:
The python code has the option rows=2 and cols=2, but in R the subplot function has just the parameter nrows, without ncols.
I tried this example in R, but nrows do not seam to work as expected:
# Basic subplot
library(plotly)
p <- plot_ly(economics, x = date, y = uempmed)
subplot(p,p,p,p,
margin = 0.05,
nrows=2
) %>% layout(showlegend = FALSE)
They are in a line instead of in a grid. See the result:
Here is the R suplots page for reference. Unfortunately, use ggplotly is not a option for me, like this
UPDATE
It was a bug. Plotly team is very fast, and it was fixed in just 3 days (check here)! Github version is already updated. Great job!
This seems to be a genuine bug in the way subplot() generates the y-axis domains for the two plots. Indeed, they overlap which can easily be seen if you execute
p <- plot_ly(economics, x = date, y = uempmed)
q <- plot_ly(economics, x = date, y = unemploy)
subplot(p,q, nrows = 2)
This will produce the following plot:
If you take a close look at the y-axis you see that they overlap. That hints at a problem in the way subplot() defines the domain of the y-axes of the subplot.
If we correct the domain specification of the y-axes manually (following the plotly documentation), we can solve the problem:
subplot(p,q, nrows = 2) %>% layout(yaxis = list(domain = c(0, 0.48)),
yaxis2 = list(domain = c(0.52, 1)))
This produces:
Now, if you want to reproduce the 4x4 subplot matrix similar to the Python example, you probably need to manually adjust the x-axis domains in a similar way.
Since this is a bug and my solution is only a workaround, I suggest, however, that you file an issue with plotly on GitHub.
Based on this:
p <- economics %>%
tidyr::gather(variable, value, -date) %>%
transform(id = as.integer(factor(variable))) %>%
plot_ly(x = ~date, y = ~value, color = ~variable, colors = "Dark2",
yaxis = ~paste0("y", id)) %>%
add_lines() %>%
subplot(nrows = 5, shareX = TRUE)
Related
Please help ! This question is very similar to this one that has been answered quite some time ago. However, I still cannot get my head around the solution:
How to create a chloropleth map in R Plotly based on a Categorical variable?
I'm trying to create an interactive Choropleth map in R for my shiny app based on categorical data, using plotly and sf data get from GADM. Here is a reproducible example:
library(raster)
library(tidyverse)
library(plotly)
library(sf)
# Get the map data in sf format
map_data <- getData("GADM", country = "FRA", level = 2, type = "sf")
# Transform sf data to modern crs object to avoid further warning message
st_crs(map_data) <- st_crs(map_data)
# Generate some random data for each region
department <- map %>% as.data.frame() %>% .[, 13]
set.seed(10, sample.kind="Rounding")
data <- sample(x = 0:1200, size = length(department), replace = T)
map_dat <- data.frame(department = department,
data = data)
# Assign Class as categories
map_dat <- map_dat %>%
mutate(Class = cut(data,
breaks = c(-Inf, 50, 100, 200, 500, 1000, Inf),
labels = c("< 50", "50 - 100", "100 - 200",
"200 - 500", "500 - 1000", "> 1000")))
# Join data and plot
plot_dat <- map %>% as.data.frame() %>%
left_join(map_dat, by = c("HASC_2" = "department")) %>%
st_as_sf()
plot_ly(plot_dat) %>%
add_sf(type = "scatter",
stroke = I("transparent"),
span = I(1),
alpha = 1,
split = ~NAME_2,
color = ~Class,
colors = "Reds",
text = ~paste0(NAME_2, "\n", data),
hoveron = "fills",
hoverinfo = "text") %>%
config(displayModeBar = F)
The problem that I have with the default legend is that it's too detail and cumbersome in some way, as I just want to display a small and compact box filled with categorical class that I assign, similar to the one in ggplot. I've try to split the map with categorical Class and the legend looks a little bit better, however my hover text does not work anymore, and still, I have no idea how to edit or change the style of the legend in order for it to look decent and neat, like to change the key symbols to become a circle or a square like we usually see in a legend map box.
plot_dat %>% plot_ly() %>%
add_sf(type = "scatter",
stroke = I("transparent"),
span = I(1),
alpha = 1,
split = ~Class,
color = ~Class,
colors = "Reds",
text = ~paste0(NAME_1, "\n", Count),
hoveron = "fills",
hoverinfo = "text") %>%
config(displayModeBar = F) %>%
layout(showlegend = F)
I've read through the documentation for plotly's Choropleth map in R and yet found no documentaion for categorical case (unlike Python). As I'm running out of options, my question is, is there any way to achive my desired goal here ? How can I create a proper legend box, or a bar for categorical data ?
Apologies if my question is not that clear. I'm eager to answer any questions if anyone has. Thank you in advance.
After getting it to work, I can't help but think...there has to be a better way.
This uses the library spPlot. This isn't a Cran package. Use the following to get this one.
devtools::install_github("GegznaV/spPlot")
You'll likely run into the same dependency issue I ran into. You will need the package ChemometricsWithR to get spPlot. It will try to install that package, but it's another one that you need to get through alternative means.
devtools::install_github("rwehrens/ChemometricsWithR")
I'm using the first plot_ly call you made. I added a few things: legendgroup = ~Class, name = ~Class, and showlegend = F. Then I piped in the function plotly_modify_legend to add the grouped legend.
(plt <- plot_ly(plot_dat) %>%
add_sf(type = "scatter",
stroke = I("transparent"),
span = I(1),
alpha = 1,
split = ~NAME_2,
legendgroup = ~Class, # group legends together by class
name = ~Class, # so region names aren't shown in the legend
color = ~Class,
showlegend = F, # don't show a legend for each region
colors = "Reds",
text = ~paste0(NAME_2, "\n", data),
hoveron = "fills",
hoverinfo = "text") %>%
config(displayModeBar = F) %>%
plotly_modify_legend(showlegend = T, traceorder = "grouped")) # group legend visible
Plotly orders the legend in the order in which the elements appear, which is inherently alphabetical. That equates to having a really odd order in the legend.
The 96 separate subplots (one for each region) needed to be reordered to fix this. I could have just found one of each and changed the first 6 (one for each legend group), but I didn't think that would be easier.
Instead, I captured the traces, extracted their assigned group, and reordered them. After that, I replaced the traces in the plotly object.
# extract the legend groups
trOrder = map(1:length(plt$x$data),
~plt$x$data[[.x]]$legendgroup) %>%
unlist()
# assign an order to the groups and reorder the data
newOrder = data.frame(id = 1:96, trOrder = trOrder) %>%
mutate(trOrder = factor(trOrder, levels(plot_dat$Class))) %>%
arrange(trOrder)
# reorder the traces using the indicies
a = map(newOrder$id,
~plt$x$data[[.x]])
# check it
a[[1]]$legendgroup
# [1] "< 50"
# replace the traces
plt$x$data <- a
plt # mum--nikan-tez - ausgezeichnet - travail exceptionnel - Фантастический
Now the legend makes sense.
I am using R, package plotly and I have problem with connecting first and last point in my graph. I want to avoid it. The code is following:
graph<-plot_ly(data, x = ~date, y = ~variable, z = ~value, mode="lines")
I tried google some solution, but nothing work so far.
The graph looks like this.
Can anyone help?
If I understand correctly, you don't want the lines in variables 1, 2, 3... to be connected to each other, right?
If this is the case, I think what is happening is that plotly is assuming all your data belongs to the same series.
You need to tell it that the data from each variable is a different series. You can do it by mapping the variable to an attribute of the line (color, linetype, stroke, etc...).
library(tidyverse)
library(plotly)
# Create a data set from EuStockMarkets data for this example
# (this is just to put the data in a dataframe in the same format as your dataset. You can skip this part)
df.data <- EuStockMarkets %>% as.data.frame() %>%
dplyr::mutate(date=time(EuStockMarkets)) %>%
dplyr::mutate(year=as.integer(floor(date))) %>%
dplyr::mutate(day.of.year = ceiling((date-year) * 365)) %>%
dplyr::mutate(date=ymd(sprintf('%4d-01-01', year))+ days(day.of.year)) %>%
dplyr::select(-year, -day.of.year) %>%
tidyr::pivot_longer(-date, names_to = "variable") %>%
dplyr::arrange(variable, date)
plot_ly(data=df.data, x = ~date, y = ~variable, z = ~value, type="scatter3d", mode='lines', color = ~variable)
If you don't want the lines in each variable to have different colors, you can use the split argument, which will create a different trace (i.e., line) for each value of variable. I had to set the color of the line manually otherwise plotly set a different color automatically. I also removed the legend.
plot_ly(data=df.data, x = ~date, y = ~variable, z = ~value, type="scatter3d", mode='lines', split=~variable, color=I('black')) %>% layout(showlegend = FALSE)
If you can add the first point in you list again, but this time after the last point, then a line between last to first will be added. Or you can add a curve to the plot consisting of the first and last oint only.
The Background
I am using the plotly API in R to create two linked plots. The first is a scatter plot and the second is a bar chart that should show the percentage of data belonging to each category, in the current selection. I can't make the percentages behave as expected.
The problem
The plots render correctly and the interactive selection works fine. When I select a set of data points in the top scatter plot, I would like to see the percentage of that selection that belongs to each category. Instead what I see is the percentage of points in that selection in that category that belong to that category, in other words always 100%. I guess this is because I set color = ~c which applies a grouping to the category.
The Example
Here is a reproducible example to follow. First create some dummy data.
library(plotly)
n = 1000
make_axis = function(n) c(rnorm(n, -1, 1), rnorm(n, 2, 0.25))
data = data.frame(
x = make_axis(n),
y = make_axis(n),
c = rep(c("A", "B"), each = n)
)
Create a sharedData object and supply it to plot_ly() for the base plot.
shared_data = data %>%
highlight_key()
baseplot = plot_ly(shared_data)
Make the individual panels.
points = baseplot %>%
add_markers(x = ~x, y = ~y, color = ~c)
bars = baseplot %>%
add_histogram(x = ~c, color = ~c, histnorm = "percent", showlegend = FALSE) %>%
layout(barmode = "group")
And put them together in a linked subplot with selection and highlighting.
subplot(points, bars) %>%
layout(dragmode = "select") %>%
highlight("plotly_selected")
Here is a screenshot of this to illustrate the problem.
An Aside
Incidentally when I set histnorm = "" in add_histogram() then I get closer to the expected behaviour but I do want percentages and not counts. When I remove color = ~c then I get closer to the expected behaviour but I do want the consistent colour scheme.
What have I tried
I have tried manually supplying the colours but then some of the linked selection breaks. I have tried creating a separate summarised data set from the sharedData object first and then plotting that but again this breaks the linkage between the plots.
If anyone has any clues as to how to solve this I would be very grateful.
To me it seems the behaviour you are looking for isn't implemented in plotly.
Please see schema(): object ► traces ► histogram ► attributes ► histnorm ► description
However, here is the closest I was able to achive via add_bars and perprocessing the data (Sorry for adding data.table, you will be able to do the same in base R, just personal preference):
library(plotly)
library(data.table)
n = 1000
make_axis = function(n) c(rnorm(n, -1, 1), rnorm(n, 2, 0.25))
DT = data.table(
x = make_axis(n),
y = make_axis(n),
c = rep(c("A", "B"), each = n)
)
DT[, grp_percent := rep(100/.N, .N), by = "c"]
shared_data = DT %>%
highlight_key()
baseplot = plot_ly(shared_data)
# Make the individual panels.
points = baseplot %>%
add_markers(x = ~x, y = ~y, color = ~c)
bars = baseplot %>%
add_bars(x = ~c, y = ~grp_percent, color = ~c, showlegend = FALSE) %>%
layout(barmode = "group")
subplot(points, bars) %>%
layout(dragmode = "select") %>%
highlight("plotly_selected")
Unfortunately, the resulting hoverinfo isn't really desirable.
Is it possible to add vertical line to a boxplot in plotly? I know it works in ggplot2, but I need it in plotly. Would be nice if I don't need to convert my static ggplot every time.
Here is a minimal example
plot_ly(x = ~rnorm(50), type = "box") %>%
add_trace(x = ~c(0.75),y=c(-2,0.5),type='scatter',mode='lines')
Instead of the line stoping before I want the line to go through the boxplot. In addition I want the same plot extent as the single boxplot.
Change the sequence of calls:
library(plotly)
plot_ly(x = ~c(0.75), y=c(-2,2), type='scatter', mode='lines') %>%
add_boxplot(x = ~rnorm(50), inherit = F) %>%
layout(yaxis = list(range = c(-2, 2)))
I am new to plotly and want to make my own bullet chart (a bit like http://bl.ocks.org/mbostock/4061961) that has markers/traces to show the values of the relevant values when comparing actual vs expected.
Below is my attempt:
q <- ggplot(data.frame(measure='',actual=25),aes(x=measure,y=actual))+
geom_bar(stat='identity')+
ylim(c(0,35))+
geom_hline(yintercept = 30,color='red')+
geom_text(y=30,label='Expected',angle=270,vjust=0)+
coord_flip()+
ylab('Num. of carrots')
q
q1 <- ggplotly(q) %>% add_markers()
q1
When converting it to plotly using ggplotly, the text looks like it has not rotated correctly, and the markers/traces do not show for the bar chart...Any help here would be much appreciated.
Kindest regards,
HLM
I do not think that plotly supports rotating text for type="scatter" (which is how ggplotly is interpreting your geom_text). You can delete the geom_text line from the ggplot graph, then add text to the plotly one using annotations:
q1 <- ggplotly(q) %>% add_markers() %>%
layout(annotations = list(x = 30, y = 1, text = "Expected", textangle=270))
q1
update
The 2nd part of your question (how to also get hover info on the bar) is slightly trickier. To get the hover info, we can create the bars using the plotly API directly like this
p.bars = plot_ly(data = data.frame(measure='', actual=25)) %>%
add_bars(y=~measure, x=~actual, orientation="h")
we can add the text annotation to it like this
p.bars.text = p.bars %>%
layout(annotations = list(x = 30, y = 0, text = "Expected", textangle=270,
showarrow=F, xanchor="center"))
But the problem is that adding a line also to this plot by
p.bars.text %>% add_segments(x=30, xend=30, y=-0.5, yend=0.5)
gives an error
Error in populate_categorical_axes(p) : Can't display both discrete & non-discrete data on same axis
I.e. we can only specify the y values of the line with respect to categorical values of y. So, for example we can do
dat1 = data.frame(measure=letters[1:2], actual=c(25,20))
plot_ly(data = dat1) %>%
add_bars(y=~measure, x=~actual, orientation="h") %>%
layout(annotations = list(x = 29, y = 0, text = "Expected", textangle=270,
showarrow=F, xanchor="center")) %>%
add_segments(x=30, xend=30, y='a', yend='b')
which gives the following in which the line is aligned with the category labels rather than with the width of the bars
The only solution I have to this at the moment is to use a numeric axis for the categories, and then set the axis labels using ticktext:
plot_ly(data = data.frame(measure=c(0,1), actual=c(25,20))) %>%
add_bars(y=~measure, x=~actual, orientation="h", showlegend=F) %>%
add_segments(x=30, xend=30, y=-0.4, yend=0.4, showlegend=F) %>%
layout(annotations = list(x = 29.5, y = 0, text = "Expected", textangle=270, showarrow=F, xanchor="center"),
yaxis = list(tickmode="array", tickvals=c(0,1), ticktext=c("", "") ))