In R Shiny, I was trying to produce multiple plots from one model (including one set of simulations) but it only returns one plot. I tried the code from the answer in another post on stack overflow and it works however when i added the second plot in the model, only the second plot can show but not the first one. Anybody can advise on this please? code as below from the answer in the post mentioned above:
library(shiny)
ui <- shinyUI(fluidPage(
br(),
actionButton("numb", "generate a random numbers"),
br(),
br(),
verbatimTextOutput("text"),
plotOutput("plot"),
plotOutput("plot2"),
tableOutput("table")
))
server <- shinyServer(function(input, output) {
model <- eventReactive(input$numb, {
# draw a random number and print it
random <- sample(1:100, 1)
print(paste0("The number is: ", random))
# generate data for a table and plot
data <- rnorm(10, mean = 100)
table <- matrix(data, ncol = 2)
# create a plot
Plot <- plot(1:length(data), data, pch = 16, xlab ="This is the first plot", ylab =
"")
# create a second plot
Plot2 <- plot(1:length(data), data, pch=16, xlab="This is the second plot", ylab =
"")
# return all object as a list
list(random = random, Plot = Plot, Plot2=Plot2, table = table)
})
output$text <- renderText({
# print the random number after accessing "model" with brackets.
# It doesn't re-run the function.
youget <- paste0("After using model()$random you get: ", model()$random,
". Compare it with a value in the console")
print(youget)
youget
})
output$plot <- renderPlot({
# render saved plot
model()$Plot
})
output$plot2 <-renderPlot({
# render second plot
model()$Plot2
})
output$table <- renderTable({
model()$table
})
})
shinyApp(ui = ui, server = server)
I moved the plots to the renderPlot server functions and modified the plot call:
server <- shinyServer(function(input, output) {
model <- eventReactive(input$numb, {
# draw a random number and print it
random <- sample(1:100, 1)
print(paste0("The number is: ", random))
# generate data for a table and plot
data <- rnorm(10, mean = 100)
table <- matrix(data, ncol = 2)
# return all object as a list
list(random = random, table = table)
})
output$text <- renderText({
# print the random number after accessing "model" with brackets.
# It doesn't re-run the function.
youget <- paste0("After using model()$random you get: ", model()$random,
". Compare it with a value in the console")
print(youget)
youget
})
output$plot <- renderPlot({
# render saved plot
mod_list=model()
data=mod_list$table
# create a plot
plot(data[,1], data[,2], pch = 16, xlab ="This is the first plot", ylab ="")
})
output$plot2 <-renderPlot({
# render second plot
mod_list=model()
data=mod_list$table
# create a second plot
plot(data[,1], data[,2], pch=16, xlab="This is the second plot", ylab ="")
})
output$table <- renderTable({
model()$table
})
})
Your x and y arguments for plot function are confusing. If you want two lines per plot, try using qplot and the melt function to reshape your dataframe to long format. If you just want a plot with 10 random values, don'y use the matrix function
Related
I expanded the result from my last question with a new idea.
Error in Running R Shiny App: Operation not allowed without an active reactive context
This time in addition to clustered points in Iris data (see my previous question), I want to show the regression line (on the plot), slope & intercept (on the sidebar) for the selected points as in:
The regression code is available here (separate server.R and ui.R files):
library(shiny)
shinyServer(function(input, output) {
model <- reactive({
brushed_data <- brushedPoints(iris, input$brush1,
xvar = "Petal.Length", yvar = "Petal.Width")
if(nrow(brushed_data) < 2){
return(NULL)
}
lm(Petal.Width ~ Petal.Length, data = brushed_data)
})
output$slopeOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][2]
}
})
output$intOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][1]
}
})
output$plot1 <- renderPlot({
plot(iris$Petal.Length, iris$Petal.Width, xlab = "Petal.Length",
ylab = "Petal.Width", main = "Iris Dataset",
cex = 1.5, pch = 16, bty = "n")
if(!is.null(model())){
abline(model(), col = "blue", lwd = 2)
}
})
})
and
library(shiny)
shinyUI(fluidPage(
titlePanel("Visualize Many Models"),
sidebarLayout(
sidebarPanel(
h3("Slope"),
textOutput("slopeOut"),
h3("Intercept"),
textOutput("intOut")
),
mainPanel(
plotOutput("plot1", brush = brushOpts(
id = "brush1"
))
)
)
))
I used the following code. However, I have a problem with merging these two ideas and the plot is not shown:
Here is the main code for this question (server and ui in one file):
# Loading Libraries and data
library(shiny)
library(caret)
library(ggplot2)
data(iris)
ui <- pageWithSidebar(
# heading 1
headerPanel(h1("Clustering Iris Data")),
sidebarPanel(
sliderInput("k", "Number of clusters:",
min = 1, max = 5, value = 3),
sliderInput("prob", "Training percentage:",
min=0.5, max=0.9, value = 0.7),
# bold text
tags$b("Slope:"),
textOutput("slopeOut"),
# empty line
br(),
# bold text
tags$b("Intercept:"),
textOutput("intOut")
),
# Enabling the submit button disables the hovering feature
# submitButton("submit")),
mainPanel(
# img(src='iris_types.jpg', align = "center", height="50%", width="50%"),
plotOutput("plot1",
click = "plot_click",
brush = brushOpts(id = "brush1")
),
verbatimTextOutput("info")
)
)
#----------------------------------------------------------------------------
server <- function(input, output) {
# the clustering part
get_training_data <- reactive({
inTrain <- createDataPartition(y=iris$Species,
p=input$prob,
list=FALSE)
training <- iris[ inTrain,]
testing <- iris[-inTrain,]
kMeans1 <- kmeans(subset(training,
select=-c(Species)),
centers=input$k)
training$clusters <- as.factor(kMeans1$cluster)
training
})
#-------------------------
# the linear model part
model <- reactive({
brushed_data <- brushedPoints(iris, input$brush1,
xvar = "Petal.Length", yvar = "Petal.Width")
if(nrow(brushed_data) < 2){
return(NULL)
}
lm(Petal.Width ~ Petal.Length, data = brushed_data)
})
# reactive
output$slopeOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][2]
}
})
# reactive
output$intOut <- renderText({
if(is.null(model())){
"No Model Found"
} else {
model()[[1]][1]
}
})
#------------------------------------------------
# if (x()<4) 1 else 0
output$plot1 <- reactive({
if(is.null(model())) {
# If no regression model exists, show the regular scatter plot
# with clustered points and hovering feature
renderPlot({
plot(Petal.Width,
Petal.Length,
colour = clusters,
data = get_training_data(),
xlab="Petal Width",
ylab="Petal Length")
})
output$info <- renderPrint({
# With ggplot2, no need to tell it what the x and y variables are.
# threshold: set max distance, in pixels
# maxpoints: maximum number of rows to return
# addDist: add column with distance, in pixels
nearPoints(iris, input$plot_click, threshold = 10, maxpoints = 1,
addDist = FALSE)
})
# closing if
}
else
# If there is a regression model, show the plot with the regression line for the brushed points
renderPlot({
plot(Petal.Width,
Petal.Length,
colour = clusters,
data = get_training_data(),
xlab = "Petal.Length",
ylab = "Petal.Width",
main = "Iris Dataset",
cex = 1.5, pch = 16, bty = "n")
if(!is.null(model())){
abline(model(), col = "blue", lwd = 2)
}
})
# closing reactive statement
})
# curly brace for server function
}
shinyApp(ui, server)
You were assigning the wrong data type to the output$plot1.
It expects something that was created by the function renderPlot(...) while you were giving it a result of reactive(...).
Restructure your code such that you immediately assign
output$plot1 <- renderPlot(...)
Since renderPlot opens a reactive environment, just as reactive does, you can just replace the function. But make sure that you remove the renderPlot calls from within the environment.
After changing that, you will run into some more errors you have in your code but I bet you can work it out from there.
I am using Shiny and R to visualize my data interactively. I would like to draw an interactive scatter plot of Petal.Width versus Petal.Length in Iris dataset and cluster the points based on k clusters (user input) and p, the percentage of data rows dedicated to the training dataset (user input). I added a hover feature to the scatterplot so that by clicking on each point, the whole dataset for that point will be demonstrated.
The output should look like this:
# Loading Libraries
library(shiny)
library(caret)
library(ggplot2)
data(iris)
ui <- pageWithSidebar(
headerPanel("Clustering iris Data"),
sidebarPanel(
sliderInput("k", "Number of clusters:",
min = 1, max = 5, value = 3),
sliderInput("prob", "Training percentage:",
min=0.5, max=0.9, value = 0.7)),
mainPanel(
# img(src='iris_types.jpg', align = "center", height="50%", width="50%"),
plotOutput("plot1", click = "plot_click"),
verbatimTextOutput("info")
)
)
server <- function(input, output) {
inTrain <- createDataPartition(y=iris$Species,
p=input$prob,
list=FALSE)
training <- iris[ inTrain,]
testing <- iris[-inTrain,]
kMeans1 <- kmeans(subset(training,
select=-c(Species)),
centers=input$k)
training$clusters <- as.factor(kMeans1$cluster)
output$plot1 <- renderPlot({
qplot(Petal.Width,
Petal.Length,
colour = clusters,
data = training,
xlab="Petal Width",
ylab="Petal Length")
})
output$info <- renderPrint({
# With ggplot2, no need to tell it what the x and y variables are.
# threshold: set max distance, in pixels
# maxpoints: maximum number of rows to return
# addDist: add column with distance, in pixels
nearPoints(iris, input$plot_click, threshold = 10, maxpoints = 1,
addDist = FALSE)
})
}
shinyApp(ui, server)
When I run the app in R Studio, I receive the following error:
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
As mentioned by #Clemsang you are using reactives values outside an oberserver/render* function.
What you want to do is to create a reactive environment, where you use your inputs. That is, something which gets recalculated whenever the inputs change. Thus, you need to wrap your training calculation in reactive and when you want to use it in your render function, you "call" it by adding (). I like to name my reactives with a verb, to highlight the fact that I actually do something when these functions are called, hence the name get_training_data:
server <- function(input, output) {
get_training_data <- reactive({ ## now your inputs are in a reactive environment
inTrain <- createDataPartition(y=iris$Species,
p=input$prob,
list=FALSE)
training <- iris[ inTrain,]
testing <- iris[-inTrain,]
kMeans1 <- kmeans(subset(training,
select=-c(Species)),
centers=input$k)
training$clusters <- as.factor(kMeans1$cluster)
training
})
output$plot1 <- renderPlot({
qplot(Petal.Width,
Petal.Length,
colour = clusters,
data = get_training_data(),
xlab="Petal Width",
ylab="Petal Length")
})
output$info <- renderPrint({
# With ggplot2, no need to tell it what the x and y variables are.
# threshold: set max distance, in pixels
# maxpoints: maximum number of rows to return
# addDist: add column with distance, in pixels
nearPoints(iris, input$plot_click, threshold = 10, maxpoints = 1,
addDist = FALSE)
})
}
Edited: I removed to the call to get a dataset from data.world and instead fed in the mtcars dataset which produces the same error of contrasts can only be applied to factors of 2 or more
I am learning shiny and wanted to create a plot that gives the linear regression line for both a) the whole plot and b) linear regression line for brushed points on the plot. I would even settle for just showing the summary stats for the regression of the brushed points.
The code below
plots the user inputs for x and y
allows the user to brush points
creates a reactive datasubset of brushed points
shows summary of the regression for all points
shows data table of the reactive datasubset of brushed points when a brush is applied
breaks down when asked to perform a regression on those brushed points for reasons I do not understand...
code:
library(shiny)
library(ggplot2)
library(data.world)
library(dplyr)
library(tidyverse)
library(DT)
#show data from data.world
gcdata_ds <- "https://data.world/llawsonwork/gcdata"
#gcdatafile <- data.world::query(
#qry_sql("SELECT * FROM gcdataclean"),
#dataset =gcdata_ds
#)
#datafile <- gcdatafile
#so that you will not need data.world
datafile <-mtcars
# Define UI for application that draws a histogram
ui <- fluidPage(
#Application Layout
sidebarLayout(
#Inputs
sidebarPanel(
#select variable for y-axis
selectInput(inputId = "ya",
label = "Y-axis",
choices = colnames(datafile),
selected = "life_expec"
),
#Select Variable for x axis
selectInput(inputId = "xa",
label = "X-axis",
choices = colnames(datafile),
selected = "life_expec"
)
),
#output
mainPanel(
plotOutput(outputId = "guilfordplot", brush = "plot_brush"),
htmlOutput(outputId = "summary"), # summary of lin regress all points
dataTableOutput(outputId = "brushedtracts"), # data table to make sure brushed points are updating correctly
textOutput(outputId = "brushedreg") # NOT Working summary of lin reg brushed points
)
)
)
#define server function
server <- function(input, output){
#this was useful in creating the regression model as X was always column 1 and Y was always column in this dataframe
datasubset <- reactive({
req(input$xa)
req(input$ya)
data.frame(X = datafile[input$xa], Y = datafile[input$ya])
})
#create datasubset of the brushed points
brushedsubset <- reactive({
req(input$xa)
req(input$ya)
req(input$plot_brush)
brushedPoints(datafile, brush = input$plot_brush) %>%
select(input$xa, input$ya)
})
#Create plot
output$guilfordplot <- renderPlot({
ggplot(data = datafile, aes_string(x = input$xa, y = input$ya)) +
geom_point() + geom_smooth(method = "lm")
})
#create summary file
output$summary <- renderUI({
model = lm(datasubset()[,2] ~ datasubset()[,1], data = datasubset())
r2 = format(summary(model)$r.squared, digits = 3)
txt = paste("The equation of the line is :\nY = ",
round(coefficients(model)[1],0), " + ",
round(coefficients(model)[2], 5), "X")
# str_3 <- format(coef(m)[1], digits = 3)
str_1 <- txt
str_2 <- paste("The R^2 value is equal to ", r2)
HTML(paste(str_1, str_2, sep = '<br/>'))
})
# create data table
output$brushedtracts <- DT::renderDataTable({
select(brushedsubset(), input$xa, input$ya)
})
# create brushed summary stats
output$brushedreg <- renderText({
modelbrush = lm(brushedsubset()[,2] ~ brushedsubset()[,1], data = brushedsubset())
br2 = format(summary(modelbrush)$r.squared, digits = 3)
btxt = paste("The equation of the line is :\nY = ",
round(coefficients(modelbrush)[1],0), " + ",
round(coefficients(modelbrush)[2], 5), "X")
paste(btxt, ' and the rsquared is: ', br2 )
})
}
# Run the application
shinyApp(ui = ui, server = server)
So the code above works for the summary regression of all points for a given x and y input.
But this code does not work for giving me the linear regression of the brushed points and I cannot figure out why because it is the nearly identical for the code for the linear regression of all points.
Any help would be appreciated and it there is a tidyer way of doing the linear regression and summary stats please let me know.
I have a problem. I want to use sidebar to curb time series plot, however, I get invalid formula error when trying to do the plot. Can someone pls help?
server.r
library(shiny)
library(BCA)
data(Eggs)
# Define server logic required to plot
shinyServer(function(input, output) {
formulaX <- reactive({
tmp <- paste(input$range,collapse = ":")
paste("Eggs[",tmp,",1]")
})
formulaY <- reactive({
tmp1 <- paste(input$range,collapse = ":")
paste("Eggs[",tmp1,",5]")
})
# Return the formula text for printing as a caption
output$caption <- renderText({
paste(formulaX(),formulaY(),sep = " ")
})
#creating plot -ERROR
output$mpgPlot <- renderPlot({
plot(as.formula(formulaX()),as.formula(formulaY()))
})
})
ui.r
library(shiny)
# Define UI
shinyUI(pageWithSidebar(
# Application title
headerPanel("Eggs"),
sidebarPanel(
# Specification of range within an interval
sliderInput("range", "Range:",
min = 1, max = 105, value = c(20,50))
),
mainPanel(
h3(textOutput("caption")),
plotOutput("mpgPlot")
)
))
"Eggs[1:10,1]" is not a formula, it is a character-representation of a subset. Since you are always choosing columns 1 and 5, your "formula" is always "Cases ~ Week" (I don't have BCA installed, I think that's correct), and you are intending to use a subset of rows from the data.
Perhaps this would work instead (hasty, some programming safeguards would be appropriate):
# no need for formulaX(), formulaY()
# not certain what you want/need from output$caption
dataX <- reactive({ Eggs[input$range[1]:input$range[2],,drop = FALSE] })
and your plot:
output$mpgPlot <- renderPlot({
plot(Cases ~ Week, data = dataX())
})
or
output$mpgPlot <- renderPlot({
x <- dataX()
plot(x$Week, x$Cases)
})
I am trying to create Shiny App which is able to display interactive plot title (dependent on the choosen value for x axis)
Very simple example:
library(shiny)
library(DT)
library(ggplot2)
x <- as.numeric(1:1000000)
y <- as.numeric(1:1000000)
z <- as.numeric(1:1000000)
data <- data.frame(x,y, z)
shinyApp(
ui = fluidPage(selectInput(inputId = "yaxis",
label = "Y-axis",
choices = list("x","y","z"),
selected = c("x")),
dataTableOutput('tableId'),
plotOutput('plot1')),
server = function(input, output) {
output$tableId = renderDataTable({
datatable(data, options = list(pageLength = 10, lengthMenu=c(10,20,30)))
})
output$plot1 = renderPlot({
filtered_data <- data[input$tableId_rows_all, ]
ggplot(data=filtered_data, aes_string(x="x",y=input$yaxis)) + geom_line()
})
}
)
I have tried this code:
ggtitle("Line plot of x vs",input$yaxis)
It was not working, plot has not been displayed, giving me an Error:
Warning: Error in ggtitle: unused argument (input$yaxis)
[IMPORTANT]
using ggtitle(input$yaxis) gives me an interactive title, however i need to build up a sentence (like: Line plot of x vs input$yaxis), in which the reactive argument (input$yaxis) is a part of it!
Thanks for any help!
Cheers
Change:
ggtitle("Line plot of x vs",input$yaxis)
To
ggtitle(paste("Line plot of x vs",input$yaxis))
As the error suggests, you have too many arguments passed to the ggtitle function, paste will create a single character out of your two inputs, with a space in between. You can vary the separation between the two with sep =.