I create a interactive pivot table by using rpivotTable package. However, I found that some of aggregators and renderName are unnecessary for my users. I would like to remove them. For example, I want to remove "Average" from aggregator dropdown menu.
Here is my example:
library(shiny)
library(rpivotTable)
df <- iris
ui <- fluidPage(
fluidRow(
column(width=10, rpivotTableOutput("pivot"))
)
)
server <- function(input, output, session) {
output$pivot<-renderRpivotTable({
rpivotTable(df,
rendererName="Heatmap",
cols=c("Species"),
rows=c("Petal.Width"),
aggregatorName="Count",
hiddenFromAggregators=["Average"]
)
})
}
shinyApp(ui = ui, server = server)
I noticed that there seems some relevant parameters called "hiddenFromAggregators" but I cannot figure out how to apply it in R/Shiny environment.
Here is where I found "hiddenFromAggregators".
https://github.com/nicolaskruchten/pivottable/wiki/Parameters
You may be looking for something like this :
rpivotTable(iris,
rendererName = "Treemap",
cols = c("Species"),
rows = c("Petal.Width"),
aggregatorName = "Count",
aggregators = list(Sum = htmlwidgets::JS('$.pivotUtilities.aggregators["Sum"]'),
Count = htmlwidgets::JS('$.pivotUtilities.aggregators["Count"]')),
subtotals = TRUE)
There is probably a faster way than adding aggregators one by one (using full pivotUtilities.aggregators)
I couldn't find a full list of the default aggregators but you can get it with web inspector on your app (with Google Chrome: right click > inspect) and typing $.pivotUtilities.aggregators in the console tab.
The hiddenFromAggregators parameter affects which dataset attributes are eligible to be used as arguments to aggregators, rather than which aggregators are available. It is rather challenging in rpivotTable to pass in a custom set of aggregators but might be possible using something similar to the approach here: https://github.com/smartinsightsfromdata/rpivotTable/issues/81
You will need to familiarize yourself with the documentation here first: https://github.com/nicolaskruchten/pivottable/wiki/Aggregators
Related
I'm looking to make some picker inputs in Shiny for each of the 50 states, but I'd like to separate them into three different groups such that no group has the same state. I was just wondering if there was a way to ensure that the three picker inputs didn't both select the same state or if there was perhaps a better way of doing this in R that I was not aware of. Thank you!
It takes a bit of work to set up, but you can accomplish that by updating the
available choices for other inputs when one changes. If you only have two or
three inputs that should be linked like this, it may be tempting to just
write out the observers and be done with it. But really, this is a
generalizable pattern, so I think it makes sense to use a helper function
instead. That way, you can link however many inputs you need, and also re-use
the logic in different apps.
All that the helper function needs to know is the IDs of the participating
inputs, and the set of shared choices. It’s not strictly necessary here, but
also making the choices reactive lets them dynamically change.
selectPool <- function(inputIds, choices = reactive(NULL)) {
stopifnot(is.reactive(choices))
session <- getDefaultReactiveDomain()
input <- session$input
# Keep track of all selected values in the pool
alreadySelected <- reactive({
Reduce(union, lapply(inputIds, \(id) input[[id]]))
})
# ... and based on that, what's left to select from.
remainingChoices <- reactive({
setdiff(choices(), alreadySelected())
})
# When an input changes, update remaining choices for others
lapply(inputIds, \(id) {
observe({
lapply(setdiff(inputIds, id), \(otherId) {
otherSelected <- input[[otherId]]
updateSelectInput(
session = session,
inputId = otherId,
# Anything already selected must remain a choice
choices = c(remainingChoices(), otherSelected),
selected = otherSelected
)
})
}) |> bindEvent(input[[id]], ignoreNULL = FALSE)
})
}
Once we’ve taken the time to do that, it’s very straightforward to use in an app:
library(shiny)
ui <- fluidPage(
titlePanel("Star Wars Alliance Builder"),
selectInput("alliance1", "Alliance 1", NULL, multiple = TRUE),
selectInput("alliance2", "Alliance 2", NULL, multiple = TRUE),
selectInput("alliance3", "Alliance 3", NULL, multiple = TRUE),
)
server <- function(input, output, session) {
selectPool(
inputIds = c("alliance1", "alliance2", "alliance3"),
choices = reactive(unique(dplyr::starwars$species))
)
}
shinyApp(ui, server)
By "picker inputs" I assume you mean selectInput/selectizeInput.
There are multiple ways you could do this. One way would be to use updateSelectInput() to update the reminding inputs after the first/second has been selected. The possible states to choose from would then be all states except the one(s) already selected. This would make it impossible to choose the same state in multiple inputs from the UI.
However, this might be a bit involved for your need. In that case I suggest that you:
either replace your three inputs with one selectInput(..., multiple = TRUE), and use validate() to check that the user has selected exactly three states
or simply just use validate() to throw an error to the user if they have selected the same state more than once in any of the three inputs.
I am trying to add a column of icons to a data table that is shown in a Shiny app. Essentially I just want an up arrow when the data has gone up, and a down arrow when it has gone down. However, I'm not sure how to display an icon. When I add a column just using for e.g. icon("arrow-up"), I get the following error:
Error: default method not implemented for type 'list'
I can see that if I try this approach outside of Shiny, it is displaying the data about the icon rather than displaying the icon.
One option might be to use the approach of adding it as an image - but it feels like there would be a more direct way? I'm also not sure how to do that with Font Awesome icons.
Apologies if this is basic - I couldn't find an answer!
Here is a simplified version of what I'm doing:
library(shiny)
library(shinydashboard)
number_compare <- data.frame(replicate(2, sample(1:100, 10, rep=TRUE)))
number_compare$direction <- ifelse(number_compare$X1 < number_compare$X2, "Up", "Down")
number_compare$direction <- ifelse(number_compare$X1 == number_compare$X2, "", number_compare$direction)
sidebar <- dashboardSidebar()
body <- dashboardBody(
fluidRow(box(width = 12, solidHeader = TRUE,
tableOutput("example_table"))
)
)
ui <- dashboardPage(dashboardHeader(title = "Example"),
sidebar,
body
)
server <- function(input, output) {
output$example_table <- renderTable(number_compare)
}
shinyApp(ui, server)
You don't say what icons you want to use, so I will assume angle-up and angle-down from fontawesome, but you can also use glyphicons.
As you point out, the output of icon() is a list. Assigning that in your ifelse would give a column repeating the values i, iconname, and NULL.
Instead, try wrapping icon() in as.character() to give the raw HTML for the icon. E.g:
number_compare$direction <- ifelse(
number_compare$X1 < number_compare$X2,
as.character(icon("angle-up")),
as.character(icon("angle-down"))
)
This will fill the column with values like <i class="fa fa-angle-up"></i> which you can print in the app as raw HTML.
Alternatively, you can use HTML escape codes to print the arrows without using icons. See https://www.w3schools.com/charsets/ref_utf_arrows.asp for a list of HTML escape codes.
Edit: Whenever you include raw HTML in a table, make sure that shiny doesn't escape it. Replace your call to renderTable(number_compare) with renderTable(number_compare, sanitize.text.function = function(x) x)
(based on r shiny table not rendering html)
I have written a well functioning Shiny App, and I want to rewrite the code a little bit, to make it more elegant.
My probem: the App is loading for ages, and when it loads, it does not respond anymore. Strange thing that it is functioning well in the old version of the App.
This is what I have right now (after removing nearly everything):
ui.R:
library(shiny)
function(request){shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
),
mainPanel(
DT::dataTableOutput("table")
)
)
))}
server.R:
library(shiny)
library(DT)
library(crosstalk)
library(tidyverse)
data <- readRDS("report_summary2.rds")
shinyServer(function(input, output, session) {
computeTable <- function(){data %>% select("Year", "Month", "Date", "Transaction")}
output$table <- DT::renderDataTable({
DT::datatable(
data = computeTable(),
filter = "top",
rownames = FALSE,
options = list(
pageLength = 100
)
)
})
})
Unfortunately I cannot share report_summary.rds, as it contains sensitive information, it has like 500.000 rows and 9 columns in total, so dealing with that should not be the issue.
This was working in the old App version like a charm, but now it loads forever, and if I try to use the filters for dataTable (at the top), the App is not responding anymore.
I've been searching and looking, but could not find anything. Possibly anybody sees something?
You are right after > 400 thousand rows DT becomes slow being a single process (as well as Basic Shiny).
Here are some options:
To store and access data in RDBMS or noSQL.
Use Shiny Pro, which allows to split the load through multiple servers\applications.
Check as well architecture of your application (too much reactivity will slow it down).
And of course run a Profiler to find out where are your real bottle-necks. All other options are
somehow cosmetic: usage of global.R, split data table for
quick\small and bigger\slow parts etc.
I have a data table in a shiny app. Setting stateSave = TRUE preserves the sorting a column by ascending or descending, but if a filter parameter is set (cyl = 6:8 for example), that is not preserved. Any idea on how to fix this?
Sample code is below:
require(shiny)
require(DT)
ui<-fluidPage(
column(12,
DT::dataTableOutput("mtcars_table")
)
)
server<-function(input,output,session)
{
output$mtcars_table <- DT::renderDataTable({datatable({mtcars},
options = list(pageLength = 50,
stateSave = TRUE
),
selection = "single", filter = "top", escape = FALSE)},
server = FALSE)
}
shinyApp(ui=ui, server=server)
It only works for server-side processing (set server = TRUE).
Unfortunate, as you probably have a larger data set and it potentially causes performance issues.
The reason is quite simple. Not the filter is saved, but the filtered data set. Notice, upon the refresh you still see the data filtered correctly (with server- side processing) but the filter itself is not displayed anymore. You can see that is not stored if you click in the filter field and notice that the displayed range/selection does not match the attributes of the filtered data set.
For the server-side processing the data is kept on the server. If you filter the data and refresh the page the data will still be filtered. However, for non server-side processing the docu (?renderDataTable) tells you, that "the entire data frame is sent to the browser at once". Good bye filter :(.
I am trying to write a shiny app that loads several data frames. The data frames for my plots all work very well, but one data.frame which I want to use as a list of options in a dropdown menu does not load. If I load the frame seperately in the R session, everything works, but if I only run the shiny app, the selectors won't update.
library(shiny)
ui <- fluidPage(
#...
selectInput("mats", "Text",
selectors)
# ...
)
server <- function(input, output){
# ...
df1=read.csv("./data/file.csv", sep=";", head=T, stringsAsFactors = F)
df1$choices=as.character(df1$choices)
selectors=c("All", df1$choices)
#...
}
shinyApp(ui = ui, server = server)
I think, I need the selectors in the server function, so I loaded the data frame there together with my other data frames. Is that the right place and what do I need to do to get this running?
Best
There is a number of problems with your code:
Following the documentation, choices in selectInput should be a list.
If you want to create an element that would be available across the ui and server please consider reading the linked article on scoping rules in Shiny and defining your object in global.R.
With respect to the 1st point, if your intention is to use a data.frame column as a base for menu selection, you can apply the following transformation:
my_new_list <- split(df$id, df$subject)
as provided in this answer by #user3710546 to a similar question that I've asked in past.
Side note
Please consider having a look at the discussion on making a reproducible example in R. If you care to redo your example using some publicly available data, it will be easy to produce a solution. If I understood the problem correctly, you want to use data.frame column as a base for UI element, which is not difficult on its own.
.