I'm writing a new Shiny app, and I would like to stay within the Hadleyverse by using dplyr commands for data manipulation. I want Shiny to print a table showing only the top 3 observations according to a variable that can be chosen by the user. For example, one user may want to see the top 3 employees in terms of number of awards received, while another will want to see the top 3 in terms of number of award points earned.
In my latest attempt, I have this for ui.R:
library(shiny)
shinyUI(fluidPage(
verticalLayout(
tableOutput("tbl"),
selectInput("top3", label = h4("Quantity"),
choices = list("Number of awards" = "NumberOfAwards",
"Total points awarded" = "TotalAwarded")),
tableOutput("t3")
)))
and this for server.R:
library(dplyr)
library(shiny)
shinyServer(function(input, output) {
employeestats <- read.table(header=TRUE, text='
Employee NumberOfAwards TotalAwarded
Al 3 10
Betty 6 20
Chuck 2 5
Donna 4 15
Ed 0 0
')
output$tbl <- renderTable({
employeestats
},include.rownames=TRUE)
datasetInput <- reactive({employeestats})
output$t3 <- renderTable({
head(datasetInput() %>% arrange(desc(input$top3)),n=3)
},include.rownames=TRUE)
})
Outside of Shiny, the command
head(employeestats %>% arrange(desc(NumberOfAwards)),n=3)
gives the answer that I want for the top 3 award winners. In Shiny, the full table and the selection box are printed without the Top 3 table, and I get the message "Error in eval(substitute(expr), envir, enclos) : cannot arrange column of class 'NULL'". I know that this has something to do with both Shiny and dplyr using nonstandard functions, and that it could make a difference if R sees NumberOfAwards or the character string "NumberOfAwards". I've tried things like deparse(substitute()), using numbers to indicate the columns to arrange by, etc. without success. This doesn't have to be elaborate; for example, I don't care about ties for 3rd place.
Any ideas? Thanks for the help.
The problem is that arrange()
function expects your argument as a symbol
.
However, your input$top3
is a string.
The trick is:
output$t3 <- renderTable({
sorted_stats <- eval(substitute(employeestats %>% arrange(desc(col)),
list(col=as.symbol(input$top3))))
head(sorted_stats,n=3)
},include.rownames=TRUE)
You can use ?substitute
to see how it works.
Short version, it parse the expression:
employeestats %>% arrange(desc(col))
into a parse tree consisting of call
's (functions) and name
's (symbols, constants, objects), allowing us to substitute the components to form a new expression.
To this point, the expression is not evaluated (meaning, the employeestats
is not arrange
d yet).
Here col
doesn't really mean anything. It simply serves as a placeholder.
And by passing list(col=as.symbol(input$top3))
to substitute
, we replace the dummy symbol col
by the actual symbol we would like to arrange by, as.symbol(input$top3)
.
Depending on the current input, this would either be TotalAwarded
or NumberOfAwards
(symbols, not strings anymore).
Finally, the eval()
function evaluated the expression (with col
replaced by the actual symbol), and returns the sorted data.frame
.
You may also want to look at arrange_ (see vignette). Really nice for in Shiny apps.
The 1st option does what you want. The 2nd is cleaner but not exactly what you are looking for. I guess the 3rd option would be ideal but desc_ is not a function. Might be a nice addition :)
input <- list()
input$top3 <- "mpg"
mtcars %>%
arrange_(paste0("desc(",input$top3,")")) %>%
head(n=3)
mtcars %>%
arrange_(input$top3) %>%
tail(n=3)
# desc_ is not a function (yet)
mtcars %>%
arrange(desc_(input$top3)) %>%
head(n=3)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With