R, Shiny, add values to a dataframe and save them - r

i have a code (copied of another post) that loads a dataframe and can add values to this data frame (with a input box), but this added values doesnt stay on the dataframe when i quit shiny. The question i have is how to save this added values to the original dataframe.
Code:
require(shiny)
setwd("C:/Users/DC7900/Documents/CENAGAS/Presupuesto/Pagos/")
iris<-read.csv("ConcentradoR.csv")
runApp(
list(
ui = fluidPage(
headerPanel('Gasto'),
sidebarPanel(
textInput("mes", label="fecha", value="Ingresar fecha"),
textInput("concepto", label="Concepto", value=""),
numericInput("partida", label="Partida", value=""),
numericInput("actividad", label="Actividad", value=""),
numericInput("monto", label="Monto", value=""),
actionButton("addButton", "UPLOAD!")
),
mainPanel(
tableOutput("table"))
),
server = function(input, output) {
# just a small part of iris for display
iris_sample <- iris[sample(nrow(iris), 10),]
row.names(iris_sample) <- NULL
# The important part of reactiveValues()
values <- reactiveValues()
values$df <- iris_sample
addData <- observe({
# your action button condition
if(input$addButton > 0) {
# create the new line to be added from your inputs
newLine <- isolate(c(input$mes, input$concepto, input$partida, input$actividad, input$monto))
# update your data
# note the unlist of newLine, this prevents a bothersome warning message that the rbind will return regarding rownames because of using isolate.
isolate(values$df <- rbind(as.matrix(values$df), unlist(newLine)))
}
})
output$table <- renderTable({values$df}, include.rownames=F)
}
)
)
Thank you very much

Adding the line below will create df called save_data that you can access in your local environment once you quit the shiny app.
# your action button condition
if(input$addButton > 0) {
# create the new line to be added from your inputs
newLine <- isolate(c(input$mes, input$concepto, input$partida, input$actividad, input$monto))
# update your data
# note the unlist of newLine, this prevents a bothersome warning message that the rbind will return regarding rownames because of using isolate.
isolate(values$df <- rbind(as.matrix(values$df), unlist(newLine)))
save_data <<- values$df
}
You could also have(in place of that line) like
write.csv(values$df, file = "ConcentradoR.csv")
but also see write.table, you may need to specify some parameters to make sure it's formatted correctly

Related

Shiny App: How to collect all text inputs into a data frame without listing them individually (how to index reactive values?)

