Shiny save user input to google sheets - r

I am running a personality survey and am trying to save the data to google sheets.
I started by creating a sheet with a first row of zero and the headers I want. I have code to display the scores of participants in output$vaules. I tried to use that to save to the google sheet but that also did not work.
I am not getting any errors but the data is not being saved to the google sheet I initialized.
library(shiny)
library(googlesheets)
shiny_token <- gs_auth()
saveRDS(shiny_token, "shiny_app_token.rds")
initial_sheet <- data.frame(open1 = 0, open2 = 0, consc1 =
0, consc2 = 0, extra1 = 0, extra2 = 0, agree1 = 0, agree2 =
0, neur1 = 0, neur2 = 0)
gdoc_name <- "big5"
ss <- gs_new(gdoc_name, input=initial_sheet)
sheetkey <- ss$sheet_key
sheetkey <- "xxxx" #actual key would be here
ss <- gs_key(sheetkey)
set.seed(3000)
xseq<-seq(1,7,.01)
densities <-dnorm(xseq, 4,1)
ui
ui <- fluidPage(
table <- "responses",
titlePanel("Personality Traits"),
sidebarLayout(
sidebarPanel(
sliderInput("extra1", "I see myself as someone who is extraverted, enthusiastic.", min = 1, max = 7, value = 1),
sliderInput("agree1", "I see myself as someone who is critical, quarrelsome.",min = 1, max = 7,value = 1),
sliderInput("consc1", "I see myself as someone who is dependable, self-disciplined.",min = 1, max = 7, value = 1),
sliderInput("neur1", "I see myself as someone who is anxious, easily upset.",min = 1, max = 7, value = 1),
sliderInput("open1", "I see myself as someone who is open to new experiences, complex.", min = 1, max = 7, value = 1),
sliderInput("extra2", "I see myself as someone who is reserved, quiet.", min = 1, max = 7, value = 1),
sliderInput("agree2", "I see myself as someone who is sympathetic, warm.", min = 1, max = 7, value = 1),
sliderInput("consc2", "I see myself as someone who is disorganized, careless", min = 1, max = 7, value = 1),
sliderInput("neur2", "I see myself as someone who is calm, emotionally stable.", min = 1, max = 7, value = 1),
sliderInput("open2", "I see myself as someone who is conventional, uncreative.", min = 1, max = 7, value = 1),
actionButton("submit", "Submit")
),
mainPanel(
tableOutput("values")
))
)
server
server <- function(input, output) {
sliderValues <- reactive({
data.frame(Name = c("Openness","Conscientiousness","Extraversion", "Agreeableness", "Neuroticism"),
Value = as.character(c((input$open1 + (8-input$open2))/2 (input$consc1 + (8 - input$consc2))/2, (input$extra1 + (8 - input$extra2))/2,(input$agree2 + (8 - input$agree1))/2,(input$neur1 + (8 - input$neur2))/2)),stringsAsFactors = FALSE)
})
observeEvent(input$submit, {
output$values <- renderTable({
sliderValues()
})
observeEvent(input$save, {
open1 <- input$open1
open2 <- input$open2
consc1 <- input$consc1
consc2 <- input$consc2
extra1 <- input$extra1
extra2 <- input$extra2
agree1 <- input$agree1
agree2 <- input$agree2
neur1 <- input$neur1
neur2 <- input$neur1
gs_add_row(input = output$save)
})
}
run app
shinyApp(ui = ui, server = server)

After making my comment early today, I played with my code and got it to update my google sheet.
In my project, I want to click payment and send the payment date to the paid column (C5) in my datasheet. This needs to be performed as many times as empty cells (Not paid) are there (therefore the 'for' loop).
I count all the rows in my spreadsheet (total_rows) and the number of rows without a date in the paid column (n_rows). These values provided me with the range of empty cells the code needs to update after the action button is activated.
This is added to the UI portion of the app:
actionButton("payments", label = "EXECUTE PAYMENTS")
This to the server:
sheet = function() {gs_title("Title of the googlesheet")}
historic = function(){ sheet() %>% gs_read(ws = "spreadsheet name")}
observeEvent(input$payments, {
total_rows = nrow(historic())
ndata = historic() %>% filter(is.na(paid))
n_rows = nrow(ndata)
if (total_rows == n_rows){n_rows = n_rows+1}
for (i in (total_rows - n_rows + 2) : (total_rows+1)) {
anchor_range = paste("R", i, "C5", sep = "", collapse = NULL)
sheet() %>% gs_edit_cells(ws = "spreadsheet name", input = Sys.Date(), anchor = anchor_range)
}
Hope this works for you.
J

Related

How to make data table transposition that previously worked, work again after a computer restart?

In the below reproducible code, clicking the radio button to transpose the data table worked fine, until I restarted my computer. Now post-restart, this App crashes when attempting to transpose. There must have been a package or some object loaded in memory that previously enabled this to work, perhaps. I've tried finding this missing object but no luck yet. Anyhow, how can I get a click of radio button (as shown in the image at the bottom) to transpose the data table?
This post is a follow-on to Why is my effort in transposing a data table not working?, whose solution worked fine (but not any longer).
Reproducible code (shortened from above referenced post):
library(DT)
library(shiny)
library(dplyr)
library(htmltools)
library(data.table)
data <-
data.frame(
ID = c(1,1,1,2,2,2,3,3,3),
Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
Values = c(5, 10, 15, 0, 2, 4, 3, 6, 9),
State = c("X0","X1","X2","X0","X2","X0", "X2","X1","X0")
)
numTransit <- function(x, from=1, to=3){
setDT(x)
unique_state <- unique(x$State)
all_states <- setDT(expand.grid(list(from_state = unique_state, to_state = unique_state)))
dcast(x[, .(from_state = State[from],
to_state = State[to]),
by = ID]
[,.N, c("from_state", "to_state")]
[all_states,on = c("from_state", "to_state")],
to_state ~ from_state, value.var = "N"
)
}
ui <- fluidPage(
tags$head(tags$style(".datatables .display {margin-left: 0;}")),
h4(strong("Base data frame:")),
tableOutput("data"),
h4(strong("Transition table inputs:")),
numericInput("transFrom", "From period:", 1, min = 1, max = 3),
numericInput("transTo", "To period:", 2, min = 1, max = 3),
radioButtons("transposeDT",
label = "Transpose table:",
choiceNames = c('Columns','Rows'),
choiceValues = c('Columns','Rows'),
selected = 'Columns',
inline = TRUE
),
h4(strong("Output transition table:")),
DTOutput("resultsDT"),
)
server <- function(input, output, session) {
results <-
reactive({
results <- numTransit(data, input$transFrom, input$transTo)
})
output$data <- renderTable(data)
output$resultsDT <- renderDT(server=FALSE, {
datatable(
data =
# the line immediately below causes the app to crash when transposing the data table
if(input$transposeDT=='Rows'){results()%>%transpose(make.names = 'to_state',keep.names = 'to_state')}
else {results()},
rownames = FALSE,
filter = 'none',
class = "display"
) %>%
formatStyle(c(1), `border-right` = "solid 1px")
})
}
shinyApp(ui, server)
YBS comment (try data.table::transpose(...)) solved the problem. See post What are the double colons (::) in R?, which explains that there may be functions with the same name in multiple packages (which seems to be the case of my use of transpose()). The double colon operator :: allows you to specify the specific function from the specific package to use in a particular situation.

How to feed a value from an output object as an input into another output object?

I'm working on a simulation-based app. I have 2 initial numeric input objects, "max imp count" and "population size". Based on what is fed into these numeric input objects, the app generates 2 more input objects (really as output objects)- "select_proportion" and "select_probability". If the "max imp count" is 3, the app should generate 8 new input objects- 4 which ask for proportion (proportion0, proportion1, proportion2, proportion3), and 4 of which ask for probability0, probability1, probability2 and probability3. I want to feed these proportion and probability values into sample functions that work in the following manner:
1) sample(c(0,input$max_imp, 1), size=input$population, replace=TRUE, prob= these take the proportion values
sample for binary values for all proportion brackets:
2) sample(c(0,1), length(proportion_i), replace=TRUE, prob=these take the probability values)
Ideally, I would like to have this all in a dataframe where I have columns for which proportion bracket a record belongs to and whether they have 0 or 1.
Code:
library(shiny)
library(shinydashboard)
ui <- dashboardPage(
dashboardHeader(title = "Simulation"),
dashboardSidebar(
sidebarMenu(
numericInput("max_imp", "max imp count", 0, min = 0, max = 15, step = 1),
numericInput("population", "population size", 1, min = 0, max = 100000, step = 1),
menuItemOutput("menuitem")
)
),
dashboardBody(
uiOutput("select_proportion"),
uiOutput("select_probability")
))
server <- function(input, output) {
output$select_proportion = renderUI(
lapply(0:(input$max_imp), function(i){
numericInput(inputId = "i1", label = paste0("proportion",i), 0, min = 0, max = 1, step = 0.05)}))
output$select_probability = renderUI(
lapply(0:(input$max_imp), function(i){
numericInput(inputId = "i2", label = paste0("probability",i), 0, min = 0, max = 1, step = 0.05)}))
}
# Run the application
shinyApp(ui = ui, server = server)
Your output elements are only UI container which are then filled with the input elements defined in the renderUI functions. You can easily access the values of these input elements as you do it with your other inputs.
The only thing here is that you have to set the IDs of the inputs in a dynamic way e.g. instead of id = "i2" for the second input use id = paste("input", i, "2", sep = "-"). This will create you input$max_imp + 1 different inputs. The values can then be accessed via input$input_1_2.
BR
Sebastian

How to edit a DT column and save the changes to trigger calculations in dependent columns?

I'm creating a shiny app that will be used by multiple people to see and edit data in a table format. I want the user to be able to edit one or more columns at a time. I've been using the DT package in R to do this, but I can't figure out how to save the edits made in the data table. This is crucial because there are dependant values in other columns that need to be recalculated.
If I set editable = TRUE I can change the value in one cell, but that is much too slow. When I set editable = "column" or "all" I can edit multiple cells quickly, but then the edits won't save, no matter how many times I hit return.
In the example below, all I'm trying to do is print the edited values to the console. If the values appear in the _cell_edit variable then I can use the editData function to save the changes.
I haven't found anything useful on StackOverflow yet, but I did find the following two blog posts helpful.
https://blog.rstudio.com/2018/03/29/dt-0-4/
https://rstudio.github.io/DT/shiny.html
# TEST APP DT
library(DT)
library(shiny)
library(tidyverse)
# Define UI for application that draws a histogram
ui <- fluidPage(
actionButton("save", "Click to Save Changes"),
DTOutput("dt_table")
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
data <-
data.frame(
A = c("Ones", "Twos", "Threes", "Total"),
B = c(1, 2, 3, 6),
C = c(1, 2, 3, 6),
stringsAsFactors = FALSE
) %>% mutate(D = B + C)
live = reactiveValues(df = NULL)
observe({
live$df <- data
})
output$dt_table <- renderDataTable({
datatable(
live$df,
rownames = FALSE,
editable = list(
target = "column",
disable = list(columns = c(1, 3))
)
)
})
proxy_dt_table <- dataTableProxy("dt_table")
observeEvent(input$save, {
info = input$dt_table_cell_edit
str(info)
row = info$row
col = info$col
val = info$value
print(paste0("Row: ", row))
print(paste0("Col: ", col))
print(paste0("Val: ", val))
})
}
# Run the application
shinyApp(ui = ui, server = server)
Right now, the values in the _cell_edit variable are empty.
NULL
[1] "Row: "
[1] "Col: "
[1] "Val: "
I would like to see something like this:
'data.frame': 200 obs. of 3 variables
[1] "Row: [1, 2, 3, 4, ...]"
[1] "Col: [1, 1, 1, 1, ...]"
[1] "Val: [5, 6, 7, 8, ...]"

Dynamic update of a label withMathJax in Shiny UI

I'm using shiny::updateSliderInput to update the label of a slider. I would like the label for the slider to render with the Greek character xi (ξ) and a subscript. The subscript is target of the updateSliderInput call.
I've been successful generating the label without the updateSliderInput call, but correctly using the withMathJax() call for the label update has not worked as well as I would prefer.
A reproducible example of a shiny app for this issue is:
library(shiny)
shinyApp(ui = shinyUI(fluidPage(sliderInput("order", withMathJax("Order, \\(k\\)"), min = 3, max = 7, value = 4, step = 1),
sliderInput("iknots", "iKnots", min = 0, max = 10, value = 5, step = 1),
sliderInput("xi1", withMathJax(), min = 0, max = 10, value = 1, step = 0.1))),
server = shinyServer(function(input, output, clientData, session) {
observe({
k <- as.integer(input$order)
l <- as.integer(input$iknots)
updateSliderInput(session, "xi1", label = paste0("\\(\\xi_{", k + l, "}\\)"))
})
}))
Upon initial loading of the app in browser we have a the desirable label, the ξ9 is correct.
After adjusting either of the first two sliders, the label for the third slider does not render as expected.
I've tried using withMathJax in label argument of updateSliderInput but have had undesirable labels. Changing the updateSliderInput line in the above to
updateSliderInput(session, "xi1", label = withMathJax(paste0("\\(\\xi_{", k + l, "}\\)")))
and setting the sliderInput to have the either label = withMathJax() or label = "" results in the initial label of
and updated label of
How can the subscript on the ξ be correctly updated in the slider label?
You might need to use renderUI and uiOutput to create the last slider otherwise the javascript used to display the MathJax is not called when you update the label.
You could do:
library(shiny)
shinyApp(ui = shinyUI(fluidPage(sliderInput("order", withMathJax("Order, \\(k\\)"), min = 3, max = 7, value = 4, step = 1),
sliderInput("iknots", "iKnots", min = 0, max = 10, value = 5, step = 1),
uiOutput("lastSlider"))),
server = shinyServer(function(input, output, clientData, session) {
output$lastSlider<- renderUI({
k <- as.integer(input$order)
l <- as.integer(input$iknots)
sliderInput("xi1",label = withMathJax(paste0("\\(\\xi_{", k + l, "}\\)")), min = 0, max = 10, step = 0.1,value=input$xi1)
})
}))

Shiny - object from reactive expression not found when used in loglm

I created a shiny app, in which I want to display the residual of a log-linear model using a mosaic plot. I need to use the data from a reactive expression and pass it to loglm. It seem pretty strait forward, but when I do that I get the following error : "objet 'mod' introuvable".
I've already figured which line is causing the problem, but I don't know how to fix it. Running the code below as is should work fine.
However, uncomment the line # mod <- loglm( formula = reformulate(f), data = mod ), in server and you should get the same error I get.
Any help would be greatly appreciated.
ui <- fluidPage(
titlePanel("Shiny Viz!"),
fluidRow( class= "R1",
tabsetPanel(type= "pills",
tabPanel("Log-linear model",
fluidRow(
column(3, offset=1,
selectInput("model", label= "Choose model to fit:",
choices= c("(SPT)","(SP,ST,PT)","(ST,PT)","(SP,PT)","(SP,ST)")),
selectInput("type", label= "Visualise the expected or observed values?",
choices = c("observed", "expected")),
sliderInput("n_breaks", label = "Degree Celcius per bin:",
min = .5, max = 5, value = 1, step = .5)),
column(8, plotOutput("loglinear.mosaic", height= "600px") )
))))
)
library(ggplot2)
library(data.table)
library(vcd)
library(vcdExtra)
server <- function(input, output) {
# Create data
DF <- data.table( Temp = runif(5000, 0, 30),
Presence = factor(rbinom(5000, 1, runif(20, 0.1, 0.60))),
Period = factor(as.integer(runif(5000, 1, 9))) )
# Reactive expression
loglinear <- reactive({
DF[ , Temperature.category := cut_interval(Temp, length= input$n_breaks)]
Tab <- xtabs(formula= ~ Period + Temperature.category + Presence,
data = DF)
return(Tab)
})
# mosaic plot
output$loglinear.mosaic <- renderPlot({
mod <- loglinear()
f <- switch(input$model,
"(SPT)"= c("Presence*Period*Temperature.category"),
"(SP,ST,PT)" = c("Presence*Period","Presence*Temperature.category","Period*Temperature.category"),
"(ST,PT)" = c("Presence*Temperature.category","Period*Temperature.category"),
"(SP,PT)" = c("Presence*Period","Period*Temperature.category"),
"(SP,ST)" = c("Presence*Period","Presence*Temperature.category"))
# mod <- loglm( formula = reformulate(f), data = mod )
mosaic(mod,
gp= shading_hcl,
spacing = spacing_highlighting,
type= input$type,
labeling_args= list(offset_varnames = c(right = 1, left=.5),
offset_labels = c(right = .1),
set_varnames = c(Temperature.category="Temperature", Period="Period",
Presence="Status")),
set_labels=list(Presence = c("Ab","Pr")),
margins = c(right = 5, left = 3, bottom = 1, top =3))
})
}
shinyApp(ui = ui, server = server)
I still haven't found what is causing the problem with loglm, but I've figured another way of getting the result I wanted.
I used glm to fit the model instead of loglm, then used mosaic.glm from the vcdExtra package to create the mosaic plot. The code is pretty much the same except that the data as to be a data.frame and the column 'Temperature.category', 'Period' and 'Presence' must be factor to be used with glm.
However, I am still clueless as to why loglm can't find the object 'mod', but glm can? I'd realy want to know the reason. Since my answers doesn't answer that question, I'll accept an other answer if someone has an explanation.
Here's what the code using glm:
ui <- fluidPage(
titlePanel("Shiny Viz!"),
fluidRow( class= "R1",
tabsetPanel(type= "pills",
tabPanel("Log-linear model",
fluidRow(
column(3, offset=1,
selectInput("model", label= "Choose model to fit:",
choices= c("(SPT)","(SP,ST,PT)","(ST,PT)","(SP,PT)","(SP,ST)")),
selectInput("type", label= "Visualise the expected or observed values?",
choices = c("observed", "expected")),
sliderInput("n_breaks", label = "Degree Celcius per bin:",
min = .5, max = 5, value = 1, step = .5)),
column(8, plotOutput("loglinear.mosaic", height= "800px") )
))))
)
library(ggplot2)
library(data.table)
library(vcd)
library(vcdExtra)
server <- function(input, output) {
DF <- data.table( Temp = runif(5000, 0, 30),
Presence = factor(rbinom(5000, 1, runif(20, 0.1, 0.60))),
Period = factor(as.integer(runif(5000, 1, 9)) ) )
# data to data.frame format
loglinear <- reactive({
DF[ , Temperature.category := cut_interval(Temp, length= input$n_breaks)]
# add 'Freq' column
dat <- data.frame(as.table(xtabs(formula= ~ Period + Temperature.category + Presence,
data = DF)), stringsAsFactors = T)
return(dat)
})
# mosaic plot
output$loglinear.mosaic <- renderPlot({
mod <- loglinear()
f <- switch(input$model,
"(SPT)"= c("Presence*Period*Temperature.category"),
"(SP,ST,PT)" = c("Presence*Period","Presence*Temperature.category","Period*Temperature.category"),
"(ST,PT)" = c("Presence*Temperature.category","Period*Temperature.category"),
"(SP,PT)" = c("Presence*Period","Period*Temperature.category"),
"(SP,ST)" = c("Presence*Period","Presence*Temperature.category"))
# fit model using glm
mod.glm <- glm(formula = reformulate(f, response = "Freq"), data= mod, family= poisson)
mosaic.glm(mod.glm,
formula = ~ Temperature.category + Period + Presence,
gp= shading_hcl,
spacing = spacing_highlighting,
type= input$type,
labeling_args= list(rot_labels = c(left = 0, right = 0),
offset_varnames = c(left=1.5, right = 1),
offset_labels = c(left=.5, right = .1),
set_varnames = c(Temperature.category="Temperature", Period="Period",
Presence="Status")),
set_labels=list(Presence = c("Ab","Pr")),
margins = c(right = 5, left = 4, bottom = 1, top =3))
})
}

Resources