I have created a plotly scatter plot which I add traces to using a for loop. When I add text labels or hoverinfo, the text for the last point overwrites all others. Does anyone know how to prevent this?
I have created a reproducible example below where the (correctly named) blue points are created outside the loop however the red points created within the loop have their names overwritten (all incorrectly labelled E as opposed to A->E):
library(plotly)
data.frame1 <- data.frame("name"=paste("name", LETTERS[6:10]), "x"=-1:-5, "y"=-1:-5)
data.frame2 <- data.frame("name"=paste("name", LETTERS[1:5]), "x"=1:5, "y"=1:5)
p <- plot_ly(data.frame1, x = ~x, y = ~y, text = ~paste0(name),
name = "Outside loop", type ="scatter",
mode = "markers+text", marker=list(color="blue") )
for(i in 1:nrow(data.frame2)) {
point <- data.frame2[i, ]
p <- p %>% add_trace(x = point$x, y = point$y, text = ~paste0(point$name),
type ="scatter", mode = "markers+text",
marker = list(color="red", size=10),
name=point$name )
}
p
The ~ sign causes the problem, remove in the loop and it should be fine. It makes sense when you refer to your data frame like in the first example but it causes the weird behavior you are observing in the loop.
library(plotly)
data.frame1 <- data.frame("name"=paste("name", LETTERS[6:10]), "x"=-1:-5, "y"=-1:-5)
data.frame2 <- data.frame("name"=paste("name", LETTERS[1:5]), "x"=1:5, "y"=1:5)
p <- plot_ly(data.frame1, x = ~x, y = ~y, text = ~paste0(name),
name = "Outside loop", type ="scatter",
mode = "markers+text", marker=list(color="blue") )
for(i in 1:nrow(data.frame2)) {
point <- data.frame2[i, ]
print(paste0(point$name))
p <- p %>% add_trace(x = point$x, y = point$y, text = paste0(point$name),
type ="scatter", mode = "markers+text",
marker = list(color="red", size=10),
name=point$name )
}
p
Related
I have a problem with the joined plot of an updatable line and static markers in R plotly. The line plot is updated via a drop down menu button, which works well on its own. The additional dots in the add_markers function are also correct when the plot is first initialized.
But after the first update, the markers are cut off (to the left side of the plot where the line starts) and remaining markers are modified (y values are different to initial ones).
For the example here the button function is simplified, but the result shows the same strange behavior.
`
sample_df <- tibble::tibble(quarter_date = rep(c("2022-06-30","2022-09-30","2022-12-31"),3),
forecast_value = runif(9,min = 10,max = 16),
forecast_date = c(rep("2022-07-23",3),rep("2022-08-26",3),rep("2022-09-15",3)))
marks = tibble::tibble(dates = c("2022-05-21","2022-06-15","2022-07-02","2022-07-26","2022-08-27"),
values = c(11,13,12,15,14))
create_buttons <- function(df, date_id) {
lapply(
date_id,
FUN = function(date_id,df) {
button <- list(
method = 'restyle',
args = list('y', list(df %>%
dplyr::filter(forecast_date == date_id) %>%
dplyr::pull(forecast_value))),
label = sprintf('Forecast # %s', date_id)
)
},
df
)
}
plotly::plot_ly(x = ~quarter_date) %>%
plotly::add_trace(data = sample_df %>%
dplyr::filter(forecast_date == max(forecast_date)),
#x = ~period_date,
y = ~forecast_value,
type = 'scatter',
mode = 'markers+lines',
name = 'forecasts') %>%
plotly::layout(
title = "Drop down menue",
yaxis = list(title = "y"),
updatemenus = list(
list(
y =1,
x = 0.9,
buttons = create_buttons(sample_df, unique(sample_df$forecast_date))
)
)) %>%
plotly::add_markers(data = marks,
x = ~dates,
y = ~values)
`
I have tried to set a wide xrange, used a second y2 axis and different approaches in the button calculation but nothing works as intended.
Does anyone have a clue why the add_markers is not working correctly after updating the line plot? Any ideas are highly appreciated!
Adding markers aren't the issue. The issue comes from the restyle. When you restyle the plot without designating that you only meant to change one trace, you changed all traces.
The solution is actually quite simple, you just need one more argument in your args call-- the trace number in a list: list(0) in this case. I've commented out your original args call, so you can see the change.
To make this repeatable, I added set.seed(46) before the creation of sample_df.
create_buttons <- function(df, date_id) {
lapply(
date_id,
FUN = function(date_id, df) {
button <- list(
method = 'restyle',
args = list('y', list(df %>% filter(forecast_date == date_id) %>%
pull(forecast_value)), list(0)),
# args = list('y', list(df %>%
# filter(forecast_date == date_id) %>%
# pull(forecast_value))),
label = sprintf('Forecast # %s', date_id)
)
},
df
)
}
Now when you run your plot, you will see that your marker data remains visible.
I’m trying to display error bars on a scatter plot with Shiny and plotly. Here’s my code in my server.R file:
data = reactiveVal()
observe({
results <- data.frame() # actually getting the data from here
# formatting output
final.results <- cbind(
"id" = paste(results$a,
results$b,
results$c,
sep = '-'),
"sigma" = sprintf("%.5g", results$s),
"c-e" = sprintf("%.3g",results$calc - results$exp)
)
data(final.results)
})
output$plot <- renderPlotly(
as.data.frame(data()[,c("id", "c-e", "sigma")]) %>% plot_ly(
x = ~`c-e`,
y = ~id,
height = 800,
type = 'scatter',
mode = 'markers',
marker = list(color = "#90AFD9"),
error_x = list(array = ~sigma, color = "#000000", type = "data")
)
)
The plot is ok except it’s not showing the error bars, what’s my mistake ?
EDIT: clarification for the origin of the data() function and what it’s return value is.
Thesprintf() function returns a character string, not a number, that is why it is not displaying the sigma values as error bars. If you want to keep 5 decimal places, use the round() function instead:
"sigma" = round(results$s, digits = 5)
The code blow generates a plotly graph with one data point. I design this plot to be able to display some text information when the users move the mouse cursor to the data point, but as the plot shows, this does not work.
library(dplyr)
library(lubridate)
library(plotly)
a1 <- data.frame(
DateTime = ymd_hms("2020-01-01 08:00:00"),
Value = 1
)
a1 <- a1 %>%
mutate(DateTimeText = as.character(DateTime))
p1 <- plot_ly(a1, x = ~DateTime, y = ~Value, type = "scatter", mode = "markers",
text = ~DateTimeText,
hovertemplate = paste(
"<br>Date Time: %{text} </br>",
"<br>Value: %{y} </br>",
"<extra></extra>"))
However, if I provided two data points. The code works. Here is an example. This is strange to me as I think both cases should work. Please give some advice.
a2 <- data.frame(
DateTime = ymd_hms(c("2020-01-01 08:00:00", "2020-01-02 08:00:00")),
Value = c(1, 2)
)
a2 <- a2 %>%
mutate(DateTimeText = as.character(DateTime))
p2 <- plot_ly(a2, x = ~DateTime, y = ~Value, type = "scatter", mode = "markers",
text = ~DateTimeText,
hovertemplate = paste(
"<br>Date Time: %{text} </br>",
"<br>Value: %{y} </br>",
"<extra></extra>"))
The issue is that your length 1 vector in R is not properly converted to a JSON array of length 1. This a known pitfall as there is some ambiguity when converting R objects to JSON, see https://plotly-r.com/json.html. This ambiguity does not arise when you have a vector of length > 1. That's why you code works in such cases.
To solve this make use of the asIs function or I, i.e. use text = ~I(DateTimeText). Try this:
library(dplyr)
library(lubridate)
library(plotly)
a1 <- data.frame(
DateTime = ymd_hms("2020-01-01 08:00:00"),
Value = 1
)
a1 <- a1 %>%
mutate(DateTimeText = as.character(DateTime))
p1 <- plot_ly(a1, x = ~DateTime, y = ~Value, type = "scatter", mode = "markers",
text = ~I(DateTimeText),
hovertemplate = paste(
"<br>Date Time: %{text} </br>",
"<br>Value: %{y} </br>",
"<extra></extra>"))
p1
I have a xts object that has dates as values, I'm using ggplot2 and shiny app for show the result.
But I want to change the default names of the tooltip when the mouse is on the line.
From:
index: 2020-03-19
value: 70
To:
Date: 2020-03-19
Cantidad: 70
Code for XTS:
data<-rnorm(10)
dates <- seq(as.Date("2016-01-01"), length =10, by = "days")
xtsMyData <- xts(x = data, order.by = dates)
Plot:
r <- ggplot(tidy(xtsMyData), aes(x=index,y=value, color=series, type = 'scatter', mode = 'lines')
) + geom_line(size=2)
The result is:
I'm triyng the following code:
r <- ggplot(tidy(xtsMyData), aes(x=index,y=value, color=series, type = 'scatter', mode = 'lines')
) + geom_line(size=2)
return(ggplotly(r, tooltip = **c("x","y", "series" )**) %>% plotly::config(displayModeBar = T) %>%
layout(legend = list(orientation = "h", x = 0.4, y = -0.2)))
And the result is:
How can I change the tooltip? Can I add words? I tried with paste("Dates","x") but doesn't work.
Thanks for your help.
You can use text in style to change the hover text.
The plotly object will have values accessible through a list as below. The date values will need to be converted with as.Date.
Edit: The code includes a full shiny app as a demo.
library(xts)
library(shiny)
data<-rnorm(10)
dates <- seq(as.Date("2016-01-01"), length =10, by = "days")
xtsMyData <- xts(x = data, order.by = dates)
ui <- fluidPage(
plotlyOutput("myplot")
)
server <- function(input, output, session) {
output$myplot <- renderPlotly({
r <- ggplot(tidy(xtsMyData), aes(x=index,y=value, color=series, type = 'scatter', mode = 'lines')) +
geom_line(size=2)
r <- ggplotly(r) %>%
plotly::config(displayModeBar = T) %>%
layout(legend = list(orientation = "h", x = 0.4, y = -0.2))
r %>%
style(text = paste0("Date:", as.Date(r$x$data[[1]]$x),
"</br></br>",
"Cantidad:", r$x$data[[1]]$y))
})
}
shinyApp(ui, server)
Plot
The first answer gave me the idea to change manually all, because I had 2 different geom_lines and that didn't work for me , this labels are stored in r$x$data[[1]]$text (the following line plots are in r$x$data[[2]]$text,r$x$data[[3]]$text... ), so, if you use an gsub, you could change everything you want, it's very dumb but it works. (You can use the same philosophy to delete the last label, manipulating strings)
I put an example for your problem, despite you already solve it, other person could have more than one line plot.
r$x$data[[1]]$text<-gsub(r$x$data[[1]]$text,pattern='index', replacement='Fecha')
r$x$data[[1]]$text<-gsub(r$x$data[[1]]$text,pattern='value', replacement='Valor')
r$x$data[[1]]$text<-gsub(r$x$data[[1]]$text,pattern='series', replacement='Serie')
Is there a way to get plotly to display the hover text on multiple lines/get it to recognize special characters line '\n' in the text?
A dummy version of what I'm looking to do is:
data <- data.frame(cbind(rnorm(10, 8), rnorm(10, 2)))
names(data)<-c("thing1", "thing2")
data$hovertext <- paste("here's a coordinate: ",
round(data$thing1,1), sep = "\n")
p <- plot_ly(data, type = 'scatter', x = thing1, y = thing2,
text = hovertext, hoverinfo = 'text', mode = "markers")
Which of course just ignores the separator and prints all on one line. Can I trick plotly/R into recognizing that line break?
Just use the HTML tag <br>:
library(plotly)
data <- data.frame(cbind(rnorm(10, 8), rnorm(10, 2)))
names(data) <- c("thing1", "thing2")
data$hovertext <- paste("here's a coordinate:",
round(data$thing1,1), sep = "<br>")
p <- plot_ly(data, type = 'scatter', x = ~thing1, y = ~thing2,
text = ~hovertext, hoverinfo = 'text', mode = "markers")
Additionally, you could use HTML tags to change the font styles as well (and much more)...