首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用DT和R Shiny进行持久选择

使用DT和R Shiny进行持久选择
EN

Stack Overflow用户
提问于 2017-08-09 22:05:14
回答 2查看 838关注 0票数 1

我有一个很棒的用例,我想让用户通过选择列并查看某些汇总统计信息来过滤数据。这个想法是为了让他们能够快速深入到更细粒度的组并查看结果。它工作得很好,除非用户在更高的级别进行了选择,然后所有的过滤器和选择都会被重置,需要重新选择。我在使这些过滤器持久化和仅在某些情况下更新方面遇到了一些麻烦。

例如,用户希望查看瑞士和德国工程师(1级)和德国(2级)的收入中位数并按年龄(3级)显示。他们将根据每个表上方的工程师值进行排序,以选择类别,然后在表中选择值,以包括“selectInput”之类的变量,如下图所示。

如果他们想看看"Pilot“如何改变结果,国家过滤器就会消失。我希望所有这些都能留在原地,这就是让我感到兴奋的部分。

对如何解决这个问题有什么想法吗?此示例的代码如下:

服务器:

代码语言:javascript
复制
library(shiny)
library(DT)
library(plyr)
library(dplyr)

# Generate income data

n <- 1000
age <- sample(20:60, n, replace=TRUE)
sex <- sample(c("M", "F"), n, replace=TRUE)
country <- sample(c("US", "CA", "UK", "DE", "CH", "NL"), n, replace=TRUE)
occupation <- sample(c("Engineer", "Doctor", "Retail", "Pilot"), n, replace=TRUE)
income <- sample(20000:120000, n, replace=TRUE)

df <- data.frame(age, sex, country, income, occupation)
categories <- c("None", "age", "sex", "country", "occupation")

shinyServer(function(input, output, session) {

  output$selection_1 <- renderUI({
    selectInput("selection_1", "Level 1 Selection", selected = "None",
                choices = categories)
  })

  output$selection_2 <- renderUI({
    selectInput("selection_2", "Level 2 Selection", selected = "None",
                choices = categories)
  })

  output$selection_3 <- renderUI({
    selectInput("selection_3", "Level 3 Selection", selected = "None",
                choices = categories)
  })

  table_1 <- reactive({
    validate(
      need(input$selection_1 != "None", "Select a variable for aggregation.")
    )
    ddply(df, input$selection_1, summarize,
          Count = length(income),
          Med_Income = median(income))
    })

  output$table_1_agg <- DT::renderDataTable(
    table_1(),
    rownames = TRUE,
    selection = list(selected = "")
    )

  # Get values to match on subsequent tables
  table_1_vals <- reactive({
    table_1()[input$table_1_agg_rows_selected, 1]
  })

  # Filter table 2
  table_2 <- reactive({
    validate(
      need(input$selection_2 != "None", "Select a variable for aggregation.")
    )
    # Filter selected values from table_1
    if(length(table_1_vals())>0){
      sel_1_col <- grep(input$selection_1, names(df))
      df2 <- df[df[,sel_1_col] %in% table_1_vals(),]
    }else{
      df2 <- df
    }
    ddply(df2, input$selection_2, summarize,
          Count = length(income),
          Med_Income = median(income))
  })

  output$table_2_agg <- DT::renderDataTable(
    table_2(),
    rownames = TRUE,
    selection = list(selected = "")
  )

  # Get values to match on subsequent tables
  table_2_vals <- reactive({
    table_2()[input$table_2_agg_rows_selected, 1]
  })

  # Filter table 3
  table_3 <- reactive({
    validate(
      need(input$selection_3 != "None", "Select a variable for aggregation.")
    )
    df3 <- df
    # Filter selected values from table_1
    if(length(table_1_vals())>0){
      sel_1_col <- grep(input$selection_1, names(df))
      df3 <- df3[df3[,sel_1_col] %in% table_1_vals(),]
    }
    if(length(table_2_vals())>0){
        sel_2_col <- grep(input$selection_2, names(df))
        df3 <- df3[df3[,sel_2_col] %in% table_2_vals(),]
    }
    ddply(df3, input$selection_3, summarize,
          Count = length(income),
          Med_Income = median(income))
  })

  output$table_3_agg <- DT::renderDataTable(
    table_3(),
    rownames = TRUE,
    selection = list(selected = "")
  )
})

