Subsetting a reactive dataframe in R shiny based on TRUE/FALSE values in one column.

huangapple go评论72阅读模式
英文:

Subsetting a reactive dataframe in R shiny based on TRUE/FALSE values in one column

问题

我有一个可编辑且可扩展的名为Dat()的数据帧,用户可以在其中输入文本和复选框。我想要的是将Dat()中标记复选框的任何行子集化为名为habit_Dat()的另一个数据帧。然后,仅选择habit_Dat()的特定列。即使在任何行中选中Habituation复选框后,第二个数据帧存在但为空。

在这种情况下有一个注意点是复选框状态的引用方式。由于该应用的一个特性是使用numericInput动态增加Dat()中的行数,因此复选框必须以比Shiny中通常更复杂的方式添加。因此,为了引用复选框是否被标记,我引用数据帧Dat()本身,因为它给出了逻辑值。

我不确定我做错了什么,因为我尝试的似乎已经被其他人在SO上建议过。然而,显然我做错了一些事情。有没有人有可能有帮助的想法?

谢谢!

英文:

I have an editable and scalable data.frame called Dat(), in which users can input text and checkboxes. What I would like is to have any rows in Dat() with marked checkboxes in the Habituation column to be subsetted into another data.frame called habit_Dat(). Then, only certain of those columns are selected for habit_Dat(). The second data.frame exists but is empty, even after checking the Habituation checkbox in any of the rows.

One catch to this situation is the way in which the status of the checkboxes is referred to. Since one feature of this app is to dynamically increase the number of rows in Dat() using a numericInput, the checkboxes had to be added in a more complicated way than usual in Shiny. Therefore, to reference whether the checkboxes are marked, I reference the data.frame Dat() itself, since it gives a logical value.

I'm unsure what I am doing wrong because it seems like what I am trying has been suggested by others on SO. Nonetheless, I'm obviously doing something wrong. Does anyone have ideas that might be helpful?

Thank you!

library(shiny)
library(tidyverse)
library(DT)

#This adds checkboxes for every row, as many rows are in the dataframe
checkboxColumn <- function(len, col, ...) { # `col` is the column index
  inputs <- character(len)
  for(i in seq_len(len)) {
    inputs[i] <- as.character(
      checkboxInput(paste0("checkb_", col, "_", i), label = NULL, ...)
    )
  }
  inputs
}

#This is where the magic for the checkboxes is done
js <- function(dtid, cols, ns = identity) {
  code <- vector("list", length(cols))
  for(i in seq_along(cols)) {
    col <- cols[i]
    code[[i]] <- c(
      sprintf(
        "$('body').on('click', '[id^=checkb_%d_]', function() {",
        col),
      "  var id = this.getAttribute('id');",
      sprintf(
        "  var i = parseInt(/checkb_%d_(\\d+)/.exec(id)[1]);",
        col),
      "  var value = $(this).prop('checked');",
      sprintf(
        "  var info = [{row: i, col: %d, value: value}];",
        col),
      sprintf(
        "  Shiny.setInputValue('%s', info);",
        ns(sprintf("%s_cell_edit:DT.cellInfo", dtid))
      ),
      "});"
    )
  }
  do.call(c, code)
}

ui <- fluidPage(
  
  
  numericInput("num_exps", 
               label = "Enter the number of experimental phases:",
               min = 1,
               max = 96,
               value = 1),
  
  DT::dataTableOutput('phasesTable'),
  
  br(),
  br(),
  
  verbatimTextOutput("reactive_verbatim"),
  br(),
  DT::dataTableOutput('habit_table'),
  br(),
  verbatimTextOutput("habit_verbatim"),
  
  
  
)


