I am new to Shiny and I've been struggling with the following. In the ui I define a set of variables that users can choose. However, when I call upon the input variable the correct label for the variable displays on the x-axis but there is no data. When I change the input$variable to any of the actual variable names, the correct results display. What am I doing wrong?
output$analysis1 <- renderPlot({
df <- m1
df <- subset(df, df$rcid %in% unique(Select_voting()$rcid))
ggplot(subset(df, unsc_region == 'Latin America'), aes(x = input$variable, y = reorder(CountryAbb, input$variable),
color = ordvote)) +
geom_point(size=3) + theme_light() + ggtitle("Latin America") + scale_colour_manual(values=vcolors)
})
Related
I am creating a plot in R shiny app based on usr input. My filtering of data is a bit complicated and I am unsure fow to pass it to "fill" in ggplot. Below is my code:
data <- reactive({
req(input$name)
req(input$type)
fp %>%
dplyr::filter(
name %in% input$name,
if_any(
matches(
str_c('status___', tolower(input$type))), ~
.x ==2),
on_date >= input$Dates[1] &
off_date <= input$Dates[2]
) %>%
group_by(country) %>%
summarize(All = n(), .groups = "drop")
})
##Plot
output$plot <- renderPlot({
g <- ggplot(data(), aes( y = All, x = country)) #this is wehere I want to use fill to color the plot by "type"
g + geom_bar(stat = "sum")
})
When you want to pass reactive input as variables in the ggplot aes() you need to use aes_string().
Try this ggplot code :
geom_bar(data = data(),
aes_string(y = "All",
x = "country",
fill = input$type),
stat = "sum")
Your problem is that your data() does not contain a column called type. You can verify that, by including a dataTableOutput element to your UI and render data to that. summarize will include only the grouping variables (country in your case) and the aggregated column (All).
Maybe you can provide a full reprex then we cna help better.
I am trying to make a shiny app that plots a custom ggplot plot1 depending on the different dates.
The parameter of plot1 is days which plots by different dates as the days are specified accordingly. However, I just can't figure out how to integrate into the server part of shiny. Here is my best approach:
# create sample data frame with dates
set.seed(1)
date = seq(Sys.Date(), by = "day", length.out = 30)
number = 100 * rnorm(30)
df = data.frame(date = date, number = number)
head(df)
# Plot
library(ggplot2)
library(shiny)
library(dplyr)
plot1 <- function(days) {
df %>% filter(between(date, max(df$date) - days, max(df$date))) %>%
ggplot(aes(x = date, y = number)) +
geom_line() +
theme_classic()
}
# Shiny
ui <- fluidPage(
dateRangeInput(
inputId = "daterange",
label = "Select the date range",
start = min(df$date),
end = max(df$date),
),
plotOutput("plotA")
)
server <- function(input, output, session) {
output$plotA <- renderPlot({
plot1(input$daterange)
})
}
shinyApp(ui, server)
Is filter a masked function for you? You can try using a qualified function call to filter via dplyr::filter().
Other than that, as the other user pointed out, input$daterange is a vector with 2 values - a "beginning" (input$daterange[1]) and an "end" (input$daterange[2]). You mentioned that you tried changing input$daterange[1] or [2], but it doesn't work: I presume that means you changed your call to plot(input$daterange) in server, right? You should be changing and specifying that up in your declaration of that function. Since you define the date from input$daterange based on min(df$date) and max(df$date) already, you can just use days[1] and days[2] to refer to the min and max of the user input. Maybe like this?
plot1 <- function(days) {
df %>% dplyr::filter(between(date, days[1], days[2])) %>%
ggplot(aes(x = date, y = number)) +
geom_line() +
theme_classic()
}
Finally, I have had some issues displaying plots from ggplot in shiny apps myself. In order to show the plot, I store in a variable, and then explicity show the plot with print(). Example:
myPlot <- ggplot(df, aes(x=..., y=...)) + geoms_...
print(myPlot)
If I simply call ggplot without the print() after, it does not always work as intended.
I'm developing a R Shiny app which allows the user to investigate the simple principles of linear regression models interactively. My code runs perfectly well. However, it's not very elegant. Find the server function for illustration below (For the sake of convenience, I leave the ui and personal defined functions out, but let me know if you want to see them):
#### Make Server ####
server = function(input, output) {
#if the users presses submit:
#take the input, format it, and forward it to 'simulation'
#which creates a dataframe(column1 = simulated response, column2 = group)
simulate <- eventReactive(input$submit, {
group1 = as.numeric(c(input$n1, input$mean1, input$sd1))
group2 = as.numeric(c(input$n2, input$mean2, input$sd2))
group3 = as.numeric(c(input$n3, input$mean3, input$sd3))
all_groups = list(group1, group2, group3)
data = simulation(all_groups)
})
#model a linear regression based on the simulated data, print the output
output$model <- renderPrint({
data = simulate()
model = lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
summary(model)
})
#plot density plots for every group in one graph
#add the intercepts/coefficients returned by the linear regression to that graph
output$hist <- renderPlot({
data = simulate()
model = lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
intercept = model[["coefficients"]][["(Intercept)"]]
intercept_g1 = model[["coefficients"]][["group1"]]
intercept_g2 = model[["coefficients"]][["group2"]]
ggplot(data, aes(x=response, fill=group)) +
geom_density(data = subset(data, group="group1"), alpha=.5) +
geom_density(data = subset(data, group="group2"), alpha=.5) +
geom_density(data = subset(data, group="group3"), alpha=.5) +
geom_vline(xintercept=intercept) +
geom_vline(xintercept=intercept_g1) +
geom_vline(xintercept=intercept_g2)
})
#if the user presses 'reset', reset all input panels to their default value
observeEvent(input$reset, {
shinyjs::reset("side-panel")
})
}
The two main issues disturbing me are:
Both, renderPlot and renderPrint create the data variable in their first line (and data is also created in eventReacitve). Is it possible to create 'data' once as the user hits the submit button (the implementation of which is not shown here)?
Both, renderPlot and renderPrint compute the linear regression model. While the first needs only the output, the second needs some values that are stored in the lme variable (here the intercepts). Is it here also possible to calculate the model only once?
If you suggest ti improve the code also w.r.t to other issues, please let me know. This is just a small part of a bigger project; several options for the user will be added and an efficient and easy maintainable code will be very useful!
I think you should calculate your model in another reactive In this way you can set up your data in your eventReactive and create a get_model reactive in which you read the data and in your render* functions you use this model.
Pseudo code would be something along these lines:
server <- function(input, output, session) {
## create a get_model reactive
## thanks to the reactive nature it will cache its values unless data changes
get_model <- reactive({
my_data <- req(simulate()) ## use require to make sure it is well defined
lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = my_data)
})
output$model <- renderPrint({
## you could use validate(need(.)) here to make sure the model is well defined
## validate(need(get_model(), "Model not yet defined! Please simulate some data first!")
summary(get_model())
})
}
In this way you calculate your model only when the data changes and only once and not twice in renderPrint and renderPlot. The data is anyways only generated anew when the button is hit. In this way you make good use of shiny's builtin reactive system.
I just saw that you also need data in your plot function, so I would simply do something like this:
simulate <- eventReactive(input$submit, {
## create data first
group1 <- as.numeric(c(input$n1, input$mean1, input$sd1))
group2 <- as.numeric(c(input$n2, input$mean2, input$sd2))
group3 <- as.numeric(c(input$n3, input$mean3, input$sd3))
all_groups <- list(group1, group2, group3)
data <- simulation(all_groups)
## create model
model <- lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
## return a list with both elements
list(data = data, model = model)
})
Then you can use it in renderPrint like this:
output$model <- renderPrint({
summary(simulate()$data))
})
And in render plot like this
output$hist <- renderPlot({
data <- simulate()$data
model <- simulate()$model
intercept = model[["coefficients"]][["(Intercept)"]]
intercept_g1 = model[["coefficients"]][["group1"]]
intercept_g2 = model[["coefficients"]][["group2"]]
ggplot(data, aes(x=response, fill=group)) +
geom_density(data = subset(data, group="group1"), alpha=.5) +
geom_density(data = subset(data, group="group2"), alpha=.5) +
geom_density(data = subset(data, group="group3"), alpha=.5) +
geom_vline(xintercept=intercept) +
geom_vline(xintercept=intercept_g1) +
geom_vline(xintercept=intercept_g2)
})
I realize that this might not be most elaborate or convoluted answer (and I do not have enough reputation to simply comment), but as a general approach, I would suggest that you summarize all your steps into different functions. For example, if I see this correctly, your renderPlot() call only depends on simulate(), while the rest of the calculations are based on the data that is provided by simulate(). Hence, you could summarize this to
plot_data <- function(data_input) {
data = data_input
model = lm(response ~ group,
contrasts = list(group = "contr.sum"),
data = data)
intercept = model[["coefficients"]][["(Intercept)"]]
intercept_g1 = model[["coefficients"]][["group1"]]
intercept_g2 = model[["coefficients"]][["group2"]]
ggplot(data, aes(x=response, fill=group)) +
geom_density(data = subset(data, group="group1"), alpha=.5) +
geom_density(data = subset(data, group="group2"), alpha=.5) +
geom_density(data = subset(data, group="group3"), alpha=.5) +
geom_vline(xintercept=intercept) +
geom_vline(xintercept=intercept_g1) +
geom_vline(xintercept=intercept_g2)
}
This would reduce your call to
output$hist <- renderPlot({plot_data(simulate())})
Furthermore, writing these functions outside your shiny app also lets you test and debug them more easily in a common R environment, while you can focus on your app in the server function.
In my shiny app, I have a data.frame that is reactive. The data.frame is then given to ggplot and the barchart is made. However, I would like to set the exact order of the bars in the barchart.
This I can do with
JOIN11$ID_Polymer <- factor(JOIN11$ID_Polymer,
levels=JOIN11$ID_Polymer[order(JOIN11[["Content"]])])
in my R script (a function() that prepares the data outside the shiny server).
I would like to set the order in the shiny server so the user can change the ordering argument (the user can decide if he wants to order the data.frame by "Content" or by some other column that he chooses).
I was trying something like this:
dataforplot <- reactive({
plot_data <- data() %>%
filter(Name %in% input$polymers)
plot_data$ID_Polymer <- factor(plot_data$ID_Polymer,
levels =plot_data$ID_Polymer[ order(plot_data[["Content"]])])
})
which does not work (the ggplot is not displayed), the error says: data must be a data frame, or other object coercible byfortify(), not a factor.
the function for ggplot goes like this:
plotInput <- reactive({
ggplot(data = dataforplot(), aes(x = ID_Polymer, y = value), position = position_dodge(width = 1)) +
geom_bar(aes_string( fill=razeni()), position = position_dodge(width = 1), stat="identity", color="white")+
theme_minimal() +
theme(legend.text=element_text(size=21))+
theme(text = element_text(size=21))+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
ggtitle(input$title_text_box_id) +
labs(x = "", y = input$ylabel_text_box_id) +
geom_text(aes(x = ID_Polymer, y = value,Group=Polymer,label=value),
position = position_dodge(width = 1),vjust=2, size=5,colour = "white", fontface = "bold") +
scale_fill_tableau("Tableau 10")+
scale_x_discrete(labels=c(xpopisky()))#puts a reactive in x labels
})
It works when I am not trying to set the order to the data.fram, when I leave out the
plot_data$ID_Polymer <- factor(plot_data$ID_Polymer,
levels =plot_data$ID_Polymer[ order(plot_data[["Content"]])])
How to solve this?
When you use:
dataforplot <- reactive({
plot_data <- data() %>%
filter(Name %in% input$polymers)
plot_data$ID_Polymer <- factor(plot_data$ID_Polymer,
levels =plot_data$ID_Polymer[ order(plot_data[["Content"]])])
})
The last line of whatever is inside your reactive() is returned as the value of that reactive element. Hence, in your case plot_data$ID_Polymer(which is not a dataframe, but a factor column of the dataframe) is returned as dataforplot(). This is the reason for the error. Change you dataforplot() definition to:
dataforplot <- reactive({
plot_data <- data() %>%
filter(Name %in% input$polymers)
plot_data$ID_Polymer <- factor(plot_data$ID_Polymer,
levels =plot_data$ID_Polymer[ order(plot_data[["Content"]])])
# Add return statement for returning the dataframe
return(plot_data)
})
I would like to know how do I solve the error:
Aesthetics must be either length 1 or the same as the data (437): x, y
I am trying to make a histogram using shiny R: I have created two widgets, and if the user chose "IL" as their choice, the x-axis would be all the counties in "IL" (this is what "x" variable is for later on)
output$hist <- renderPlot({
# Store x and y values to produce the chart
x <- chart_two_data$county[chart_two_data$state == input$x_var]
y <- chart_two_data[[input$y_var]]
title <- "Number of Population Race in county by State"
# Create ggplot hist
ggplot(data = chart_two_data) +
geom_bar(mapping = aes(x = x, y = y),
stat = "identity") +
ggtitle("Population Number Of Each Race Based On State")
})
This is my chart, organized from midwest dataset:
If you wanted input$x_var to give a choice of State and input$y_var to select the column like 'poptotal' or 'area' then this is the solution. You have to use this line to extract the y value y<-chart_two_data[,input$y_var].
Heres the modified code:
output$hist <- renderPlot({
# Store x and y values to produce the chart
x <- chart_two_data$county[chart_two_data$state == input$x_var]
y<-chart_two_data[,input$y_var]
title <- "Number of Population Race in county by State"
# Create ggplot hist
ggplot(chart_two_data) +
geom_bar(aes(x = x, y = y),stat = "identity") +
labs(x=paste0("County of State ",input$x_var),y=paste0(input$y_val),title="Population Number Of Each Race Based On State")+
theme(panel.background=element_blank())
})
Hope this helps!