)
I would like to visualize the input of a shinyMatrix using a plotlymesh3d-plot. I was using a for-loop to convert the matrix input into a data frame having 3 columns (x, y and z) - see below. This works when being run outside the shiny app. Unfortunately, I'm stuck with using this for-loop inside a reactive() environment to pass this into plot_ly(). It says "object of type closure is not subsettable". I read, that this error often comes if you don't treat your reactive object as a function (which I did).
I know that I'm a beginner and I don't have much clue about the syntax of a shiny app. Most likely I did a supid mistake :-) But I don't know how to solve this.
Thanks!
library(shiny)
library(shinyMatrix)
ui <- fluidPage( titlePanel("shinyMatrix: Simple App"),
sidebarPanel(width = 6,tags$h4("Data"),
matrixInput("mat",
value = matrix(1:100, 10, 10),
rows = list(names=T, extend = TRUE),
cols = list(names = TRUE))),
mainPanel(width = 6,
plotlyOutput(
"plotly",
width = "100%",
height = "400px",
inline = FALSE,
reportTheme = TRUE
)))
server <- function(input, output, session) {
df <- data.frame(x="", y="", z="")
df <- reactive({
n=1
for(i in 1:nrow(input$mat)){
for(j in 1:ncol(input$mat)){
df[n,1] <- j*laenge
df[n,2] <- i*laenge
df[n,3] <- input$mat[i,j]
n=n+1
}
}
})
output$plotly <- renderPlotly({plot_ly(df(), x=~x, y=~y, z=~z, type="mesh3d")})
}
shinyApp(ui, server)
Main issue is that you used the same name df for both the reactive and the dataframe. Additionally your reactive has to return something to make your code work, i.e. return the dataframe after your for-loops.
server <- function(input, output, session) {
dd <- data.frame(x = NA, y = NA, z = NA)
df <- reactive({
n <- 1
for (i in 1:nrow(input$mat)) {
for (j in 1:ncol(input$mat)) {
dd[n, 1] <- j * laenge
dd[n, 2] <- i * laenge
dd[n, 3] <- input$mat[i, j]
n <- n + 1
}
}
dd
})
output$plotly <- renderPlotly({
plot_ly(df(), x = ~x, y = ~y, z = ~z, type = "mesh3d")
})
}
Related
I have a fairly large simulation, that I currently run in Shiny using double for loop and it takes very long. I read about possibility of using foreach, but it does not work out, whatever I try, I and up in errors. Maybe some can spot the error and help me correct it?
app.R that runs (albeit very slowly (on real data) here with example data for reprex
require(shiny)
require(tidyverse)
require(foreach)
require(doMC)
registerDoMC()
options(cores = detectCores())
df <- data.frame(a=rnorm(n=26), b=1:26, c=100:125)
calc <- function(let=0.5, var1=0.1, var2=0.5){
df%>%
mutate(p1=ifelse(a<let,var1,0))%>%
mutate(p2=ifelse(a<let, var2,2))%>%
summarise(mean_b=mean(b*p1),
mean_c=mean(c*p2))
}
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Example"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput(inputId="selected_let",
label="LET",
value=0.5,
min=0,
max=1,
step=0.1),
submitButton("CALCULATE")
),
# Show a plot of the generated distribution
mainPanel(
h1(paste0("Table1")),
tableOutput("table_1"),
h1(paste0("Table2")),
tableOutput("table_2")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
data <- reactive({
data <- data.frame()
for (i in seq(0,1,by=0.1)) {
for (j in seq(0,1,by=0.1)) {
tmp <- calc(let = input$selected_let, var1 = i, var2 = j)
tmp_df <- data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
data <- rbind(data, tmp_df)
}
}
return(data)
})
output$table_1 <- renderTable({
data()%>%
select(var1,var2,mean_b)%>%
spread(var2, mean_b)
})
output$table_2 <- renderTable({
data()%>%
select(var1,var2,mean_c)%>%
spread(var2, mean_c)
})
}
# Run the application
shinyApp(ui = ui, server = server)
My goal was to change the data <-... part with foreach package and as my PC runs on UNIX I use the doMC.
to be replaced with:
data <- reactive({
foreach(i=rep(seq(0,1,by=0.1),each=11),
j=rep(seq(0,1,by=0.1),times=11),
.combine="rbind") %dopar% {
val <- calc(let=input$selected_let,
var1=i,
var2=j)
data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
}
})
But this ends up in permanent errors:
I tried to out require(dplyr) in the the server part, but that did not help either.
Any suggestions for solutions?
As stand alone, the foreach part runs well with let=0.5 as input, given its not in reactive
foreach(i=rep(seq(0,1,by=0.1),each=11),
j=rep(seq(0,1,by=0.1),times=11),
.combine="rbind") %dopar% {
val <- calc(let=0.5,
var1=i,
var2=j)
data.frame(var1=i,
var2=j,
mean_b=tmp$mean_b,
mean_c=tmp$mean_c)
}
Here is a way to avoid the double for-loop using library(data.table):
library(shiny)
library(data.table)
set.seed(0)
DF <- data.frame(a = rnorm(n = 26), b = 1:26, c = 100:125)
setDT(DF)
DT <- setDT(expand.grid(var1 = seq(0, 1, by = 0.1), var2 = seq(0, 1, by = 0.1)))
setorder(DT, var1, var2)
calc <- function(DF, let = 0.5, var1 = 0.1, var2 = 0.5) {
DF[, c("mean_b", "mean_c") := .(b * fifelse(a < let, var1, 0), c * fifelse(a < let, var2, 2))]
as.list(colMeans(DF[, .(mean_b, mean_c)]))
}
ui <- fluidPage(titlePanel("Example"),
sidebarLayout(
sidebarPanel(
sliderInput(
inputId = "selected_let",
label = "LET",
value = 0.5,
min = 0,
max = 1,
step = 0.1
),
submitButton("CALCULATE")
),
mainPanel(
h1(paste0("Table1")),
tableOutput("table_1"),
h1(paste0("Table2")),
tableOutput("table_2")
)
))
server <- function(input, output) {
data <- reactive({
DT[, c("mean_b", "mean_c") := calc(DF, let = input$selected_let, var1 = var1, var2 = var2), by = seq_len(NROW(DT))]
})
output$table_1 <- renderTable({
dcast(data(), var1 ~ var2, value.var = "mean_b")
})
output$table_2 <- renderTable({
dcast(data(), var1 ~ var2, value.var = "mean_c")
})
}
shinyApp(ui = ui, server = server)
Here you can find a benchmark taking into account dplyr and data.table (among others).
I am plotting a matrix using heatmaply and plotly
I want to make the plotylOutput height and width reactive to the size of the matrix
If I use a reactive value in the plotlyOutput width argument I get the following error:
Error in htmltools::validateCssUnit: CSS units must be a single-element numeric or character vector
See below for a minimum version of my code:
library(shiny)
library(heatmaply)
library(plotly)
ui <- tags$div(id = "placeholderimage", uiOutput("plotlyimage"))
server <- shinyServer(function(input, output, session) {
matsample <- reactive({cbind(c("A","A","A","T"),c("A","A","A","T"),c("A","A","A","G"))})
ranges <- reactiveValues(width_im = NULL,height_im=NULL)
ranges$width_im <- reactive(ncol(matsample())*20)
ms <- reactive({
alph <- c("A"=1, "C"=2, "G"=3, "T"=4, "-"=5, "N"=6, "S"=7)
ms <- matrix(alph[matsample()], ncol = ncol(matsample()), byrow=FALSE))
rownames(ms) <- rownames(matsample())
return(ms)
})
mm <- reactive({
data.frame(do.call(rbind, lapply(rownames(ms()), rep, ncol(matsample()))))
})
output$matimage <- renderPlotly({
heatmaply(ms(), custom_hovertext = mm(),
cellnote = matsample(),cellnote_size = 6,
cellnote_textposition = "middle center",
fontsize_col = 6,grid_gap = 1,grid_size = 0.01,
show_dendrogram = c(FALSE, FALSE),
Rowv=NULL, Colv=NULL, color=rainbow(7),
hide_colorbar=FALSE, plot_method = "plotly")
})
output$plotlyimage <- renderUI(
tags$div(class="superbigimage",
plotlyOutput("testplot", **width = isolate(ranges$width_im)**)
)
})
This code currently throws the error because of width = isolate(ranges$width_im), how can I make the width reactive to size of matrix?
Note my real work has matrices with over 600 columns
I forgot brackets and did not need to use isolate
output$plotlyimage <- renderUI(
tags$div(class="superbigimage",
plotlyOutput("testplot", width = ranges$width_im())
)
Right now I'm using shiny and Plotly in R to make graphs to visualize data.
I have this list with items and for each item I want to generate a graph with the name of this item.
Is it possible to have your graph output name based on this list item?
In the simplest terms:
What I have:
output$plot <- renderPlotly({})
What I want:
listitems <- c("graph1", "graph2")
output$listitems[1] <- renderPlotly({})
This situation would be ideal, as I want to generate multiple graphs by using a function to minimalize code.
If I understand correctly, you don't want to assign every plot manually. Accordingly we can use a for-loop or lapply like this:
library(shiny)
library(plotly)
ui <- fluidPage(
uiOutput("myPlots")
)
server <- function(input, output, session) {
listItems <- paste0("graph", 1:10)
dfList <- replicate(10, data.frame(x = 1:10, y = runif(10)), simplify = FALSE)
names(dfList) <- listItems
lapply(seq_along(dfList), function(i){
output[[listItems[i]]] <- renderPlotly({plot_ly(dfList[[i]], x = ~x, y = ~y, type = "scatter", mode = "lines+markers") %>% layout(title = listItems[i])})
})
output$myPlots <- renderUI({
lapply(listItems, plotlyOutput)
})
}
shinyApp(ui, server)
Take a look at subplots. In your example, this would have to be something like:
library(shiny)
library(plotly)
ui <- fluidPage(
plotlyOutput("plot")
)
server <- function(input, output, session) {
p1 <- plot_ly(economics, x = ~date, y = ~unemploy) %>%
add_lines(name = ~"unemploy")
p2 <- plot_ly(economics, x = ~date, y = ~uempmed) %>%
add_lines(name = ~"uempmed")
listitems <- list(p1, p2)
output$plot <- renderPlotly({
subplot(listitems)
})
}
shinyApp(ui, server)
Output:
Following the example at: https://plot.ly/r/shinyapp-linked-click/ I was able to in a blank shiny project get this working (correlation matrix linked to a scatter graph). However, when I do the same in a shiny module the event_data based click action doesnt seem to work (the scatter remains blank no mater what happens, seems like the click is not connecting).
My reproducible example is below, any ideas or solutions would be much appreciated.
library(plotly)
#### Define Modules ####
correlation_matrix_shinyUI <- function(id) {
ns <- NS(id)
mainPanel(
plotlyOutput(ns("corr_matrix"), height = '650px'),
plotlyOutput(ns("scatterplot"), height = '550px')
)
}
correlation_matrix_shiny <- function(input, output, session) {
data_df <- reactive({
mtcars
})
corr_data <- reactive({
if (is.null(data_df()))
return()
corr_data <- cor(data_df())
diag(corr_data) <- NA
corr_data <- round(corr_data, 4)
corr_data
})
corr_names <- reactive({
if (is.null(data_df()))
return()
corr_names <- colnames(data_df())
corr_names
})
output$corr_matrix <- renderPlotly({
if (is.null(corr_names()))
return()
if (is.null(corr_data()))
return()
g <- plot_ly(x = corr_names(), y = corr_names(), z = corr_data(),
key = corr_data(), type = "heatmap", source = "CORR_MATRIX", zmax = 1, zmin = -1)
g
})
output$scatterplot <- renderPlotly({
if (is.null(data_df()))
return()
data_use <- data_df()
s <- event_data("plotly_click", source = "CORR_MATRIX")
if (length(s)) {
vars <- c(s[["x"]], s[["y"]])
d <- setNames(data_use[vars], c("x", "y"))
yhat <- fitted(lm(y ~ x, data = d))
plot_ly(d, x = x, y = y, mode = "markers") %>%
plotly::add_trace(x = x, y = yhat, mode = "lines") %>%
plotly::layout(xaxis = list(title = s[["x"]]),
yaxis = list(title = s[["y"]]),
showlegend = FALSE)
} else {
plot_ly()
}
})
}
############ End Module Definition ######
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
),
correlation_matrix_shinyUI(id = "cor_module")
)
)
server <- function(input, output, session) {
callModule(correlation_matrix_shiny, id = "cor_module")
}
shinyApp(ui = ui, server = server)
Your question really is interesting. I will answer with some text passages from the shiny modules page.
Foremost, your problem is a scoping issue. In more detail:
[...] input, output, and session cannot be used to access inputs/outputs that are outside of the namespace, nor can they directly access reactive expressions and reactive values from elsewhere in the application [...]
In your module, you are trying to access the plotly-owned and therefore server-level variable event_data that is used to store click (or other) events. The plots react normal, as you could see if you'd add
observe({print(event_data("plotly_click", source = "CORR_MATRIX"))})
inside your server function (and outside of the module). But this kind of input was not defined directly within the correlation_matrix_shinyUI namespace and so it remains inaccessible.
These restrictions are by design, and they are important. The goal is not to prevent modules from interacting with their containing apps, but rather, to make these interactions explicit.
This is well meant, but in your case, you weren't really given the chance to assign a name to this variable, since plotly handles everything under its cover. Luckily, there is a way:
If a module needs to access an input that isn’t part of the module, the containing app should pass the input value wrapped in a reactive expression (i.e. reactive(...)):
callModule(myModule, "myModule1", reactive(input$checkbox1))
This of course goes a bit contrary to the whole modularization...
So, the way this can be fixed is to fetch the click event outside of the module and then send it as extra input to the callModule function. This part in the code may look a bit redundant, but I found this to be the only way it worked.
Well, the rest can be best explained by the code itself. Changes have only been made to the server function and inside the correlation_matrix_shiny, where the variable s is defined.
I hope this helps!
Best regards
Code:
library(plotly)
#### Define Modules ####
correlation_matrix_shinyUI <- function(id) {
ns <- NS(id)
mainPanel(
plotlyOutput(ns("corr_matrix"), height = '650px'),
plotlyOutput(ns("scatterplot"), height = '550px')
)
}
correlation_matrix_shiny <- function(input, output, session, plotlyEvent) {
data_df <- reactive({
mtcars
})
corr_data <- reactive({
if (is.null(data_df()))
return()
corr_data <- cor(data_df())
diag(corr_data) <- NA
corr_data <- round(corr_data, 4)
corr_data
})
corr_names <- reactive({
if (is.null(data_df()))
return()
corr_names <- colnames(data_df())
corr_names
})
output$corr_matrix <- renderPlotly({
if (is.null(corr_names()))
return()
if (is.null(corr_data()))
return()
g <- plot_ly(x = corr_names(), y = corr_names(), z = corr_data(),
key = corr_data(), type = "heatmap", source = "CORR_MATRIX", zmax = 1, zmin = -1)
g
})
output$scatterplot <- renderPlotly({
if (is.null(data_df()))
return()
data_use <- data_df()
s <- plotlyEvent()
if (length(s)) {
vars <- c(s[["x"]], s[["y"]])
d <- setNames(data_use[vars], c("x", "y"))
yhat <- fitted(lm(y ~ x, data = d))
plot_ly(d, x = x, y = y, mode = "markers") %>%
plotly::add_trace(x = x, y = yhat, mode = "lines") %>%
plotly::layout(xaxis = list(title = s[["x"]]),
yaxis = list(title = s[["y"]]),
showlegend = FALSE)
} else {
plot_ly()
}
})
}
############ End Module Definition ######
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
),
correlation_matrix_shinyUI(id = "cor_module")
)
))
server <- function(input, output, session) {
plotlyEvent <- reactive(event_data("plotly_click", source = "CORR_MATRIX"))
callModule(correlation_matrix_shiny, id = "cor_module", reactive(plotlyEvent()))
}
shinyApp(ui = ui, server = server)
I'm following this example to plot multiple graphs depending of different parameters (as data frame columns). So the case is that the number of plots to do will vary each day.
I have modified the code to use Highcharter to get javascript charts instead of basic plots but it doesn't work.
Also I would like to know what I have to add to this code to plots charts in 2,3 or 4 columns?
Thanks
ui.R
fluidPage(
# Application title
titlePanel("Hello World!"),
# Show a plot
fluidRow(
column(width = 6,
highchartOutput("hcontainer", height = "400px")
)
)
)
server.R
get_plot_output_list <- function() {
plot_output_list <- lapply(1:NCOL(df), FUN = function(i) {
plot_output_object <- highchartOutput("hcontainer")
plot_output_object <- renderHighchart({
hc <- highchart() %>%
hc_add_serie(name = "df name", data = df)
return(hc)
})
})
do.call(tagList, plot_output_list) # needed to display properly.
return(plot_output_list)
}
observe({
output$hcontainer <- renderUI({ get_plot_output_list() })
#output$hcontainer <- renderHighchart({ get_plot_output_list() })
})
Hi you can try this solution, it do not use the same function as you but one by #jenesaisquoi (found here), this function create several plots and handle the layout correctly :
# Packages
library("highcharter")
library("shiny")
# data
df <- data.frame(
var1 = rnorm(10),
var2 = rnorm(10),
var3 = rnorm(10),
var4 = rnorm(10),
var5 = rnorm(10),
var6 = rnorm(10),
var7 = rnorm(10)
)
# Fun by #jenesaisquoi (modified with highchartOutput)
makePlotContainers <- function(n, ncol=2, prefix="plot", height=100, width="100%", ...) {
## Validate inputs
validateCssUnit(width)
validateCssUnit(height)
## Construct plotOutputs
lst <- lapply(seq.int(n), function(i)
highchartOutput(sprintf('%s%g', prefix, i), height=height, width=width))
## Make columns
lst <- lapply(split(lst, (seq.int(n)-1)%/%ncol), function(x) column(12/ncol, x))
do.call(tagList, lst)
}
You can use #jenesaisquoi's function directly in the ui, and use lapply in the server for define as many outputs as the number of cols :
# App
ui <- fluidPage(
# Application title
titlePanel("Hello World!"),
# Show plots
makePlotContainers(n = ncol(df), ncol = 3, prefix = "hcontainer", height = "400px")
)
server <- function(input, output) {
lapply(
X = seq_len(ncol(df)),
FUN = function(i) {
output[[paste0("hcontainer", i)]] <- renderHighchart({
highchart() %>%
hc_add_serie(name = paste("df name", i), data = df[[i]])
})
}
)
}
shinyApp(ui = ui, server = server)