【问题标题】:how to make a copy of reactive table in R shiny in `reactiveValues()`如何在 `reactiveValues()` 中制作 R 中的反应表副本
【发布时间】:2021-05-25 17:26:45
【问题描述】:

我正在构建应用程序,用户可以在其中编辑数据表并单击按钮以反映此数据表的不可编辑副本中的更改(在最终项目中,我需要有两个数据集需要手动匹配),但现在这个小 MWE 显示了我在制作可以进行更改的反应表副本时遇到的问题,而无需更改原始反应表的数据。我想让这个应用程序工作,您单击编辑表 dat_joined$data/output$mytable 中的单元格,并且这些更改确实反映在新表 mydf$data/output$table2 中。最初(在进行任何更改之前)执行 mydf$data 需要是 dat_joined$data 的副本这是对这个问题和答案的跟进:how to make a copy of a reactive value in shiny server function


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


# create master dataframe
dat_total <- tibble(ID_1 = 1:10,  names =   letters[1:10],
                    ID_2 = 11:20, names_2 = LETTERS[c(3:5, 1, 2, 6:8, 10, 9)])


shinyApp(
  ui = fluidPage(
    title = 'Radio button and a dropdown manue ',
    sliderInput("n_rows_table", "Number of rows:",
                min = 0, max = 10,
                value = 5),
    actionBttn(
      inputId = "button_1",
      label = "Make tables",
      size = "sm",
      color = "warning"
    ),
    DT::dataTableOutput("mytable"),
    actionBttn(
      inputId = "button_2",
      label = "Process",
      size = "sm",
      color = "success"),
    DT::dataTableOutput("table2")),
  
  server = function(input, output, session) {
    
    # set up reactive values
    dat_left <- reactiveValues(data=NULL)
    dat_right <- reactiveValues(data=NULL)
    dat_joined <- reactiveValues(data=NULL)
    
    
    # create reactive daraframe
    dat <- eventReactive(input$button_1, {
      dat_total[1:input$n_rows_table, ] %>%
        rowid_to_column()})
    
    
    # Split the data into a right and a left set
    
    observe({
      dat_left$data <- dat() %>%
        select(rowid, ID_1, names)
    })
    
    observe({
      dat_right$data <- dat() %>%
        select(rowid,  ID_2, names_2,ID_1)
    })
    
    
    # join these again
    # This is needed because my actual app will
    # be used to manually  match 2 datasets
    observe({
      if (is.null( dat_right$data )) {
        NULL   
      }else{
        dat_joined$data <- left_join(dat_left$data,
                                     dat_right$data,
                                     by = "rowid")
      }
    })
    
    
    # Print the the datasets
    
    output$mytable <- renderDT({
      datatable(dat_joined$data , 
                rownames = F,
                editable = "cell")
    })
    # I want to make a copy of the dat_joined$data dataset into dat$mydf
    # none of these function as expected
    
    #mydf <- reactiveValues(data=isolate(dat_joined$data))
    #mydf <- reactiveValues(data=local(dat_joined$data))
    #mydf <- reactiveValues(data=dat_joined$data)
    #mydf <- reactiveValues(data=NULL)
    
    # This works, but only saves the cells to w
    mydf <- reactiveValues(data=matrix(NA, nrow=10, ncol = 5))
    
    # Ideally the computation only happens when this both an edit is made 
    # and the button is pressed (now I need to press it between every edit)
    
    # validate_event <- reactive({
    #   req(input$mytable_cell_edit) & req(input$button_2)
    # })
    
    
    #observeEvent(input$button_2validate_event(), {  DOes not work
  
      observeEvent(input$button_2,{
      info = input$mytable_cell_edit
      str(info)
      i = info$row
      j = info$col
      v = info$value
      
      mydf$data[i, j] <- DT::coerceValue(v, mydf$data[i, j])
      
    })
    
    
    # print
    output[["table2"]] <- renderDT({
      datatable(mydf$data)
    })
    
  }
)

【问题讨论】:

  • 对于它的工作原理,reactiveValues 是一个类似列表的结构(注意复数 's')。它可以在单个对象中包含所有反应值,只需为条目赋予不同的名称。
  • 您的问题到底出在哪里?
  • 我想让这个应用程序正常工作,您可以在其中单击编辑表 dat_joined$data/output$mytable 中的单元格,并且这些更改确实反映在新表 mydf$data/output$table2 中.最初(在进行任何更改之前)执行 mydf$data 需要是 dat_joined$data 的副本
  • 我不确定我是否完全理解您的问题。您是否希望仅在按下 button_2 时才看到表 1(顶部)中已编辑的更改反映在表 2(底部)中?
  • 是的(这是一方面),但主要是我希望我在顶部表格中所做的更改反映在底部的表格中,同时也允许我(在应用程序的后期阶段) ) 对底部表格进行更改(删除行、重新排序列、将数据从 ID_1 匹配到 ID_2 等),而这些更改不会反映在顶部的表格中。

