UPDATE (18-Dec-2015) The currest best apporach for this problem is here: Get Selected Row From DataTable in Shiny App
======================
I am trying to reactively re-display a dataset using a row selection provided by the user. As a toy example,
ui.R
library(shiny)
shinyUI(pageWithSidebar(
headerPanel('Examples of DataTables'),
sidebarPanel(
checkboxGroupInput('show_vars', 'Columns to show:', names(mtcars),
selected = names(mtcars))
),
mainPanel(
dataTableOutput("mytable")
)
)
)
server.R
library(shiny)
shinyServer(function(input, output) {
# sorted columns are colored now because CSS are attached to them
output$mytable = renderDataTable({
addRadioButtons <- paste0('<input type="radio" name="row', 1:nrow(mtcars), '" value="', 1:nrow(mtcars), '">',"")
#Display table with radio buttons
cbind(Pick=addRadioButtons, mtcars[, input$show_vars, drop=FALSE])
}, options = list(bSortClasses = TRUE, aLengthMenu = c(5, 25, 50), iDisplayLength = 25))
})
I would like to use the radio button to collect the row number (say, row 4 in image above) from the user using some reactive expression, say rowSelect()
, and then dynamically re-display the table following some operation on the dataset, for example,
mtcars[mtcars[,1] > mtcars[rowSelect(),1], input$show_vars, drop=FALSE]
which would drop certain rows dynamically every time the user selects a row.
UPDATE (7-feb-14)
Incorporating the changes suggested by @Julien and implementing the method suggested by @Vincent, server.R becomes:
library(shiny)
shinyServer(function(input, output) {
#reactive row-selection
rowSelect <- reactive({
if(is.null(input[["row"]])) 1 #initialize
else as.numeric(input[["row"]])})
# User-selected sorting of dataset
output$mytable = renderDataTable({
addRadioButtons <- paste0('<input type="radio" name="row" value="', 1:nrow(mtcars), '">')
cbind(Pick=addRadioButtons, mtcars[order(mtcars[,1] - rnorm(nrow(mtcars), mtcars[rowSelect(),1])), input$show_vars, drop=FALSE])
}, options = list(bSortClasses = TRUE, aLengthMenu = c(5, 10, 20), iDisplayLength = 10))
})
As guessed by @agstudy, it was important to keep the number of rows in the datatable the same as before (not subsetting), hence the weird sorting operation above order(mtcars[,1] - rnorm(nrow(mtcars), mtcars[rowSelect(),1]))
.
UPDATE 2 (7-feb-14)
Anyway, this exercise does reveal a flaw in my example. My original intent was to use a separate covariance/similarity matrix to get the sorting order for the display datatable based on the user-selected row. Since the matrix and table would be very large, it didn't make sense to include them as drop-down input list or any other radiobutton input method in the side panel. It had to come from user-selection after displaying the whole datatable.
To achieve this, just replace the value
in the radiobutton with the unique row ID. In server.R, using the rowSelect()
expression (data
is the similarity matrix and meta
is the datatable displayed, ID
is the unique row identifier shared by data
and meta
):
addRadioButtons <- paste0('<input type="radio" name="row" value=', meta[order(data[rowSelect(),]),"ID"], '>')
cbind(Pick=addRadioButtons, meta[order(data[rowSelect(),]),input$show_vars])
This would keep resorting the datatable based on the user-selected row while choosing the order based on the similarity matrix via a unique row ID. Hope this helps someone.
But, if I use the rowSelect
reactive expression, then the row-selection based on a sorted datatable would not give me the correct row-selection for the similarity matrix (the order would have changed for the dataset but not for the matrix - recursion issue). This means that I would need to collect something other than a radio input of row number -- something more like an identifying row ID (which would match for both the table -- one of the columns -- and the matrix) using a form or a click submission -- along the lines of this: Selecting all text in HTML text input when clicked
Thanks for the help. @Vincent, your App is very cool. Hope to get there eventually. I will accept your answer.
Not an answer but a longish comment: I think you could do this but it would get a bit messy. Once some selects a radio button and a subset of the data is shown how do you go back to showing all the data? Would this only apply to the first column? Are you going to recreate all the radio buttons when the data has fewer rows? renderDataTable will (at some point) have the option to include something like '> 20' in the boxes below each column. In the meantime you might be better off trying to subset the data with a textInput. As an example, see Data > View in this app. If that is not what you are looking for let me (us) know what you are trying to achieve.