Related
Using the code snippet from the help page here, I'm trying to create a unique main label for each subplot but am not entirely successful. Any suggestions on how to do this?
library(plotly)
vars <- setdiff(names(economics), "date")
plots <- lapply(vars, function(var) {
plot_ly(economics, x = ~date, y = as.formula(paste0("~", var))) %>%
add_lines(name = var) %>% layout(title = paste("Title for", var, sep=' ') )
})
subplot(plots, nrows = length(plots), shareX = TRUE, titleX = FALSE)
I wish I had noticed this question sooner! Here is one way that you can make this happen.
I took the titles out of the plot build. Otherwise, you'll get an error.
library(plotly)
vars <- setdiff(names(economics), "date")
plots <- lapply(vars, function(var) {
plot_ly(economics, x = ~date, y = as.formula(paste0("~", var))) %>%
add_lines(name = var) #%>% layout(title = paste("Title for", var, sep=' '))
})
Then I created a vector of the titles.
nms <- invisible(lapply(vars, function(v){paste0("Title for ", v)}) %>% unlist())
Here I used annotations to create title objects to add to the subplot.
annots = lapply(
1:length(plots),
function(j){
list(x = .5,
y = 1 - (j - 1) * .205,
xanchor = "center",
yanchor = "center",
xref = "paper",
yref = "paper",
showarrow = F,
text = nms[j])
}
)
subplot(plots, nrows = length(plots), shareX = TRUE, titleX = FALSE) %>%
layout(annotations = annots)
I am creating a plot with several time series, plotting the mean value and two quantiles in the process and highlighting the region between the quantiles. Then I have a unified hoverinfo showing me all points at one point in time. I do not want this hoverinfo to display the dummy trace that is only there for the highlighting. I tried both hoverinfo = skip and hoverinfo = none, but that only hides any text info, not the trace itself in the hoverinfo.
Here is the code I have (without most "beautification parameters" such as axis width and the likes...):
###creating dummy data
test_data <- data.frame(matrix(0,nrow = 27,ncol = 4))
colnames(test_data) <- c("x","y","value","date")
test_data$x <- c(-1,-1,-1,0,0,0,1,1,1,-1,-1,-1,0,0,0,1,1,1,-1,-1,-1,0,0,0,1,1,1)
test_data$y <- c(-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1)
test_data$value <- rnorm(27,mean = 5,sd = 1)
date_vec <- c(rep(as.Date("2022-01-01"),9),rep(as.Date("2022-01-02"),9),rep(as.Date("2022-01-03"),9))
test_data$date <- date_vec
x_levels <- unique(test_data$x)
y_levels <- unique(test_data$y)
avg <- data.frame(matrix(0,nrow = 3,ncol = 1))
colnames(avg) <- c("value")
avg$date <- unique(date_vec)
Q75 <- data.frame(matrix(0,nrow = 3,ncol = 1))
colnames(Q75) <- c("value")
Q75$date <- unique(date_vec)
Q25 <- data.frame(matrix(0,nrow = 3,ncol = 1))
colnames(Q25) <- c("value")
Q25$date <- unique(date_vec)
for (i in 1:length(unique(date_vec))){
avg$value[i] <- mean(test_data$value[test_data$date == unique(date_vec)[i]])
Q75$value[i] <- quantile(test_data$value[test_data$date == unique(date_vec)[i]],0.75)
Q25$value[i] <- quantile(test_data$value[test_data$date == unique(date_vec)[i]],0.25)
}
##creating plot
fig <- plot_ly()
fig <- fig %>% layout(hovermode = 'x unified')
for(i in 1:length(x_levels)){
for(j in 1:length(y_levels)){
fig <- fig %>% add_trace(data = test_data, type = 'scatter', mode = 'lines+markers',
x = test_data$date[test_data$x == x_levels[i] & test_data$y == y_levels[j]],
y = test_data$value[test_data$x == x_levels[i] & test_data$y == y_levels[j]],
showlegend = FALSE, line = list(color = 'grey'), marker = list(color = 'grey'),
hoverinfo = 'text', text = paste0("some text dependent on x and y"))
}}
fig <- fig %>% add_trace(data = Q25, type = 'scatter', mode = 'lines', name = '25%-quantile', x = Q25$date,
y = Q25$value, showlegend = TRUE, line = list(color = 'darkred', dash = 'dot'),
hoverinfo = 'text', text = paste0("some text dependent on Q25"))
fig <- fig %>% add_trace(data = avg, type = 'scatter', mode = 'lines', name = 'mean value', x = avg$date,
y = avg$value, showlegend = TRUE, line = list(color = 'darkred', dahs = 'dash'),
hoverinfo = 'text', text = paste0("some text depenent on avg"))
fig <- fig %>% add_trace(data = Q75, type = 'scatter', mode = 'lines', name = '75%-quantile', x = Q75$date,
y = Q75$value, showlegend = TRUE, line = list(color = 'darkred', dash = 'dot'),
hoverinfo = 'text', text = paste0("some text dependent on Q75"))
#### This is the dummy trace in question:
fig <- fig %>% add_trace(x = Q25$date, y = Q25$value, type = 'scatter', mode = 'lines', fill = 'tonexty',
fillcolor = 'rgba(139,0,0,0.2)', line = list(color = 'darkred', width = 0.1), showlegend = FALSE,
hoverinfo = 'skip')
print(fig)
Is this a bug? Am I missing something obvious? Is there a better way to make the highlighting that does not create an additional hoverinfo? Thanks in advance!
Alright, this answer is not ideal—but it works.
There is good news, though. Plotly developers are already working on a solution. They're in the testing phase, which is where I got the idea for this answer. You can read about what Plotly's doing here.
First, I set a seed because you have random data. I used set.seed(3509). There were no changes to the data or fig.
When you hover over any point on your graph, your fill color will disappear. The tooltip does not show the trace that shouldn't be there anyway.
The fill color will return as soon as you move on or when the tooltip goes away.
This is the Javascript to call this hover event.
# the notation [12] is the curvenumber; it represnts a single trace
hoverer = "function(el, x) {
el.on('plotly_hover', function(d){
dt = {'fill': 'none'};
Plotly.restyle(el.id, dt, [12]);
});
el.on('plotly_unhover', function(d){
ft = {'fill':'tonexty'};
Plotly.restyle(el.id, ft, [12]);
});
}"
To visualize your plot with this event:
fig %>% htmlwidgets::onRender(hoverer)
Here is my code
fig = plot_ly(type = "scatter", mode = "lines") %>%
add_lines(
x = 1:200
, y = (rnorm(200) %>% cumsum) + 10
, name = "left side"
, line = list(color = "black")
, yaxis = "y"
) %>% add_trace(
x = seq(1, 200, by = 10)
, y = rnorm(20) + 100
, name = "right side"
, yaxis = "y2"
) %>% layout(
yaxis = list(
side = "left"
, zeroline = FALSE
)
, yaxis2 = list(
side = "right"
, zeroline = FALSE
)
)
fig
All I see is a single line for the "right side".
I am running on windows. Ultimately I want this as part of a shiny app which I will be sharing with others as an R project.
Why does it always happen that I ponder over a question for a long time before giving in and posting a question, only for me to realise the answer shortly thereafter?
Apparently I need to specify the overlaying argument for the second axis.
, yaxis2 = list(
side = "right"
, overlaying = "y"
)
I'm trying to make a shiny app for some user-friendly data analysis of some data I have, and I'd like to change the outputted Plotly plot depending on which file i'm looking at. Basically, I'd like to have one plot outputted at a time, where I can cycle through several plots (that don't change place in my shiny app) depending on which folder and criteria i'm using. Currently I'm struggeling with this, and I don't know exactly what to do from here. I've attached a few images to clarify what I mean and what I want.
This photo shows my UI and how I want my figures to be displayed. I'd like all figures to show in that same location, depending on the selected file.
When I switch to 'Datalogger', a new plot is generated, and it is outputted below the first one. I'd like it to be placed on top of it, in the exact same location.
Any help you can offer would be very welcome.
Best,
T.
Script:
# Load packages
library(shiny)
library(shinythemes)
library(dplyr)
library(readr)
library(lubridate)
library(plotly)
#picarro
time = as.character(seq(as.POSIXct("2018-06-01 12:00:00"), as.POSIXct("2018-06-01 12:10:00"), by=seconds() )); ch4.corr = runif(length(time), 1980, 2000);
data = data.frame(time, ch4.corr); data$time = as.POSIXct(time);
#datalogger
time = as.character(seq(as.POSIXct("2018-06-01 12:00:00"), as.POSIXct("2018-06-01 12:10:00"), by=seconds() )); PressureOut = runif(length(time), 1010, 1020);
dlog = data.frame(time, PressureOut); dlog$time = as.POSIXct(time);
#dronelog
time = as.character(seq(as.POSIXct("2018-06-01 12:00:00"), as.POSIXct("2018-06-01 12:10:00"), by=seconds() ));
ulog = data.frame(time); ulog$time = as.POSIXct(time);
#------------------------------------------------------------------------------
ui <- fluidPage(
titlePanel("Active AirCore analysis"),
hr(),
fluidRow(
column(3,
radioButtons("fileInput", "File",
choices = c("Picarro", "Datalogger", "Dronelog"),
selected = "Picarro"),
hr(),
conditionalPanel(
condition = "input.fileInput == 'Picarro'",
sliderInput("timeInputPicarro", "Time", as.POSIXct(data$time[1]), as.POSIXct(data$time[length(data$time)]), c(as.POSIXct(data$time[1])+minutes(1), as.POSIXct(data$time[length(data$time)])-minutes(1)), timeFormat = "%H:%M:%S", ticks = T, step = seconds(1), pre = "")),
conditionalPanel(
condition = "input.fileInput == 'Datalogger'",
sliderInput("timeInputDatalogger", "Time", as.POSIXct(dlog$time[1]), as.POSIXct(dlog$time[length(dlog$time)]), c(as.POSIXct(dlog$time[1]), as.POSIXct(dlog$time[length(dlog$time)])), timeFormat = "%H:%M:%S", ticks = T, step = seconds(1), pre = "")),
conditionalPanel(
condition = "input.fileInput == 'Dronelog'",
sliderInput("timeInputDronelog", "Time", as.POSIXct(ulog$time[1]), as.POSIXct(ulog$time[length(ulog$time)]), c(as.POSIXct(ulog$time[1])+minutes(1), as.POSIXct(ulog$time[length(ulog$time)])-minutes(1)), timeFormat = "%H:%M:%S", ticks = T, step = seconds(1), pre = "")),
hr(),
conditionalPanel(
condition = "input.fileInput == 'Picarro'",
radioButtons("picarroPlotInput", "Plot type",
choices = c("Time-series", "Process"),
selected = "Time-series")),
conditionalPanel(
condition = "input.fileInput == 'Datalogger'",
radioButtons("dataloggerPlotInput", "Plot type",
choices = c("Time-series", "Altitude"),
selected = "Time-series")),
hr(),
checkboxGroupInput(inputId='sidebarOptions',
label=('Options'),
choices=c('Blabla', 'Store data', 'BlablaBla')),
hr()),
br(),
mainPanel(
plotlyOutput("dataplot"),
hr(),
plotlyOutput("dlogplot")
)
)
)
server <- function(input, output, session) {
datasetInputPic <- reactive({ data = data; })
datasetInputPicSamp <- reactive({ dat = data[(data$time>=input$timeInputPicarro[1]) & (data$time<=input$timeInputPicarro[2]),]; })
datasetInputDatalogger <- reactive({ dlog = dlog })
datasetInputDronelog <- reactive({ ulog = ulog })
output$dataplot <- renderPlotly({
if( (input$fileInput == 'Picarro' ) & (input$picarroPlotInput == 'Time-series')){
data = datasetInputPic();
data$time = as.POSIXct(data$time);
dat = datasetInputPicSamp();
dat$time = as.POSIXct(dat$time);
sec.col = "red";
f = list(size = 8);
x <- list(title = " ")
y <- list(title = "CH<sub>4</sub> [ppb]")
p2 = plot_ly() %>%
add_trace(data = data,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black')) %>%
add_trace(data = dat,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = sec.col)) %>%
layout(xaxis = x, yaxis = y, title = '', showlegend = F, titlefont = f);
s1 = subplot(p2, margin = 0.06,nrows=1,titleY = TRUE) %>%
layout(showlegend = F, margin = list(l=50, r=0, b=50, t=10), titlefont = f);
s1
}
})
output$dlogplot <- renderPlotly({
if( (input$fileInput == 'Datalogger' ) & (input$dataloggerPlotInput == 'Time-series')){
data = datasetInputDatalogger();
data$time = as.POSIXct(data$time);
x <- list(title = " ")
y <- list(title = "Outside pressure [mbar]")
p1 = plot_ly() %>%
add_trace(data = data,
y = ~PressureOut,
x = ~time,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black'));
s1 = subplot(p1, margin = 0.07, nrows=2, titleY = TRUE, titleX = FALSE)
layout(s1, showlegend = F, margin = list(l=100, r=100, b=0, t=100), title = "Datalogger data")
s1
}
})
outputOptions(output, c("dataplot", "dlogplot"), suspendWhenHidden = TRUE)
}
runApp(list(ui = ui, server = server))
Your issue is that in your ui you have written:
mainPanel(
plotlyOutput("dataplot"),
hr(),
plotlyOutput("dlogplot")
)
Using this structure, the "dlogplot" will always display below the "dataplot" because you essentially gave it its own position in the main panel that is below the "dataplot". One solution, if you want the plots to be displayed in the same exact spot when clicking the various buttons, is to give only one plotlyOutput. Next you would put conditional if, else if and else in renderPlotly. For example:
output$dataplot <- renderPlotly({
if( (input$fileInput == 'Picarro' ) & (input$picarroPlotInput == 'Time-series')){
data = datasetInputPic();
data$time = as.POSIXct(data$time);
dat = datasetInputPicSamp();
dat$time = as.POSIXct(dat$time);
sec.col = "red";
f = list(size = 8);
x <- list(title = " ")
y <- list(title = "CH<sub>4</sub> [ppb]")
p2 = plot_ly() %>%
add_trace(data = data,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black')) %>%
add_trace(data = dat,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = sec.col)) %>%
layout(xaxis = x, yaxis = y, title = '', showlegend = F, titlefont = f);
s1 = subplot(p2, margin = 0.06,nrows=1,titleY = TRUE) %>%
layout(showlegend = F, margin = list(l=50, r=0, b=50, t=10), titlefont = f);
s1
}
else if( (input$fileInput == 'Datalogger' ) & (input$dataloggerPlotInput == 'Time-series')){
data = datasetInputDatalogger();
data$time = as.POSIXct(data$time);
x <- list(title = " ")
y <- list(title = "Outside pressure [mbar]")
p1 = plot_ly() %>%
add_trace(data = data,
y = ~PressureOut,
x = ~time,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black'));
s1 = subplot(p1, margin = 0.07, nrows=2, titleY = TRUE, titleX = FALSE)
layout(s1, showlegend = F, margin = list(l=100, r=100, b=0, t=100), title = "Datalogger data")
s1
}
})
This code will put the "dlogplot" and the "dataplot" in the same position in your main panel. (You would also need to get rid of output$dlogplot <- renderPlotly({...}) so that it isn't also trying to make that plot.)
Try this out and see if it works for your purposes.
I would like to create a plotly plot where the fill under each line is toggled upon mouseover/hover. The closest that I've come is using a combination of plotly and Shiny in the code below. Basically, I use the function event_data("plotly_hover") with a call to add_trace, which generates the fill for the line. However, when the mouse is moved away from the line, or unhovered, I get an error message: Error: incorrect length (0), expecting: 2366. In addition, the hoverinfo text no longer appears, or only briefly before the fill appears.
I'm not sure what the program is looking for when unhovering, so am not sure why I'm getting this error. Or perhaps there is different and simpler way to toggle the fill for plotly graphs?
ui.R
shinyUI(fluidPage(
titlePanel("Snow Weather Stations"),
mainPanel(
plotlyOutput("testplot", height = "500px")
)
)
)
server.R
library(shiny)
library(plotly)
library(dplyr)
library(tidyr)
csvf <- read.csv(file = "http://bcrfc.env.gov.bc.ca/data/asp/realtime/data/SW.csv",
check.names = FALSE, stringsAsFactors = FALSE)
swe <- csvf %>%
gather(STATION, SWE, -1) %>%
separate(`DATE (UTC)`, c('DATE', 'TIME'), sep = " ") %>%
filter(TIME == "15:00:00") %>%
select(-TIME) %>%
filter(substr(STATION,1,2) == "1A")
swe$DATE <- as.Date(swe$DATE)
swe$HOVERTEXT <- paste(swe$STATION, paste0(swe$SWE, " mm"), sep = "<br>")
xmin <- as.numeric(as.Date("2015-10-01")) * 24 * 60 * 60 * 1000
xmax <- as.numeric(as.Date("2016-09-30")) * 24 * 60 * 60 * 1000
shinyServer(function(input, output) {
output$testplot <- renderPlotly({
plot_ly(swe, x = DATE, y = SWE, group = STATION,
line = list(color = '#CCCCCC'),
text = HOVERTEXT, hoverinfo = "text+x",
hoveron = "points",
key = STATION) %>%
layout(showlegend = FALSE,
hovermode = 'closest',
xaxis = list(title = "",
showgrid = FALSE, showline = TRUE,
mirror = "ticks", ticks = "inside", tickformat = "%b",
hoverformat = "%b %-d",
range = c(xmin, xmax)),
yaxis = list(title = "Snow Water Equivalent (mm)",
showgrid = FALSE, showline = TRUE,
mirror = "ticks", ticks = "inside",
rangemode = "tozero")) %>%
config(displayModeBar = FALSE)
d <- event_data("plotly_hover")
if (is.null(d)) {
stn <- "1A01P Yellowhead Lake"
} else {
stn <- d$key[1]
}
add_trace(filter(swe, STATION == stn), x = DATE, y = SWE,
line = list(color = "404040"), fill = "tozeroy")
})
})