I have a Shiny app which dynamically loads any number of outputPlot UIs. In the following example I am simply iteration over the first three letters of the alphabet.
To render the plot in the dynamically loaded UIs, I call renderPlot in a loop as such:
for (a in LETTERS[1:3]) {
output[[paste0('p',a)]] <- renderPlot(plot.df(df, a))
}
but the result is that all three outputPlots (pA, pB and pC) are all rendered with plot.df(df, 'C')). Seems to me renderPlot is rendered after the loop has completed and a = 'C'. Instead, the output UIs pA, pB and pC should have been rendered with plot.df(df, 'A'), plot.df(df, 'B') and plot.df(df, 'C'), respectively. But this is clearly not the case when viewing the output.
I have previous had success with this, if the output UI was a module and in the loop calling callModule, which somehow forced the evaluation of the arguments. But I for now am trying to avoid making a separate module for my output UI.
Full reproducible example
library(shiny)
library(dplyr)
# Define UI for application that draws a histogram
ui <- fluidPage(
plotOutput('pltA'),plotOutput('pltB'),plotOutput('pltC'),
tags$hr(),
tags$div(id='placeholder')
)
col <- c(A='#66bd63', B='#fdae61', C='#74add1')
plot.df <- function(df, a) {
#browser()
df <- filter(df, letter==a)
if (nrow(df) == 0) return()
plot(df$i, df$y, type='p', col=col[a], pch=19, main=a)
}
# Define server logic required to draw a histogram
server <- function(input, output, session) {
my_data <- reactiveVal(data.frame())
autoInvalidate <- reactiveTimer(2000)
# Generate some random data and assign to one of three different letters:
observe({
autoInvalidate()
a <- sample(LETTERS[1:3], 1)
data.frame(y=rnorm(5), letter=a) %>%
bind_rows(isolate(my_data())) %>%
group_by(letter) %>%
mutate(i=seq_along(y)) %>%
my_data
})
# Proof of function making a plot.
output$pltA <- renderPlot(plot.df(my_data(), 'A'))
output$pltB <- renderPlot(plot.df(my_data(), 'B'))
output$pltC <- renderPlot(plot.df(my_data(), 'C'))
# Dynamically load output UIs.
observe({
let <- unique(my_data()$letter)
if (is.null(let)) return()
for (l in let) {
if (is.null(session$clientData[[paste0('output_p',l,'_hidden')]])) {
insertUI('#placeholder', 'beforeEnd', ui=plotOutput(paste0('p',l)))
}
}
})
# Update dynamically loaded plots
observe({
df <- my_data()
if (nrow(df) == 0) return()
for (a in LETTERS[1:3]) {
cat('Updating ', a, '\n')
output[[paste0('p',a)]] <- renderPlot(plot.df(df, a))
}
})
}
# Run the application
shinyApp(ui = ui, server = server)
You have to use local (see here).
for (a in LETTERS[1:3]) {
local({
aa <- a
output[[paste0('p',aa)]] <- renderPlot(plot.df(df, aa))
})
}
Related
I have a simple shiny app that holds a dataset as a reactive value.
Once a button is pressed, a function should be applied to each row and the result is added as another variable to that dataset.
The dataset is also shown as a DT.
The result variable should be rendered as soon as the computation for that row is finished.
At the moment, the loop/apply that applies the function to each row finishes and only afterwards the results are displayed.
As the function can run for a long time, I want the DT to be updated as soon as a run is finished, not when all runs finish.
I understand that this means I need to use promises/future so that the main shiny code block spawns new processes which do not block in this case the main thread from updating the values. Correct?
However, I am not able to get it to work.
Here is a small MWE using a simple for loop
library(shiny)
library(DT)
ui <- fluidPage(
actionButton("run", "RUN"),
hr(),
DT::dataTableOutput("table")
)
calc_fun <- function(val) {
Sys.sleep(0.5)
val * 10
}
server <- function(input, output, session) {
set.seed(123)
data_res <- reactiveVal(data.frame(id = 1:10, val = rnorm(10), val10 = NA))
observe({
for (i in seq(nrow(data_res()))) {
print(paste("Looking at row", i))
d <- data_res()
d[i, "val10"] <- calc_fun(val = d[i, "val"])
data_res(d)
}
}) %>% bindEvent(input$run)
# This should be rendered whenever a round in the for-loop has finished
# at the moment it is only run once the loop is finished
output$table <- DT::renderDataTable(data_res())
}
shinyApp(ui, server)
Thanks to #ismirsehregal, I came up with the following solution which uses futures to start the calculation in the background, which in turn write the current status to a file.
Shiny then reactively reads the file and updates the values.
The full MWE looks like this:
library(shiny)
library(DT)
library(future)
library(promises)
library(qs) # for fast file read/write, replace with csv if needed
plan(multisession)
ui <- fluidPage(
actionButton("run", "RUN"),
hr(),
textOutput("prog"),
uiOutput("status"),
hr(),
fluidRow(
column(6,
h2("Current Status"),
DT::dataTableOutput("table")
),
column(6,
h2("Data in File"),
tableOutput("file_data")
)
)
)
calc_fun <- function(val) {
Sys.sleep(runif(1, 0, 2))
val * 10
}
# main function that goes through the rows and starts the calculation
# note that the output is saved to a .qs file to be read in by another reactive
do_something_per_row <- function(df, outfile) {
out <- tibble(id = numeric(0), res = numeric(0))
for (i in seq(nrow(df))) {
v <- df$val[i]
out <- out %>% add_row(id = i, res = calc_fun(v))
qsave(out, outfile)
}
return(out)
}
# create a data frame of tasks
set.seed(123)
N <- 13
tasks_init <- tibble(id = seq(N), val = round(rnorm(N), 2), status = "Open", res = NA)
server <- function(input, output, session) {
# the temporary file to communicate over
outfile <- "temp_progress_watch.qs"
unlink(outfile)
data <- reactiveVal(tasks_init) # holds the current status of the tasks
data_final <- reactiveVal() # holds the results once all tasks are finished
output$prog <- renderText(sprintf("Progress: 0 of %i (0.00%%)", nrow(data())))
output$status <- renderUI(div(style = "color: black;", h3("Not yet started")))
# on the button, start the do_something_per_row function as a future
observeEvent(input$run, {
# if a file exists => the code runs already
if (file.exists(outfile)) return()
print("Starting to Run the code")
output$status <- renderUI(div(style = "color: orange;", h3("Working ...")))
d <- data()
future({do_something_per_row(d, outfile)}, seed = TRUE) %...>% data_final()
print("Done starting the code, runs now in the background! freeing the session for interaction")
# return(NULL) # hide future
})
observe({
req(data_final())
output$status <- renderUI(div(style = "color: green;", h3("Done")))
print("All Done - Results came back from the future!")
})
output$file_data <- renderTable(req(df_done()))
output$table <- DT::renderDataTable({
# no need to fire on every refresh, this is handled automatically later
DT::datatable(isolate(data())) %>%
formatStyle("status", color = styleEqual(c("Open", "Done"), c("white", "black")),
backgroundColor = styleEqual(c("Open", "Done"), c("red", "green")))
})
dt_proxy <- DT::dataTableProxy("table")
# look for changes in the file and load it
df_done <- reactiveFileReader(300, session, outfile, function(f) {
r <- try(qread(f), silent = TRUE)
if (inherits(r, "try-error")) return(NULL)
r
})
observe({
req(df_done())
open_ids <- data() %>% filter(status == "Open") %>% pull(id)
if (!any(df_done()$id %in% open_ids)) return()
print(paste("- new entry found:", paste(intersect(df_done()$id, open_ids), collapse = ", ")))
rr <- data() %>% select(-res) %>% left_join(df_done(), by = "id") %>%
mutate(status = ifelse(is.na(res), "Open", "Done"))
data(rr)
DT::replaceData(dt_proxy, rr)
# replace the progress text
txt <- sprintf("Progress: % 4i of % 4i (%05.2f%%)",
nrow(df_done()), nrow(data()), 100 * (nrow(df_done()) / nrow(data())))
output$prog <- renderText(txt)
})
}
shinyApp(ui, server)
or as a picture:
Currently, i am trying to output multiple tables i managed to retrieve with an api call onto a dashboard page with a single uiOutput().
took some reference from this post:
R Shiny - Display multiple plots selected with checkboxGroupInput
however, while i was succesful in putting it into a list for the overall layout and output it into uioutput(), i was not able to acheive the desired results as all the tables were the same, in reality it should be different as i have already tagged a unique dataframe to each renderdatatable()
below shows the screen shot and the code. would appreciate some help here thank you!
[1]: https://i.stack.imgur.com/W0B26.png
library(httr)
library(jsonlite)
library(plyr)
library(data.table)
library(rlist)
library(shiny)
########### UI ############
ui <- fluidPage(uiOutput('datatables'))
######### SERVER ###########3
server <- function(input, output, session){
output$datatables <- renderUI({
link <- 'https://api.zapper.fi/v1/protocols/balances/supported?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'
test <- GET(link)
test <- fromJSON(rawToChar(test$content))
counter1 <- 0
out <- list()
df <- list()
for (i in seq(from = 1, to = length(test$network))){
network <- test$network[i]
#counter <- counter + 1
out <- list(out, h2(paste0(str_to_title(network),' Network')))
for (e in ldply(test$protocols[i], data.frame)$protocol){
link1 <- paste0(paste0('https://api.zapper.fi/v1/protocols/',e),paste0(paste0('/balances?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&network=',network),'&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'))
data <- fromJSON(rawToChar(GET(link1)$content))
wallet <- '0x58bbae0159117a75225e72d941dbe35ffd99f894'
#info <- ldply(eval(parse(text=sprintf("data$'%s'$products$assets",wallet))),data.frame)
out <- list(out, h3(paste0(str_to_title(e),' Protocol')))
counter1 <- counter1 + 1
df[[counter1]] <- ldply(eval(parse(text=sprintf("data$'%s'$products$assets",wallet))),data.frame)
out<- list(out, renderDataTable(df[[counter1]]))
}
}
return(out)
})
}
shinyApp(ui, server)
UPDATE: I ALSO TRIED TO WRAP IT IN AN OBSERVE() AND LOCAL () FOR THE DIFFERENT OUTPUTS, STILL DIDNT ACHEIVE THE DESIRED RESULTS, ALL SAME TABLES WHICH IS WRONG
I almost achieved the desired result with the following code:
library(httr)
library(jsonlite)
library(plyr)
library(data.table)
library(rlist)
library(shiny)
# added :
library(DT)
library(stringr)
########### UI ############
ui <- fluidPage(
uiOutput('datatables')
)
######### SERVER ###########
server <- function(input, output, session){
link <- 'https://api.zapper.fi/v1/protocols/balances/supported?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'
test <- GET(link)
test <- fromJSON(rawToChar(test$content))
output$datatables <- renderUI({
outputlist <- lapply(1:length(test$network), function(i) {
network <- test$network[i]
networkTitle <- paste0("networktitle", i)
lapply(seq_along(ldply(test$protocols[i], data.frame)$protocol), function(j) {
protocolTitle <- paste0("protocoltitle", i, j)
outputId <- paste0("network", i, "protocol", j)
tagList(
#uiOutput(networkTitle),
uiOutput(protocolTitle),
DTOutput(outputId)
)
})
})
})
lapply(1:length(test$network), function(i) {
network <- test$network[i]
my_i <- i
networkTitle <- paste0("networktitle", my_i)
#local({
# my_network <- network
# output[[networkTitle]] <- renderUI({
# tags$h2(paste0(str_to_title(my_network),' Network'))
# })
#})
lapply(seq_along(ldply(test$protocols[my_i], data.frame)$protocol), function(j) {
e <- ldply(test$protocols[my_i], data.frame)$protocol[[j]]
local({
my_network <- network
my_e <- e
my_j <- j
protocolTitle <- paste0("protocoltitle", my_i, my_j)
outputId <- paste0("network", my_i, "protocol", my_j)
link1 <- paste0(paste0('https://api.zapper.fi/v1/protocols/',my_e),paste0(paste0('/balances?addresses%5B%5D=0x58bbae0159117a75225e72d941dbe35ffd99f894&network=',my_network),'&api_key=96e0cc51-a62e-42ca-acee-910ea7d2a241'))
data <- fromJSON(rawToChar(GET(link1)$content))
wallet <- '0x58bbae0159117a75225e72d941dbe35ffd99f894'
output[[protocolTitle]] <- renderUI({
tags$h3(paste0(str_to_title(my_network),' Network - ', str_to_title(my_e),' Protocol'))
})
output[[outputId]] <- renderDT({
ldply(eval(parse(text=sprintf("data$'%s'$products$assets",wallet))),data.frame)
})
})
})
})
}
shinyApp(ui, server)
I used two nested lapply but this might work with for loops as well (using local()).
As you mentioned, we have several difficulties here:
the need to use local() to get unique IDs for each protocol title and datatable (see this)
the need to use one renderUI in the server part to generate several types of outputs dynamically (uiOutput for titles, DTOutput for datatables), all contained in a tagList(). See this and this again.
The need to use two nested for loops/lapply functions to render the shiny outputs used in the renderUI() part.
I combined the network and protocol titles because I was not able to get network titles as unique h2 titles as shown in your example figure. Duplicate output IDs were still generated for h2 titles when using the commented code. I let the commented code as reference if someone wants to try improve it.
I want to create an app that displays based on user input in real time . For this, I am using reactive function. Here is my entire code. The question is somewhat based on Ellis Valentiner but it extends the app little further. The app runs the first iteration but then it is not able to find my_data.
library(shiny)
library(magrittr)
ui <- shinyServer(fluidPage(
textInput("Somenumber", "Somenumber", value = 5),
actionButton(inputId = "click", label = "Predict"),
plotOutput("first_column")
))
server <- shinyServer(function(input, output, session){
nameoftext <- reactive({
data <- eventReactive(input$click, {
input$Somenumber
})
usernumber <- as.numeric(data())
return(usernumber)
})
# Function to get new observations
get_new_data <- function(){
numerinumber <- nameoftext()
data <- rnorm(numerinumber) %>% rbind %>% data.frame
return(data)
}
# Initialize my_data
my_data <<- reactive({get_new_data()})
# Function to update my_data
update_data <- function(){
my_data <<- rbind(get_new_data(), my_data())
}
observeEvent(input$click, {
# Plot the 30 most recent values
output$first_column <- renderPlot({
print("Render")
invalidateLater(1000, session)
playthins<- update_data()
print(my_data())
appdata <- my_data()
plot(X1 ~ 1, data=playthins[1:30,], ylim=c(-3, 3), las=1, type="l")
})
})
})
shinyApp(ui=ui,server=server)
Based on Stéphane Laurent's comments, if I remove <<-, the data is not appended. So the live line chart does not form over the plot.
If I keep <<-, I get following error message.
Warning: Error in my_data: could not find function "my_data"
[No stack trace available]
Below is the minimum code. It works, but there is a weird problem. Here is what works:
User can select a number of plots (default is 3).
User can click in a plot and have that value represented (partly works).
Steps to reproduce the "partly works":
At launch, click in plot #3, no problem.
Click in plot #2, nothing happens.
Reduce the number of plots from 3 to 2 and then back to 3.
Click in plot #2, now it works.
Click in plot #1, nothing happens.
Reduce the number of plots from 3 to 1 and then back to 3.
Click in plot #1, now it works.
If you reload the app, and start with step 6 above, all plots are interactive as expected.
rm(list=ls())
library(shiny)
#
# Dynamic number of plots: https://stackoverflow.com/questions/26931173/shiny-r-renderplots-on-the-fly
# That can invalidate each other: https://stackoverflow.com/questions/33382525/how-to-invalidate-reactive-observer-using-code
#
ui <- (fluidPage(sidebarLayout(
sidebarPanel(
numericInput("np", "Plots:", min=0, max=10, value=3, step=1)
)
,mainPanel(
fluidRow(uiOutput("plots"))
)
)))
server <- function(input, output, session) {
val <- reactiveValues()
dum <- reactiveValues(v=0)
obs <- list()
### This is the function to break the whole data into different blocks for each page
plotInput <- reactive({
print("Reactive")
np <- input$np
for(i in 1:np) {
cx <- paste0("clk_p",i); dx <- paste0("dbl_p",i); px <- paste0("p",i)
obs[[cx]] <- observeEvent(input[[cx]], {
req(input[[cx]]); val[[px]] <- input[[cx]]$x; dum$v <- dum$v+1; print(paste("Dum",dum$v))
})
obs[[dx]] <- observeEvent(input[[dx]], {
req(input[[dx]]); val[[px]] <- NULL
})
}
return (list(np=np))
})
##### Create divs######
output$plots <- renderUI({
print("Tag plots")
pls <- list()
for(i in 1:plotInput()$np) {
pls[[i]] <- column(4,
plotOutput(paste0("p",i), height=200, width=200
,click=paste0("clk_p",i)
,dblclick=paste0("dbl_p",i))
)
}
tagList(pls)
})
observe({
print("Observe")
lapply(1:plotInput()$np, function(i){
output[[paste("p", i, sep="") ]] <- renderPlot({
print(paste("Plot",dum$v))
x <- val[[paste0("p",i)]]
x <- ifelse(is.null(x),"NA",round(x,2))
par(mar=c(2,2,2,2))
plot(x=runif(20), y=runif(20), main=i, xlim=c(0,1), ylim=c(0,1), pch=21, bg="gray", cex=1.5)
if(is.numeric(x)) abline(v=x, col="blue")
rm(x)
})
})
})
}
shinyApp(ui, server)
Here is a working version of what you're trying to do:
library(shiny)
ui <- fluidPage(
sidebarPanel(
numericInput("num", "Plots:", 3)
),
mainPanel(
uiOutput("plots")
)
)
server <- function(input, output, session) {
obs <- list()
val <- reactiveValues()
observe({
lapply(seq(input$num), function(i){
output[[paste0("plot", i) ]] <- renderPlot({
click_id <- paste0("clk_p",i);
plot(x = runif(20), y = runif(20), main=i)
if (!is.null(val[[click_id]])) {
abline(v = val[[click_id]], col = "blue")
}
})
})
})
observe({
lapply(seq(input$num), function(i){
id <- paste0("clk_p",i);
if (!is.null(obs[[id]])) {
obs[[id]]$destroy()
}
val[[id]] <- NULL
obs[[id]] <<- observeEvent(input[[id]], {
cat('clicked ', id, ' ', input[[id]]$x, '\n')
val[[id]] <- input[[id]]$x
}, ignoreInit = TRUE)
})
})
output$plots <- renderUI({
lapply(seq(input$num), function(i) {
id <- paste0("plot", i)
plotOutput(id, height=200, width=200, click=paste0("clk_p",i))
})
})
}
shinyApp(ui,server)
A few main pointers for anyone who sees this in the future:
The main issue with the original code was that all the observers were registering only for the last ID. This is a bit of an advanced concept and has to do with the way environments in R work and because they were created in a for loop. The fix for this is to use lapply() instead of a for loop to create the observers
Another issue is that obs was overwriting the observers in the list, but the previous observers still exist and can still fire, so I added logic to destroy() existing observers.
One of the most important rules in shiny is to not place side effects inside reactives (plotInput has side effects) so I rewrote the code in a way that avoids that
I have a single-cell gene x cell expression data that I'd like to explore using a shiny app. The cells come from samples and are clustered according to a prior clustering run.
Here is a toy example data set:
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(plotly))
suppressPackageStartupMessages(library(shiny))
set.seed(1)
mat <- matrix(rnorm(1000*1000),nrow=1000,dimnames = list(paste0("gene",1:1000),paste0("cell",1:1000)))
meta.df <- data.frame(cell=colnames(mat),
sample=sample(paste0("sample",1:10),1000,replace=T),
cluster=sample(paste0("cluster",1:5),200,replace=T),
stringsAsFactors = F)
There are two functionalities that I'd like to enable:
View the tSNE 2D embedding of the entire data allowing to choose a gene where the points in the tSNE 2D embedding scatter plot will be colored by its expression values.
Be able to select a specific set of genes and clusters and/or samples on which tSNE will be re-run, and again view this 2D embedding coloring the points according to the expression levels of a chosen gene (in this case obviously the selection options is subsetted to be one of the selected genes).
Here's the tSNE embedding on all the data:
all.data.tsne.df <- data.frame(Rtsne::Rtsne(t(mat))$Y) %>%
rename(tSNE1=X1,tSNE2=X2) %>% cbind(meta.df)
For the second functionality, since one might like to color code many genes using the same tSNE embedding, and since a Rtsne run can take a while to run, I thought I'd save any gene and cell subset tSNE embedding in a list named by the chosen genes and cells, and always check if this subset already exists before running the Rtsne on it.
So at the top of the shiny code I create the options for subsetting samples and clusters and an empty tSNE list:
samples <- c("all",unique(meta.df$sample))
samples.choices <- 1:length(samples)
names(samples.choices) <- samples
clusters <- c("all",unique(meta.df$cluster))
clusters.choices <- 1:length(clusters)
names(clusters.choices) <- clusters
color.vec <- c("lightgray","darkred")
subset.tsne.map <- NULL
Here's my server code:
server <- function(input, output)
{
chosen.samples <- reactive({
validate(
need(input$samples.choice != "",'Please choose at least one of the sample checkboxes')
)
samples.choice <- input$samples.choice
if("all" %in% samples.choice) samples.choice <- samples[-which(samples == "all")]
samples.choice
})
chosen.clusters <- reactive({
validate(
need(input$clusters.choice != "",'Please choose at least one of the cluster checkboxes')
)
clusters.choice <- input$clusters.choice
if("all" %in% clusters.choice) clusters.choice <- clusters[-which(clusters == "all")]
clusters.choice
})
output$gene <- renderUI({
if(input$plotType == "Gene-Subset tSNE"){
selectInput("gene", "Color by Gene", choices = unique(input$subset.genes))
} else{
selectInput("gene", "Color by Gene", choices = rownames(mat))
}
})
scatter.plot <- reactive({
if(!is.null(input$gene)){
row.idx <- which(rownames(mat) == input$gene)
col.idx <- which(colnames(mat) %in% filter(meta.df,cluster %in% chosen.clusters(),sample %in% chosen.samples())$cell)
#col.idx <- which(colnames(mat) %in% filter(meta.df,cluster %in% "cluster4",sample %in% unique(meta.df$sample))$cell)
if(input$plotType != "Gene-Subset tSNE"){
# subset of data
gene.tsne.df <- left_join(all.data.tsne.df %>% filter(cluster %in% chosen.clusters(),sample %in% chosen.samples()),data.frame(cell=colnames(mat)[col.idx],value=mat[row.idx,col.idx]),by=c("cell"="cell"))
scatter.plot <- plot_ly(marker=list(size=6),type='scatter',mode="markers",color=~gene.tsne.df$value,x=~gene.tsne.df$tSNE1,y=~gene.tsne.df$tSNE2,showlegend=F,colors=colorRamp(color.vec)) %>%
layout(xaxis=list(title="tSNE1",zeroline=F,showticklabels=F),yaxis=list(title="tSNE2",zeroline=F,showticklabels=F)) %>%
colorbar(limits=c(min(gene.tsne.df$value,na.rm=T),max(gene.tsne.df$value,na.rm=T)),len=0.4,title="Expression")
break
} else{
subset.genes <- sort(unique(input$subset.genes))
subset.row.idx <- which(rownames(mat) %in% subset.genes)
if(!is.null(subset.tsne.map)){
idx <- which(names(subset.tsne.map) == paste0(paste(subset.row.idx,collapse="_"),":",paste(col.idx,collapse="_")))
if(length(idx) > 0){
subset.tsne.df <- subset.tsne.map[[idx]] %>% mutate(value=mat[row.idx,col.idx])
scatter.plot <- plot_ly(marker=list(size=6),type='scatter',mode="markers",color=~subset.tsne.df$value,x=~subset.tsne.df$tSNE1,y=~subset.tsne.df$tSNE2,showlegend=F,colors=colorRamp(color.vec)) %>%
layout(xaxis=list(title="tSNE1",zeroline=F,showticklabels=F),yaxis=list(title="tSNE2",zeroline=F,showticklabels=F)) %>%
colorbar(limits=c(min(subset.tsne.df$value,na.rm=T),max(subset.tsne.df$value,na.rm=T)),len=0.4,title="Expression")
}
} else{
subset.tsne.df <- data.frame(t(mat[subset.row.idx,col.idx]),cell=colnames(mat)[col.idx]) %>% left_join(meta.df %>% filter(cell %in% colnames(mat)[col.idx]) %>% select(cell,cluster),by=c("cell"="cell"))
tsne.perplexity <- 10*length(subset.row.idx)
while(tsne.perplexity >= 1){
set.seed(1)
tsne.obj <- try(Rtsne::Rtsne(subset.tsne.df %>% select(-cell,-cluster),perplexity=tsne.perplexity),silent=T)
if(class(tsne.obj)[1] != "try-error"){
subset.tsne.df <- cbind(subset.tsne.df,data.frame(tsne.obj$Y) %>% rename(tSNE1=X1,tSNE2=X2))
subset.tsne.map[[length(subset.tsne.map)+1]] <- subset.tsne.df
names(subset.tsne.map)[length(subset.tsne.map)] <- paste0(paste(subset.row.idx,collapse="_"),":",paste(col.idx,collapse="_"))
subset.tsne.df <- subset.tsne.df %>% mutate(value=mat[row.idx,col.idx])
scatter.plot <- plot_ly(marker=list(size=6),type='scatter',mode="markers",color=~subset.tsne.df$value,x=~subset.tsne.df$tSNE1,y=~subset.tsne.df$tSNE2,showlegend=F,colors=colorRamp(color.vec)) %>%
layout(xaxis=list(title="tSNE1",zeroline=F,showticklabels=F),yaxis=list(title="tSNE2",zeroline=F,showticklabels=F)) %>%
colorbar(limits=c(min(subset.tsne.df$value,na.rm=T),max(subset.tsne.df$value,na.rm=T)),len=0.4,title="Expression")
} else{
tsne.perplexity <- tsne.perplexity-2
}
}
}
}
scatter.plot
}
})
output$Embedding <- renderPlotly({
scatter.plot()
})
output$save <- downloadHandler(
filename = function() {
paste0("../shiny/",input$gene,".",input$plotType,".pdf")
},
content = function(file) {
plotly::export(scatter.plot(),file=file)
}
)
}
And here's my UI code:
ui <- fluidPage(
# App title ----
titlePanel("Results Explorer"),
# Sidebar layout with a input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
## custom CSS for 3 column layout (used below for mechanics filter options)
tags$head(
tags$style(HTML(".multicol {-webkit-column-count: 3; /* Chrome, Safari, Opera */-moz-column-count: 3; /* Firefox */column-count: 3;}"))),
## use the css, assuming your long list of vars comes from global.R
wellPanel(tags$div(class="multicol",checkboxGroupInput("samples.choice", "Samples",choices = names(samples.choices),selected="all"))),
wellPanel(tags$div(class="multicol",checkboxGroupInput("clusters.choice", "Clusters",choices = names(clusters.choices),selected="all"))),
# select plot type
selectInput("plotType", "Plot Type", choices = c("tSNE","Gene-Subset tSNE")),
#in case Gene Subset tSNE was chose select the genes
conditionalPanel(condition="input.plotType=='Gene-Subset tSNE'",
selectizeInput(inputId="subset.genes",label="Subset Genes for tSNE",choices=rownames(mat),selected=rownames(mat)[1],multiple=T)),
# select gene
uiOutput("gene"),
# save plot as html
downloadButton('save', 'Save as PDF')
),
# Main panel for displaying outputs ----
mainPanel(
# The plot is called Embedding and will be created in ShinyServer part
plotlyOutput("Embedding")
)
)
)
shinyApp(ui = ui, server = server)
It doesn't seem to be updating subset.tsne.map and each gene that is selected, even for the same sets of cells and genes, it run Rtsne again.
Is it possible to update subset.tsne.map with previously selected subsets at all? and if so am I doing it correctly?