用户界面:

代码语言:javascript
复制
shinyUI(fluidPage(
  fluidRow(
    column(6,
           uiOutput("selection_1"),
           DT::dataTableOutput("table_1_agg")),
    column(6,
           uiOutput("selection_2"),
           DT::dataTableOutput("table_2_agg"))
  ),
  fluidRow(
    column(6,
           br(),
           uiOutput("selection_3"),
           DT::dataTableOutput("table_3_agg"))
  )
))

谢谢!

EN

回答 2

Stack Overflow用户

发布于 2017-08-10 06:39:40

一种选择是存储选定的行,并在稍后重新绘制表时使用。这可以使用额外的renderUI来放置表的创建,并使用参数selection来指示要选择哪些行。

代码语言:javascript
复制
library(shiny)
library(DT)
library(dplyr)
library(plyr)

# Generate income data

n <- 1000
age <- sample(20:60, n, replace=TRUE)
sex <- sample(c("M", "F"), n, replace=TRUE)
country <- sample(c("US", "CA", "UK", "DE", "CH", "NL"), n, replace=TRUE)
occupation <- sample(c("Engineer", "Doctor", "Retail", "Pilot"), n, replace=TRUE)
income <- sample(20000:120000, n, replace=TRUE)

df <- data.frame(age, sex, country, income, occupation)
categories <- c("None", "age", "sex", "country", "occupation")

ui <- shinyUI(fluidPage(
  fluidRow(
    column(6,
           uiOutput("selection_1"),
           DT::dataTableOutput("table_1_agg")),
    column(6,
           uiOutput("selection_2"),
           uiOutput("table_2_aggUI")
    )
  ),
  fluidRow(
    column(6,
           br(),
           uiOutput("selection_3"),
           uiOutput("table_3_aggUI")
    )
  )
))

server <- shinyServer(function(input, output, session) {

  table2_selected <- NULL
  table3_selected <- NULL

  output$selection_1 <- renderUI({
    selectInput("selection_1", "Level 1 Selection", selected = "None",
                choices = categories)
  })

  output$selection_2 <- renderUI({
    selectInput("selection_2", "Level 2 Selection", selected = "None",
                choices = categories)
  })

  output$selection_3 <- renderUI({
    selectInput("selection_3", "Level 3 Selection", selected = "None",
                choices = categories)
  })

  table_1 <- reactive({
    validate(
      need(input$selection_1 != "None", "Select a variable for aggregation.")
    )
    ddply(df, input$selection_1, summarize,
          Count = length(income),
          Med_Income = median(income))
  })

  output$table_1_agg <- DT::renderDataTable(
    table_1(),
    rownames = TRUE,
    selection = list(selected = "")
  )

  # Get values to match on subsequent tables
  table_1_vals <- reactive({
    table_1()[input$table_1_agg_rows_selected, 1]
  })

  # Filter table 2
  table_2 <- reactive({
    validate(
      need(input$selection_2 != "None", "Select a variable for aggregation.")
    )
    # Filter selected values from table_1
    if(length(table_1_vals())>0){
      sel_1_col <- grep(input$selection_1, names(df))
      df2 <- df[df[,sel_1_col] %in% table_1_vals(),]
    }else{
      df2 <- df
    }
    ddply(df2, input$selection_2, summarize,
          Count = length(income),
          Med_Income = median(income))
  })


  output$table_2_aggUI <- renderUI({
    # to redraw UI if data on table_2() change
    table_2()
    output$table_2_agg <- DT::renderDataTable(
      isolate(table_2()),
      rownames = TRUE,
      selection = list(target = 'row', selected = table2_selected)
    )
    DT::dataTableOutput("table_2_agg")
  })

  # keep record of selected rows
  observeEvent(input$table_2_agg_rows_selected, {
    table2_selected <<- as.integer(input$table_2_agg_rows_selected)
    cat("Table 2 selected:", table2_selected, "\n")
  })

  # Get values to match on subsequent tables
  table_2_vals <- reactive({
    table_2()[input$table_2_agg_rows_selected, 1]
  })

  # Filter table 3
  table_3 <- reactive({
    validate(
      need(input$selection_3 != "None", "Select a variable for aggregation.")
    )
    df3 <- df
    # Filter selected values from table_1
    if(length(table_1_vals())>0){
      sel_1_col <- grep(input$selection_1, names(df))
      df3 <- df3[df3[,sel_1_col] %in% table_1_vals(),]
    }
    if(length(table_2_vals())>0){
      sel_2_col <- grep(input$selection_2, names(df))
      df3 <- df3[df3[,sel_2_col] %in% table_2_vals(),]
    }
    ddply(df3, input$selection_3, summarize,
          Count = length(income),
          Med_Income = median(income))
  })


  output$table_3_aggUI <- renderUI({
    # to redraw UI if data on table_3() change
    table_3()
    output$table_3_agg <- DT::renderDataTable(
      isolate(table_2()),
      rownames = TRUE,
      selection = list(target = 'row', selected = table3_selected)
    )
    DT::dataTableOutput("table_3_agg")
  })

  # keep record of selected rows
  observeEvent(input$table_3_agg_rows_selected, {
    table3_selected <<- as.integer(input$table_3_agg_rows_selected)
    cat("Table 3 selected:", table3_selected, "\n")
  })

})

