динамически добавлять строки в rhandsontable в блестящем и R - PullRequest
0 голосов
/ 02 июня 2018

Я пытаюсь создать приложение, которое в конечном итоге нуждается в среднем и сд. Концентрации белка в логарифмическом масштабе .Поскольку значения логарифмической шкалы почти никогда не сообщаются, я нашел ссылки, которые позволяют мне приблизить логарифмическую шкалу, используя общедоступные данные (среднее значение + sd, медиана + диапазон, медиана + IQR, сводка по 5 баллам и т. Д.).

Пользователи будут вводить данные, используя таблицу, в настоящее время реализованную с использованием rhandsontable, пока я не добавлю достаточно обработки ошибок для размещения файлов CSV, и я хочу ограничить столбцы, отображаемые в этой таблице, чтобы они не перегружали.Это я сделал, как видно из следующего воспроизводимого примера.

library(shiny)
library(rhandsontable)
library(tidyverse) 

make_DF <- function(n) {
  DF <- data_frame(
    entry = 1:n,
    protein = NA_character_,
    MW = NA_real_,
    n = NA_integer_,
    mean = NA_real_,
    sd = NA_real_,
    se = NA_real_,
    min = NA_real_,
    q1 = NA_real_,
    median = NA_real_,
    q3 = NA_real_,
    max = NA_real_,
    log_mean = NA_real_,
    log_sd = NA_real_,
    log_min = NA_real_,
    log_q1 = NA_real_,
    log_median = NA_real_,
    log_q3 = NA_real_,
    log_max = NA_real_,
    units = factor("ng/mL", levels  = c("pg/mL", "ng/mL", 'mcg/mL', 'mg/mL', 'g/mL')
    )
  )
  DF[-1]
}

ui <- fluidPage(
  tabPanel("Input", 
  column(4,
    wellPanel(
      checkboxGroupInput("data_format",
        "The data consists of",
        c("Mean and standard deviation" = "mean_sd",
          "Mean and standard error" = "mean_se",
          "Mean and standard deviation (log scale)" = "log_mean_sd",
          "Mean and standard error (log scale)" = "log_mean_se",
          "Median, min, and max" =  "median_range",
          "Median, Q1, and Q3" = 'median_iqr',
          "Five point summary" = 'five_point'
          # "Other combination" = 'other')
        )
      ),
      # p("Please note that selecting 'other' may result in invalid combinations."),
      # titlePanel("Number of Entries"),
      numericInput("n_entries",
        "Number of Concentrations to estimate:",
        value = 1,
        min = 1),
      actionButton("update_table", "Update Table")
    )
  ),
  column(8,
    rHandsontableOutput("input_data") )
),
  tabPanel("Output",
    column(12,
      tableOutput("test_output")
    )
  )
)

server <- function(input, output) {
  # create or update the data frame by adding some rows
  DF <- eventReactive(input$update_table, {
    DF_new <- make_DF(input$n_entries)

    # if a table does not already exist, this is our DF
    if (input$update_table == 1) {
      return(DF_new)
    } else { # otherwise, we will append the new data frame to the old.

      tmp_df <- hot_to_r(input$input_data)
      return(rbind(tmp_df, DF_new))
    }
  })

  # determine which variables to show based on user input
  shown_variables <- eventReactive(input$update_table, {
    unique(unlist(lapply(input$data_format, function(x) {
      switch(x,
        "mean_sd" = c('mean', 'sd'),
        "mean_se" = c('mean', 'se'),
        'log_mean_sd' = c("log_mean", 'log_sd'),
        "log_mean_se" = c('log_mean', 'log_se'),
        "median_range" = c('median','min', 'max'),
        'median_IQR' = c("median", 'q1','q3'),
        "five_point" = c('median', 'min', 'q1', 'q3', 'max'))
    })))
  })

  # # finally, set up table for data entry
  observeEvent(input$update_table, {
    DF_shown <- DF()[c('protein', 'MW', 'n', shown_variables(), "units")]
    output$test_output <- renderTable(DF())
    output$input_data <- renderRHandsontable({rhandsontable(DF_shown)})
  })
}

shinyApp(ui = ui, server = server)

Я также хочу иметь возможность динамически изменять, какие поля отображаются без потери данных.Например, предположим, что пользователь вводит данные для 5 белков, где доступны среднее значение и sd.Затем у пользователя есть еще 3, где сообщается о медиане и диапазоне.Если пользователь отменит выбор среднего значения / sd при выборе медианы / диапазона, текущий рабочий код потеряет среднее значение и стандартное отклонение.В контексте того, что я делаю сейчас, это означает, что мне нужно эффективно выполнить rbind, используя DF() и только что запрошенные строки.Это приводит меня к ошибкам:

