10

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))

})

Hypothetical Reactive Input from DataTable

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.

Community
  • 1
  • 1
harkmug
  • 2,725
  • 22
  • 26
  • You can't do this with shiny. It is kind of dynamic input. I think here the solution is to embed some `jquery`. See [this](http://stackoverflow.com/questions/15512510/in-rstudio-shiny-wishing-to-customize-a-table) for example. – agstudy Feb 07 '14 at 00:35
  • Why would you not be able to do this in Shiny? input$row1 etc have values that you can access and use to subset the data. Whether this is good way to achieve that goal is another question. – Vincent Feb 07 '14 at 00:46
  • No. it is not input$row1 , it is input$row_i where "i" is unkown ( you should local loop to get it ). – agstudy Feb 07 '14 at 00:49
  • 1
    It's a detail but radios buttons should have all the same name, beacause when a user clicks on a radio-button, it becomes checked, and all other radio-buttons with equal name become unchecked. If you want several choices it's not radios but checkboxes that you need, (and they should have the same `names` too). – Julien Navarre Feb 07 '14 at 05:53

1 Answers1

1

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.

Vincent
  • 5,063
  • 3
  • 28
  • 39
  • I don't see any relation between your example and the Op request. – agstudy Feb 07 '14 at 00:32
  • OP seems to want to subset data based on mpg value. In data > view there is a textInput that allow you to enter something like mpg > 20 (or for the diamonds data in the app price > 5000). If I am wrong about OP's intentions I can remove the reference. – Vincent Feb 07 '14 at 00:36
  • 1
    The main question here is how to detect which radio buttons is selected ( it is a gui problem not a data one) , what you do with the selected rows is just a use case or an example. This is how I read the question but I can be wrong. – agstudy Feb 07 '14 at 00:42
  • 1
    Adding observe({ for(i in 1:nrow(mtcars)) print(input[[paste0("row",i)]]) }) to server.R will show which radio buttons are selected in the R-terminal. Lets wait and see what OP is looking for :) – Vincent Feb 07 '14 at 00:49
  • Thanks for the comments. Sorry, had stepped out for a bit. @agstudy is correct the operation once the row is identified is just an example. I will try the `observe` and get back ASAP. – harkmug Feb 07 '14 at 01:31
  • @Vincent did you test this `observe({ for(i in 1:nrow(mtcars)) print(input[[paste0("row",i)]]) })` ? This will not work because of the `for loop`. The value of i in the `reactive` will be the same across all instances, you should At least put your loop statement within `local` and I am not sure that this will be sufficient.. – agstudy Feb 07 '14 at 23:12