Как правильно реализовать объекты, которые зависят друг от друга, в Shiny R?

Я пытаюсь использовать Shiny. Я привык к другим языкам программирования (немного Java из разработки приложений), где есть onClick события, которые вызывают изменения других переменных. Я знаю, что в MVC после изменения элемента представления контроллер может изменять переменные модели в фоновом режиме, а затем обновлять представление без запуска бесконечного цикла событий для других элементов.

Код здесь работает, но вызывает ненужные и нежелательные циклы:

Если вы выберете 49 в качестве n, число дюжин изменится на 4, что изменит n на 48, что изменит число десятков на 4, что не вызовет событие изменения.

Если вы затем измените n на 49, десятки изменятся на 4, что не вызовет события изменения.

Вроде расточительно и ресурсоемко ..

library(shiny)

ui <- fluidPage(
  sliderInput(inputId = "dozens",
              label = "Dozens:",
              min = 1,
              max = 5,
              value = 2),
  sliderInput(inputId = "n",
              label = "n:",
              min = 1,
              max = 60,
              value = 24)
)
server <- function(input, output,session) {
  observeEvent(input$dozens,{
    updateSliderInput(session,"n",value=12*input$dozens)
  })
  observeEvent(input$n,{
    updateSliderInput(session,"dozens",value=round(input$n/12))
  })
}

shinyApp(ui = ui, server = server)

Как правильно?


person Tobias Hofmann    schedule 30.08.2019    source источник
comment
Не уверен, но, возможно, вы можете сделать observeEvent(list(input$dozens,input$n),{ ...... } и сгруппировать два updateSliderInput вместо ........ Извините, если глупо, не пробовал.   -  person Stéphane Laurent    schedule 30.08.2019
comment
Спасибо за идею. Затем мне нужно было бы различать, какое из двух значений изменилось внутри выражения. Как мне это сделать?   -  person Tobias Hofmann    schedule 30.08.2019
comment
Ах да, извините, не очень хорошая идея. Мне нужно проснуться. Я знаю, что пару дней назад задавали аналогичный вопрос.   -  person Stéphane Laurent    schedule 30.08.2019
comment
Здесь я описал возможность различать, какое событие запускает наблюдателя. . Или здесь упрощенную версию.   -  person ismirsehregal    schedule 30.08.2019


Ответы (1)


observeEvent (своего рода) антипаттерн в Shiny. Не поймите меня неправильно, он очень полезен, незаменим, я постоянно им пользуюсь. Но реактивность обычно работает лучше в Shiny, если не заботиться о ней и записывать вычисления только в описательной манере.

В случае OP, однако, я согласен, что это кажется невозможным.
И круговые зависимости между входами особенно сложны.

Решение, которое не является универсальным, но может работать в случае OP, - это добавить простой if():

server <- function(input, output, session) {
  observeEvent(input$dozens, {
    if (round(input$n / 12) != input$dozens) {
      updateSliderInput(session, "n", value = 12 * input$dozens)
    }
  })

  observeEvent(input$n, {
    updateSliderInput(session, "dozens", value = round(input$n / 12))
  })
}
person Aurèle    schedule 30.08.2019
comment
Спасибо за совет. Я думаю, что абстрактная переформулировка моего вопроса теперь заключается в том, может ли элемент быть как вводом пользователя, так и выводом вычисленной информации, и, похоже, это не рекомендуется в приложениях Shiny. Мой пример кода - это лишь абстракция моей реальной проблемы, и поскольку функции, связывающие переменные n и десятки, более сложны, я бы предпочел не выполнять их часто. - person Tobias Hofmann; 30.08.2019
comment
Я понимаю. Не могли бы вы придумать более сложный пример, который делает предлагаемое решение непригодным, чтобы я мог больше об этом подумать? Я также думаю об еще одном блестящем антипаттерне, который позволяет более точно контролировать управление состоянием, реактивные значения, но трудно найти абстрактный пример, и, возможно, это совсем не ваш вариант использования. - person Aurèle; 30.08.2019
comment
Я даже могу сказать вам свою настоящую проблему. Я получил результат алгоритма машинного обучения, в простейшей форме - фрейм данных со столбцами для истинной метки (фактор с уровнями A и B) и значения с плавающей запятой от 0 до 1 для классификации. В тройке (порог, чувствительность, специфичность) одно из значений определяет два других. Затем пороговое значение приводит к классификации и построению графиков. У меня есть функции для вычисления каждого из трех значений по каждому из остальных. Пользователь должен иметь возможность дать одно из трех и сразу увидеть результирующие значения для двух других. - person Tobias Hofmann; 30.08.2019
comment
Я понимаю. Я не знаю чистого решения этой проблемы. То, что вы придумали, - это шаблон, который, как я вижу, наиболее часто используется для этого. (возможно, с адаптациями, как в моем ответе). То, что предлагает @ismirsehregal выше, выглядит интересно, но я не пробовал и не знаю о надежности. - person Aurèle; 30.08.2019