# infinite loop error
server <- function(input, output) {
  # create or update the data frame by adding some rows
  DF <- eventReactive(input$update_table, {
    DF_new <- make_DF(input$n_entries)

    # if a table does not already exist, this is our DF
    if (input$update_table == 1) {
      return(DF_new)
    } else { # otherwise, we will append the new data frame to the old.

      tmp_df <- hot_to_r(input$input_data)
      return(rbind(DF(), DF_new))
    }
  })

  # determine which variables to show based on user input
  shown_variables <- eventReactive(input$update_table, {
    unique(unlist(lapply(input$data_format, function(x) {
      switch(x,
        "mean_sd" = c('mean', 'sd'),
        "mean_se" = c('mean', 'se'),
        'log_mean_sd' = c("log_mean", 'log_sd'),
        "log_mean_se" = c('log_mean', 'log_se'),
        "median_range" = c('median','min', 'max'),
        'median_IQR' = c("median", 'q1','q3'),
        "five_point" = c('median', 'min', 'q1', 'q3', 'max'))
    })))
  })

  # # finally, set up table for data entry
  observeEvent(input$update_table, {
    DF_shown <- DF()[c('protein', 'MW', 'n', shown_variables(), "units")]
    output$test_output <- renderTable(DF())
    output$input_data <- renderRHandsontable({rhandsontable(DF_shown)})
  })
}

Я видел других людей с похожими проблемами (например, Добавить реактивный фрейм данных в блестящем R ), но, похоже, нетпринятого ответа пока нет.Есть идеи о решениях или обходных решениях?Я открыт для любых идей, которые позволяют пользователям ограничивать видимые поля, но сохраняют все введенные данные независимо от того, отображаются ли они на самом деле.

1 Ответ

0 голосов
/ 04 июня 2018

Благодаря Джо Ченгу и Хао Ву и их ответам на github (https://github.com/rstudio/shiny/issues/2083), решение состоит в том, чтобы использовать функцию reactiveValues для хранения фрейма данных. Как я понимаю их объяснение, проблема возникает из-за (в отличие от традиционных фреймов данных), фрейм реактивных данных DF() никогда не заканчивает вычисление.

Вот рабочее решение на основе их ответов:

library(shiny)
library(rhandsontable)
library(tidyverse) 

make_DF <- function(n) {
  DF <- data_frame(
    entry = 1:n,
    protein = NA_character_,
    MW = NA_real_,
    n = NA_integer_,
    mean = NA_real_,
    sd = NA_real_,
    se = NA_real_,
    min = NA_real_,
    q1 = NA_real_,
    median = NA_real_,
    q3 = NA_real_,
    max = NA_real_,
    log_mean = NA_real_,
    log_sd = NA_real_,
    log_min = NA_real_,
    log_q1 = NA_real_,
    log_median = NA_real_,
    log_q3 = NA_real_,
    log_max = NA_real_,
    units = factor("ng/mL", levels  = c("pg/mL", "ng/mL", 'mcg/mL', 'mg/mL', 'g/mL')
    )
  )
  DF[-1]
}

ui <- fluidPage(
  tabPanel("Input", 
    column(4,
      wellPanel(
        checkboxGroupInput("data_format",
          "The data consists of",
          c("Mean and standard deviation" = "mean_sd",
            "Mean and standard error" = "mean_se",
            "Mean and standard deviation (log scale)" = "log_mean_sd",
            "Mean and standard error (log scale)" = "log_mean_se",
            "Median, min, and max" =  "median_range",
            "Median, Q1, and Q3" = 'median_iqr',
            "Five point summary" = 'five_point'
            # "Other combination" = 'other')
          )
        ),
        # p("Please note that selecting 'other' may result in invalid combinations."),
        # titlePanel("Number of Entries"),
        numericInput("n_entries",
          "Number of Concentrations to estimate:",
          value = 1,
          min = 1),
        actionButton("update_table", "Update Table")
      )
    ),
    column(8,
      rHandsontableOutput("input_data") )
  ),
  tabPanel("Output",
    column(12,
      tableOutput("test_output")
    )
  )
)

server <- function(input, output) {
  # create or update the data frame by adding some rows
  values <- reactiveValues()

  observeEvent(input$update_table, {

    # determine which variables to show based on user input
    values$shown_variables <- unique(unlist(lapply(input$data_format, function(x) {
      switch(x,
        "mean_sd" = c('mean', 'sd'),
        "mean_se" = c('mean', 'se'),
        'log_mean_sd' = c("log_mean", 'log_sd'),
        "log_mean_se" = c('log_mean', 'log_se'),
        "median_range" = c('median','min', 'max'),
        'median_IQR' = c("median", 'q1','q3'),
        "five_point" = c('median', 'min', 'q1', 'q3', 'max'))
    })))

    # if a table does not already exist, this is our DF
    if (input$update_table == 1) {
      values$df <- make_DF(input$n_entries)
    } else { # otherwise,  append the new data frame to the old.
      tmp_data <- hot_to_r(input$input_data)
      values$df[,names(tmp_data)] <- tmp_data

      values$df <- bind_rows(values$df, make_DF(input$n_entries))
    }

    # finally, set up table for data entry
    DF_shown <- values$df[c('protein', 'MW', 'n', values$shown_variables, "units")]
    output$test_output <- renderTable(values$df)
    output$input_data <- renderRHandsontable({rhandsontable(DF_shown)})
  })

}

shinyApp(ui = ui, server = server)
...