I am outputting a scatterplot in R using plotly with the code below:
library(tidyverse)
library(plotly)
set.seed(1)
data.frame(state = c(rep(state.name, 2)),
value = sample(1:100, 100)) %>%
plot_ly(x = ~value,
y = ~state,
type = "scatter",
mode = "markers") %>%
layout(title = list(text = "State Information"))
The issue that I am running into is that the code above renders a plot with an excessive amount of space between the y-axis and the plot title and x-axis ticks respectively:
Can anyone tell me how I can shrink this space so that the plot margins are tighter?
Edit: I know that a similar question was asked here, but this relates to a numeric y-axis, not a categorical one:
R Plotly - Large unused space on top and bottom of graph when setting height
We can use the same procedure for a categorical axis.
Please run schema() and navigate: object ► layout ► layoutAttributes ► yaxis ► range:
[...] If the axis type is category, it should be numbers, using
the scale where each category is assigned a serial number from zero in
the order it appears.
library(plotly)
library(datasets)
set.seed(1)
DF <- data.frame(state = c(rep(state.name, 2)),
value = sample(1:100, 100))
plot_ly(
data = DF,
x = ~ value,
y = ~ state,
type = "scatter",
mode = "markers"
) %>%
layout(
title = list(text = "State Information"),
xaxis = list(
range = ~ c(-1, length(unique(value)) + 1)
),
yaxis = list(
range = ~ c(-1, length(unique(state))),
title = list(text = "state", standoff = 0L)) # maybe needed? sets distance between title and labels
)
Related
I want to create an interactive histogram in R using crosstalk. Specifically, I want to use a slider to select what data appears on a histogram. To do so, I used the following code:
shared_data <- highlight_key(mpg)
widgets <- bscols(
widths = 12,
filter_slider("displ", "displ", shared_data, ~displ))
bscols(widths = 10, widgets,
plot_ly(x = ~mpg$displ, type = "histogram",
histnorm = "probability"))
This creates a histogram as well as an interactive slider. However, the slider doesn't actually do anything.
I've tried an alternate piece of code to do this, but similarly to the previous code, it creates the histogram and a slider which fails to filter the data.
shared_data <- mpg %>%
SharedData$new()
plot_ref <- plot_ly(x = ~mpg$displ, type = "histogram",
histnorm = "probability") %>%
layout(title = "Reference Histogram (Displ)",
xaxis = list(title = "Displ"),
yaxis = list(title = "Percentage (%)"))
bscols(widths = 10,
list(filter_slider(id = "slider_ap", label = "Displ",
sharedData = shared_data, column = ~displ),
plot_ref))
Can anyone explain what is wrong with the code above? I read somewhere that crosstalk interactivity isn't specifically optimized for histograms, could this be the reason it doesn't work? Any help is greatly appreciated!
The purpose of SharedData is to share the data. When you called the plot, you didn't use the shared data, so the filter had no way of matching the plot.
Check it out:
shared_data <- mpg %>%
SharedData$new()
plot_ref <- plot_ly(data = shared_data, # <- share it
x = ~displ, type = "histogram",
histnorm = "probability") %>%
layout(title = "Reference Histogram (Displ)",
xaxis = list(title = "Displ"),
yaxis = list(title = "Percentage (%)"))
bscols(widths = 10,
list(filter_slider(id = "slider_ap", label = "Displ",
sharedData = shared_data, column = ~displ),
plot_ref))
Context & problem:
I am trying to show the evolution of a value over time and some events that occured during the same period. One x-axis shows dates and I would like to get another x-axis, on top of the plot, that shows alternative tick-labels for dates i.e. events.
Here is a ggplot version of this plot:
library(ggplot2)
# here is a dummy dataset
df <- data.frame(
timeaxis = seq.Date(from = as.Date.character("2020-01-01"),
to = as.Date.character("2020-02-01"),
by = "days"),
avalue = runif(32)
)
# now I want to add a secondary axis to show events that occured during this time period
df_event <- data.frame(
eventdate = as.Date.character(c("2020-01-05", "2020-01-17", "2020-01-20", "2020-01-25")),
eventlabel = c("diner", "exam", "meeting", "payday")
)
# and the basic plot related to it
p <- ggplot(data = df, aes(x = timeaxis, y = avalue)) +
geom_line()
# I can add a new axis like so:
p <- p + scale_x_date(sec.axis = sec_axis(
~ .,
breaks = df_event$eventdate,
labels = df_event$eventlabel
))
print(p)
Created on 2022-02-11 by the reprex package (v2.0.1)
I need to use plotly for the awesome rangeslider that adds a very nice to use way to zoom in specific time periods.
Demo:
library(dplyr)
library(plotly)
plotly::ggplotly(p) %>%
plotly::layout(
# as you can see the rangeslider is linked to xaxis, is it possible to link it to xaxis2?
xaxis = list(rangeslider = list(visible = TRUE))
)
# note that using rangeslider() does not show the plot right...
As you can see, the second x-axis is not showing. Problem is that plotly does not have, yet, a good handling of secondary axis. First, if you use ggplot2::scale_x_date(sec.axis = ggplot2::sec_axis() to get a secondary x-axis, it is not transfered to the plotly plot by using plotly::ggplotly(). Second, if you manually define a secondary x-axis using plotly functions (as proposed here or there, this axis does not change according to the rangeslider (I guess because the rangeslider is actually bound to xaxis and should de defined for xaxis2 as well). Not to mention, I was not able to change the labels of the breaks to get the "events" displayed (even the example on
Demo:
plotly::ggplotly(p) %>%
# making an invisible trace
plotly::add_lines(
data = df_event,
x = ~ eventdate,
y = 0,
color = I("transparent"),
hoverinfo = "skip",
showlegend = FALSE,
xaxis = "x2"
) %>%
plotly::layout(
xaxis = list(rangeslider = list(visible = TRUE)),
xaxis2 = list(overlaying = "x", side = "top")
)
And with the actual event labels:
plotly::ggplotly(p) %>%
# making an invisible trace
plotly::add_lines(
data = df_event,
x = ~ eventdate,
y = 0,
color = I("transparent"),
hoverinfo = "skip",
showlegend = FALSE,
xaxis = "x2"
) %>%
plotly::layout(
xaxis = list(rangeslider = list(visible = TRUE)),
xaxis2 = list(overlaying = "x",
side = "top",
tickvals = df_event$eventdate,
ticktext = df_event$eventlabel)
)
I tried to add matches = "x", anchor = "x", scaleanchor = "x" to xaxis2 list but nothing changed.
Question:
How to make a second axis in a plotly plot that reacts to "zoom" using rangeslider?
If you think there is a better way of achieving this, please go ahead! Any idea is very welcome, I am quite new to plotly and I certainly have overlooked its functionalities.
I'm looking to see if there's a way to change the order of the hoverlabels when using hovermode = "x unified" in the newest version of the R package of plotly (4.9.3). Alternatively, is it possible to revert back to the way the old version of plotly displayed the hoverlabels while still using the current version of the plotly package? From a data visualization perspective, the old way is much clearer in my opinion.
I've included a minimum reproducible example below. When I run this using plotly v4.9.2.1, I get the result shown in Figure A and when I run it in plotly v4.9.3, I get the result shown in Figure B. The benefits to Figure A over Figure B are:
Figure A labels are in descending order relative to the data on each line at the time specified. Also this is reactive to the time period, so if one line moves above another at a different time period, the relative positioning of the label also moves to reflect the ordering of the data. You can see in Figure B that the dark green (y1) line has the lowest value (66) yet it is shown at the top of the hoverlabel box. In figure B, the y1 label is at the bottom.
Figure A labels are attached to the individual lines, so its easier to see the hovertext as it applies to the line in question
Figure A:
Figure B:
Code:
library(plotly)
library(tidyr)
df <- data.frame(Date = seq(as.Date("2018-01-01"),
as.Date("2021-01-01"),
by = "months"),
stringsAsFactors = F)
df$y1 <- seq(0, 100, length.out = nrow(df))
df$y2 <- seq(0, 600, length.out = nrow(df))
df$y3 <- seq(0, 300, length.out = nrow(df))
df$y4 <- seq(0, 200, length.out = nrow(df))
df <- df %>%
pivot_longer(cols = -Date,
names_to = "yname",
values_to = ) %>%
arrange(yname, Date)
mycols <- c("#006633", "#70AD47", "#1F4E78", "#2F75B5", "#C65911", "#EF8C4F",
"#C00000", "#FF8B8B", "#7030A0", "#9966FF")
mycols <- mycols[1:length(unique(df$yname))]
p <- plot_ly()
p <- p %>%
add_trace(data = df,
x = ~Date,
y = ~value,
text = ~yname,
hovertemplate = paste('<b>%{text}</b>',
'<br>%{x}',
'<br>%{y}',
'<extra></extra>'),
color = ~yname, colors = mycols,
name = ~yname, yaxis = "y",
type = "scatter", mode = "lines",
showlegend = T)
p <- p %>%
layout(hovermode = "x unified",
legend = list(x = 1.12, y = .5, xanchor = "left"),
yaxis = list(fixedrange = T),
xaxis = list(title = "",
fixedrange = T,
hoverformat = "%b %d, %Y"),
showlegend = T)
p
Two answers:
the ordering of traces in the unified hoverlabel is always the same, regardless of the relative Y values of the traces. The order is the same as in the legend, so it will follow the ordering of the colors.
You can revert to the previous behavior with hovermode = "x" rather than hovermode = "x unified"
I have some data like this:
data <- data.frame(x=runif(500), y=runif(500), z=runif(500))
I want a scatterplot with points colored independently/discretely in each dimension (X, Y, and Z) using RGB values.
This is what I have tried:
Code:
library(dplyr)
library(plotly)
xyz_colors <- rgb(data$x, data$y, data$z)
plot_ly(data = data,
x = ~x, y = ~y, z = ~z,
color= xyz_colors,
type = 'scatter3d',
mode='markers') %>%
layout(scene = list(xaxis = list(title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z')))
Plot:
RColorBrewer thinks I'm trying to create a continuous scale from 500 intermediate colors:
Warning messages:
1: In RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
2: In RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
What are some correct ways to color the points like this in R with Plotly?
Also, how can one generally assign colors to data points in R with Plotly, individually?
To clarify, I am trying to color each point where the color is of the format "#XXYYZZ" where 'XX' a value between 00 and FF linearly mapped to the value of data$x from 0 to 1. That is, the X dimension determines the amount of red, the Y dimension determines the amount of green, and the Z dimension determines the amount of blue. At 0,0,0 the point should be black and at 1,1,1 the point should be white. The reason for this is to make as easy to visualize the 3D position of the points as possible.
Updated answer after comments:
So, is there no way to color every point separately?
Yes, there is through the power and flexibility of add_traces(). And it's a lot less cumbersome than I first thought.
Just set up an empty plotly figure with some required 3D features:
p <-plot_ly(data = data, type = 'scatter3d', mode='markers')
And apply add_traces() in a loop over each defined color:
for (i in seq_along(xyz_colors)){
p <- p %>% add_trace(x=data$x[i], y=data$y[i], z=data$z[i],
marker = list(color = xyz_colors[i], opacity=0.6, size = 5),
name = xyz_colors[i])
}
And you can easily define single points with a color of your choice like this:
p <- p %>% add_trace(x=0, y=0, z=0,
marker = list(color = rgb(0, 0, 0), opacity=0.8, size = 20),
name = xyz_colors[i])
Plot:
Complete code:
library(dplyr)
library(plotly)
# data and colors
data <- data.frame(x=runif(500), y=runif(500), z=runif(500))
xyz_colors <- rgb(data$x, data$y, data$z)
# empty 3D plot
p <-plot_ly(data = data, type = 'scatter3d', mode='markers') %>%
layout(scene = list(xaxis = list(title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z')))
# one trace per color
for (i in seq_along(xyz_colors)){
p <- p %>% add_trace(x=data$x[i], y=data$y[i], z=data$z[i],
marker = list(color = xyz_colors[i], opacity=0.6, size = 5),
name = xyz_colors[i])
}
# Your favorite data point with your favorite color
p <- p %>% add_trace(x=0, y=0, z=0,
marker = list(color = rgb(0, 0, 0), opacity=0.8, size = 20),
name = xyz_colors[i])
p
Original answer:
In 3D plots you can use the same color for all of the points, discern different clusters or categories from each other using different colors, or you use individual colors for each point to illustrate a fourth value (or fourth dimension if you like, as described here) in your dataset. All these approaches are, as you put it, examples of '[...] correct ways to color the points [...]'. Have a look below and see if this suits your needs. I've included fourthVal <- data$x+data$y+data$z as an example for an extra dimension. What you end up using will depend entirely on your dataset and what you'd like to illustrate.
Code:
library(dplyr)
library(plotly)
data <- data.frame(x=runif(500), y=runif(500), z=runif(500))
xyz_colors <- rgb(data$x, data$y, data$z)
fourthVal <- data$x+data$y+data$z
plot_ly(data = data,
x = ~x, y = ~y, z = ~z,
color= fourthVal,
type = 'scatter3d',
mode='markers') %>%
layout(scene = list(xaxis = list(title = 'X'),
yaxis = list(title = 'Y'),
zaxis = list(title = 'Z')))
Plot:
I am having an issue with a plotly bar plot when I define the date range for the x-axis.
When there is one or more data points with the same x-value, the bars do not show in the plot. If there is at least two different x-values or if I do not use a x-axis range, then the bars show as they should.
Below follows an example (I am currently using lubridate to deal with dates).
library(lubridate)
library(plotly)
# Same x-value: bar does not show
plot_ly(x = c(ymd("2019-08-25"), ymd("2019-08-25")), y = c(1, 2), type = "bar") %>%
layout(xaxis = list(range = ymd(c("2019-08-20", "2019-08-30"))))
# Different x-values: bars are shown
plot_ly(x = c(ymd("2019-08-25"), ymd("2019-08-26")), y = c(1, 2), type = "bar") %>%
layout(xaxis = list(range = ymd(c("2019-08-20", "2019-08-30"))))
# No x-axis range defined, same x-values: the bar is shown
plot_ly(x = c(ymd("2019-08-25"), ymd("2019-08-25")), y = c(1, 2), type = "bar")
Any solution?
Edit: For comparison, ggplot2 does not have the same issue:
# ggplot works like expected
library(lubridate)
library(ggplot2)
ggplot(NULL, aes(x = ymd(c("2019-08-25", "2019-08-25")), y = c(1, 2))) +
geom_col() +
xlim(ymd(c("2019-08-20", "2019-08-30")))
Your code is actually being understood in your first version, but you need to set the width of the bars so they show up in the end.
I'm not sure what the units are (maybe miliseconds???) so you may need to play around with it or do research to get a good width for your actual scenario.
plot_ly() %>%
add_bars(x = c(ymd("2019-08-25"), ymd("2019-08-25")), y = c(1, 2), type = "bar",width=100000000)%>%
layout(xaxis = list(range = ymd(c("2019-08-20", "2019-08-30"))))