How to make the variable to aggregate data frame columns by reactive? - r

In the below MWE code and as shown in the image below, the aggregate() function is used to sum columns in a data frame. I'd like the user to be able to choose which variable to aggregate by, either Period_1 or Period_2 via clicking the radio button. Currently the below is coded only for Period_1.
How would I modify the $Period... in each aggregate() function, to reflect the user radio button input? So the user can also aggregate by Period 2 in this example.
MWE code:
library(shiny)
data <- data.frame(Period_1=c("2020-01","2020-02","2020-03","2020-01","2020-02","2020-03"),
Period_2=c(1,2,3,3,1,2),
ColA=c(10,20,30,40,50,60),
ColB=c(15,25,35,45,55,65)
)
ui <-
fluidPage(
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = 'vetaDataView2',
label = NULL,
choices = c('By period 1','By period 2'),
selected = 'By period 1',
inline = TRUE
),
tableOutput("totals")
)
server <- function(input, output, session) {
sumColA <- aggregate(data$ColA~Period_1,data,sum)
sumColB <- aggregate(data$ColB~Period_1,data,sum)
totals <- as.data.frame(c(sumColA, sumColB[2]))
colnames(totals) <- c("Period_1","Sum Col A","Sum Col B")
output$data <- renderTable(data)
output$totals <- renderTable(totals)
}
shinyApp(ui, server)

One option to achieve your desired result would be to use paste and as.formula to create the formula to aggregate your data base on the user input:
Note: To make my life a bit easier I switched to choiceNames and choiceValues.
library(shiny)
data <- data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(10, 20, 30, 40, 50, 60),
ColB = c(15, 25, 35, 45, 55, 65)
)
ui <-
fluidPage(
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "vetaDataView2",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_2",
inline = TRUE
),
tableOutput("totals")
)
server <- function(input, output, session) {
sumColA <- reactive({
fmlaA <- as.formula(paste("ColA", input$vetaDataView2, sep = " ~ "))
aggregate(fmlaA, data, sum)
})
sumColB <- reactive({
fmlaB <- as.formula(paste("ColB", input$vetaDataView2, sep = " ~ "))
aggregate(fmlaB, data, sum)
})
output$data <- renderTable(data)
output$totals <- renderTable({
totals <- as.data.frame(c(sumColA(), sumColB()[2]))
colnames(totals) <- c(input$vetaDataView2, "Sum Col A", "Sum Col B")
totals
})
}
shinyApp(ui, server)
#>
#> Listening on http://127.0.0.1:6231

Related

How to reactively format data table columns?

In the simplified functioning code at the bottom, in the colDefs = list() section of datatable() under renderDT(), in server section, I manually center-align the two right-most column outputs of the table using the instructions targets = 1:2, class = "dt-center".
I've been trying to make the number of columns that are formatted in this manner reactive, based on the actual number of columns detected in the output table -- because in the full code this is extracted from, the number of output table columns varies based on the actual composition of the data. In the below code commented out with # you can see my latest attempt to reactively format the columns, and of course it doesn't work.
Please, how do I reactively format the data table columns where all columns to the right of the header rows are center-aligned?
Simplified functioning code:
library(dplyr)
library(DT)
library(shiny)
library(shinyWidgets)
library(tidyverse)
ui <-
fluidPage(
fluidRow(
column(width = 8,
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE),
DT::dataTableOutput("sums")
)
)
)
server <- function(input, output, session) {
data <- reactive({
data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(1000.01, 20, 30, 40, 50, 60),
ColB = c(15.06, 25, 35, 45, 55, 65)
)
})
summed_data <- reactive({
data() %>%
group_by(!!sym(input$grouping)) %>%
select("ColA","ColB") %>% summarise(across(everything(), sum))
})
output$data <- renderTable(data())
output$sums <- renderDT({
datatable(
data = summed_data(),
rownames = FALSE,
options =
list(
columnDefs = list(
list(targets = 0, class = "dt-left"),
list(targets = 1:2, class = "dt-center")
# list(targets = 1:ncol(summed_data()), class = "dt-center")
)
),
)
})
}
shinyApp(ui, server)
The following seems to work. However, I'm not sure why your initial approach doesn't work - it looks good to me.
library(dplyr)
library(DT)
library(shiny)
library(shinyWidgets)
library(tidyverse)
ui <-
fluidPage(fluidRow(
column(
width = 8,
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
DT::dataTableOutput("sums")
)
))
server <- function(input, output, session) {
data <- reactive({
data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(1000.01, 20, 30, 40, 50, 60),
ColB = c(15.06, 25, 35, 45, 55, 65)
)
})
summed_data <- reactive({
data() %>%
group_by(!!sym(input$grouping)) %>%
select("ColA", "ColB") %>% summarise(across(everything(), sum))
})
output$data <- renderTable(data())
output$sums <- renderDT({
print(names(summed_data())[1])
datatable(
data = summed_data(),
rownames = FALSE,
options = list(columnDefs = list(
list(className = 'dt-left', targets = 0),
list(className = 'dt-center', targets = seq_len(ncol(summed_data())) - 1)
))
)
})
}
shinyApp(ui, server)

