I must be having trouble with less than a one liner,
but have a look at the snippet below: I cannot the legend option to enter the text that I want in the legend as the name of the species (say, "foo1", "foo2", "foo3").
Notice that I do not want to change the original (iris in this case) dataset.
Any suggestion?
library(tidyverse)
library(plotly)
plot_ly(iris, x = ~Sepal.Length,
y = ~Sepal.Width,
type = 'scatter', color = ~Species,
symbol = ~Species,
mode = 'markers') %>%
layout(legend=list(title=list(text='My title')))
Plotly considers legend names to be associated with each trace rather than with the legend itself. So redefining names has to occur on each trace. If you don't want to modify the original dataset, you'd have to filter it by Species and add the trace with the new name one at a time, with something like this:
library(tidyverse)
library(plotly)
plot_ly(iris %>% filter(Species == "setosa"),
x = ~Sepal.Length,
y = ~Sepal.Width,
type = 'scatter',
color = ~Species,
symbol = ~Species,
mode = 'markers',
name = "foo 1") %>%
add_trace(data = iris %>% filter(Species == "versicolor"),
x = ~Sepal.Length,
y = ~Sepal.Width,
type = 'scatter',
color = ~Species,
symbol = ~Species,
mode = 'markers',
name = "foo 2") %>%
add_trace(data = iris %>% filter(Species == "virginica"),
x = ~Sepal.Length,
y = ~Sepal.Width,
type = 'scatter',
color = ~Species,
symbol = ~Species,
mode = 'markers',
name = "foo 3") %>%
layout(legend=list(title=list(text='My title')))
In a more complex case, it may be best to indeed modify the dataset itself or use a loop. A similar question about a more complex case is here: Manipulating legend text in R plotly and the reference documentation on plotly legend names is here: https://plotly.com/r/legend/#legend-names
Related
I am using Plotly to plot a scatterplot of GWAS data and want to highlight a certain point a different colour to the rest of the data. I have tried multiple times but unable to find away around this in Plotly. Any advice would be great please.
input data looks like this:
fig <- fig %>% add_trace(data=data_1, x = ~BP, y = ~log, name = "data", mode = "markers", type = "scatter",
y = c(117300000, 117900000), marker = list(size = 8, color = '#d62728'),
x = c(117558703), y = c(19.75696195), marker = list(color = 'blue',size = 8), type = "scatter")
fig
One of the easiest ways is to create a variable to identify that specific point. I created sample data here and assigned a colour variable equal to 1 for the point I want in another color.
df = tibble(bp = round(rnorm(10,5,2),2),
log = round(rnorm(10,6,1.5),2))
df$colour <- as.factor(ifelse(df$bp == 4.41,1 ,0))
fig <- plot_ly(data = df, x = ~bp, y = ~log, group_by = ~colour,marker = list(color = factor(df$colour,labels=c("red","purple")))) %>%
add_trace(data = df, x = ~bp, y = ~log, mode = 'markers', type = 'scatter')
fig
Link to plot produced by this code
One option to achieve your desired result would be to add an indicator variable to your data to indicate which points you want to highlight. This variable could then be mapped on the color attribute. The colors could then be set via the colors attribute.
Using a minimal reproducible example base on mtcars:
library(plotly)
data_1 <- mtcars
data_1$highlight <- row.names(data_1) %in% c("Honda Civic", "Porsche 914-2")
plot_ly() %>%
add_trace(
data = data_1, x = ~hp, y = ~mpg, color = ~highlight,
mode = "markers", type = "scatter",
marker = list(size = 8), colors = c("#d62728", "blue")
)
Im trying to display in my hovertext Name,lab and Week by creating a list named customdata which I pass to plot_ly(). The problem is that Im getting the error Size 2: Columns x, y, color, hovertemplate, .plotlyGroupIndex, and 2 more. * Size 6: Column customdata. [34mℹ[39m Only values of size one are recycled. but I do not understand why there is a difference in size. I mean that I get a new dataset after adding lab and cannot make it work due to issue above
library(plotly)
library(dplyr)
full_data<-data.frame("Name"=c("Q1","Q2","Q3","Q1","Q2","Q3"),"Values"=c(245645,866556,26440,65046,641131,463265),
"Week"=c("a","b","c","d","e","f"))
desc <- full_data %>%
group_by(Name,Week) %>%
summarise(values = sum(Values)) %>%
mutate(lab = scales::label_number_si(accuracy = 0.1)(values))
plot_ly(desc,
x = ~Week,
y = ~values,
#text = ~values,
color = ~Name,
colors = c("#60ab3d","#6bbabf","#c4d436","#3e5b84","#028c75","red"),
customdata = mapply(function(x,y) list(x,y), desc$lab, desc$Name, SIMPLIFY = FALSE)) %>%
add_trace(
type = 'scatter',
mode = 'lines+markers',
hovertemplate = paste(
"%{x}",
"%{customdata[0]}",
"%{customdata[1]}",
"<extra></extra>",
sep = "\n"),
hoveron = 'points')
You just need to format your customdata with the vector of character instead
plot_ly(desc,
x = ~Week,
y = ~values,
#text = ~values,
color = ~Name,
# I saw that you have only 3 Name in the sample data so I reduce
# this to only 3 color instead of 7 like you have originally
colors = c("#60ab3d","#6bbabf", "#c4d436"),
# combine the lab & Name from desc data using paste0
customdata = paste0(desc$lab, "\n", desc$Name)) %>%
add_trace(
type = 'scatter',
mode = 'lines+markers',
hovertemplate = paste(
"%{x}",
"%{customdata}",
sep = "\n"),
hoveron = 'points')
Here is the output
This code makes a simple 3d scatter plot of the Fisher iris dataset, with an additional categorical variable added:
library(plotly)
roots <- factor(round(runif(n = dim(iris)[2],min = -.499,max = 2.499)))
my_iris <- cbind(data.frame(roots), iris)
plot_ly() %>%
add_trace(data = my_iris, type = 'scatter3d', mode = "markers",
x = ~Sepal.Length,
y = ~Petal.Length,
z = ~Sepal.Width,
color = ~Species,
colors = c("red","blue","green")
)
By looking at this help page: https://plot.ly/r/marker-style/
I was able to figure out that you can add an outline to the points like this:
plot_ly() %>%
add_trace(data = my_iris, type = 'scatter3d', mode = "markers",
x = ~Sepal.Length,
y = ~Petal.Length,
z = ~Sepal.Width,
color = ~Species,
colors = c("#00FA9A34","#B22222dd","#00BFFFee"),
marker = list(
line = list(
color = "#aabbffdd",
width = 2
)
)
)
Looking at this site https://plot.ly/r/reference/#scatter3d made the idea that lines are a property of scatter3d markers that in turn have the properties color and width make sense.
Now I attempt to map colors to the outlines based on my new roots variable,
plot_ly() %>%
add_trace(data = my_iris, type = 'scatter3d', mode = "markers",
x = ~Sepal.Length,
y = ~Petal.Length,
z = ~Sepal.Width,
color = ~Species,
colors = c("#00FA9A34","#B22222dd","#00BFFF66"),
marker = list(
line = list(
color = ~roots,
colors = c("#000000ff","#f00f3355","#dd22ccbb"),
width = 2
)
)
)
and it doesn't quite work: The first hex+alpha value I use should map to completely opaque black, but that is not one of the colors I get, and I would expect to see legend entries that describe the output.
So my question is: is there a way to do this aesthetic mapping? Perhaps instead of using add_trace I should use add_markers? Is there a way to do this in 2d scatters in Plotly R? Also would appreciate hints on how to learn Plotly for R properly as the documentation page I linked to above is a bit opaque and there seem to be fewer great resources to learn plotly than there are for ggplot2.
it seems to me that plotly doesn't allow to specifying the colors of markers outline.
For the labels you can change the hover text.
Or use annotations:
https://plotly.com/r/text-and-annotations/
Maybe this doesn't solve your problem but I hope that be useful someway.
# Yours data
library(dplyr)
library(plotly)
roots <- as.character(round(runif(n = dim(iris)[2],min = -.499,max = 2.499)))
my_iris <- cbind(data.frame(roots), iris)
# Changing plot
my_iris %>%
plot_ly(type = 'scatter3d', mode = "markers") %>%
add_trace(x = ~Sepal.Length,
y = ~Petal.Length,
z = ~Sepal.Width,
color = ~Species,
colors = c("#00FA9A34","#B22222dd","#00BFFF66"),
marker = list(
line = list(
color = ~roots,
colors = c("#000000","#E35F13","#E3138B"),
width = 6
)
),
hoverinfo = 'text',
text = ~paste('</br> Species: ', Species,
'</br> Petal Length: ', Petal.Length,
'</br> Petal Width: ', Petal.Width,
'</br> Roots: ',roots)
)
The Output:
I was able to find a solution that I still think is inadequate: Set the variable that you are using to color the marker outlines to the colors you want to use themselves:
library(plotly)
roots <- round(runif(n = dim(iris)[1],min = -.499,max = 2.499))
roots_colors <- vector(mode="character", length=length(roots))
#setting the value of roots colors to be the colors we want
roots_colors[roots == 0] <- "#000000ff"
roots_colors[roots == 1] <- "#ff0000ff"
roots_colors[roots == 2] <- "#0000ffff"
my_iris <- cbind(data.frame(roots_colors), iris)
plot_ly() %>%
add_trace(data = my_iris, type = 'scatter3d', mode = "markers",
x = ~Sepal.Length,
y = ~Petal.Length,
z = ~Sepal.Width,
color = ~Species,
colors = c("#00FA9A34","#B22222dd","#00BFFF66"),
marker = list(
line = list(
color = ~roots_colors,
width = 2
)
)
)
We now have the appropriate colors that we want, but no legend describing them. Now I want to know how to make the legend reflect these colors.
I want to achieve the same result as this ggplot code with plotly:
mtcars %>% add_rownames('car') %>%
ggplot(aes(x = mpg,
y = disp,
color = as.factor(gear),
shape = as.factor(cyl))) +
geom_point()
which results in:
My plotly code is:
library(dplyr)
mtcars %>% add_rownames('car') %>%
plot_ly(x = ~mpg,
y = ~disp,
text = ~car,
color = ~as.factor(gear),
symbol = ~as.factor(cyl),
mode = 'markers')
which enumerates all possible combinations of colors and shapes in the legend.
Is there a way to have a similar legend to the ggplot?
UPDATE: To overcome some of the issues mentioned for my previous solution (see below) and to increase the usability of the legend, one can simply add the column name to the legend description and then assign the legendgroups to each category.
mtcars %>% rownames_to_column('car') %>%
plot_ly() %>%
#Plot symbols for cyl
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
symbol = ~paste0(cyl," cyl."),
mode = 'markers',
marker = list(color = "grey", size = 15)) %>%
#Overlay color for gears
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
color = ~paste0(gear, " gears"),
mode = 'markers')
This is the previous solution, which is visually closer to the ggplot2 equivalent:
Based on the answer of dww in this thread, we can manually create the groups for cylinders and gears. Subsequently, with the answer of Artem Sokolov this thread, we can add the legend titles as annotations.
mtcars %>% rownames_to_column('car') %>%
plot_ly() %>%
#Plot symbols for cyl
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
symbol = ~as.factor(cyl),
mode = 'markers',
legendgroup="cyl",
marker = list(color = "grey", size = 15)) %>%
#Overlay color for gears
add_trace(type = "scatter",
x = ~mpg,
y = ~disp,
text = ~car,
color = ~as.factor(gear),
mode = 'markers',
legendgroup="gear") %>%
#Add Legend Titles (manual)
add_annotations( text="Cylinders:", xref="paper", yref="paper",
x=1.02, xanchor="left",
y=0.9, yanchor="bottom", # Same y as legend below
legendtitle=TRUE, showarrow=FALSE ) %>%
add_annotations( text="Gears:", xref="paper", yref="paper",
x=1.02, xanchor="left",
y=0.7, yanchor="bottom", # Y depends on the height of the plot
legendtitle=TRUE, showarrow=FALSE ) %>%
#Increase distance between groups in Legend
layout(legend=list(tracegroupgap =30, y=0.9, yanchor="top"))
Unsolved issues:
Groups have to be created manually
Groups are just overlayed (color over shape). This means that only the whole group can be dis-/activated in the legend (e.g., it is not possible to only show only the entries with 4 cylinders)
The position of the second legend title (annotation) depends on the height of the plot!
Is there any way to make a stacked bar chart only using plot_ly in R? I'm aware a possible solution is to use ggplot and then convert with ggplotly but it doesn't look as nice as other plotly charts. The Plotly site has an example, but the totals stay the same when a category is removed via clicking on the legend.
Make example data:
library(tidyverse)
library(plotly)
# Create some data
grpnames <- c("Thing_3", "Thing_2", "Thing_1")
xval <- as.factor(c(100, 101, 102, 103))
frame <- merge(grpnames, xval, all=T)
yval <- runif(12, 0, .2)
df <- tbl_df(cbind(frame, yval))
colnames(df) <- c("GroupName", "X", "Y")
df.wide <- spread(df, key = GroupName, value = Y)
The stacked bar works:
# Creates a legit stacked bar where values sum to highest point
plot_ly(df, x = ~X, y = ~Y, color = ~GroupName, type='bar') %>%
layout(barmode = 'stack')
I couldn't find an analogue to "barmode = 'stack'" for a line chart:
# Attempt with tidy data
df %>%
plot_ly(
x = ~X,
y = ~Y,
color = ~GroupName,
type='scatter',
mode = 'lines',
fill = 'tonexty',
fillcolor = ~GroupName)
And the example from the Plotly side, attempted here, doesn't add the values of Y for each value of X -- it simply overlays them.
# Attempt with wide data
df.wide %>%
plot_ly(
x = ~X,
y = ~Thing_1,
name = 'Thing 1',
type = 'scatter',
mode = 'none',
fill = 'tozeroy',
fillcolor = 'aquamarine') %>%
add_trace(
x = ~X,
y = ~Thing_2,
name = 'Thing 2',
fill = 'tonexty',
fillcolor = 'orange') %>%
add_trace(
x = ~X,
y = ~Thing_3,
name = 'Thing 3',
fill = 'tonexty',
fillcolor = 'gray')
Has anyone been able to do this successfully? Thanks!
Edit for clarification: I'm aware that it's possible to do a cumsum first and then create the chart but still appreciate the responses! I'm wondering if it's possible to do the sum within the chart so that it behaves like the stacked bars, where clicking the legend to remove a group shows the sum for the remaining groups.
You can adjust your data to use the cumulative sum of the y value for that point to calculate the stacked value, e.g.
library(plotly)
library(tidyverse)
# group, sort (to keep cumulative sum in right order), and adjust Y
df %>% group_by(X) %>% arrange(GroupName) %>% mutate(Y = cumsum(Y)) %>%
plot_ly(type = 'scatter', x = ~X, y = ~Y, color = ~GroupName,
mode = 'lines', fill = 'tonexty')
For those few that stumble across this question even after a few years (like myself):
In 2021, the second link mentioned by #kkd42 is quite useful, the solution is stackgroup='one' here.
plot_ly(df,x=~X, y=~Y, color=~GroupName,
type='scatter', mode='line',
stackgroup='one')
does the job for me.
You can calculate the heights of the stacked areas by adding together the things you want to stack. Then plot these already-stacked cumulative values. The 'reproducible' data from the original question was not reproducible, so I demonstrate with some new data here.
[note that the data used in the example on the plotly page is also converted into a cumulative table like this - https://plot.ly/r/filled-area-plots/#stacked-area-chart-with-cumulative-values]
set.seed(123)
df.wide = data.frame(
X = 100:105,
Thing_1 = cumsum(rnorm(6,10,3)),
Thing_2 = cumsum(rnorm(6,6,2)),
Thing_3 = cumsum(rnorm(6,3,1)))
df.wide$T1 = df.wide$Thing_1
df.wide$T2 = df.wide$Thing_1 + df.wide$Thing_2
df.wide$T3 = df.wide$T2 + df.wide$Thing_3
plot_ly(df.wide, fill = 'tozeroy', line = list(color = '#00000000')) %>%
add_trace(x = ~X, y = ~T3, name = 'Thing 3',
type = 'scatter', mode = 'lines', fillcolor = 'green') %>%
add_trace(x = ~X, y = ~T2, name = 'Thing 2',
type = 'scatter', mode = 'lines', fill = 'tozeroy', fillcolor = 'blue') %>%
add_trace(x = ~X, y = ~T1, name = 'Thing 1',
type = 'scatter', mode = 'lines', fill = 'tozeroy', fillcolor = 'orange')