Обновить другие меню на основе выбора из другого меню

В настоящее время у меня есть приложение Shiny с 3 меню (дополнительные будут добавлены, когда ошибки будут исправлены) .
Я нашел в Интернете примеры подхода к фильтрации с помощью меню сверху вниз. Это означает, что пользователь должен выбрать из первого меню, затем из второго меню и так далее, но по порядку. Если они сначала выбирают из 2-го меню, тогда оно не фильтрует первое меню, а только те, что под ним, и, очевидно, это проблема. Я хочу, чтобы мои пользователи могли переходить к меню в любом порядке и фильтровать их. В моем примере есть 3 меню, и я пытаюсь сделать следующее: если observeEvent в меню любой (пользователь делает выбор из любого меню), то:

  1. Отфильтровать данные на основе сделанного выбора
  2. updateSelectInput для любых меню, для которых еще не выбран вход

Это обеспечит актуальность меню в соответствии с тем, что на самом деле содержится в данных, и гарантирует, что пользователь не перейдет к тому, чего на самом деле не существует в данных. Кроме того, Примечание, что шаг № 2 очень важен - обновлять только меню без выбора, у меня были проблемы с этим, потому что, если я просто обновляю все другие меню, он очищает выбранный пользователем ввод, что по-прежнему является неправильным поведением. Я знаю, что мне нужно сделать, но я еще не смог это сделать, поэтому помощь приветствуется.

Обновлять Я обновил свой код, чтобы он работал с одним ответом, опубликованным ниже, но он по-прежнему работает некорректно. Теперь он фильтрует меню, однако, как только подмножество было создано, оно не позволяет ему «фильтровать» резервную копию. Я имею в виду, что если я выберу значение 3 из первого меню TreeNumber, то последнее меню отфильтруется только до значения 300 - это хорошо. НО, если я затем вернусь в первое меню и также выберу значение 4, я ожидаю, что в меню Circumference теперь будут отображаться значения: 300 и 400, однако оно по-прежнему показывает только значение 300.

Обновленный код:

d <- data.frame("TreeNumber" = c(replicate(7, 1), replicate(7, 2), 
                                 replicate(7, 3), replicate(7, 4)),
                "TreeAge" = c(1:28),
                "Circumference" = c(replicate(7, 100), replicate(7, 200), 
                                    replicate(7, 300), replicate(7, 400)))
col_names <- names(d)
# TODO - change these to: "Tree Number", "Tree Age", "Circumference"
user_friendly_names <- c('TreeNumber', 'TreeAge', 'Circumference')

ui <- fluidPage(  
  sidebarLayout(
    sidebarPanel(
      h3("Filters:"),
      uiOutput("filters"),

      # Plot button
      fluidRow(column(2, align = "right",
                      actionButton("plot_graph_button", "Plot")))
    ),
    mainPanel(tableOutput("summary"))
  )
)

server <- function(input, output, session) {
  #### Create the filter lists for UI ####
  output$filters <- renderUI({
    if (is.null(col_names)) return(NULL)
    lapply(1:length(col_names), function(i) {
      col <- paste0(col_names[i])
      alias <- user_friendly_names[i]
      # Populate input with unique values from column
      pickerInput(inputId = alias, label = paste(alias,':'),
                     choices = unique(d[[col]]), multiple = T)
    })
  })


  # lapply(X = vars, FUN = function(x) {
  #   vals <- sort(unique(data[[x]]))
  #   updatePickerInput(session = session, inputId = x, choices = vals)
  # })

  my_filter <- function(data, var) {
    # TODO - Need to convert from user_friendly_names --> col_names in here
    if (length(input[[var]]) == 0) return(data)
    data %>% subset(data[[var]] %in% input[[var]])
  }  

  subsettedData <- reactive({
    d %>% my_filter("TreeNumber") %>% my_filter("TreeAge") %>%
      my_filter("Circumference")
    # TODO - get into for loop versus hard coding this step:
    # for(z in 1:length(col_names)){
    #   d %>% my_filter(col_names[z])
    # }
  })

  observeEvent(subsettedData(), {
    lapply(col_names, function(var) {
      selections <- unique(subsettedData()[[var]])
      if (length(input[[var]]) == 0)
        updatePickerInput(session = session, inputId = var, choices = selections)
    })
  }) 


  observeEvent(input$plot_graph_button, {
    for (j in seq_along(d)) {
      updateSelectInput(session = session, inputId = user_friendly_names[j], 
                        choices = c("All", unique(d[[j]])), selected = "All")
    }
  })


  output$summary <- renderTable({
    # Do not show a plot when the page first loads
    # Wait until the user clicks "Plot" button
    if (input$plot_graph_button == 0){
      return()
    }
    # Update code below everytime the "Plot" button is clicked
    input$plot_graph_button

    isolate({
      # Fresh copy of the full data set every time "Plot" button is clicked
      d <- copy(Orange)

      # Filter data based on UI
      for(f in 1:length(col_names)){

        if (eval(parse(text = paste0('is.null(input$',user_friendly_names[f],')')))){
          # Default to "All" - do not filter
          print("All")
        }else{                
          d <- d[d[[col_names[f]]] == 
                    unlist(eval(parse(text = 
                       paste0('input$',user_friendly_names[f])))), ]
        }
      }
      final_summary_table <<- d
    })
  })
}

