【问题标题】:organize reactivity with pre-loaded data使用预加载的数据组织反应性
【发布时间】:2020-01-10 17:37:25
【问题描述】:

我有几个表存储在一个单独的 txt 文件中,并且还合并在一个 Rdat 文件中,如下所示:

# generate tables
df.A <- data.frame(colA1=letters[1:20],colA2=LETTERS[1:20], stringsAsFactors = F)
df.B <- data.frame(colB1=rnorm(20),colB2=rnorm(20), stringsAsFactors = F)

# save tables as separate files
write.csv(df.A, 'tableA.txt')
write.csv(df.B, 'tableB.txt')
# I may have more tables

# save all tables in a single Rdat file
save(df.A,df.B, file = 'alldata.RDat')

现在在我闪亮的 Markdown 文档中,我想从 RDat 文件中预加载这些表,但也让用户可以重新加载它们 a) 与单个文件分开,或 b) 完全从 RDat文件。这是我来到的第一个版本:

---
runtime: shiny
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```

```{r}
load('alldata.RDat')
```

Tables below:
```{r}
inputPanel(
  actionButton('load.A','Reload Table A'),
  actionButton('load.B','Reload Table B'),
  actionButton('load.Rdat','Load all from Rdat file')
)

 tabsetPanel(type = "tabs",
                  tabPanel("table A", dataTableOutput("toA")),
                  tabPanel("table B", dataTableOutput("toB"))
      )

 # next 2 lines render pre-loaded tables before any buttons are pressed:
 output$toA  <- renderDataTable(df.A, options=list(pageLength=5))
 output$toB  <- renderDataTable(df.B, options=list(pageLength=5))

 observeEvent(input$load.A, {
   df.A <- read.csv('tableA.txt'); 
   output$toA  <- renderDataTable(df.A, options=list(pageLength=5))
   })

 observeEvent(input$load.B, {
   df.B <- read.csv('tableB.txt'); 
   output$toB  <- renderDataTable(df.B, options=list(pageLength=5))
   })

 observeEvent(input$load.Rdat, {
   load('alldata.RDat', verbose = T);
   output$toA  <- renderDataTable(df.A, options=list(pageLength=5));
   output$toB  <- renderDataTable(df.B, options=list(pageLength=5))
 })

```

它工作得很好,但我觉得这不是使用反应性的正确方法。例如,每次更新我的表对象(df.AdfB)后,我都必须显式调用render output$toA &lt;- renderDataTable(...)(每个表3 次!)。

我想到的一个解决方案是将我的表格放入reactiveValues(),它给出了以下内容:

---
runtime: shiny
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```

```{r}
load('alldata.RDat')
```

Tables below:
```{r}
inputPanel(
  actionButton('load.A','Reload Table A'),
  actionButton('load.B','Reload Table B'),
  actionButton('load.Rdat','Load all from Rdat file')
)

 tabsetPanel(type = "tabs",
                  tabPanel("table A", dataTableOutput("toA")),
                  tabPanel("table B", dataTableOutput("toB"))
      )

 rv <- reactiveValues(df.A = df.A, df.B = df.B)

 output$toA  <- renderDataTable(rv$df.A, options=list(pageLength=5))
 output$toB  <- renderDataTable(rv$df.B, options=list(pageLength=5))

 observeEvent(input$load.A, {
   rv$df.A <- read.csv('tableA.txt'); 
   })

 observeEvent(input$load.B, {
   rv$df.B <- read.csv('tableB.txt'); 
   })

 observeEvent(input$load.Rdat, {
   load('alldata.RDat', verbose = T);
   rv$df.A <- df.A;
   rv$df.B <- df.B;
 })

```

这看起来更好,因为我的代码中每个 renderDataTable() 都只有一个调用。但是现在我的每个表都存储在内存中两次——一个在df.A(从'alldata.RDat'加载)和另一个在rv$df.A...这是否有意义,或者有更犹太的方式解决这些问题?

【问题讨论】:

    标签: r shiny reactive shiny-reactivity


    【解决方案1】:

    您想从 csv 或 Rdat 文件加载 df.Adf.B 的数据。这几乎肯定需要一个中间变量 rv,正如您正确创建的那样。

    您创建的第二个示例比第一个要好得多,我们必须避免对同一个 output 进行多次渲染调用,尤其是在观察者内部进行渲染调用。

    下面的代码从观察者内部执行load('alldata.RDat', verbose = T)rv 的初始值是在观察者内部设置的,而不是使用全局load('alldata.RDat', verbose = T)。这样可以防止数据被加载两次。

    ---
    runtime: shiny
    output: html_document
    ---
    
    ```{r setup, include=FALSE}
    knitr::opts_chunk$set(echo = FALSE)
    ```
    
    ```{r}
    #load('alldata.RDat')
    ```
    
    Tables below:
    ```{r}
    inputPanel(
      actionButton('load.A','Reload Table A'),
      actionButton('load.B','Reload Table B'),
      actionButton('load.Rdat','Load all from Rdat file')
    )
    
     tabsetPanel(type = "tabs",
                      tabPanel("table A", dataTableOutput("toA")),
                      tabPanel("table B", dataTableOutput("toB"))
          )
    
     rv <- reactiveValues(df.A = NULL, df.B = NULL)
    
     output$toA  <- renderDataTable({
       req(rv$df.A)
       rv$df.A
       }, options=list(pageLength=5))
    
     output$toB  <- renderDataTable({
       req(rv$df.B)
       rv$df.B
       }, options=list(pageLength=5))
    
     observeEvent(input$load.A, {
       rv$df.A <- read.csv('tableA.txt'); 
       })
    
     observeEvent(input$load.B, {
       rv$df.B <- read.csv('tableB.txt'); 
       })
    
     observeEvent(c(input$load.Rdat), {
       load('alldata.RDat', verbose = T);
       rv$df.A <- df.A;
       rv$df.B <- df.B;
     })
    ```
    

    【讨论】:

    • 谢谢@Sada93!对不起我的无知 - 你能详细说明为什么我们需要像req(rv$df.A) 这样的电话吗?我检查了如果我删除这两个调用会发生什么,它似乎以相同的方式工作......
    • 另一个问题 - 将input$load.Rdat 包装成c() 如何在observeEvent() 中工作?我知道即使没有按下按钮 load.Rdat,它也会触发代码加载 - 但它是如何产生这种差异的?
    • req() 只是一种习惯力量,以确保在继续之前存在值。您可以删除它们,但将它们保留在那里是安全的。关于你的第二个问题......不幸的是,我不明白它的机制。我也不知道这是否是闪亮的作者的预期行为。您介意我使用此示例的修改版本在 github 上打开问题吗?
    • 当然,去吧!我想把它作为一个问题在这里发布,但如果你认为它值得一个 GH 问题 - 为什么不呢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-24
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    • 2020-11-25
    • 2021-02-27
    相关资源
    最近更新 更多