shinyApp(ui = ui, server = server)
票数 0
EN

Stack Overflow用户

发布于 2017-08-12 00:29:36

您可以通过添加以下功能来实现此目的:

  1. 初始化temp反应变量。在t0时刻,此变量将以NULL或0值开始,但在重新绘制表之前,它将临时捕获表的当前选定行和过滤器选项

prev_selections = reactiveValues(table2 = NULL,prev_rows_t2 = NULL,new_rows_t2 = NULL,filterop_t2 = 0,table3 = NULL,prev_rows_t3 = NULL,new_rows_t3 = NULL,filterop_t3 =0)

  • ,因为您在表N中选择的行将向下筛选表N+1,...您需要先创建下游表的副本,然后再重新绘制它们。使用observeEvent捕获所应用过滤器的表和值(如下表2所示)

observeEvent(input$table_2_agg_rows_selected,{ prev_selections$table2 = table_2() prev_selections$filterop_t2 = input$selection_2 })

  • 为每个表创建第二个observeEvent集合,以在重绘表之前和之后捕获当前选定的行。这个observeEvent集合将由上游表中发生的行选择触发(下面的表2)

observeEvent({input$table_1_agg_rows_selected input$selection_2},{ prev_selections$prev_rows_t2 = isolate(prev_selections$table2input$table_2_agg_rows_selected,) prev_selections$new_rows_t2 = isolate(if ( input$selection_2 == prev_selections$filterop_t2 ){which(table_2(),1%in% prev_selections$prev_rows_t2,1)} else {NULL}) })

  • 使用步骤3中的值作为DT::renderDataTableselection = list(selected = )参数的输入。不要忘记按照HubertL's answer here

DT::renderDataTable内部调用datatable

完整代码如下:

代码语言:javascript
复制
library(shiny)
library(DT)
library(plyr)
library(dplyr)

# Generate income data

n <- 1000
age <- sample(20:60, n, replace=TRUE)
sex <- sample(c("M", "F"), n, replace=TRUE)
country <- sample(c("US", "CA", "UK", "DE", "CH", "NL"), n, replace=TRUE)
occupation <- sample(c("Engineer", "Doctor", "Retail", "Pilot"), n, replace=TRUE)
income <- sample(20000:120000, n, replace=TRUE)

df <- data.frame(age, sex, country, income, occupation)
categories <- c("None", "age", "sex", "country", "occupation")