server <- function(input, output, session) {
  
  ##### Number output for number of conditions #####
  output$value <- renderPrint({ input$num_exps })
  
  #Putting each phase and its duration into a data frame
  phases_df <- reactive({data.frame(
    'Phase Number' = sapply(1: as.integer(input$num_exps), function(i) { i }),
    'Phase Comparison Groups' = 0,
    'Phase Name' = "",
    Duration = 0
  )
  })
  
  #calculating the start and end times for each phase, and adding them to phases_df as timings
  timings <- reactive({
    req(phases_df()) %>% 
      mutate(Duration = 0) %>%
      mutate(End = cumsum(Duration)) %>%
      mutate(Start = End - Duration) %>%
      relocate(Start, .before = End)
  })
  
  #This adds the columns into which the checkboxes will be added
  dat1 <- reactive({
    cbind(timings(), 
          'Linear Regression' = FALSE, 
          'Habituation' = FALSE, 
          'Freezing Index' = FALSE)
  })
  
  #This uses the checkboxColumn function to add check boxes to the declared
  #columns 
  dat2 <- reactive({cbind(
    timings(),
    'Linear Regression' = checkboxColumn(nrow(phases_df()), 7),
    'Habituation' = checkboxColumn(nrow(phases_df()), 8),
    'Freezing Index' = checkboxColumn(nrow(phases_df()), 9)
  )})
  
  # Convert dat1 reactive to data frame and set to a reactiveVal
  Dat = reactiveVal()
  
  observe({
    d = dat1()
    d = as.data.frame(d)
    Dat(d)
  })
  
  checkboxesColumns_list <- c(0, 1, 5:9)
  
  output[["phasesTable"]] <- renderDT({
    datatable(
      dat2(), 
      escape = FALSE,
      editable = list(
        target = "cell", disable = list(columns = checkboxesColumns_list)
      ),
      options = list(paging = FALSE,
                     ordering = FALSE,
                     scrollx = FALSE,
                     searching = FALSE,
                     stringsAsFactors = FALSE,
                     info = FALSE,
                     columnDefs = list(list(width = '200px', targets = "_all"))),
      selection = "none",
      callback = JS(js("phasesTable", checkboxesColumns_list))
    )
  }, server = FALSE)
  
  observeEvent(input[["phasesTable_cell_edit"]], { 
    info <- input[["phasesTable_cell_edit"]] 
    
    Dat(editData(Dat(), info))
  })
  
  output[["reactive_verbatim"]] <- renderPrint({ 
    str(Dat())
  })
  
  #This creates a subset of the phase data.frame for only the phases that
  #habituation were selected for
  habit_Dat <- reactive({
    req(Dat()) %>%
      subset('Habituation' == TRUE) %>%
      select(c(1:6))
  })
  
  output[['habit_table']] <- renderDT({
    datatable(
      habit_Dat(),
      #escape = FALSE,
      editable = list(
        target = 'cell', disable = list(columns = '0, 1, 5, 6')),
      options = list(paging = FALSE,
                     ordering = FALSE,
                     scrollx = FALSE,
                     searching = FALSE,
                     stringsAsFactors = FALSE,
                     info = FALSE,
                     columnDefs = list(list(width = '200px', targets = "_all"))),
      selection = 'none')
  })
  
  output[["habit_verbatim"]] <- renderPrint({ 
    str(habit_Dat())
  })
  
}
shinyApp(ui, server)

答案1

得分: 1

主要问题是您在 subset 中传递的是字符串,而不是实际的变量名。因此,逻辑表达式 "Habituation" == TRUE 将始终返回 FALSE,从而返回一个空表。要纠正这个问题,只需删除引号。

说到这一点,我建议使用 filter 而不是 subset,如果您尝试更加符合 dplyr 风格的话。

  habit_Dat <- reactive({
    req(Dat()) %>%
      filter(Habituation == TRUE) %>%
      select(1:6)
  })
英文:

The main issue is you're passing in a string not the actual variable name in subset. So the logical expression, "Habituation" == TRUE will always return FALSE thus returning an empty table. To correct it, just remove the quotes.

With that said, I'd recommend using filter instead of subset. If you're trying to be more dplyr-centric.

  habit_Dat &lt;- reactive({
req(Dat()) %&gt;%
subset(Habituation == TRUE) %&gt;%
select(1:6)
})

huangapple
  • 本文由 发表于 2023年6月1日 05:35:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76377444.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定