标签: r shiny datatable


【解决方案1】:

您在顶部表格中所做的任何更改都会在您按下“处理”按钮后反映在底部表格中。试试这个

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


# create master dataframe
dat_total <- tibble(ID_1 = 1:10,  names =   letters[1:10],
                    ID_2 = 11:20, names_2 = LETTERS[c(3:5, 1, 2, 6:8, 10, 9)])


shinyApp(
  ui = fluidPage(
    title = 'Radio button and a dropdown manue ',
    sliderInput("n_rows_table", "Number of rows:",
                min = 0, max = 10,
                value = 5),
    actionBttn(
      inputId = "button_1",
      label = "Make tables",
      size = "sm",
      color = "warning"
    ),
    DT::dataTableOutput("mytable"),
    actionBttn(
      inputId = "button_2",
      label = "Process",
      size = "sm",
      color = "success"),
    DT::dataTableOutput("table2")),
  
  server = function(input, output, session) {
    
    # set up reactive values
    dat_left <- reactiveValues(data=NULL)
    dat_right <- reactiveValues(data=NULL)
    dat_joined <- reactiveValues(data=NULL)
    dfon <- reactiveValues(top=NULL,
                           bottom=NULL)
    
    # create reactive daraframe
    dat <- eventReactive(input$button_1, {
      dat_total[1:input$n_rows_table, ] %>%
        rowid_to_column()})
    
    
    # Split the data into a right and a left set
    
    observe({
      req(dat())
      dat_left$data <- dat() %>%
        dplyr::select(rowid, ID_1, names)
    })
    
    observe({
      req(dat())
      dat_right$data <- dat() %>%
        dplyr::select(rowid,  ID_2, names_2,ID_1)
    })
    
    
    # join these again
    # This is needed because my actual app will
    # be used to manually  match 2 datasets
    observe({
      req(dat())
      if (!is.null( dat_right$data )) {
        dat_joined$data <- left_join(dat_left$data,
                                     dat_right$data,
                                     by = "rowid")
      }
    })
    
    observe({ ###assign your orig data to a reactiveValues object
      req(dat_joined$data)
      if (!is.null(dat_joined$data)) {
        dfon$top <- dat_joined$data 
      }
    })
    
    
    # Print the the datasets
    
    output$mytable <- renderDT({
      datatable(dfon$top, 
                rownames = F,
                editable = "cell")
    })
    
    # Ideally the computation only happens when this both an edit is made 
    # and the button is pressed (now I need to press it between every edit)

    observeEvent(input$mytable_cell_edit, {
      info = input$mytable_cell_edit
      str(info)
      #i = info$row
      #j = info$col + 1  # offset by 1
      #v = info$value
      
      #dfon$top[i, j] <<- DT::coerceValue(v, dfon$top[i, j])
      dfon$top <<- editData(dfon$top, info)
    })
    
    observeEvent(input$button_2,{
      dfon$bottom <- dfon$top
      output$table2 <- renderDT({
        datatable(dfon$bottom)
      })
    })
    
    ## further editing of dfon$bottom is performed below...with...observeEvent(input$table2_cell_edit, {...
    
  }
)

在下面的输出中,我为名称列中的第三个元素输入了cccc,但我没有点击按钮处理。因此,编辑后的单元格不会反映在底部表格中。

【讨论】:

  • 谢谢,这几乎可行,但我尝试编辑任何数字变量(ID_1.x、ID_2 或 ID_1.y),它会崩溃并显示消息 “DT 中的警告::coerceValue(v, dfon$top[i, j]) : 不支持数据类型: tbl_df, tbl, data.frame 警告: 错误: 分配的数据DT::coerceValue(v, dfon$top[i, j]) 必须与现有数据兼容。i 发生错误对于列 ID_2.x 无法将 转换为 。`"
  • 我在收到j = info$col 之前遇到了这个错误。所以,试试j = info$col,而不是j = info$col + 1
  • 也许你需要更新你的包,包括 DT。
  • 我认为它只是强制输出为字符串,即使它最初是数字。我找到了解决办法。
  • 如果renderDT中有rownames=FALSE,则需要j = info$col,否则需要j = info$col + 1
猜你喜欢
  • 1970-01-01
  • 2020-07-07
  • 2019-07-19
  • 2013-04-22
  • 2014-07-03
  • 2012-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多