server <- shinyServer(function(input, output, session) {

  output$selection_1 <- renderUI({
    selectInput("selection_1", "Level 1 Selection", selected = "None",
                choices = categories)
  })

  output$selection_2 <- renderUI({
    selectInput("selection_2", "Level 2 Selection", selected = "None",
                choices = categories)
  })

  output$selection_3 <- renderUI({
    selectInput("selection_3", "Level 3 Selection", selected = "None",
                choices = categories)
  })

  table_1 <- reactive({
    validate(
      need(input$selection_1 != "None", "Select a variable for aggregation.")
    )
    ddply(df, input$selection_1, summarize,
          Count = length(income),
          Med_Income = median(income))
  })

  output$table_1_agg <- DT::renderDataTable(
    table_1(),
    rownames = TRUE,
    selection = list(selected = "")
  )

  # Get values to match on subsequent tables
  table_1_vals <- reactive({
    table_1()[input$table_1_agg_rows_selected, 1]
  })

  # Filter table 2
  table_2 <- reactive({
    validate(
      need(input$selection_2 != "None", "Select a variable for aggregation.")
    )
    # Filter selected values from table_1
    if(length(table_1_vals())>0){
      sel_1_col <- grep(input$selection_1, names(df))
      df2 <- df[df[,sel_1_col] %in% table_1_vals(),]
    }else{
      df2 <- df
    }
    ddply(df2, input$selection_2, summarize,
          Count = length(income),
          Med_Income = median(income))
  })

  output$table_2_agg <- DT::renderDataTable(
    datatable(table_2(),
    rownames = TRUE,
    selection = list(target = 'row', selected = prev_selections$new_rows_t2))
  )

  # Get values to match on subsequent tables
  table_2_vals <- reactive({
    table_2()[input$table_2_agg_rows_selected, 1]
  })

  # Filter table 3
  table_3 <- reactive({
    validate(
      need(input$selection_3 != "None", "Select a variable for aggregation.")
    )
    df3 <- df
    # Filter selected values from table_1
    if(length(table_1_vals())>0){
      sel_1_col <- grep(input$selection_1, names(df))
      df3 <- df3[df3[,sel_1_col] %in% table_1_vals(),]
    }
    if(length(table_2_vals())>0){
      sel_2_col <- grep(input$selection_2, names(df))
      df3 <- df3[df3[,sel_2_col] %in% table_2_vals(),]
    }
    ddply(df3, input$selection_3, summarize,
          Count = length(income),
          Med_Income = median(income))
  })

  output$table_3_agg <- DT::renderDataTable(
    datatable(table_3(),
    rownames = TRUE,
    selection = list(target = 'row', selected = prev_selections$new_rows_t3))
  )


  ## Retain highlighted rows in temp variables and enable persistent filtering

  #initialize temp variables
  prev_selections = reactiveValues(table2 = NULL, prev_rows_t2 = NULL, new_rows_t2 = NULL, filterop_t2 = 0,
                                   table3 = NULL, prev_rows_t3 = NULL, new_rows_t3 = NULL, filterop_t3 = 0)

  #Capture current selections/highlights in Table N
  observeEvent(input$table_2_agg_rows_selected, 
               {
                 prev_selections$table2 = table_2()
                 prev_selections$filterop_t2 = input$selection_2
               })

  observeEvent(input$table_3_agg_rows_selected, 
               {
                 prev_selections$table3 = table_3()
                 prev_selections$filterop_t3 = input$selection_3
               })

  #Observe upstream events (e.g. highlights in Table N-1,...) and enable persistent selection
  #Table 2
  observeEvent({input$table_1_agg_rows_selected
    input$selection_2}, 
    {
      prev_selections$prev_rows_t2 = isolate(prev_selections$table2[input$table_2_agg_rows_selected,][1])
      prev_selections$new_rows_t2 = isolate(if ( input$selection_2 == prev_selections$filterop_t2 ) 
      {which(table_2()[,1] %in% prev_selections$prev_rows_t2[,1])} else {NULL})

    })

  #Table 3
  observeEvent({
    input$table_1_agg_rows_selected
    input$table_2_agg_rows_selected
    input$selection_3
  }, 
  {
    prev_selections$prev_rows_t3 = isolate(prev_selections$table3[input$table_3_agg_rows_selected,][1])
    prev_selections$new_rows_t3 = isolate(if ( input$selection_3 == prev_selections$filterop_t3 ) 
    {which(table_3()[,1] %in% prev_selections$prev_rows_t3[,1])} else {NULL})

  })


})


ui <- shinyUI(fluidPage(
  fluidRow(
    column(6,
           uiOutput("selection_1"),
           DT::dataTableOutput("table_1_agg")),
    column(6,
           uiOutput("selection_2"),
           DT::dataTableOutput("table_2_agg"))
  ),
  fluidRow(
    column(6,
           br(),
           uiOutput("selection_3"),
           DT::dataTableOutput("table_3_agg"))
  )
))

shinyApp(ui = ui, server = server)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45593000

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档