I have a tab of my app where I display a bunch of text inputs based on a three-column data frame that contains: variable_name, text_prompt, and example_data. The code below seems to work fine since it displays how I want it to. Eventually, I will be feeding it different data frames, depending on the circumstances, so I need to be able to do everything programmatically.
library(shiny)
library(tidyverse)
library(DT)
additional.data.fields <- tibble (var.name = c("project.id", "director.name"),
prompt.text = c("Enter Project ID", "Enter Director's name"),
var.value = c("e.g. 09-111", "e.g. Paul Smith"))
ui <- fluidPage(
tabsetPanel(
#Generate Input fields from dataframe
tabPanel("Input", #value = "input.2",
# Generate input fields with pmap
actionButton("submit", "Submit"),
pmap(additional.data.fields, ~textInput(..1, ..2, value = ..3)),
),
#Output data to tell if it updates with button click
tabPanel("Output", value = "output",
DT::dataTableOutput("data")
)
)
)
server <- function(input, output, session) {
# Create a reactive values object to store the input data
values <- reactiveValues()
# Set the reactive values object when the submit button is clicked
observeEvent(input$submit, {
var.names <- pull(additional.data.fields, var.name)
#THIS IS THE PART I DON'T KNOW HOW TO DO
#input.data <- ???
#I'll add dummy data so that the program loads
input.data <- tibble(var.names,
temp = 1:length(var.names))
values$data <- input.data
})
# Render the input data table
output$data <- DT::renderDataTable({
values$data
})
}
shinyApp(ui, server)
But what I want - and really have no idea how to do - is to get it back into a data frame after the user hits "submit" (I only need two columns in the subsequent data frame; I don't need the text_prompt data again.)
I know that the user input creates a list of read-only ReactiveValues called "input". But I can't figure out how to do anything with this list besides access using known names (i.e. I know that there is a variable named "project_id" which I can access using input$project_id). But what I want is not to have to write them all out, so that I can change the data used to create the input fields. So I need a way to collect them in a data frame without knowing all the individual names of the variables or even how many there are.
I figured this out on my own. You can't index reactive values with []. However, for some reason you can using [[]].
I would love to know why this is, if anyone has an answer that can help me understand why it works this way.
Here's the key bit of code that I was missing before:
input.data <- tibble (names = var.names,
values = map_chr(var.names, ~input[[.x]]))
The full code that works as I want it is pasted below. I'd still appreciate any feedback or recommendations for improvement.
library(shiny)
library(tidyverse)
library(DT)
additional.data.fields <- tibble (var.name = c("project.id", "director.name"),
prompt.text = c("Enter Project ID", "Enter Director's name"),
var.value = c("e.g. 09-111", "e.g. Paul Smith"))
ui <- fluidPage(
tabsetPanel(
#Generate Input fields from dataframe
tabPanel("Input", #value = "input.2",
# Generate input fields with pmap
actionButton("submit", "Submit"),
pmap(additional.data.fields, ~textInput(..1, ..2, value = ..3)),
),
#Output data to tell if it updates with button click
tabPanel("Output", value = "output",
DT::dataTableOutput("data")
)
)
)
server <- function(input, output, session) {
# Create a reactive values object to store the input data
values <- reactiveValues()
# Set the reactive values object when the submit button is clicked
observeEvent(input$submit, {
var.names <- pull(additional.data.fields, var.name)
input.data <- tibble (names = var.names,
values = map_chr(var.names, ~input[[.x]]))
values$data <- input.data
})
# Render the input data table
output$data <- DT::renderDataTable({
values$data
})
}
shinyApp(ui, server)

Subsetting dataframe based on dynamically generated checkBoxGroupInput: checkbox keeps resetting

I would like to use a Shiny app to load a file (tab-separated), dynamically create a checkboxGroupInput, after the loading of the file (using observeEvent) using the column headers, then subset the data frame that comes from the file based on the selected checkboxes. The data is then plotted using code I can't share right now.
All is working fine, apart from the last bit: subsetting the dataframe based on the selected checkboxes in checkboxGroupInput. The checkboxes all start selected, and the plot is created fine. If you un-select one of the checkboxes, the plot re-plots appropriately for a split second (so the subsetting is working fine) then the unselected checkbox re-selects itself and the plot goes back to the old plot.
This is the tiny problem I'm trying to solve, guessing it's one line of code. I'm assuming it's because of some reactivity that I don't understand and the checkbox constantly resetting itself.
Here is an example:
###
## Some functions I can't share
### Shiny app
library(shiny)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("MagicPlotter"),
# Sidebar
sidebarLayout(
sidebarPanel(
fileInput(inputId = "myInputID",
label = "Your .csv file",
placeholder = "File not uploaded"),
uiOutput("mylist"),
uiOutput("submitbutton")
),
# Show a plot
mainPanel(
verticalLayout(
plotOutput("myPlot"))
)
)
)
# Define server
server <- function(input, output) {
output$myPlot <- renderPlot({
inputfile <- input$myInputID
if(is.null(inputfile))
{return()}
mydataframe <- read.table(file=inputfile$datapath, sep="\t", head=T, row.names = 1)
mydataframecolumnnames <- colnames(mydataframe[1:(length(mydataframe)-1)])
# the last column is dropped because it's not relevant as a column name
observeEvent(input$myInputID, {
output$mylist <- renderUI({
checkboxGroupInput(inputId="mylist",
label="List of things to select",
choices=mydataframecolumnnames,
selected=mydataframecolumnnames)
})
})
observeEvent(input$myInputID, {
output$submitbutton <- renderUI({
submitButton("Subset")
})
})
mysubset <- mydataframe[input$mylist]
myPlot(mysubset)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Thanks all
I think there are a few things that might help...
One, you can move your observeEvent methods outside of your renderPlot.
Also, you can create a reactive function to read in the data table.
I hope this helps.
server <- function(input, output) {
myDataFrame <- reactive({
inputfile <- input$myInputID
if(is.null(inputfile))
{return()}
read.table(file=inputfile$datapath, sep="\t", head=T, row.names = 1)
})
output$myPlot <- renderPlot({
req(input$mylist)
mysubset <- myDataFrame()[input$mylist]
plot(mysubset)
})
observeEvent(input$myInputID, {
mydata <- myDataFrame()
mydataframecolumnnames <- colnames(mydata[1:(length(mydata)-1)])
output$mylist <- renderUI({
checkboxGroupInput(inputId="mylist",
label="List of things to select",
choices=mydataframecolumnnames,
selected=mydataframecolumnnames)
})
})
observeEvent(input$myInputID, {
output$submitbutton <- renderUI({
submitButton("Subset")
})
})
}

Storing a reactive output in a vector - Shiny R

I am working on building a shiny App. I have used some filters and rendered a data frame and the data frame changes dynamically as per the user input. But I cannot store a particular column value from a data frame into a vector. I need to store the reactive output every time into a vector so that I can use the values later again. Here the values are stored in text_vec and i need to pass that into the API but I cannot access the values from text_vec and i have to pass the updated values every time into the API
library(dplyr)
library(shiny)
shinyApp(ui = fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "cyl",
label = "Number cylinders:",
choices = c("all",sort(unique(mtcars$cyl))),
selected = "all"),
actionButton("capture",
"capture value")
), # closes sidebarPanel
mainPanel(
tableOutput("text"),
tableOutput("text2"),
tableOutput("text3"),
tableOutput("table")
) # closes mainPanel
) # closes sidebarLayout
), # closes fluidPage
server = function(input, output) {
# some example reactive data
cars_react <- reactive({
mtcars %>%
filter(cyl == input$cyl | input$cyl == "all")
})
# simply global assignment of a reactive vector
observeEvent(cars_react(), {
# here is a globally assigned vector taken from the reactive data
# reused in a render statement it will not react to change, since it is not reactive
test_vec3 <<- unique(cars_react()$hp)
})
# here a file is written to the working directory of your shiny app
# everytime cars_react() changes write (and overwrite) vector to a file
observeEvent(cars_react(), {
test_vec = unique(cars_react()$hp)
saveRDS(test_vec, file = "test_vec.Rdata")
})
# same as above but the file is gradually growing and not overwritten
# everytime cars_react() changes add vector to a (over several sessions growing) list
observeEvent(cars_react(), {
test_vec2 = unique(cars_react()$hp)
if (file.exists("test_list.Rdata")) {
temp = readRDS("test_list.Rdata")
test_list = c(temp, list(test_vec2))
} else {
test_list = list(test_vec2)
}
saveRDS(test_list, file = "test_list.Rdata")
})
# here we access the reactive data with isolate and make it non-reactive, but can update the values through a button click
text_vec <<- eventReactive(input$capture, {
isolate(unique(cars_react()$hp))
})
# output of our reactive data as table
output$table <- renderTable({
cars_react()
})
# text output of globally assigned non-reactive vector test_vec3 (not changing!)
output$text <- renderText({
test_vec3
})
# you can capture values of reactives with isolate, but then, they don't change anymore
# text output of isolated formely reactive vector unique(cars_react()$hp (not changing!)
output$text2 <- renderText({
isolate(unique(cars_react()$hp))
})
# text output of new reactive vector (changes when input$capture button is clicked)
output$text3 <- renderText({
text_vec()
})
for (i in text_vec)
{
url = "https://oscar.com/prweb/PRRestService/"
parameters<-'{
{
"Reference":"Account"
,"ReferenceValue":""
}'
b<-fromJSON(parameters)
b["ReferenceValue"]=i
r <- POST(url, body = parameters,encode = "json")
r_c<-toJSON(content(r))
print(r_c)
}
}
)
A simple way to get a data frame to persist across all environments used within your Shiny app, is to use the '<<-' assignment instead of the '<-" assignment. This is not a great programming technique, but it may be what you're hoping to find.
# To get a data frame to persist, use
a <<- b
# instead of
a <- b
** Updated answer **
Based on your updated answer, I would wrap you API call into an observeEvent which gets triggered once the action button is pressed. Since you do not provide a working example with some real code, I am not sure whether the example below is of help. I further assume that your for loop is correct and working (on my end, I cannot know without a real API and some real values).
library(dplyr)
library(shiny)
library(httr)
library(jsonlite)
shinyApp(ui = fluidPage(
selectInput(inputId = "cyl",
label = "Number cylinders:",
choices = c("all",sort(unique(mtcars$cyl))),
selected = "all"),
actionButton("capture",
"capture value")
), # closes fluidPage
server = function(input, output) {
# some example reactive data
cars_react <- reactive({
mtcars %>%
filter(cyl == input$cyl | input$cyl == "all")
})
# here we access the reactive data with isolate and make it non-reactive, but can update the values through a button click
observeEvent(input$capture, {
for (i in unique(cars_react()$hp))
{
url = "https://oscar.com/prweb/PRRestService/"
parameters<-'{
"Reference":"Account"
,"ReferenceValue":""
}'
b<-fromJSON(parameters)
b["ReferenceValue"]=i
r <- POST(url, body = parameters,encode = "json")
r_c<-toJSON(content(r))
print(r_c)
}
})
}
)
Old answer
It is not clear from your question how, where and how often you want to use the vector of your reactive data frame. But it is an important question, since the concept of reactivity and how to access it is very hard to grasp when you come from a pure non reactive R environment.
Below is a simple example app which shows how to access vectors in reactive data frames, and how they could be used.
I hope it helps to get a better understanding of reactivity in shiny.
library(dplyr)
library(shiny)
shinyApp(ui = fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId = "cyl",
label = "Number cylinders:",
choices = c("all",sort(unique(mtcars$cyl))),
selected = "all"),
actionButton("capture",
"capture value")
), # closes sidebarPanel
mainPanel(
tableOutput("text"),
tableOutput("text2"),
tableOutput("text3"),
tableOutput("table")
) # closes mainPanel
) # closes sidebarLayout
), # closes fluidPage
server = function(input, output) {
# some example reactive data
cars_react <- reactive({
mtcars %>%
filter(cyl == input$cyl | input$cyl == "all")
})
# simply global assignment of a reactive vector
observeEvent(cars_react(), {
# here is a globally assigned vector taken from the reactive data
# reused in a render statement it will not react to change, since it is not reactive
test_vec3 <<- unique(cars_react()$hp)
})
# here a file is written to the working directory of your shiny app
# everytime cars_react() changes write (and overwrite) vector to a file
observeEvent(cars_react(), {
test_vec = unique(cars_react()$hp)
saveRDS(test_vec, file = "test_vec.Rdata")
})
# same as above but the file is gradually growing and not overwritten
# everytime cars_react() changes add vector to a (over several sessions growing) list
observeEvent(cars_react(), {
test_vec2 = unique(cars_react()$hp)
if (file.exists("test_list.Rdata")) {
temp = readRDS("test_list.Rdata")
test_list = c(temp, list(test_vec2))
} else {
test_list = list(test_vec2)
}
saveRDS(test_list, file = "test_list.Rdata")
})
# here we access the reactive data with isolate and make it non-reactive, but can update the values through a button click
text_vec <- eventReactive(input$capture, {
isolate(unique(cars_react()$hp))
})
# output of our reactive data as table
output$table <- renderTable({
cars_react()
})
# text output of globally assigned non-reactive vector test_vec3 (not changing!)
output$text <- renderText({
test_vec3
})
# you can capture values of reactives with isolate, but then, they don't change anymore
# text output of isolated formely reactive vector unique(cars_react()$hp (not changing!)
output$text2 <- renderText({
isolate(unique(cars_react()$hp))
})
# text output of new reactive vector (changes when input$capture button is clicked)
output$text3 <- renderText({
text_vec()
})
}
)

