r - 如何在 Shiny 应用程序中将数据记录到表中
问题描述
我有 numericInput 和 tableOutput 小部件。目前,在框中键入的任何数字都打印在同一行的表格中。我想做的是在新行上打印每个新条目,以便表格自动扩展(变高)并记录所有条目。我不一定需要使用闪亮包中的表格。例如,对于 DT 或 Formattable 包中的表,解决方案会有所不同吗?代表是:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput(inputId = "num", label = h3("Enter value:"), value = "")
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output) {
output$table <- renderTable({
paste("Value is:", input$num, sep = "\n")
},
bordered = TRUE)
}
shinyApp(ui = ui, server = server)
谢谢!
解决方案
您首先需要解决如何添加到data.frame
. 我将预先说明 R 中不断增长的对象 ( data.frame
s) 在内存方面效率低下:每次添加一行时,整个帧都会复制到内存中。有一段时间(直到 R 进行一些内存管理和垃圾收集),内存中有两个完整的帧副本,无论是 2 行还是 2000 万行。对于小数字,这很好,但扩展性很差。尽管如此,我还是建议使用此解决方案,因为我相信您使用的强文本将是小规模的,并且可能不会成为问题。但是,如果您打算将其应用于其他更大的东西,请记住这一点;不仅内存不好,而且每个副本(有很多行)都会比上一个慢。
有两种方法可以将 a 扩展data.frame
一行或多行:(rbind
具有data.frame
S3 方法)或只是重建它。下面的两个mylog
函数演示了这两个:给定先前的数据(或NULL
第一次)和一个条目,它返回一个增强的帧。
mylog1 <- function(dat, entry) {
if (is.null(dat)) dat <- data.frame(timestamp = character(0), entry = integer(0))
rbind.data.frame(dat, data.frame(
timestamp = rep(format(Sys.time(), format = "%H:%M:%S"), length(entry)),
entry = entry,
stringsAsFactors = FALSE
), stringsAsFactors = FALSE)
}
mylog2 <- function(dat, entry) {
if (is.null(dat)) dat <- data.frame(timestamp = character(0), entry = integer(0))
data.frame(
timestamp = c(dat$timestamp, rep(format(Sys.time(), format = "%H:%M:%S"), length(entry))),
entry = c(dat$entry, entry),
stringsAsFactors = FALSE
)
}
library(shiny)
shinyApp(
ui = fluidPage(
sidebarLayout(
sidebarPanel(
numericInput(inputId = "num", label = h3("Enter value:"), value = "")
),
mainPanel(
tableOutput("table")
)
)
),
server = function(input, output) {
mydata <- reactiveVal( mylog1(NULL, integer(0)) )
num_debounced <- debounce(reactive(input$num), 3000)
observeEvent(num_debounced(), {
req(input$num)
dat <- mylog1(mydata(), num_debounced())
mydata(dat)
})
output$table <- renderTable({
req(mydata())
},
bordered = TRUE)
}
)
笔记:
每当我有一个应该始终具有相同结构(列的数量和名称)的“日志”或类似表时,我发现最好将它定义在一个地方,通常是一个辅助函数。这绝对不是必需的,但是如果/当您更改表格的格式并错过插入其中的一个位置时,您就会明白我的意思。当它的格式在多个地方增加时,格式化时很容易漏掉一个。就我而言,更改表格布局的唯一地方是函数本身。
输入
num
字段可能过于“响应”,因为一旦有人放慢打字速度,它的值就会被读取和使用。我发现这在用户界面中可能会出现问题,所以我补充说shiny::debounce
:在用户停止输入字段一段时间(此处为 3 秒)之前,输入字段不被视为可用。我认为 3 秒对于这个演示来说太长了,但我认为它明白了这一点。如果要 1 秒,将 3000 更改为 1000。如果要删除它,请将其全部更改num_debounced()
为input$num
并删除该debounce
行代码。几乎总是(实际上,我想不出一个可行的例外)将数据反应式与渲染反应式分开。也就是说,我认为您不应该尝试在诸如
renderTable
. 最好在其他地方(在它自己的反应块中)制定数据,然后在渲染函数中使用该表。这有几个原因,主要(对我而言)是我通常想在多个地方使用该数据,无论是用于渲染还是仅用于引用(这个原因在这个应用程序中并不明显,但我还是这样做了)。另一个原因是稍微简化了渲染反应锁。我
req(...)
习惯req
认为价值观是“真实的”(不是NULL
等)。例如,如果没有它,日志表将从具有空条目的行开始,因为它在第一次射击时触发。此外,如果用户输入某些内容(添加一行)然后删除该条目,这将防止添加空行。要查看它是如何工作的,请删除req(input$num)
然后添加一个条目,然后清空输入字段。
推荐阅读
- elasticsearch - 简单的弹性搜索正则表达式
- database - VB6 Recordset,将记录存储到数据库末尾
- kubernetes - Kubernetes - kubectl 获取从节点调用的 pod
- node.js - 带有 typeorm 的模块外的节点导入错误
- javascript - Google AppScripts 将电子邮件发送到单元格值或仅单击按钮
- spring-boot - Spring JPA @ManyToOne 和 @OneToMany 不会全部更新
- sql - 复式记账如何办理入账?
- proxy - 针对特定 Proxy 使用 JMeter Proxy 来记录请求
- reactjs - 样式化 MTableToolbar 元素
- angular - 在 Angular 9 中重置单选按钮状态