shinyApp(ui = ui, server = server)

(Где определяется copy?) Можно ли это сделать с shiny::updateSelectizeInput?

r2evans 04.09.2018 21:38

@ r2evans copy - это функция. Просто делаю копии данных, чтобы убедиться, что я ничего не переписываю. Я предполагаю, что в решении будет использоваться update____, да.

Bear 04.09.2018 21:40

Копия @ r2evans находится в пространстве имен data.table для создания глубокой копии объекта data.table. Его также можно применить к data.frames, и в этом случае он действует как as.data.table.

Gregor de Cillia 04.09.2018 21:53

Моя проблема ... Я сделал обычный ??copy, и он не вернул data.table::copy (и я не слишком хорошо знаком с data.table), но я вижу (вручную), что он там. Спасибо. (Конечно, теперь ??copyявляется возвращает эту функцию, поэтому мне нужно больше - или меньше - кофеина.)

r2evans 04.09.2018 21:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
95
1

Ответы 1

Вот приложение, которое применяет фильтрацию на основе всех входных данных. Я не уверен, насколько интуитивно понятно, чтобы выбрать «все» в selectInput с multiple = TRUE. Возможно, лучше было бы вместо этого добавить кнопку сброса для каждого выбора.

Я заменил набор данных Orange на tips, чтобы получить больше факторных переменных. Кроме того, я не использовал data.table в этом примере, поскольку он не имеет отношения к вашей проблеме.

library(shiny)
library(dplyr)

data(tips, package = "reshape2")
filter_vars <- c("sex", "smoker", "day", "time")

ui <- fluidPage(
  lapply(filter_vars, function(var) {
    selectInput(var, var, unique(tips[[var]]), multiple = TRUE)
  }),
  tableOutput("table")
)

server <- function(input, output, session) {
  my_filter <- function(data, var) {
    if (length(input[[var]]) == 0) return(data)
    data %>% subset(data[[var]] %in% input[[var]])
  }  

  subsettedData <- reactive({
    tips %>% my_filter("sex") %>% my_filter("smoker") %>% 
      my_filter("day") %>% my_filter("time")
  })

  observeEvent(subsettedData(), {
    lapply(filter_vars, function(var) {
      selections <- unique(subsettedData()[[var]])
      if (length(input[[var]]) == 0)
        updateSelectInput(session, var, choices = selections)
    })
  })   

  output$table <- renderTable({ subsettedData() })
}

shinyApp(ui, server)

Я использовал свой пример кода только для упрощения, но в моем более сложном коде я отказался от All, потому что это не лучший способ обрабатывать меню, как вы указали.

Bear 04.09.2018 22:11

Ваш код создает меню ввода текста, мой - раскрывающееся меню. Этот код недостаточно похож на мой пример, чтобы он работал так, как мне нужно. В вашем примере, когда что-то удаляется, оно исчезает, и это недостаток.

Bear 04.09.2018 23:30

См. Мое отредактированное решение. Для меня было непонятно, должна ли подустановка быть обратимой.

Gregor de Cillia 05.09.2018 00:19

Для этого раздела: subsettedData <- reactive({ tips %>% my_filter("sex") %>% my_filter("smoker") %>% my_filter("day") %>% my_filter("time") }) есть ли способ избежать жесткого кодирования каждой категории?

Bear 25.09.2018 19:53

Да, вы можете просто использовать цикл for для имен переменных и каждый раз заменять данные отфильтрованными.

Gregor de Cillia 25.09.2018 20:04

Также обратите внимание на shinyWidgets::pickerGroupServer для готового решения для этого

Gregor de Cillia 25.09.2018 20:13

Я обновил вопрос, включив в него ваш ответ. Он фильтрует меню, но не отменяет фильтр. Это означает, что после фильтрации набора данных он не может вернуться вверх, если были сделаны другие выборки. (Я объяснил это более подробно в редактировании моего вопроса выше)

Bear 02.10.2018 22:14

Другие вопросы по теме