I must design a graph that accumulates variables as they are added in Shiny R using plotly.
For example, if I graph the variable x with respect to the date t with a select input, I add the variable and it is located on the right side of the variable x, indicating with a separator that it is the variable y and so with as many variables are selected.
This is my code:
library(shiny)
library(plotly)
library(dplyr)
set.seed(123)
df <- data.frame(x = seq.Date(as.Date("2000/1/1"), by = "month", length.out = 100),
cat = sample(c("m1","m2","m3"),100, replace = TRUE),
a = cumsum(rnorm(100)),
b = rnorm(100),
c = rnorm(100),
d = rnorm(100))
ui <- fluidPage(
selectInput("x","Variable",names(df)[-1],NULL,TRUE),
selectInput("y", "category", unique(df$cat), NULL, TRUE),
numericInput("ls","limite superior",NULL,-100,100),
numericInput("li","limite superior",NULL,-100,100),
plotlyOutput("plot1")
)
server <- function(input, output, session) {
output$plot1 <- renderPlotly({
req(input$y, input$x)
df <- df%>%
filter(cat %in% input$y)%>%
select(one_of("x",input$x))
estado <- ifelse(df[[2]]>input$ls,"red",
ifelse(df[[2]]<input$ls & df[[2]]>input$li,
"orange","green"))
df$estado <- estado
p <- plot_ly(df,
x = ~x,
y = ~df[[2]],
type = "scatter",
mode = "lines")
## Makers
p <- p %>%
add_trace(x = ~x,
y= df[[2]],
marker = list(color = ~estado, size = 20, symbol = "square"),
showlegend = FALSE)
## Lengends and labels
p <- p %>%
layout(legend = list(orientation = 'h'))%>%
layout(title = paste('Comportamiento de calidad de agua residual', input$estacion, sep=' '),
plot_bgcolor = "#e5ecf6",
xaxis = list(title = 'Fecha'),
yaxis = list(title = paste(input$x,"mg/l", sep=" ")))
print(p)
})
}
shinyApp(ui, server)
I need that when adding the variables a, b, c, d, the graph will be made just after the variable that was already there so that it looks something like this:
Use subplot and do function.
df %>%
group_by(category) %>%
do(p = plot_ly(...) %>% (plot_features...)) %>%
subplot(sharex= FALSE,sharey=TRUE, nrow=1, margin = 0.0001)
With plot feautures i mean all the deatils of the plot (markers, lines, colors, etc)
Related
I am trying to create a line graph with two y-axises. The x-axis is the date and both of the y-axises are continuous data. I have working code to do this. It works perfectly, however when I push that to my shiny server (on Ubuntu) I get an error saying that 'x' must be a list. Not sure why this works locally but not on my shiny server.
server.R
dataset <- reactive({
infile <- input$datafile
if (is.null(infile)) {
return(NULL)
}
else {read_excel(infile$datapath)}
})
output$plot_data <- renderPlotly({
# Bring in the data
data <- subset(dataset(), select = c(input$date, input$var1, input$var2))
date <- data[[input$date]]
y_var1 <- data[[input$var1]]
y_var2 <- data[[input$var2]]
y1 <- list(tickfont = list(color = "blue"),
side = "left",
title = input$var1
)
y2 <- list(tickfont = list(color = "green"),
overlaying = "y",
side = "right",
title = input$var2
)
plot <- plot_ly() %>%
add_lines(x = date,
y = y_var1,
name = input$var1,
line = list(color = "blue")) %>%
add_lines(x = date,
y = y_var2,
name = input$var2,
yaxis = "y2",
line = list(color = "green")) %>%
layout(title = "Data Over Time",
yaxis = y1,
yaxis2 = y2
)
plot
ui.R
plotlyOutput('plot_data', height = 500)
Here is some sample data that has a date column and two continuous columns.
Date Impressions Sessions
01/01/2019 34124114 11234323
01/02/2019 43523523 12341244
01/03/2019 56547634 11124324
01/04/2019 65756844 12341234
01/05/2019 32454355 11412432
01/06/2019 23543664 12342412
01/07/2019 23534262 12341244
01/08/2019 12341324 12341234
01/09/2019 34645623 23412341
01/10/2019 64364363 12342123
01/11/2019 24114124 13412342
01/12/2019 23411242 13423442
01/13/2019 24124124 11234242
01/14/2019 42141132 12342144
I am trying to generate multiple graphs in Plotly for 30 different sales offices. Each graph would have 3 lines: sales, COGS, and inventory. I would like to keep this on one graph with 30 buttons for the different offices. This is the closest solution I could find on SO:
## Create random data. cols holds the parameter that should be switched
l <- lapply(1:100, function(i) rnorm(100))
df <- as.data.frame(l)
cols <- paste0(letters, 1:100)
colnames(df) <- cols
df[["c"]] <- 1:100
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly(df,
type = "scatter",
mode = "lines",
x = ~c,
y= ~df[[cols[[1]]]],
name = cols[[1]])
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[-1]) {
p <- p %>% add_lines(x = ~c, y = df[[col]], name = col, visible = FALSE)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(cols, function(col) {
list(method="restyle",
args = list("visible", cols == col),
label = col)
})
)
)
)
print(p)
It works but only on graphs with single lines/traces. How can I modify this code to do the same thing but with graphs with 2 or more traces? or is there a better solution? Any help would be appreciated!
### EXAMPLE 2
#create fake time series data
library(plotly)
set.seed(1)
df <- data.frame(replicate(31,sample(200:500,24,rep=TRUE)))
cols <- paste0(letters, 1:31)
colnames(df) <- cols
#create time series
timeseries <- ts(df[[1]], start = c(2018,1), end = c(2019,12), frequency = 12)
fit <- auto.arima(timeseries, d=1, D=1, stepwise =FALSE, approximation = FALSE)
fore <- forecast(fit, h = 12, level = c(80, 95))
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly() %>%
add_lines(x = time(timeseries), y = timeseries,
color = I("black"), name = "observed") %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 2], ymax = fore$upper[, 2],
color = I("gray95"), name = "95% confidence") %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 1], ymax = fore$upper[, 1],
color = I("gray80"), name = "80% confidence") %>%
add_lines(x = time(fore$mean), y = fore$mean, color = I("blue"), name = "prediction")
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[2:31]) {
timeseries <- ts(df[[col]], start = c(2018,1), end = c(2019,12), frequency = 12)
fit <- auto.arima(timeseries, d=1, D=1, stepwise =FALSE, approximation = FALSE)
fore <- forecast(fit, h = 12, level = c(80, 95))
p <- p %>%
add_lines(x = time(timeseries), y = timeseries,
color = I("black"), name = "observed", visible = FALSE) %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 2], ymax = fore$upper[, 2],
color = I("gray95"), name = "95% confidence", visible = FALSE) %>%
add_ribbons(x = time(fore$mean), ymin = fore$lower[, 1], ymax = fore$upper[, 1],
color = I("gray80"), name = "80% confidence", visible = FALSE) %>%
add_lines(x = time(fore$mean), y = fore$mean, color = I("blue"), name = "prediction", visible = FALSE)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(cols, function(col) {
list(method="restyle",
args = list("visible", cols == col),
label = col)
})
)
)
)
p
You were very close!
If for example you want graphs with 3 traces,
You only need to tweak two things:
Set visible the three first traces,
Modify buttons to show traces in groups of three.
My code:
## Create random data. cols holds the parameter that should be switched
library(plotly)
l <- lapply(1:99, function(i) rnorm(100))
df <- as.data.frame(l)
cols <- paste0(letters, 1:99)
colnames(df) <- cols
df[["c"]] <- 1:100
## Add trace directly here, since plotly adds a blank trace otherwise
p <- plot_ly(df,
type = "scatter",
mode = "lines",
x = ~c,
y= ~df[[cols[[1]]]],
name = cols[[1]])
p <- p %>% add_lines(x = ~c, y = df[[2]], name = cols[[2]], visible = T)
p <- p %>% add_lines(x = ~c, y = df[[3]], name = cols[[3]], visible = T)
## Add arbitrary number of traces
## Ignore first col as it has already been added
for (col in cols[4:99]) {
print(col)
p <- p %>% add_lines(x = ~c, y = df[[col]], name = col, visible = F)
}
p <- p %>%
layout(
title = "Dropdown line plot",
xaxis = list(title = "x"),
yaxis = list(title = "y"),
updatemenus = list(
list(
y = 0.7,
## Add all buttons at once
buttons = lapply(0:32, function(col) {
list(method="restyle",
args = list("visible", cols == c(cols[col*3+1],cols[col*3+2],cols[col*3+3])),
label = paste0(cols[col*3+1], " ",cols[col*3+2], " ",cols[col*3+3] ))
})
)
)
)
print(p)
PD: I only use 99 cols because I want 33 groups of 3 graphs
How can I create a grouped bar chart in plotly that has a dropdown (or something else), so a viewer can select the grouping variable?
Working example:
library(dplyr)
library(plotly)
library(reshape2)
iris$Sepal.L <- iris$Sepal.Length %>%
cut(breaks = c(4,5,7,8),
labels = c("Length.a","Length.b","Length.c"))
iris$Sepal.W <- iris$Sepal.Width %>%
cut(breaks = c(1,3,5),
labels = c("Width.a","Width.b"))
# Get percentages
data1 <- table(iris$Species, iris$Sepal.L) %>%
prop.table(margin = 1)
data2 <- table(iris$Species, iris$Sepal.W) %>%
prop.table(margin = 1)
# Convert to df
data1 <- data.frame(Var1=row.names(data1), cbind(data1))
row.names(data1) <- NULL
data2 <- data.frame(Var1=row.names(data2), cbind(data2))
row.names(data2) <- NULL
plot_ly(
data = data1,
name = "Length.a",
x = ~Var1, y = ~Length.a,
type = "bar") %>%
add_trace(y=~Length.b, name = "Length.b") %>%
add_trace(y=~Length.c, name = "Length.c")
plot_ly(
data = data2,
name = "Width.a",
x = ~Var1, y = ~Width.a,
type = "bar") %>%
add_trace(y=~Width.b, name = "Width.b")
For example if I would like to select between viewing a plot with table(iris$Species, iris$Sepal.Length) and a plot with table(iris$Species, iris$Sepal.Width)
Bonus:
If it's easy; being able to interactively select the x variable as well would be cool, but not necessary.
You can find a solution here.
The idea is to plot your bar charts (with data1 and data2) all together and to make visible only one at a time.
items <- list(
list(label="Var1",
args=list(list(visible=c(T,T,T,F,F)))),
list(label="Var2",
args=list(list(visible=c(F,F,F,T,T))))
)
plot_ly(data=data1) %>%
add_bars(name = "Length.a",
x = ~Var1, y = ~Length.a, visible=T) %>%
add_bars(name = "Length.b",
x = ~Var1, y = ~Length.b, visible=T) %>%
add_bars(name = "Length.c",
x = ~Var1, y = ~Length.c, visible=T) %>%
add_bars(name = "Width.a",
x = ~Var1, y = ~Width.a, visible=F, data=data2, marker=list(color="#377EB8")) %>%
add_bars(name = "Width.b",
x = ~Var1, y = ~Width.b, visible=F, data=data2, marker=list(color="#FF7F00")) %>%
layout(
title = "Bar chart with drop down menu",
xaxis = list(title="x"),
yaxis = list(title = "y"),
showlegend = T,
updatemenus = list(
list(y = 0.9,
buttons = items)
))
I'm trying to build a bilingual dashboard. In this dashboard I want to choose the right language column (either ENG or NL) based on input$language. This column serves as the levels input for a function in which a plotly graph is made.
The problem is now that when I use the radiobutton and change the language, nothing changes in the plotly graph. I'm guessing the regular function is not updating when something changes in the 'custom_levels_lang' reactive variable.
How can I make this work?
server.R
library(shinydashboard)
library(dplyr)
library(tidyr)
library(shiny)
library(plotly)
#make bilangual df
ID = c("level_1_graph1","level_1_graph1")
NL = c("Ja","Nee")
ENG = c("Yes","No")
levels_lang = data.frame(ID,NL,ENG)
#create df for pie-chart
S <- c("Ja","Nee")
n <- c(645,544)
percentage <- c(54,46)
df <- data.frame(S,n,percentage)
function(input, output, session) {
# Creating levels by language
custom_levels_lang <- reactive({
#select chosen language for input$language, then transpose all levels per
#graph number to separate columns
#gives custom_levels_lang$'name'
df <- levels_lang %>%
select(ID,one_of(input$language)) %>%
mutate(row = row_number()) %>%
spread_("ID",input$language)
#make list
df <- as.list(df)
#remove na's from list
df <- lapply(df, function(x) x[!is.na(x)])
return(df)
})
#create pie-chart
plot_pie <- function(custom_levels){
plt <- renderPlotly({
#give right levels based on chosen language
levels(df$S) <- custom_levels
#construct plot
df %>%
plot_ly(
labels = df$S,
values = ~percentage,
type = 'pie',
hole = 0.5,
textinfo = 'percent',
text = ~paste("n = ", n),
hoverinfo = 'text') %>%
layout(
showlegend = TRUE,
legend = list(x = 0.2, y = -0.3),
title = "title") %>%
config(
displaylogo = FALSE,
collaborate = FALSE,
modeBarButtonsToRemove = list('zoom2d','pan2d','zoomIn2d','zoomOut2d',
'autoScale2d','resetScale2d','toggleHover',
'toggleSpikelines','hoverClosestCartesian','hoverCompareCartesian'))
})
return(plt)
}
output$plt1 <- plot_pie(custom_levels = custom_levels_lang()$level_1_graph1)
}
ui.R
library(shinydashboard)
library(dplyr)
library(tidyr)
library(shiny)
library(plotly)
header <- dashboardHeader(
title = "Welcome",
titleWidth = 450)
sidebar <- dashboardSidebar(width = 300, radioButtons("language", label = "Kies taal", choices = list("Nederlands" = "NL", "English" ="ENG"), selected = "NL"))
body <- dashboardBody( plotlyOutput('plt1') )
dashboardPage(header,sidebar,body)
The renderPlotly function has to be outside the function call so that it gets notified whenever its dependency (custom_levels_lang()$level_1_graph1) changes.
In your code it's not in a reactive context, so it only gets rendered once.
plot_pie <- function(custom_levels){
#give right levels based on chosen language
levels(df$S) <- custom_levels
#construct plot
plt <- df %>%
plot_ly(
labels = df$S,
values = ~percentage,
type = 'pie',
hole = 0.5,
textinfo = 'percent',
text = ~paste("n = ", n),
hoverinfo = 'text') %>%
layout(
showlegend = TRUE,
legend = list(x = 0.2, y = -0.3),
title = "title") %>%
config(
displaylogo = FALSE,
collaborate = FALSE,
modeBarButtonsToRemove = list('zoom2d','pan2d','zoomIn2d','zoomOut2d',
'autoScale2d','resetScale2d','toggleHover',
'toggleSpikelines','hoverClosestCartesian','hoverCompareCartesian'))
return(plt)
}
output$plt1 <- renderPlotly(plot_pie(custom_levels = custom_levels_lang()$level_1_graph1))
anybody can help? trying to create plotly scatter chart where I can change color of selected markers. I checked the dataframe source and indt$active is a num, not factor, yet plotly chart interprets it as a factor
see the code, everything is fine until i set color = ~active. I tried using inside the reactive indt$active <- as.numeric(as.character( indt$active )) but it still does nothing. I am struggling to find what factor the chart fails on. It fails to load (you can comment out the color=~active to see without the error). To select values, you need to draw a box
library(plotly)
library(shiny)
library(dplyr)
library(tidyr)
tms<-format(seq(as.POSIXct("2013-01-01 00:00:00", tz="GMT"),
length.out=48, by='30 min'), '%H:%M')
dts<-c( "Day Before BH","BH","Sunday","Saturday","Friday","Thursday","Wednesday","Tuesday", "Monday" )
indt <- as.data.frame(matrix( c(0), nrow=9, ncol=48, byrow = TRUE))
indt<-cbind(dts,indt)
colnames(indt) <- c("dt",tms)
indt<-gather(indt,tm,active,-dt)
pal <- c("red", "blue")
pal <- setNames(pal, c(0, 1))
ui <- fluidPage(
plotlyOutput("plot")
)
server <- function(input, output, session) {
ind<-reactive({
d <- event_data("plotly_selected")
indt[d$pointNumber+1,3]<-1
indt<<-indt
return(indt)
})
output$plot <- renderPlotly({
f1 <- list(size = 8)
yform <- list(title = "",
categoryorder = "array",
categoryarray = dts
,tickfont = f1)
plot_ly(ind()
, x = ~tm, y = ~dt, mode = "markers", type = "scatter",
color = ~active,
colors = pal,
marker = list(size = 20)
) %>%
layout(dragmode = "select") %>%
layout(xaxis = list(title = "", tickfont = f1),
yaxis = yform)
})
}
shinyApp(ui, server)
In your plot_ly function try using color = ~as.character(active)instead of color = ~active