updateSelectInput in R Shiny module won't pass existing inputs to 'selected' argument

I'm writing an app that will help wrangle data files into standard formats to feed reusable dashboards. Part of this effort involves creating a user interface to make it easy for users to map the random column names they have in their input files to the "standard" column names that the dashboard is going to expect.
I actually got this code working nicely. But the app needs to do this same mapping exercise for several different input files (each with their own set of standard column names), so it seemed like a good candidate to modularize!
Here's the workflow:
The user loads a "mapping input" file. If they've done this mapping
exercise before, I want to use this file to pre-populate the
drop-downs. I'm also pulling the list of standard column names from
this table. Each standard column name gets an associated drop down.
They load their file to be wrangled - the one with the wackly column
names. The column names in this file will become the options in the
drop-downs.
As the user starts mapping their column names to the different
standard name drop downs, their selections will disappear from the
other drop-down lists. This makes it easier to map columns in files
with many columns.
I feel like I am so close. The issue is when the module runs updateSelectInput. I'm using updateSelectInput to remove options from the drop-downs that have already been used. This works, but it clears out the pre-populated values that were set in the renderUI function.
Here's the code with the pre-populated values working (having removed the problematic updateSelectInput):
# Load libraries and options ----------------------------------------------
library(shiny)
library(dplyr)
library(tidyr)
options(stringsAsFactors = FALSE)
# Modules -----------------------------------------------------------------
input_ui <- function(id, row_label, file_description) {
ns <- NS(id)
fluidRow(
uiOutput(ns("colmapping")) # References the dynamic dropdowns created by the server module.
)
}
# Creates dynamic dropdowns which ultimately will be used to rename columns from a number of different files.
input_server <- function(input, output, session, parent) {
# Create a fake file with misnamed columns that need remapped.
input_file <- reactive({
return(data.frame(Account.Number = 1:2,
Account.Name = c("Account 1", "Account 2"),
Quota.2018 = c(1000, 2000)))
})
# Get a list of what the columns SHOULD be named. These will also do double-duty as the labels for our dropdown inputs.
standard_columns <- reactive({
c("AccountId", "AccountName", "SalesGoal")
})
# Get the actual column names from the file with misnamed columns.
actual_columns <- reactive({
colnames(input_file())
})
# A separate input can be loaded that documents how the misnamed columns have been mapped to the correct names in the past.
# We want to pre-populate the dropdowns with these selections.
quickstart_columns <- reactive({
c("Account.Number", "Account.Name", "Quota")
})
# Create a drop-down selectInput for each of the "standard" column names, allowing the user to choose from the column names in their own misnamed file.
output$colmapping <- renderUI({
ns <- session$ns
dropdowns = tagList()
for (i in seq_len(length(standard_columns()))) { # For i in 1:number of standard names associated with this table
dropdowns[[i]] = selectInput(ns(paste0("input_", standard_columns()[i])), # Use the standard name value for the input object name
label = paste0(standard_columns()[i]), # And for the UI label
choices = actual_columns(),
selected = quickstart_columns()[i],
multiple = FALSE) #Use choices from loaded input table
}
return(dropdowns)
})
}
# UI ----------------------------------------------------------------------
ui <- fluidPage(
input_ui("acct_info")
)
# Server ------------------------------------------------------------------
server <- function(input, output, session) {
acct_info_mod_results <- callModule(input_server,
"acct_info",
parent = session)
}
shinyApp(ui = ui, server = server)
And here's the same code with updateSelectInput turned on (so selected-elsewhere options drop from the choices), but where the pre-populated values don't show up.
# Load libraries and options ----------------------------------------------
library(shiny)
library(dplyr)
library(tidyr)
options(stringsAsFactors = FALSE)
# Modules -----------------------------------------------------------------
input_ui <- function(id, row_label, file_description) {
ns <- NS(id)
fluidRow(
uiOutput(ns("colmapping")) # References the dynamic dropdowns created by the server module.
)
}
# Creates dynamic dropdowns which ultimately will be used to rename columns from a number of different files.
input_server <- function(input, output, session, parent) {
# Create a fake file with misnamed columns that need remapped.
input_file <- reactive({
return(data.frame(Account.Number = 1:2,
Account.Name = c("Account 1", "Account 2"),
Quota.2018 = c(1000, 2000)))
})
# Get a list of what the columns SHOULD be named. These will also do double-duty as the labels for our dropdown inputs.
standard_columns <- reactive({
c("AccountId", "AccountName", "SalesGoal")
})
# Get the actual column names from the file with misnamed columns.
actual_columns <- reactive({
colnames(input_file())
})
# A separate input can be loaded that documents how the misnamed columns have been mapped to the correct names in the past.
# We want to pre-populate the dropdowns with these selections.
quickstart_columns <- reactive({
c("Account.Number", "Account.Name", "Quota")
})
# Create a drop-down selectInput for each of the "standard" column names, allowing the user to choose from the column names in their own misnamed file.
output$colmapping <- renderUI({
ns <- session$ns
dropdowns = tagList()
for (i in seq_len(length(standard_columns()))) { # For i in 1:number of standard names associated with this table
dropdowns[[i]] = selectInput(ns(paste0("input_", standard_columns()[i])), # Use the standard name value for the input object name
label = paste0(standard_columns()[i]), # And for the UI label
choices = actual_columns(),
selected = quickstart_columns()[i],
multiple = FALSE) #Use choices from loaded input table
}
return(dropdowns)
})
# This is the chunk of code giving me trouble!
# For some of these files, there's like 20-some columns that will need renamed. That's a lot of scanning through long dropdown lists.
# As the user starts to map some of the columns, I want their selections to disappear from the other drop downs.
# The good news is, this works!
# The bad news is, it also clears out the pre-populated inputs. How can I keep the pre-populated inputs from disappearing when I apply updateSelectInput?
observe({
ns <- session$ns
n <- isolate(length(standard_columns()))
for (i in seq_len(n)) {
already_selected <- unlist(lapply((1:n)[-i], function(i)
input[[ paste0("input_",standard_columns()[i]) ]]))
print(i)
selected_i <- input[[ paste0("input_", standard_columns()[i]) ]]
print(selected_i) # For debugging. These return empty values until selections are made, but I never had the problem with analogous code until I tried to put it in the module.
updateSelectInput(session = parent,
ns(paste0("input_",standard_columns()[i])),
choices = append(c("Empty"),setdiff(actual_columns(), already_selected)),
selected = input[[ paste0("input_", standard_columns()[i]) ]]
)
}
})
}
# UI ----------------------------------------------------------------------
ui <- fluidPage(
input_ui("acct_info")
)
# Server ------------------------------------------------------------------
server <- function(input, output, session) {
acct_info_mod_results <- callModule(input_server,
"acct_info",
parent = session)
}
shinyApp(ui = ui, server = server)
This is the first time I've gotten so totally stuck on a project!! I hugely appreciate any insight or suggestions!
EDIT: After much pain, I've figured out how to get a list of session inputs that I can loop through to create the updateSelectInput in the parent session. I've also figured out how to put it into a function in the main session. Here's a minimal example of the working fix, but I'm all ears if anyone has a smarter way to solve the problem!
# Load libraries and options ----------------------------------------------
library(shiny)
library(dplyr)
options(stringsAsFactors = FALSE)
updateDropDowns <- function(session, all_inputs) {
inputs <- setdiff(all_inputs$names, all_inputs$names %>% str_subset(pattern="selectize"))
selected <- unname(unlist(all_inputs %>% filter(names %in% inputs) %>% select(selected)))
values <- c("a", "b", "c", "d")
n <- length(inputs)
for (i in seq_len(n)) {
already_selected <- unlist(lapply((1:n)[-i], function(i)
selected[i]))
updateSelectInput(session,
inputs[i],
choices = setdiff(values, already_selected),
selected = selected[i])
}
}
# UI ----------------------------------------------------------------------
ui <- fluidPage(
uiOutput("colmapping")
)
# Server ------------------------------------------------------------------
server <- function(input, output, session) {
output$colmapping <- renderUI({
dropdowns = tagList()
for (i in 1:3) {
dropdowns[[i]] = selectInput(paste0("input_",i),
label = paste0("input_",i),
choices = c("a", "b", "c", "d"),
selected = NULL,
multiple = FALSE)
}
return(dropdowns)
})
all_inputs <- reactive({ # get a dataframe of input names and values, else return an empty df
x <- reactiveValuesToList(input)
y <- data.frame(
names = names(x),
selected = unlist(x, use.names = FALSE)
)
empty <- as.data.frame(setNames(rbind(data.frame(matrix(ncol = 2, nrow = 1)),
c(rep("999",2))),
c("names", "selected")))
if(nrow(y) == 0) {empty} else {y}
})
observe({
updateDropDowns(session, all_inputs())
})
}
shinyApp(ui = ui, server = server)

