Как записать данные в таблицу в приложении Shiny - PullRequest
0 голосов
/ 24 апреля 2020

У меня есть виджеты 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)

Спасибо!

1 Ответ

1 голос
/ 24 апреля 2020

Сначала нужно решить, как добавить к data.frame. Я предвосхищу это заранее, что растущие объекты (data.frame s) в R неэффективны с точки зрения памяти: каждый раз, когда вы добавляете одну строку, весь кадр копируется в память. На мгновение (пока R не выполнит некоторое управление памятью и сборку мусора), в памяти будет две полные копии фрейма, имеет ли он 2 строки или 20 миллионов строк. Для небольших номеров это хорошо, но плохо масштабируется. Я предлагаю это решение несмотря на это, так как я верю, что сильный текст при вашем использовании будет в небольшом масштабе и, вероятно, не будет проблемой. Однако, если вы планируете применить это к чему-то немного большему, имейте это в виду; он не только плохо работает с памятью, но каждая копия (с большим количеством строк) будет работать медленнее, чем предыдущая.

Существует два способа увеличить 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)
  }
)

sample shiny app

Примечания:

  1. Всякий раз, когда у меня есть «журнал» или подобная таблица, которая всегда должна иметь одинаковую структуру (количество и имена столбцов), я лучше всего определить его в одном месте , обычно это вспомогательная функция. Это абсолютно не обязательно, но если / когда вы когда-нибудь измените формат таблицы и пропустите одно место, которое вставляете в нее, вы поймете, что я имею в виду. Когда его формат увеличивается в нескольких местах, его легко пропустить при форматировании. В моем случае единственное место, где можно изменить расположение таблицы, - это сама функция.

  2. Поле ввода num может быть слишком "отзывчивым" в этом, как только кто-то замедляет набор текста, его значение читается и используется. Я считаю, что это может быть проблематично c в пользовательских интерфейсах, поэтому я добавил shiny::debounce: поле ввода не считается пригодным для использования, пока пользователь не перестал вводить данные в поле в течение некоторого времени (3 секунды здесь). Я думаю, что 3 секунды - это слишком много для этой демонстрации, но я думаю, что это понятно. Если вы хотите 1 секунду, измените 3000 на 1000. Если вы хотите удалить ее, измените все num_debounced() на input$num и удалите строку кода debounce.

  3. It почти всегда (на самом деле, я не могу придумать жизнеспособного исключения) для отделения реактивов данных от реактивов рендеринга. То есть я думаю, что вы не должны пытаться увеличивать данные внутри чего-то вроде renderTable. Лучше сформулировать данные в другом месте (в своем собственном реактивном блоке), а затем использовать эту таблицу в функции рендеринга. Это по нескольким причинам, ведущая (для меня) в том, что я обычно хочу использовать эти данные в нескольких местах, будь то для рендеринга или просто для справки (эта причина не очевидна в этом приложении, но я делаю это в любом случае). Другая причина в том, что это немного упрощает реактивную блокировку рендеринга.

  4. Я использую req(...) до req uire, значения которых будут "правдивыми" (не NULL, et c). Без этого, например, таблица журнала будет начинаться со строки, в которой есть пустая запись, потому что она сработала при первом выстреле. Кроме того, если пользователь вводит что-то (добавляя строку), а затем удаляет запись, это предотвратит добавление пустой строки. Чтобы увидеть, как это работает, удалите req(input$num), затем добавьте запись, затем очистите поле ввода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...