How to create a flexible data stratification table?

When working with data all roads for me lead to "stratification tables" so one can get a feel for the dispersion of the data. Visualization is both by numeric table and plot.
Can someone please recommend a flexible way to generate a stratification table; by "flexible" I mean where the user can input stratification parameters? In the below code I present a sample data frame, and the ways I'd like the user to be eventually able to cut (stratify) the data.
I'm pretty new to R and have always run stratifications in Excel. In the image at the bottom you can see you how I normally stratify in Excel, with the end product highlighted in yellow. I also include a 2nd image that shows the formulas used to generate the stratification table in the first image.
I've been trying to limit the use of packages (other than shiny and the amazing dplyr, DT) but I imagine there are some nice packages too for running stratifications.
Note that my stratifications are run as of a specific point-in-time (in my data there 2 ways to measure time, via Period_1 and Period_2). So only those rows meeting that time criteria are included in the stratification.
Does anyone have suggestions for doing this?
Code:
library(shiny)
library(tidyverse)
library(shinyWidgets)
ui <-
fluidPage(
h5(strong("Raw data:")),
tableOutput("data"),
h5(strong("Grouped data:")),
radioButtons(
inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
tableOutput("summed_data"),
h5(strong("Point-in-time stratification table:")),
selectInput(inputId = "time",
label = "Choose a point-in-time:",
list(`By Period_1:` = list("2020-01", "2020-02", "2020-03", "2020-04"),
`By Period_2:` = list(1, 2, 3, 4)),
selected = "2020-04"),
numericInput(label = "Stratify by range of values:", 'strat_gap','',value=5,step=1,width = '100%'),
panel(
checkboxGroupInput(
inputId = "vars",
label = "Select characteristics to filter data by:",
choices = c("Category"),
selected = c("Category"),
inline = TRUE
),
selectizeGroupUI(
id = "my-filters",
params = list(
Category = list(inputId = "Category", title = "Category:")
)
),
status = "primary"
),
)
server <- function(input, output, session) {
data <- reactive({
data.frame(
ID = c(1,1,2,2,2,2,3,3,3),
Period_1 = c("2020-03", "2020-04", "2020-01", "2020-02", "2020-03", "2020-04", "2020-02", "2020-03", "2020-04"),
Period_2 = c(1, 2, 1, 2, 3, 4, 1, 2, 3),
Category = c("Toad", "Toad", "Stool", "Stool", "Stool", "Stool","Toad","Toad","Toad"),
Values = c(15, 25, 35, 45, 55, 87, 10, 20, 30)
)
})
choice <- reactive(input$grouping)
summed_data <- reactive({
data() %>%
group_by(across(choice())) %>%
select("Values") %>%
summarise(across(everything(), sum, na.rm = TRUE)) %>%
filter(across(1,.fns = ~ .x %>% negate(is.na)() ))
})
output$data <- renderTable(data())
output$summed_data <- renderTable(summed_data())
}
shinyApp(ui, server)
Excel example (2nd image shows stratification formulas):
In the interest of making this a more generalizable effort, here's how I would do it. In the UI, you can upload a CSV file and it grabs the names of the variables to use from the names in the file. There is one caveat here - the grouping variables have to have "Period" in their names somewhere. Otherwise, from there, you can choose the values to be summed from a list of the names of variables. The point in time values are taken from the observed values of the stratifying variable. You can also choose to filter on single variable and the values you can filter on are taken from the observed values of the filtering variable. Here's what it looks like:
and here is the code:
library(shiny)
library(tidyverse)
ui <-
fluidPage(
fluidRow(column(3, h5(strong("File Upload:"))),
column(3, h5(strong("Grouping:"))),
column(3, h5(strong("Point-in-time stratification table:"))),
column(3, h5(strong("Filtering:")))),
fluidRow(
column(3,
#actionButton("browser", "Browser"),
fileInput("file1", "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
tags$hr(),
# Input: Checkbox if file has header ----
checkboxInput("header", "Header", TRUE),
# Input: Select separator ----
radioButtons("sep", "Separator",
choices = c(Comma = ",",
Semicolon = ";",
Tab = "\t"),
selected = ","),
# Input: Select quotes ----
radioButtons("quote", "Quote",
choices = c(None = "",
"Double Quote" = '"',
"Single Quote" = "'"),
selected = '"')),
column(3,
uiOutput("values"),
uiOutput("period")),
column(3,
uiOutput("time"),
numericInput(label = "Stratify by range of values:", 'strat_gap','',value=5,step=1,width = '100%'),
),
column(3,
uiOutput("filter_var"),
uiOutput("filter_val")
)),
fluidRow(
column(6,
h5(strong("Raw data:")),
tableOutput("data"),
),
column(6,
h5(strong("Grouped data:")),
tableOutput("summed_data"),
)
)
)
server <- function(input, output, session) {
dat <- reactive({
req(input$file1)
read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
})
output$period <- renderUI({
req(dat())
pds <- dat() %>% select(contains("Period")) %>% names
chc_pd <- pds
names(chc_pd) <- paste0("By ", gsub("_", "", pds))
selectInput(inputId = "period",
label = NULL,
choices = chc_pd,
selected = pds[1]
)
})
output$time <- renderUI({
req(dat())
req(input$period)
chc <- unique(na.omit(dat()[[input$period]]))
selectInput(inputId = "time",
label = "Choose a point-in-time:",
choices = chc,
selected = chc[1])
})
output$filter_var <- renderUI({
req(dat())
chc_filt <- names(dat())
selectizeInput("filter_var",
label = "Filtering Variable",
choices = c("", names(dat())),
selected="")
})
output$filter_val <- renderUI({
req(dat())
if(input$filter_var != ""){
chc_fv <- sort(unique(na.omit(dat()[[input$filter_var]])))
selectizeInput("filter_vals",
label="Filter Values",
choices = c("", chc_fv),
selected="",
multiple=TRUE)
}
})
output$values <- renderUI({
req(dat())
selectInput("vals",
"Variable to be Summarised",
choices = names(dat()),
selected = names(dat())[ncol(dat())])
})
output$data <- renderTable(dat())
output$summed_data <- renderTable({
breaks <- seq(min(dat()[[input$vals]], na.rm=TRUE),
max(dat()[[input$vals]], na.rm=TRUE),
by=input$strat_gap)
if(max(breaks) < max(dat()[[input$vals]], na.rm=TRUE)){
breaks <- c(breaks, max(breaks) + input$strat_gap)
}
qs <- ifelse(is.character(dat()[[input$period]]), "'", "")
filter_exp1 <- parse(text=paste0(input$period, "==", qs,input$time, qs))
tmp <- dat() %>%
filter(eval(filter_exp1))
if(input$filter_var != ""){
if(is.character(dat()[[input$filter_var]])){
fv <- paste("c(", paste("'", input$filter_vals, "'", collapse=",", sep=""), ")", sep="")
}else{
fv <- paste("c(", paste(input$filter_vals, collapse=",", sep=""), ")", sep="")
}
filter_exp2 <- parse(text=paste0(input$filter_var, "%in%", fv))
tmp <- tmp %>% filter(eval(filter_exp2))
}
tmp <- tmp %>%
mutate(sumvar = cut(!!sym(input$vals), breaks=breaks, include.lowest=TRUE)) %>%
group_by(sumvar) %>%
summarise(Count = n(),
Values = sum(!!sym(input$vals))) %>%
complete(sumvar, fill = list(Count = 0,
Values = 0)) %>%
ungroup %>%
mutate(Count_pct = sprintf("%.1f%%", (Count/sum(Count))*100),
Values_pct = sprintf("%.1f%%", (Values/sum(Values))*100)) %>%
dplyr::select(everything(), Count, Count_pct, Values, Values_pct)
names(tmp)[1] <- "Range"
tmp
})
# observeEvent(input$browser, {
# browser()
# })
}
shinyApp(ui, server)

Why does group_by() in dplyr not work when switching from sum to counting unique occurrences?

The below MWE code works fine for summing data frame values, whereby the user selects which type of period to group by in the radio buttons in the "Sum the data table columns:" section rendered at the bottom. This grouping is performed in the summed_data() object below in the server section.
However I'm also trying to count the number of occurrences where Period_2 == 1. When I comment out the currently uncommented summed_data() section below, and uncomment the currently commented-out summed_data() for performing unique row counts, and try running the code, it fails. But if I run this unique row count function in the R console, as shown immediately below, it works fine and gives the desired results (manually changing the "Period..." in the group_by(...) section)!
data <- data.frame(
ID = c(115,115,111,88,120,16),
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 1, 1, 4),
ColA = c(1000.01, 20, 30, 40, 50, 60),
ColB = c(15.06, 25, 35, 45, 55, 65)
)
filter(data, Period_2 == "1") %>%
group_by(Period_1) %>%
summarise(count = length(unique(ID)))
Period_1 count
<chr> <int>
1 2020-01 2
2 2020-02 1
So, to me the problems appears to lie in the dplyr code below group_by(!!sym(input$grouping)). Does anyone have a suggestion for solving this?
MWE code:
library(dplyr)
library(DT)
library(shiny)
library(shinyWidgets)
library(tidyverse)
ui <-
fluidPage(
fluidRow(
column(width = 8,
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
DT::dataTableOutput("sums")
)
)
)
server <- function(input, output, session) {
data <- reactive({
data.frame(
ID = c(115,115,111,88,120,16),
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 1, 1, 4),
ColA = c(1000.01, 20, 30, 40, 50, 60),
ColB = c(15.06, 25, 35, 45, 55, 65)
)
})
colNames <- reactive({c(input$grouping, "Col A", "Col B") })
# summed_data <- reactive({
# filter(data(), Period_2 == "1") %>%
# group_by(!!sym(input$grouping)) %>%
# summarise(count = length(unique(ID)))
# })
summed_data <- reactive({
data() %>%
group_by(!!sym(input$grouping)) %>%
select("ColA","ColB") %>%
summarise(across(everything(), sum))
})
output$data <- renderTable(data())
output$sums <- renderDT({
summed_data() %>%
datatable(
rownames = FALSE,
colnames=colNames() # < add colNames()
)
})
}
shinyApp(ui, server)
The problem were the colNames() that you defined and added to your call to datatable. I commented those lines out and it works. The problem didn't arise with your sum data.frame because here the colnames were actually present in the data.frame, which is not the case in the length(unique)) data.frame.
library(dplyr)
library(DT)
library(shiny)
library(shinyWidgets)
library(tidyverse)
ui <-
fluidPage(
fluidRow(
column(width = 8,
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
DT::dataTableOutput("sums")
)
)
)
server <- function(input, output, session) {
mydat <- reactive({
data.frame(
ID = c(115,115,111,88,120,16),
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 1, 1, 4),
ColA = c(1000.01, 20, 30, 40, 50, 60),
ColB = c(15.06, 25, 35, 45, 55, 65)
)
})
# colNames <- reactive({c(input$grouping, "Col A", "Col B") })
summed_data <- reactive({
print(input$grouping)
mydat() %>%
dplyr::filter(Period_2 == 1) %>%
dplyr::group_by(!!sym(input$grouping)) %>%
dplyr::summarise(count = length(unique(ID)))
})
# summed_data <- reactive({
# print(input$grouping)
# data() %>%
# group_by(across(all_of(input$grouping))) %>%
# select("ColA","ColB") %>%
# summarise(across(everything(), sum))
# })
output$data <- renderTable(mydat())
output$sums <- renderDT({
summed_data() %>%
datatable(
rownames = FALSE,
# colnames=colNames() # < add colNames()
)
})
}
shinyApp(ui, server)

