Adding hover annotations to a Heatmap plotly R - r

I'm currently using plotly's heatmap to make a risk matrix (if anyone knows a more efficient way to do it I'd be open to suggestions). Currently I add information about the different risks with annotations but I would like that instead of annotations it was a marker that contains the information when hovering over it, as in the image that I attached below.
Below I attach the code and an image of how I would like it to be.
I'll appreciate any suggestion, advice, etc. Thanks!
df.risk <- data.frame(
Risk = paste0("R",1:5),
Prob = runif(5),
Cons = runif(5,1,5))
m <- c(rep(0.1,4),0.5,rep(0.1,2),rep(0.5,3),0.1,rep(0.5,3),0.9,0.1,rep(0.5,2),rep(0.9,2),rep(0.5,2),rep(0.9,3))
scale <- matrix(m, nrow = 5, ncol = 5)
pal <- c("#1A9641",
"#EFE90F",
"#F44336")
fig <- plot_ly(
x = seq(1,16,by = 4),
y = seq(0,1,by = 0.2),
z = scale,
type = "heatmap",
colors = colorRamp(pal))
fig <- fig %>%
layout(xaxis = list(title = 'Cons'),
yaxis = list(title = 'Prob'))
fig %>%
add_annotations(y = df.risk$Prob,
x = df.risk$Cons,
text = df.risk$Risk,
showarrow = FALSE) %>% hide_colorbar()

Let me know if this is what you were looking for. I changed your data in df.risk, so that the template was a bit more obvious. Other than that the content leading up to the plot_ly object remained as you presented it in your question.
The data changes first
df.risk <- data.frame(
Risk = rep(c("Product rendered obsolete",
"Strict legal requirements",
"Sufficient human resources",
"Sufficient material resources",
"Sufficient manufacturing equipment",
"Sufficient sales",
"Reliable suppliers",
"Unknown/unintended costs exceeding ROI",
"Evidence technology will work",
"Sufficient market share long term"), 3),
Prob = runif(10),
Cons = runif(10,1,5))
Then the initial creation of the plot_ly object.
fig <- plot_ly(
x = seq(1,16,by = 4),
y = seq(0,1,by = 0.2),
z = scale,
hoverinfo = "none",
type = "heatmap",
colors = colorRamp(pal)) %>%
add_markers(
inherit = F,
x = ~Cons, y = ~Prob,
data = df.risk,
showlegend = F,
text = ~Risk,
color = I("white"), # I("transparent") or whatever color you prefer
hovertemplate = paste0("%{text}<br><br>", # risks
"Probability: %{y:.0%}<br>", # prob % rounded
"Severity: %{x:.2f}", # severity, rounded
"<extra></extra>")) # no trace info
fig %>%
layout(xaxis = list(title = 'Consequences/Severity'),
yaxis = list(title = 'Probability')) %>% hide_colorbar()
This is what it looks like.

Related

R Chorpleth Plotly Colorscale Keeps Resetting to Default when Moving through Frames

I'm currently making a choropleth map in plotly that tracks the population of ever US state from 1910 - 2020. The data is sorted into categorical variables with the percent change of every state in a decade. When I run my code the first year (1910) looks right, however when pressing "play" or moving the slider to any other year the colorscale resets to the default and the discrete scale and ticks are replaced with a continuous version. To be honest, I'm completely lost as to why only the first frame is correct, and when I return to the first frame after navigating to any of the other frames it changes to be like the other frames (colorscale wise).
Here is my code:
`
library(plotly)
library(dplyr)
library(readr)
library("RColorBrewer")
states <- read_csv("states.csv")
AllCountryPop <- read_csv("All50%d.csv",
col_types = cols(Year = col_number(),
Percent = col_number()))%>%
inner_join(states, by.x = State, by.x = state) %>%
select(Year, Code, Percent, Category) %>%
mutate(hover = paste(Code, "\n", 100*Percent, "%"))
AllCountryPop$Category = factor(AllCountryPop$Category)
AllCountryPop$Val = as.numeric(AllCountryPop$Category)
nfactor = length(levels(AllCountryPop$Category))
colr <- brewer.pal(n = nfactor,name = "Blues") #Color scale should be blue with five leves (for each category)
levels(AllCountryPop$Category) <- c("-30% - 0%" , "0% - 30%", "30% - 60%", "60% - 90%", "90% - 130%")
names(colr) = levels(AllCountryPop$Category)
colrS = function(n){
CUTS = seq(0,1,length.out=n+1)
rep(CUTS,ifelse(CUTS %in% 0:1,1,2))
}
graph_properties <- list(
scope = 'usa',
showland = TRUE,
landcolor = toRGB("white"),
color = toRGB("white")
)
font = list(
family = "DM Sans",
size = 15,
color = "black"
)
label = list(
bgcolor = "#EEEEEE",
bordercolor = "transparent",
font = font
)
colorScale <- data.frame(z=colrS(nfactor),
col=rep(colr,each=2),stringsAsFactors=FALSE)
p <- plot_geo(AllCountryPop,
locationmode = "USA-states",
frame = ~Year)%>%
add_trace(locations = ~ Code,
locationmode = "USA-states",
z = AllCountryPop$Val,
zmin = min(AllCountryPop$Val),
zmax = max(AllCountryPop$Val),
text = ~hover,
hoverinfo = 'text',
colorbar=list(tickvals=1:nfactor, ticktext=names(colr)),
colorscale= colorScale) %>%
layout(geo = graph_properties,
title = "Population Percent Change in the US\n1910 - 2020",
font = list(family = "DM Sans")) %>%
config(displayModeBar = FALSE) %>%
style(hoverlabel = label) %>%
colorbar(title = "Percent")
p
`
In case my CSV data may be of use:
The first two lines of "states.csv":
"State","Abbrev","Code" "Alabama","Ala.","AL"
The first two lines of "All50%d.csv":
State,Percent,Year,Category Alabama,0.051,2020,0% - 30%
P.S. First time asking a question here, please let me know if I need to change how I ask a question, provide info, etc,

Plotly R : how to add dynamic annotation which belongs to frame

I'm struggeling on a simple task. I have a database with 3 columns :
Year (numeric)
Age (numeric)
Pop (numeric)
Part60 : The % of individuals with age >= 60 (string like '% of poeple over 60 : 12%'). This value is the same for each rows of a year.
Dataset looks like :
I built a plotly bargraph with a frame based on the year. So I have a slider which allow me to show for each age the number of individuals and this is animated year by year.
I would like to add an anotation which shows the value of Part60 for the year of the frame... I know that it's possible with a ggplot sent to ggplotly function, however I want to do it from scratch with a plot_ly function as parameters are (for me) easier to control and follow the logic of my code.
This is my code :
gH <- plot_ly(data = dataH,
name = 'Hommes',
marker = list(color = ispfPalette[4]),
x = ~Pop,
y = ~Age,
frame = ~Annee)
gH <- gH %>% layout(yaxis = list(categoryorder="array",
categoryarray=dataH$Age))
gH <- gH %>% layout(yaxis = list(title = '',
zeroline = TRUE,
showline = TRUE,
showticklabels = TRUE,
showgrid = FALSE),
xaxis = list(title = '',
zeroline = TRUE,
showline = TRUE,
autorange = "reversed"),
shapes = hline(60))
gH <- gH %>% add_annotations(
x = 3000,
y = 62,
text = 'Part des 60 ans et + : 12 %',
showarrow = F,
color = ispfPalette[8]
Where text = 'Part des 60 ans et + : 12 %' should be replaced by something which allow me to get the value which belongs to the year of the slider.
Is someone may help me to do it ?
Thanks in advance for your great help.
Since I don't have your data, it's pretty difficult to give you the best answer. Although, here is a method in which you can add text that changes throughout the animation.
library(plotly)
library(tidyverse)
data(gapminder, package = "gapminder")
str(gapminder)
funModeling::df_status(gapminder)
# continent, lifeExp, year
gap <- gapminder %>% group_by(year, continent) %>%
summarise(Expectancy = mean(lifeExp))
# plot
p1 <- plot_ly(gap, x = ~Expectancy, y = ~continent,
frame = ~year, type = 'bar',
showlegend = F,
hovertemplate = paste0("Continent: %{y}<br>",
"<extra></extra>"),
texttemplate = "Life Expectancy: %{x:.2f}") %>%
layout(yaxis=list(title=""),
xaxis=list(title="Average Life Expectancy per Continent By Year"),
title=list(text=paste("Fancy Title")),
margin = list(t = 100))
p1
If you had text you wanted to animate that is not connected to each marker (bar, point, line), then you could do it this way.
# Something to add in the annotation text
gap2 <- gap %>% filter(continent == "Asia") %>%
droplevels() %>%
arrange(year)
# build to see frames
p2 <- plotly_build(p1)
# modify frames; need an annotation for each frame
# make sure the data is in order by year (order by frame)
lapply(1:nrow(gap2), # for each frame
function(i){
annotation = list(
data = gap2,
type = "text",
x = 77,
y = .5,
yref = "paper",
showarrow = F,
text = paste0("Asian Life Expectancy<br>",
sprintf("%.2f", gap2[i, ]$Expectancy)),
font = list(color = "#b21e29", size = 16))
p2$x$frames[[i]]$layout <<- list(annotations = list(annotation)) # change plot
})
p2
If anything is unclear, let me know.

Choropleth world map - convert k thousand numbers

When hovering over a country the numbers are shown as say 125.115k - I want to show it as 125,115
I tried using library(formattable) and something along the lines of z = comma(worldmap$Deaths,digits = 1)
My code is below
output$World_Map <- renderPlotly({
g <- list(
showframe = FALSE,
showcoastlines = FALSE,
projection = list(type = 'Mercator')
)
plot_ly(
worldmap,
type = 'choropleth',
locations = worldmap$COUNTRY_ALPHA_3_CODE,
z = worldmap$Deaths,
text = paste("Number of Deaths: ",worldmap$COUNTRY_SHORT_NAME,"\n",comma(worldmap$Cases, digits = 0),"\n","Number of Cases: ",worldmap$COUNTRY_SHORT_NAME),
colorscale = "Reds"
) %>% layout(title = "<b>Covid19 Global Pandemic .... Data Source: <a href='https://coronavirus.jhu.edu/'>Johns Hopkins University of Medicine</a><b>", geo = g)
})
For comma rendering numbers, I use prettyNum from base R, also comma function is available in scales package.
number_a <- 123456
prettyNum(number_a, big.mark = ",")
[1] "123,456"
Your question is about text tooltip in Plotly. You can do something like this, with hoverinfo/hovertemplate and text parameters.
Of course there are other manners to do it.
Because I don't have your data, I use an example from plotly website.
library(plotly)
# code for example
# https://plotly.com/r/choropleth-maps/#using-builtin-country-and-state-geometries
# doc for hovertemplate
# https://plotly-r.com/controlling-tooltips.html#tooltip-text
df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv')
# light grey boundaries
l <- list(color = toRGB("grey"), width = 0.5)
# specify map projection/options
g <- list(
showframe = FALSE,
showcoastlines = FALSE,
projection = list(type = 'Mercator')
)
fig <- plot_geo(df)
fig <- fig %>% add_trace(
z = ~GDP..BILLIONS., color = ~GDP..BILLIONS., colors = 'Blues',
locations = ~CODE, marker = list(line = l),
hoverinfo = "text",
text = ~glue::glue("{COUNTRY} <b>{CODE}</b>"),
hovertemplate = "%{z:,0f}<extra>%{text}</extra>"
)
fig <- fig %>% colorbar(title = 'GDP Billions US$', tickformat = ",0f")
fig <- fig %>% layout(
title = '2014 Global GDP<br>Source:CIA World Factbook',
geo = g
)
fig
With this you can easily configure your hover info / tooltip.
See here for more examples :
https://plotly.com/r/choropleth-maps/#customize-choropleth-chart
For French number (space instead of comma), you can at the end config locale:
fig %>%
config(locale = 'fr')

How can I make a chart title go over multiple lines and be left-justified in plotly?

I'm creating a 3D scatterplot in plotly and want to have a title that is a few sentences long (in order to describe the figure), with the title left-justified, not interfering with the figure itself, and not being cut off by the plot area.
The code below incorporates a stackoverflow answer on left-justified titles in plotly, but it does not help when you have a long title that needs to go over 3-4 lines.
Using \n makes the title's text go to a second line, but it still gets cut off by the plot area and does not left-justify.
library(plotly)
metric1 <- c(116,230,120,200,210,188,130,99,101,120)
metric2 <- c(79,109,120,95,130,98,118,130,140,88)
metric3 <- c(30,28,42,22,6,2,17,43,20,28)
class <- c(3,4,2,1,1,4,4,3,2,3)
df <- data.frame(metric1,metric2,metric3,class)
my_colors=c("red", "blue", "green", "#000000")[df$class]
p <- plot_ly(df,
x = ~metric1,
y = ~metric2,
z = ~metric3, text = class, type = "scatter3d",
mode = "markers", marker = list(color = my_colors)) %>%
add_annotations(
x=0, y=1.15,
text="Figure: The title of the figure will explain what information can be gleaned from the figure. Then the next sentence, which is still in this title, will elaborate on implications from the results. I want to be able to extend this as needed.",
font=list(size=17)
) %>%
layout(title = FALSE,
scene = list(xaxis = list(title = 'metric 1', range = c(0,300)),
yaxis = list(title = 'metric 2', range = c(0,150)),
zaxis = list(title = 'metric 3', range = c(0,100))), showlegend = FALSE)
p
The output I get shows only the end of the title and cuts it off:
Thanks for any help.
I think with the help of align = "left" and /n will give you what you want:
metric1 <- c(116,230,120,200,210,188,130,99,101,120)
metric2 <- c(79,109,120,95,130,98,118,130,140,88)
metric3 <- c(30,28,42,22,6,2,17,43,20,28)
class <- c(3,4,2,1,1,4,4,3,2,3)
df <- data.frame(metric1,metric2,metric3,class)
my_colors=c("red", "blue", "green", "#000000")[df$class]
p <- plot_ly(df,
x = ~metric1,
y = ~metric2,
z = ~metric3, text = class, type = "scatter3d",
mode = "markers", marker = list(color = my_colors)) %>%
add_annotations(
x=0.4, y=0.9,
align = "left",
# text="Figure: The title of the figure will explain what information can be gleaned from the figure. Then the next sentence, which is still in this title, will elaborate on implications from the results. I want to be able to extend this as needed.",
text=paste("Figure: The title of the figure will explain what information can be gleaned from the figure", "Then the next sentence, which is still in this title", "I want to be able to extend this as needed.", sep="\n") ,
font=list(size=17)
) %>%
layout(title = FALSE,
scene = list(xaxis = list(title = 'metric 1', range = c(0,300)),
yaxis = list(title = 'metric 2', range = c(0,150)),
zaxis = list(title = 'metric 3', range = c(0,100))), showlegend = FALSE)
p

R Plotly Rangeslider covers tick-labels - Is it possible to set location of Rangeslider?

I´m using the Plotly package in R to make a plot with both a rangeslider and rangeselector.
The problem is that the rangeslider tends to cover the x-axis tick labels.
A solution could be to manually set the location of the rangeslider, but I can't seem to find any documentation on how to do that.
Below you find a minimal working example of the problem together with a picture of it.
# Make Some Data:
Dates = as.POSIXct(c("2017-08-08 00:00")) + (0:71)*60^2
Values = rep_len(mtcars$mpg, 72)
tb = dplyr::tibble(Values, Dates)
# Plot
p = tb %>% plot_ly(type = "scatter", mode = 'markers', x = ~Dates, y = ~Values) %>%
layout(xaxis = list(
rangeslider = list(type = "date"),
rangeselector = list(
buttons = list(list(step = "all", label = "All")))
))
p
Any help will be greatly appreciated!
Cheers,
By messing around with various options/settings, I found that increasing the tick lengths and let the ticks appear on the inside of x-axis rather than on the outside solved my problem.
See the code below for an example
# Make Some Data:
Dates = as.POSIXct(c("2017-08-08 00:00")) + (0:71)*60^2
Values = rep_len(mtcars$mpg, 72)
tb = dplyr::tibble(Values, Dates)
# Plot
p = tb %>% plot_ly(type = "scatter", mode = 'markers', x = ~Dates, y = ~Values) %>%
layout(xaxis = list(ticks = "inside", ticklen = 10,
rangeslider = list(type = "date", thickness=0.1),
rangeselector = list(
buttons = list(list(step = "all", label = "All")))
))
p
However, this is a hacker solution, and I´m still on the lookout for a method to set the position of the rangeslider.
Try to solve the overlapping problem tuning the thickness parameter of rangeslider:
# Make Some Data:
Dates = as.POSIXct(c("2017-08-08 00:00")) + (0:71)*60^2
Values = rep_len(mtcars$mpg, 72)
tb = dplyr::tibble(Values, Dates)
# Plot
p = tb %>% plot_ly(type = "scatter", mode = 'markers', x = ~Dates, y = ~Values) %>%
layout(xaxis = list(
rangeslider = list(type = "date", thickness=0.3),
rangeselector = list(
buttons = list(list(step = "all", label = "All")))
))
p
Hope it can help you.

Resources