Related
I'm trying to set the colour in R for "points" or markers in plotly with a custom palette. It doesn't work. I'm also trying to add a title to the graph, but it won't appear. In addition, I'd like to remove some elements of the legend (like the extra "z") and add a title to the legend elements. Nothing seems to work, even if it is present in the code.
library(plotly)
library(tidyverse)
set.seed(123456)
pal <- c("black", "orange", "darkgreen", "pink")
pal <- setNames(pal, levels(as.factor(c("gr1","gr2","gr3","gr4"))))
linedat = data.frame(x = rep(mean(1:50),2),
y = rep(mean(1:50),2),
z = c(0,1))
zoom = 3
pg = plotly::plot_ly(x = 1:50,
y = 1:50,
z = outer(1:50,1:50,"+")/100) %>%
add_surface(contours = list(
z = list(show = TRUE, start = 0, end = 1, size = 0.05)),
opacity = 1) %>%
add_markers(x = rnorm(50,25,5),
y = rnorm(50,25,5),
marker = list(opacity = 0.9),
type="scatter3d",
mode = "markers",inherit = FALSE,
colors = pal,
color = as.factor(sample(1:4,50,replace = T)),
z = rbinom(50,1,.5)) %>%
layout(scene=list(title = "Title won't show up????",
xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom, y = sin(pi*1.3)*zoom, z= 2.00)),
legend = list(title=list(text='<b> Groups </b>')))) %>%
add_trace(data=linedat, x=~x, y=~y, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[1],
width = 14),name = "Avg. data1")%>%
add_trace(data=linedat, x=~x+20, y=~y+20, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[4],
width = 14),name = "Avg. data2")
pg
See how here I'm able to set the colour of the points, but I'm not able to get the names of the colours to show in the legend (I modified the code to match what #Kat suggested):
library(plotly)
library(tidyverse)
set.seed(123456)
pal <- c("black", "orange", "darkgreen", "pink")
pal <- setNames(pal, levels(as.factor(c("gr1","gr2","gr3","gr4"))))
linedat = data.frame(x = rep(mean(1:50),2),
y = rep(mean(1:50),2),
z = c(0,1))
zoom = 3
pg = plotly::plot_ly(x = 1:50,
y = 1:50,
z = outer(1:50,1:50,"+")/100) %>%
add_surface(contours = list(
z = list(show = TRUE, start = 0, end = 1, size = 0.05)),
opacity = 1, colorbar = list(title = "Only one Z")) %>%
add_markers(x = rnorm(50,25,5),
y = rnorm(50,25,5),
marker = list(color = pal[as.factor(sample(1:4,50,replace = T))],opacity = 0.9),
type="scatter3d",
mode = "markers",inherit = FALSE,
colors = pal,
z = rbinom(50,1,.5)) %>%
layout(title = "Title that won't show",
margin = list(t = 40),
legend = list(title = list(
text = "<br>Legends deserve names, too")),
scene=list(xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom,
y = sin(pi*1.3)*zoom, z= 2.00)))) %>%
add_trace(data=linedat, x=~x, y=~y, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[1],
width = 14),name = "Avg. data1")%>%
add_trace(data=linedat, x=~x+20, y=~y+20, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[4],
width = 14),name = "Avg. data2");pg
Your original plot_ly call and add_trace calls can remain as is. I've included the changes needed for the layout call and added the call needed for colorbar.
The layout first.
layout(title = "Title that won't show", # <------ give my plot a name
margin = list(t = 40), # don't smash that title, either
legend = list(title = list(
text = "<br>Legends deserve names, too")), # <--- name my legend
scene=list(title = "Title won't show up????", # <- this can go away
xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom,
y = sin(pi*1.3)*zoom, z= 2.00)),
legend = list(title=list(text='<b> Groups </b>')))) %>%
colorbar(title = "Only one Z") # <--- give me one z colorbar title
(Some colors are different in the image; I didn't realize I had the pal object...sigh)
Update
This addresses your additional questions.
First, I'm not getting an error from the method in which I title the color bar. You'll have to share what error you're getting.
I didn't realize that it was ignoring the colors you set in markers, either. The easiest way to address this is to call that trace first. Since nothing else was declared within markers, I called opacity outside of the list, but it's fine the way you have it.
First, I commented out the setNames call, because that won't work for the marker's trace and it doesn't matter to the other traces.
library(plotly)
set.seed(123456)
pal <- c("black", "orange", "darkgreen", "pink")
# pal <- setNames(pal, levels(as.factor(c("gr1","gr2","gr3","gr4"))))
linedat = data.frame(x = rep(mean(1:50),2),
y = rep(mean(1:50),2),
z = c(0,1))
zoom = 3
I made plot_ly() empty and put all the data for the surface in that trace. I also added inherit = false so that the layout could go at the end without the data errors.
set.seed(123456)
pg = plotly::plot_ly() %>%
add_trace(inherit = F,
x = rnorm(50,25,5),
y = rnorm(50,25,5),
type="scatter3d",
mode = "markers",
opacity = .8,
colors = pal,
color = as.factor(sample(1:4, 50, replace = T)),
z = rbinom(50,1,.5)) %>%
add_surface(x = 1:50,
y = 1:50,
z = outer(1:50,1:50,"+")/100,
colorscale = "Viridis",
contours = list(
z = list(show = TRUE, start = 0, end = 1, size = 0.05)),
opacity = 1) %>%
add_trace(data=linedat, x=~x, y=~y, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[1],
width = 14),name = "Avg. data1", inherit = F) %>%
add_trace(data=linedat, x=~x+20, y=~y+20, z=~z,
type="scatter3d", mode="lines",
line = list(color = pal[4],
width = 14),name = "Avg. data2", inherit = F) %>%
The last part is the layout, but this is not different than my original answer.
layout(title = "Title that won't show", # <------ give my plot a name!
margin = list(t = 40), # don't smash that title, either
legend = list(title = list(
text = "<br>Legends deserve names, too"),
tracegroupgap = 350), # <--- name my legend!
scene=list(title = "Title won't show up????", # <--- this can go away!
xaxis = list(title = 'trait1'),
yaxis = list(title = 'trait1',autorange = "reversed"),
camera = list(eye = list(x = cos(0.8*pi)*zoom,
y = sin(pi*1.3)*zoom, z= 2.00)),
legend = list(title=list(text='<b> Groups </b>')))) %>%
colorbar(title = "Only one Z") # <--- give me one z for the title!
pg
I am working with the R Programming language.
Using the following link as a tutorial (https://plotly.com/r/lines-on-maps/), I was able to make an interactive plot:
#load libraries
library(dplyr)
library(leaflet)
library(plotly)
library(data.table)
#generate data for example (longitude and latitude of cities)
lat = rnorm(100, 43, 3)
long = rnorm(100, -79, 3)
map_data = data.frame(lat, long)
map_data$type = as.factor(1:100)
#change format of the data so that it is compatible for this example
result = rbind(
cbind(map_data[1:nrow(map_data)-1,c(1,2)], map_data[-1,c(1,2)]),
cbind(map_data[nrow(map_data), c(1,2)], map_data[1,c(1,2)])
)
colnames(result) <- c("start_lat", "start_long", "end_lat", "end_long")
my_data = result
my_data$type = as.factor(1:nrow(my_data))
my_data$type1 = as.character(1:100)
my_data$count = as.integer(1)
my_data$id = 1:100
#### begin visualization
# map projection
geo <- list(
scope = 'north america',
projection = list(type = 'azimuthal equal area'),
showland = TRUE,
landcolor = toRGB("gray95"),
countrycolor = toRGB("gray80")
)
fig <- plot_geo(locationmode = 'USA-states', color = I("red"))
fig <- fig %>% add_markers(
data = my_data, x = ~start_long, y = ~start_lat, alpha = 0.5
)
fig <- fig %>% add_markers(
data = my_data, x = ~start_long, y = ~start_lat, hoverinfo = "text", alpha = 0.5
)
fig <- fig %>% add_segments(
data = group_by(my_data, type),
x = ~start_long, xend = ~end_long,
y = ~start_lat, yend = ~end_lat,
alpha = 0.3, size = I(1), hoverinfo = "none"
)
fig <- fig %>% layout(
title = 'Plot 1',
geo = geo, showlegend = FALSE, height=800
)
#final result
fig
This produces the following result:
Now, I am trying to get the "interactive text" to work:
# map projection
geo <- list(
scope = 'north america',
projection = list(type = 'azimuthal equal area'),
showland = TRUE,
landcolor = toRGB("gray95"),
countrycolor = toRGB("gray80")
)
fig <- plot_geo(locationmode = 'USA-states', color = I("red"))
fig <- fig %>% add_markers(
data = my_data, x = ~start_long, y = ~start_lat, alpha = 0.5
)
fig <- fig %>% add_markers(
data = my_data, x = ~start_long, y = ~start_lat, text = ~type1, size = ~count, hoverinfo = "text", alpha = 0.5
)
fig <- fig %>% add_segments(
data = group_by(my_data, type),
x = ~start_long, xend = ~end_long,
y = ~start_lat, yend = ~end_lat,
alpha = 0.3, size = I(1), hoverinfo = "none"
)
fig <- fig %>% layout(
title = 'Plot 1',
geo = geo, showlegend = FALSE, height=800
)
fig
The interactive text is now working, but the data points are appearing "much bulkier".
My Question: Is it possible to make the interactive text work, but have the data points appear the same way they do in the first picture?
I originally tried to do this without a "count" variable:
fig <- fig %>% add_markers(
data = my_data, x = ~start_long, y = ~start_lat, text = ~type1, hoverinfo = "text", alpha = 0.5
)
But when I do this, the interactive text isn't working - the interactive text only works when a "count" variable is added.
Is this "count" variable necessary? Can someone please show me how to fix this?
Thanks!
You don't need to use count. However, there is something odd here with the segments. Either way, this achieves what I think you're looking for.
I have provided two examples because you didn't say what you wanted to have in the hover text. In the first example, I just use the x and y (lat and long). In the second, I used custom hover content.
Everything that precedes the creation of fig was left unchanged.
Notable changes:
the order the fig elements are assembled; segments seems to only work if it is before the markers
hoverinfo for the segments add is now set to text--this didn't add hover content, but for some reason none here was a problem...odd
I dropped a call to fig or two, that seemed to be doing nothing...
in add_markers, this changed differently in the two options
in one, hovertext = "text" was changed to hovertext = "lat+lon"
in the other, there were multiple changes--you'll have to look at the code for this one
in layout, I deleted the height argument; it's ignored
fig <- plot_geo(locationmode = 'USA-states', color = I("red"))
fig <- fig %>% add_segments( # add segments
data = group_by(my_data, type),
x = ~start_long, xend = ~end_long,
y = ~start_lat, yend = ~end_lat,
alpha = 0.3, size = I(1), hoverinfo = "text" # changed hoverinfo
)
fig <- fig %>% add_markers(
data = my_data, x = ~start_long, y = ~start_lat,
alpha = 0.5, hoverinfo = "lat+lon" # changed hoverinfo
)
fig <- fig %>% layout(
title = 'Plot 1',
geo = geo, showlegend = FALSE # removed height argument
)
#final result
fig
Here's the custom text version
fig <- plot_geo(locationmode = 'USA-states', color = I("red"))
fig <- fig %>% add_segments( # add segments
data = group_by(my_data, type),
x = ~start_long, xend = ~end_long,
y = ~start_lat, yend = ~end_lat,
alpha = 0.3, size = I(1), hoverinfo = "text" # changed hoverinfo
)
fig <- fig %>% add_markers(
data = my_data, x = ~start_long, y = ~start_lat,
alpha = 0.5, hoverinfo = "text", # hoverinfo unchanged
text = ~paste0("Longitude: ", # text changed here**
round(my_data$start_long, 2),
"<br>Latitude: ",
round(my_data$start_lat, 2))
)
fig <- fig %>% layout(
title = 'Plot 1',
geo = geo, showlegend = FALSE # removed height argument
)
#final result
fig
Let me know if you have any questions!
I have a choropleth map created using plotly::plot_geo. I would like to add labels on top of the map so that, for instance, over the location of Alabama on the map, it would say 'AL (68)', but for all states, as in the example below:
Can anyone tell me if there is a way to do this?
library(tidyverse)
library(plotly)
set.seed(1)
density <- sample(1:100, 50, replace = T)
g <- list(
scope = 'usa',
projection = list(type = 'albers usa'),
lakecolor = toRGB('white')
)
plot_geo() %>%
add_trace(
z = ~density, text = state.name, span = I(0),
locations = state.abb, locationmode = 'USA-states'
) %>%
layout(geo = g)
Using your example, this is possible with plotly::plot_ly()
set.seed(1)
density <- sample(1:100, 50, replace = T)
g <- list(
scope = 'usa',
projection = list(type = 'albers usa'),
lakecolor = toRGB('white')
)
plot_ly() %>%
layout(geo = g) %>%
add_trace(type = "choropleth", locationmode = 'USA-states',
locations = state.abb,
z = ~density, text = state.name,
color = ~density, autocolorscale = TRUE) %>%
add_trace(type = "scattergeo", locationmode = 'USA-states',
locations = state.abb, text = paste0(state.abb, "\n", density),
mode = "text",
textfont = list(color = rgb(0,0,0), size = 12))
Output is:
Still not sure how to do this with plotly::plot_geo(), but this solution does allow you to stay within the plotly family.
I don't think this is reasonably possible in R, at least for the time being. However, this is supported in python (see #r-beginners comment and https://plotly.com/python/text-and-annotations/).
I am showing a couple of examples of alternative approaches using ggplot and leaflet, but each presents considerable drawbacks if you are wedded to plotly. Mapbox appears to be another option, but I have never used it. Examples using all of these packages in the article that was already linked in the comments (https://plotly-r.com/maps.html).
Example dataset
library(sf)
set.seed(1)
nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
nc$density <- sample(1:100, nrow(nc), replace = T)
nc$lat <- st_coordinates(st_centroid(nc))[,"Y"]
nc$lon <- st_coordinates(st_centroid(nc))[,"X"]
Plot with ggplot (static)
library(ggplot2)
ggplot(nc) +
geom_sf(aes(fill = density)) +
geom_text(
aes(x = lon, y = lat),
label = paste0(nc$NAME, "\n", "(", nc$density, ")"),
check_overlap = TRUE) +
scale_fill_viridis_c() +
theme_void()
Plot with leaflet (interactive)
library(leaflet)
library(viridis)
pal <- colorNumeric(viridis_pal(option = "C")(2), domain = nc$density)
leaflet() %>%
addProviderTiles("CartoDB.Positron") %>%
setView(-80, 34.5, zoom = 6.2) %>%
addPolygons(
data = nc,
fillColor = ~pal(nc$density),
fillOpacity = 0.8,
weight = 0.2,
smoothFactor = 0.2,
popup = ~density) %>%
addLabelOnlyMarkers(
lng = nc$lon,
lat = nc$lat,
label = paste0(nc$NAME, "\n", "(", nc$density, ")"),
#label = "LABEL",
labelOptions = labelOptions(noHide = T, textOnly = TRUE)) %>%
addLegend(
pal = pal,
values = nc$density,
position = "bottomright",
title = "Density")
Using tmap in view-mode allows to reach nearly what you want (shapes are used via the urbnmapr package):
library(tidyverse)
library(tmap)
library(sf)
library(urbnmapr)
states <- get_urbn_map("states", sf = T) %>%
as.tibble() %>%
mutate(density = sample(1:100, 51, replace = T)) %>%
mutate(abbvAndDens = str_c(state_abbv, " (", density, ")")) %>%
st_as_sf()
tmap_mode("view")
tm_shape(states) +
tm_fill("density",
palette = "viridis",
style = "cont",
breaks = seq(0, 100, 20)) +
tm_borders(lwd = .5, col = "black") +
tm_text("abbvAndDens", size= .75, col = "black")
I made a boxplot:
dat %>%
plot_ly(y = ~xval, color = ~get(col), type = "box",
boxpoints = "all", jitter = 0.7,
pointpos = 0, marker = list(size = 3),
source = shiny_source, key = shiny_key,
hoverinfo = 'text', text = txt)
but problem is that jittered points are not interactive and cannot be marked separately, so I came with an idea to add those points using add_markers:
dat %>%
plot_ly(y = ~xval, color = ~get(col), type = "box",
boxpoints = FALSE, jitter = 0.7,
pointpos = 0, marker = list(size = 3),
source = shiny_source, key = shiny_key,
hoverinfo = 'col', text = txt
) %>%
add_markers(x = ~get(col), y = ~varval, size = I(6))
but now points are in straight line and I'd like to add some jitter (for example by using beeswarm package). But I don't know how to get coordinates of qualitative variable IC0 on X axis. Any ideas?
I find myself in the same potential case often with plotly and ggplot2-- 3 lines of code to get 90% of what I want, and 30 lines of code to get the aesthetics just right.
One potential solution/workaround: Take advantage of R's "factors are coded with integers" paradigm, plot everything on a numeric scale, and then cover your tracks by hiding x labels and x hover values.
dat <- data.frame(xval = sample(100,1000,replace = TRUE),
group = as.factor(sample(c("a","b","c"),1000,replace = TRUE)))
dat %>%
plot_ly() %>%
add_trace(x = ~as.numeric(group),y = ~xval, color = ~group, type = "box",
hoverinfo = 'name+y') %>%
add_markers(x = ~jitter(as.numeric(group)), y = ~xval, color = ~group,
marker = list(size = 6),
hoverinfo = "text",
text = ~paste0("Group: ",group,
"<br>xval: ",xval),
showlegend = FALSE) %>%
layout(legend = list(orientation = "h",
x =0.5, xanchor = "center",
y = 1, yanchor = "bottom"
),
xaxis = list(title = "Group",
showticklabels = FALSE))
Yields the following
I am currently trying to change the sizes of the Bubbles for Plotly's bubble map manually. I was successful in changing the colors of the map using the data provided but I am unable to use the same logic to change the size. To change the colors I simply called: colors_wanted <- c("red", "blue", "black", "pink") and passed this command to colors within plot_ly. Do you think it is possible to change the sizes rather than using the formula in this case sqrt to claim the sizes?
library(plotly)
df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_us_cities.csv')
df$hover <- paste(df$name, "Population", df$pop/1e6, " million")
df$q <- with(df, cut(pop, quantile(pop), include.lowest = T))
levels(df$q) <- paste(c("1st", "2nd", "3rd", "4th"), "Quantile")
df$q <- as.ordered(df$q)
g <- list(scope = 'usa',projection = list(type = 'albers usa'),showland = TRUE,landcolor = toRGB("gray85"),
subunitwidth = 1, countrywidth = 1, subunitcolor = toRGB("white"),countrycolor = toRGB("white"))
plot_ly(df, lon = lon, lat = lat, text = hover,
marker = list(size = sqrt(pop/10000) + 1, line = list(width = 0)),
color = q, colors= colors_wanted, type = 'scattergeo', locationmode = 'USA-states') %>%
layout(title = '2014 US city populations<br>(Click legend to toggle)', geo= g)
If you want the size to correspond to a quartile then this works (and there are any number of variations on this that you could do to make the size more analytically meaningful):
plot_ly(df, lon = lon, lat = lat, text = hover, size = as.numeric(df$q),
#marker = list(size = sqrt(pop/10000) + 1, line = list(width = 0)),
color = q, colors= colors_wanted, type = 'scattergeo', locationmode = 'USA-states') %>%
layout(title = '2014 US city populations<br>(Click legend to toggle)', geo= g)
Here's an interesting variation:
plot_ly(df, lon = lon, lat = lat, text = hover, size = aggregate(df$pop,by=list(df$q),sqrt)$x,
#marker = list(size = sqrt(pop/10000) + 1, line = list(width = 0)),
color = q, colors= colors_wanted, type = 'scattergeo', locationmode = 'USA-states') %>%
layout(title = '2014 US city populations<br>(Click legend to toggle)', geo= g)