How to create a function with a reactive object?

The below MWE code works fine. It allows the user to click on a radio button to choose the method for aggregating data: by either period 1 or period 2 in this case.
In the larger App this is to be deployed in, there are many columns to aggregate. Not just 2 like in this MWE. So I'm trying to create a general function that serves the purpose of sumColA() and sumColB() shown below. In the commented-out code below you can see one of my attempts. The lines are commented-out because they don't work.
How can I create a reactive function similar in concept to sumCol() where the it would be invoked with something like sumCol("ColA"), sumCol("ColB"), or something similar? In the full App there are too many columns to aggregate to create multiple versions of sumColA(), sumColB(), etc.
MWE code:
library(shiny)
data <- data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(10, 20, 30, 40, 50, 60),
ColB = c(15, 25, 35, 45, 55, 65)
)
ui <-
fluidPage(
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "dataView",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
tableOutput("totals")
)
server <- function(input, output, session) {
sumColA <- reactive({
fmlaA <- as.formula(paste("ColA", input$dataView, sep = " ~ "))
aggregate(fmlaA, data, sum)
})
sumColB <- reactive({
fmlaB <- as.formula(paste("ColB", input$dataView, sep = " ~ "))
aggregate(fmlaB, data, sum)
})
### Create sumCol function ###
# sumCol <- function (x)
# {reactive({
# fmla <- as.formula(paste("x", input$dataView, sep = " ~ "))
# aggregate(fmla, data, sum)
# })
# }
### End sumCol ###
output$data <- renderTable(data)
output$totals <- renderTable({
totals <- as.data.frame(c(sumColA(), sumColB()[2]))
# totals <- as.data.frame(c(sumCol(ColA), sumCol(ColB)[2]))
colnames(totals) <- c(input$dataView, "Sum Col A", "Sum Col B")
totals
})
}
shinyApp(ui, server)
Just create one reactive object data and another reactive table summed_data containing the sums of all columns:
library(shiny)
library(tidyverse)
ui <-
fluidPage(
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
tableOutput("sums")
)
server <- function(input, output, session) {
data <- reactive({
# example data. Might change dynamically
data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(10, 20, 30, 40, 50, 60),
ColB = c(15, 25, 35, 45, 55, 65)
)
})
summed_data <- reactive({
data() %>%
group_by(!!sym(input$grouping)) %>%
select(matches("^Col")) %>%
summarise(across(everything(), sum))
})
output$data <- renderTable(data())
output$sums <- renderTable(summed_data())
}
shinyApp(ui, server)
Here is a solution with dplyr and magrittr package.
Details of the change are in code comments.
library(shiny)
library(dplyr) # for data manipulation
library(magrittr) # for pipe operator
data <- data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(10, 20, 30, 40, 50, 60),
ColB = c(15, 25, 35, 45, 55, 65)
)
dataView_choices <- c("Period_1", "Period_2") # define choices for select input
ui <-
fluidPage(
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "dataView",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = dataView_choices, # choices for select input
selected = "Period_1",
inline = TRUE
),
tableOutput("totals")
)
server <- function(input, output, session) {
output$data <- renderTable(data)
output$totals <- renderTable({
totals <- data %>%
select(-setdiff(dataView_choices, input$dataView)) %>% # remove other periods in the select input
group_by_(input$dataView) %>% # group by the selected period
summarise(across(everything(), sum, .names = "Sum_{.col}")) # sum of all columns with a "Sum_" prefix
totals
})
}
shinyApp(ui, server)

