I am attempting to code a Shiny application that hits a JSON API as its main data source. The app would hit the API once per minute to retrieve updated data in JSON format, then use jsonlite package to parse it into a data frame for use in the Shiny app. My goal is to create valueBox and chart outputs and have those update each minute as the data count goes up and changes, to create a "stream-like" dashboard.
I've attempted to use these two examples to code this out, but my app does not seem to be able to access my data source when it's loaded.
Twitter Sentiment Analysis
Shiny CRANDash
Here's some sample code. Right now, I am just using a dummy test JSON source and appending the current time to see that the 60 second update is working.
helper_functions.R:
library(jsonlite)
getDataSet <- function() {
URL = "http://jsonplaceholder.typicode.com/posts"
results <- fromJSON(URL)
results_df <- data.frame(results)
results_df$RowCreated <- Sys.time()
#I have also added this call to return (results_df) to no avail, so it is commented out
#return(results_df)
}
server.R
source('helper_functions.R')
shinyServer(function(input, output, session) {
autoInvalidate = reactiveTimer(6000,session)
get_input = reactive({
autoInvalidate()
#show progress bar
withProgress(session, {
setProgress(message = 'Collecting API data...')
getDataSet()
})
})
output$currentTime <- renderText({
invalidateLater(1000,session)
format(Sys.time())
})
#attempting to just write the data frame to a table to see it
output$SampleDataFrame <- renderDataTable(
getDataSet
)
#simply output the first row's RowCreated as a text field
output$RowCreated <- renderText(
getDataSet[1,]$RowCreated
)
})
ui.R
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = "Tab 1", icon = icon("check-circle"), badgeLabel = "testing", badgeColor = "red"),
menuItem("Tab 2", tabName = "Tab 2", icon = icon("database"), badgeLabel = "testing", badgeColor = "blue"),
menuItem("Tab 3", tabName = "Tab 3", icon = icon("database"), badgeLabel = "testing", badgeColor = "blue")
)
)
body <- dashboardBody(
fluidRow(
tags$code(
"Data Last Updated from API: ", textOutput("currentTime", container = span)
),
h2("Example Header"),
fluidRow(
textOutput("RowCreated")
)
)
)
dashboardPage(
skin="black",
dashboardHeader(title = "Sample"),
sidebar,
body
)
The dashboard renders, but in the section that outputs "RowCreated" this is the message:
error: object of type 'closure' is not subsettable
I'd expect this to update every 6 seconds, per the invalidate call.
Any help is appreciated. Thanks!
Corrected code which works.
helper_functions.R:
library(jsonlite)
getDataSet <- function() {
URL = "http://jsonplaceholder.typicode.com/posts"
results <- fromJSON(URL)
results_df <- data.frame(results)
results_df$RowCreated <- Sys.time()
return(results_df)
}
server.R
source('helper_functions.R')
shinyServer(function(input, output, session) {
autoInvalidate = reactiveTimer(6000,session)
get_input = reactive({
autoInvalidate()
#show progress bar
withProgress(session, {
setProgress(message = 'Collecting API data...')
getDataSet()
})
})
output$currentTime <- renderText({
invalidateLater(1000,session)
format(Sys.time())
})
output$SampleDataFrame <- renderDataTable(
getDataSet()
)
#simply output the first row's RowCreated as a text field
#minor tweak to format date correctly
output$RowCreated <- renderText(
format(getDataSet()[1,]$RowCreated[1])
)
})
ui.R
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Tab 1", tabName = "Tab 1", icon = icon("check-circle"), badgeLabel = "testing", badgeColor = "red"),
menuItem("Tab 2", tabName = "Tab 2", icon = icon("database"), badgeLabel = "testing", badgeColor = "blue"),
menuItem("Tab 3", tabName = "Tab 3", icon = icon("database"), badgeLabel = "testing", badgeColor = "blue")
)
)
body <- dashboardBody(
fluidRow(
tags$code(
"Data Last Updated from API: ", textOutput("currentTime", container = span)
),
h2("Example Header"),
fluidRow(
textOutput("RowCreated")
)
)
)
dashboardPage(
skin="black",
dashboardHeader(title = "Sample"),
sidebar,
body
)
I know I am way late to this party, but I don't think your solution should work...
you have:
output$currentTime <- renderText({
then in your second renderText you have:
output$RowCreated <- renderText(
notice the none existent { in the second one?
Just trying to replicate the code, but found that in my attempt
Related
I am using the R programming language. I am interested in learning how to save several "html widgets" together. I have been able to manually create different types of html widgets:
#widget 1
library(htmlwidgets)
library(leaflet)
library(RColorBrewer)
# create map data
map_data <- data.frame(
"Lati" = c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), "Longi" = c(-79.3871, -79.3860, -79.3807, -79.3806, -79.3957),
"Job" = c("Economist", "Economist", "Teacher", "Teacher", "Lawyer"),
"First_Name" = c("John", "James", "Jack", "Jason", "Jim"),
"Last_Name" = c("Smith", "Charles", "Henry", "David", "Robert"),
"vehicle" = c("car", "van", "car", "none", "car")
)
kingdom <- c("Economist", "Lawyer", "Teacher")
my_palette <- brewer.pal(3, "Paired")
factpal <- colorFactor(my_palette, levels = kingdom)
groups <- unique(map_data$Job)
# finalize map
map <- leaflet(map_data) %>%
addTiles(group = "OpenStreetMap") %>%
addCircleMarkers(~Longi, ~Lati, popup = ~Job,
radius = 10, weight = 2, opacity = 1, color = ~factpal(Job),
fill = TRUE, fillOpacity = 1, group = ~Job
)
widget_1 = map %>%
addLayersControl(overlayGroups = groups, options = layersControlOptions(collapsed = FALSE)) %>%
addTiles() %>%
addMarkers(lng = ~Longi,
lat = ~Lati,
popup = ~paste("Job", Job, "<br>",
"First_Name:", First_Name, "<br>",
"Last_Name:", Last_Name, "<br>", "vehicle:", vehicle, "<br>"))
widget 2:
##### widget 2
library(plotly)
library(ggplot2)
p_plot <- data.frame(frequency = c(rnorm(31, 1), rnorm(31)),
is_consumed = factor(round(runif(62))))
p2 <- p_plot %>%
ggplot(aes(frequency, fill = is_consumed)) +
geom_density(alpha = 0.5)
widget_2 = ggplotly(p2)
widget 3:
#####widget_3
today <- Sys.Date()
tm <- seq(0, 600, by = 10)
x <- today - tm
y <- rnorm(length(x))
widget_3 <- plot_ly(x = ~x, y = ~y, mode = 'lines', text = paste(tm, "days from today"))
widget 4:
####widget_4
library(igraph)
library(dplyr)
library(visNetwork)
Data_I_Have <- data.frame(
"Node_A" = c("John", "John", "John", "Peter", "Peter", "Peter", "Tim", "Kevin", "Adam", "Adam", "Xavier"),
"Node_B" = c("Claude", "Peter", "Tim", "Tim", "Claude", "Henry", "Kevin", "Claude", "Tim", "Henry", "Claude")
)
graph_file <- data.frame(Data_I_Have$Node_A, Data_I_Have$Node_B)
colnames(graph_file) <- c("Data_I_Have$Node_A", "Data_I_Have$Node_B")
graph <- graph.data.frame(graph_file, directed=F)
graph <- simplify(graph)
nodes <- data.frame(id = V(graph)$name, title = V(graph)$name)
nodes <- nodes[order(nodes$id, decreasing = F),]
edges <- get.data.frame(graph, what="edges")[1:2]
widget_4 = visNetwork(nodes, edges) %>% visIgraphLayout(layout = "layout_with_fr") %>%
visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE)
From here, I found another stackoverflow post where a similar question was asked: Using R and plot.ly, how to save multiples htmlwidgets to my html?
In this post, it explains how to save several html widgets together - the person who answered the question wrote a function to do so:
library(htmltools)
save_tags <- function (tags, file, selfcontained = F, libdir = "./lib")
{
if (is.null(libdir)) {
libdir <- paste(tools::file_path_sans_ext(basename(file)),
"_files", sep = "")
}
htmltools::save_html(tags, file = file, libdir = libdir)
if (selfcontained) {
if (!htmlwidgets:::pandoc_available()) {
stop("Saving a widget with selfcontained = TRUE requires pandoc. For details see:\n",
"https://github.com/rstudio/rmarkdown/blob/master/PANDOC.md")
}
htmlwidgets:::pandoc_self_contained_html(file, file)
unlink(libdir, recursive = TRUE)
}
return(htmltools::tags$iframe(src= file, height = "400px", width = "100%", style="border:0;"))
}
I tried using this function to save the 4 widgets together:
save_tags(widget_1, widget_2, widget_3, widget_4)
But doing so, I got the following error:
Error in dirname(file) : a character vector argument expected
Is there a straightforward and simple way for saving multiple html widgets together?
Thanks
NOTE: I know that you can use the combineWidgets() function in R:
library(manipulateWidget)
combineWidgets(widget_1, widget_2, widget_3, widget_4)
However, I am working with a computer that has no internet access or USB ports. This computer has a pre-installed copy of R with limited libraries (it has all the libraries used throughout my question except "manipulateWidget"). I am looking for the simplest way to save multiple html widgets together (e.g. is this possible in base R)?
Thanks
If format doesn't matter too much, you can merge the widgets using tagList and save them directly:
htmltools::save_html(tagList(widget_1, widget_2, widget_3, widget_4), file = "C://Users//Me//Desktop//widgets.html")
(It goes without saying that you will need to edit the filepath!)
If you want to control the layout of the widgets, you can wrap each in a div, and then style those:
doc <- htmltools::tagList(
div(widget_1, style = "float:left;width:50%;"),
div(widget_2,style = "float:left;width:50%;"),
div(widget_3, style = "float:left;width:50%;"),
div(widget_4, style = "float:left;width:50%;")
)
htmltools::save_html(html = doc, file = "C://Users//Me//Desktop//widgets.html")
I'm trying to update a radiobutton list on the server side with HTML code but I'm not succeeding.
I'm using this example for you to understand what I'm trying to do. I refer again that has to be on the server side because the elements on my list will be related to other inputs made by the user.
Can someone help to figure out how it can be made?
thanks
## Only run examples in interactive R sessions
if (interactive()) {
ui <- fluidPage(
radioButtons("rb", "Choose one:",
choiceNames = list("icon", "html", "text"),
choiceValues = c(1,2,3)),
textOutput("txt")
)
server <- function(input, output,session) {
a<-HTML("<p style='color:red;'>option2</p>")
list1=as.list(c("option1",a,"option3"))
updateRadioButtons(session, "rb", choiceNames = list1, choiceValues = c(1,2,3))
output$txt <- renderText({
paste("You chose", input$rb)
})
}
shinyApp(ui, server)
}
There are some problems in your code:
choiceValues must be an atomic vector, not a list
ui <- fluidPage(
radioButtons("rb", "Choose one:",
choiceNames = list("icon", "html", "text"),
choiceValues = c(1,2,3)),
textOutput("txt")
)
if you use updateXXX you have to set the argument session to the server function:
server <- function(input, output, session) {
in updateRadioButtons you have to set both choiceNames and choiceValues:
if(TRUE){
list=list(icon("calendar"),
HTML("<p style='color:red;'>Red Text</p>"),
"Normal text"
)
updateRadioButtons(session, "rb", choiceNames = list, choiceValues = c(1,2,3))
}
And the icon does not work.
[Previous question]How to include an action link into a button's label?
How I can align "get help" on the right of sidbarPanel?
library(shiny)
ui <- fluidPage(
br(),
selectInput(
inputId = "some_id",
label = HTML("Please choose A or B",
as.character(actionLink(inputId = 'action_link', label = 'get help'))),
choices = c("choice A", "choice B"),
selected = "choice A",
selectize = F
)
)
server <- function(input, output) {}
shinyApp(ui, server)
as far as I understand from the problem is you want a helper icon or link on the side of you select input.
you can use shinyhelper library for that.For Detailed Documentation yo can refer to here
I tried a sample for using this: hope this help
library(shiny)
library(shinyhelper)
library(magrittr)
ui <- fluidPage(
titlePanel(title = "Demo APP"),
sidebarLayout(
sidebarPanel = sidebarPanel(
selectInput(inputId = "dataset", "choose DataSet",
choices = c("MTCARS","IRSIS")
) %>%
helper(type = "inline",
title = "Inline Help",
content = c("This helpfile is defined entirely in the UI!",
"This is on a new line.",
"This is some <b>HTML</b>."),
size = "s")
),
mainPanel = mainPanel(
verbatimTextOutput(outputId = "TABLE")
)
)
)
server <- function(input, output) {
observe_helpers()
output$TABLE<-renderText({
paste0("Dataset selcted: ",input$dataset)
})
}
shinyApp(ui, server)
Output Looks like:
after clicking the icon:
The app below contains an actionButton Add data that inserts a UI element each time it is clicked. Each UI element is a box that contains one selectInput Select data and an actionButton Edit that opens a modal when clicked.
Each modal contains:
A data table with two columns: Parameter and Value (this column is editable).
An actionButton Apply, which applies any changes made to the Value
column.
When the user selects a dataset inside box x, a reactiveValue is created to store the corresponding parameters in a data.frame x_paramset (where x is the id of the box inserted via insertUI) and add a val column which has the same value as default (see list at the top of code below). I then use renderDataTable to add the Value column (which contains the numericInput) - this data table is displayed inside the modal.
To update the data.frame to apply any changes the user may have made in the modal, I use an observeEvent that listens for the Apply button and updates the val column in the data.frame x_paramset with the values inside the numericInputs in the Value column.
Here is the app (the bsModal has been commented out and replaced with a shinyWidgets::dropdownButton):
library(shiny)
library(shinydashboard)
library(shinyjs)
library(shinyWidgets)
library(DT)
library(tidyverse)
all = list(p1 = list(a = list(id = "a", default = 10)),
p2 = list(x = list(id = "x", default = 20)))
# UI ----------------------------------------------------------------------
ui<-fluidPage(shinyjs::useShinyjs(),
tags$head(
tags$script("
$(document).on('click', '.dropdown-shinyWidgets li button', function () {
$(this).blur()
Shiny.onInputChange('lastClickId',this.id)
Shiny.onInputChange('lastClick',Math.random())
});
")
),
box(title = "Add data",
column(width = 12,
fluidRow(
tags$div(id = 'add')
),
fluidRow(
actionButton("addbox", "Add data")
))
)
)
# SERVER ------------------------------------------------------------------
server <- function(input, output, session) {
rvals = reactiveValues()
getInputs <- function(pattern){
reactives <- names(reactiveValuesToList(input))
name = reactives[grep(pattern,reactives)]
}
observeEvent(input$addbox, {
lr = paste0('box', input$addbox)
insertUI(
selector = '#add',
ui = tags$div(id = lr,
box(title = lr,
selectizeInput(lr, "Choose data:", choices = names(all)),
shinyWidgets::dropdownButton(inputId = paste0(lr, "_settings"),
circle = F, status = "success", icon = icon("gear"), width = "1000px",
tooltip = tooltipOptions(title = "Click to edit"),
tags$h4(paste0("Edit settings for Learner", lr)),
hr(),
DT::dataTableOutput( paste0(lr, "_paramdt") ),
bsButton(paste0(lr, "_apply"), "Apply")
) # end dropdownButton
)
) #end tags$div
) # end inserUI
# create reactive dataset
rvals[[ paste0(lr, "_paramset") ]] <- reactive({
do.call(rbind, all[[ input[[lr]] ]]) %>%
cbind(., lr) %>%
as.data.frame %>%
mutate(val = default)
}) # end reactive
# render DT in modal
output[[ paste0(lr, "_paramdt") ]] <- renderDataTable({
DT <- rvals[[ paste0(lr, "_paramset") ]]() %>%
mutate(
Parameter = id,
Value = as.character(numericInput(paste0(lr,"value",id), label = NULL, value = default))) %>%
select(Parameter:Value)
datatable(DT,
selection = 'none',
#server = F,
escape = F,
options = list(preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')))
}) # end renderDT
# Apply changes
observeEvent(input$lastClick, {
# replace old values with new
rvals[[ paste0(lr, "_paramset") ]](rvals[[ paste0(lr, "_paramset") ]]() %>%
mutate(
val = input$box1valuea
)
)
}) # end apply changes observeEvent
}) #end observeEvent
}
shinyApp(ui=ui, server=server)
I encounter errors when I try the following:
Add data >> Edit >> make some change to numericInput >> Apply - this
resets the numericInput inside the modal back to its default whereas
I would like the user-specified value to persist upon applying
changes or closing the modal.
The app crashes when I try:
Add data >> Edit >> Apply >> close modal >> Add data OR
Click Add data twice and then click Edit in either box.
I am not sure where my server logic is failing. I know Shiny does not support "persistent use" modals (https://github.com/rstudio/shiny/issues/1590) but I was wondering if there was a workaround? I am also not sure what inside the insertUI observeEvent is causing the app to crash in the cases described above. Any help you can offer would be greatly appreciated!
The HTML output is created by summarytool::dfSummary function.
summarytools
summarytools uses Bootstrap’s stylesheets to generate standalone HTML documents that can be displayed in a Web Browser or in RStudio’s Viewer using the generic print() function.
When the HTML gets rendered on the tabpanel, the whole UI changes. Is there a way to render the HTML on the tabpanel without changing the UI?
library(summarytools)
ui <- fluidPage(
titlePanel("dfSummary"),
sidebarLayout(
sidebarPanel(
uiOutput("dfSummaryButton")
),
mainPanel(
tabsetPanel(
tabPanel("Data Input",
dataTableOutput("dataInput"),
br(),
verbatimTextOutput("profileSTR")),
tabPanel("dfSummary Output",
htmlOutput("profileSummary")))
)
)
)
server <- function(input, output, session) {
#Read in data file
recVal <- reactiveValues()
dfdata <- iris
#First 10 records of input file
output$dataInput <- renderDataTable(head(dfdata, n = 10), options = list(scrollY = '500px',
scrollX = TRUE, searching = FALSE, paging = FALSE, info = FALSE,
ordering = FALSE, columnDefs = list(list(className = 'dt-center',
targets = '_all'))))
#str() of input file
output$profileSTR <- renderPrint({
ProStr <- str(dfdata)
return(ProStr)
})
#Create dfSummary Button
output$dfSummaryButton <- renderUI({
actionButton("dfsummarybutton", "Create dfSummary")
})
### Apply dfSummary Buttom
observeEvent(input$dfsummarybutton, {
recVal$dfdata <- dfdata
})
#dfSummary data
output$profileSummary <- renderUI({
req(recVal$dfdata)
SumProfile <- print(dfSummary(recVal$dfdata), omit.headings = TRUE, method = 'render')
SumProfile
})
}
shinyApp(ui, server)
Version 0.8.3 of summarytools has a new boolean option, bootstrap.css which will prevent this from happening. Also, graph.magnif allows adjusting the graphs' size.
SumProfile <- print(dfSummary(recVal$dfdata),
method = 'render',
omit.headings = TRUE,
footnote = NA,
bootstrap.css = FALSE,
graph.magnif = 0.8)
The latest version can be installed with devtools:
devtools::install_github("dcomtois/summarytools")
Good luck :)