R Shiny: Modify output values in a data frame

The following simple shiny app displays a word and its sentiment as stored in the R data frame named sent.
library(shiny)
sent <- data.frame(word=c('happy', 'sad', 'joy', 'upset'),
sentiment=c('positive', 'negative', 'positive', 'negative'),
stringsAsFactors = FALSE)
ui <- fluidPage(
numericInput(inputId = 'num', label='', value=1, min=1, max=nrow(sent)),
br(),
h4("Word:"),
textOutput('word'),
br(),
h4("Sentiment:"),
textOutput('sentiment')
)
server <- function(input, output){
output$word <- renderText({ sent$word[input$num] })
output$sentiment <- renderText({ sent$sentiment[input$num] })
}
shinyApp(ui=ui, server=server)
I would like to modify this in 2 ways:
(1) I would like the user to be able to scroll through the words in the column sent$word, rather than using numericInput()
(2) More importantly, I would like the user to be able to modify the sentiment value associated with each word. Ideally, this would be a drop down menu (with 'positive' and 'negative' as options), which would display the current sentiment value stored in sent for that word, but which could be changed by the user and overridden in the dataframe.
Any suggestions?
This should do the trick
library(shiny)
sent <- data.frame(word=c('happy', 'sad', 'joy', 'upset'),
sentiment=c('positive', 'negative', 'positive', 'negative'),
stringsAsFactors = FALSE)
sent2 <- reactiveVal(sent)
i <- 1
i2 <- reactiveVal(i)
ui <- fluidPage(
uiOutput("wordSelect"),
br(),
h4("Word:"),
textOutput('word'),
br(),
h4("Sentiment:"),
textOutput('sentiment'),
br(),
uiOutput("change"),
actionButton("go","Change")
)
server <- function(input, output){
output$wordSelect <- renderUI({
selectizeInput(inputId = 'wrd', label='select word', choices=sent$word, selected=sent$word[i2()])
})
output$word <- renderText({ input$wrd })
output$sentiment <- renderText({ sent$sentiment[which(sent2()$word==input$wrd)] })
observeEvent(input$go, {
out <- sent
out$sentiment[which(sent$word==input$wrd)] <- input$newLabel
sent <<- out
sent2(out)
i <<- which(sent$word==input$wrd)+1
if(i > length(sent$word)) {
i <<- i - 1
}
i2(i)
})
output$change <- renderUI({
radioButtons("newLabel", label="Change value", choices=c('positive','negative'), sent$sentiment[which(sent2()$word==input$wrd)])
})
}
shinyApp(ui=ui, server=server)
The adjusted output is first stored in a reactiveVal named sent2(). This is required for you see the adjure values while running the Shiny App.
A selectizeInput() is used to scroll through the words (Q1).
radioButtons() are used to select positive and negative values. The default value is whatever value is currently applied to the corresponding word.
An actionButton() is used to make the change when wanted.
UPDATE: I added sent <<- out so that your sent dataframe actually gets updated. Be aware that this will overwrite the values you had stored in sent before.
UPDATE: Each time the action button is clicked, the index of the currently selected word is determined using which(). Then it is incremented and stored in i and i2(). The new index is used to determine the default value of selectizeInput(). This way, when no manual selection of words is done, you will scroll through all options. When a word is selected manually, you will continue incrementing from that word onwards. When the last word is reached, the value does not increment further

Resources