How to perform calculations on a data table rendered with a reactive data frame, in R Shiny?

The below MWE code works as intended for summing columns of a reactive data frame (data() and summed_data() in the code). If you run the code or look at the image at the bottom, you'll see where the data frame columns are summed, based on either of two user input grouping criteria, under the header "Sum the data table columns:". The App works fine, through "Sum the data table columns:".
However, I am now trying to generate a new data table that takes the grouped values from summed_data() and performs the calculations described in the image under the heading "Calculations performed on summed data table columns:" (basically, [colA] divided by [average of colB in moving from one row to the next]). The user would be able to choose how to group the data in the calculations too, by Period_1 or by Period_2. The "Sum the data table columns" and "Calculations performed..." user input for grouping selection would be independent of each other.
Is there an efficient way to accomplish this? I'm trying to stick to base R and packages tidyverse and dplyr. I'd like avoid "package bloat".
Note that in the fuller App this is deployed, there are many more columns to calculate than in this simple MWE.
MWE code:
library(shiny)
library(tidyverse)
ui <-
fluidPage(
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
tableOutput("sums"),
h3("Calculations performed on summed data table columns:"),
radioButtons(
inputId = "grouping2",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
)
)
server <- function(input, output, session) {
data <- reactive({
# example data. Might change dynamically
data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(10, 20, 30, 40, 50, 64),
ColB = c(15, 25, 35, 45, 55, 33)
)
})
summed_data <- reactive({
data() %>%
group_by(!!sym(input$grouping)) %>%
select(matches("^Col")) %>%
summarise(across(everything(), sum))
})
output$data <- renderTable(data())
output$sums <- renderTable(summed_data())
}
shinyApp(ui, server)
I figured out one solution, creating a new reactive object calculated_data() and using dplyr group_by() and mutate() functions to perform the calculations, as shown below in revised code:
ui <-
fluidPage(
h3("Data table:"),
tableOutput("data"),
h3("Sum the data table columns:"),
radioButtons(
inputId = "grouping1", # changed
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
tableOutput("sums"),
h3("Calculations performed on summed data table columns:"),
radioButtons(
inputId = "grouping2", # changed
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
tableOutput("calc") # added
)
server <- function(input, output, session) {
data <- reactive({
data.frame(
Period_1 = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
Period_2 = c(1, 2, 3, 3, 1, 2),
ColA = c(10, 20, 30, 40, 50, 64),
ColB = c(15, 25, 35, 45, 55, 33)
)
})
summed_data <- reactive({
data() %>%
group_by(!!sym(input$grouping1)) %>%
select("ColA","ColB") %>%
summarise(across(everything(), sum))
})
calculated_data <- reactive({ # added this section
data() %>%
group_by(!!sym(input$grouping2)) %>%
select("ColA","ColB") %>%
summarise(across(everything(), sum)) %>%
mutate(avgColB=case_when(is.na(lag(ColB)) ~ ColB, TRUE ~ (lag(ColB) + ColB)/2)) %>%
mutate(ColAB = ColA / avgColB) %>%
select(-ColA,-ColB,-avgColB)
})
output$data <- renderTable(data())
output$sums <- renderTable(summed_data())
output$calc <- renderTable(calculated_data()) # added
}
shinyApp(ui, server)

Resources