Ограничить несколько слайдеров в блестящем виде до 100
Я боролся с этим весь день и вроде как решил (ужасный хак). Однако опыт не является гладким и показывает побочные эффекты.
Мне нужны три ползунка с ограниченным диапазоном от 0 до 100, чтобы их сумма всегда была равна 100.
Это скриншот того, как это выглядит
Вот блестящий код server.R.
library(shiny)
oldState<-NULL
newState<-NULL
getState<-function(input) c(input$slider1, input$slider2, input$slider3)
# Define server logic required
shinyServer(function(input, output, session) {
observe({
newState<<-getState(input)
i<-which(oldState-newState != 0)[1]
if(!is.na(i)){
rem <- 100-newState[i]
a<-sum(newState[-i])
if(a==0) newState[-i]<<-rem/length(newState[-i])
else newState[-i]<<-rem*(newState[-i]/a)
for(j in 1:length(newState))
if(j!=i)
updateSliderInput(session, paste0("slider", j), value=newState[j])
}
oldState<<-newState
})
output$restable <- renderTable({
myvals<-getState(input)
myvals<-c(myvals, sum(myvals))
data.frame(Names=c("Slider 1", "Slider 2", "Slider 3", "Sum"),
Values=myvals)
})
})
и вот блестящий код ui.R
library(shiny)
# Define UI for application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Sliders should sum to 100!"),
# Sidebar with sliders whos sum should be constrained to be 100
sidebarPanel(
sliderInput("slider1", "Slider 1: ", min = 0, max = 100, value = 40, step=1),
sliderInput("slider2", "Slider 2: ", min = 0, max = 100, value = 30, step=1),
sliderInput("slider3", "Slider 3: ", min = 0, max = 100, value = 30, step=1)
),
# Create table output
mainPanel(
tableOutput("restable")
)
))
Теперь это делает в значительной степени то, что должно, за исключением двух вещей:
- Это похоже на взлом, то есть должен быть лучший способ сделать это
- Когда я перемещаю ползунок в положение, он иногда переходит в немного более низкое или более высокое положение. Понятия не имею почему.
Как мне исправить эти две проблемы?
3 ответа
Я думаю, что использование dynamicUI может решить вашу проблему.
Если вы знаете, что должно быть ровно 3 входа, сумма которых равна 1, то вы можете ограничить своего пользователя только двумя входами ползунка и сделать 2-й вход ползунка зависимым от первого, как показано ниже. Используя ваш код шаблона:
server.R
library(shiny)
# Define server logic required
shinyServer(function(input, output) {
output$slider2 <- renderUI {
sliderInput("slider2", "Slider 2", min = 0, max = 100 - input$slider1, value = 0)
})
output$restable <- renderTable({
myvals<- c(input$slider1, input$slider2, 100-input$slider1-input$slider2)
data.frame(Names=c("Slider 1", "Slider 2", "Slider 3"),
Values=myvals)
})
})
Ключ здесь renderUI
функция, которая ищет input$slider1
значение, чтобы ограничить значение slider2
(и поэтому slider3
)
ui.R
library(shiny)
# Define UI for application
shinyUI(pageWithSidebar(
# Application title
headerPanel("Sliders should sum to 100!"),
# Sidebar with sliders whos sum should be constrained to be 100
sidebarPanel(
sliderInput("slider1", "Slider 1: ", min = 0, max = 100, value = 0, step=1),
uiOutput("slider2")
),
# Create table output
mainPanel(
tableOutput("restable")
)
))
Как видно (если вы косите) на прикрепленном изображении, slider2
ограничено до 0-35, один раз slider1
был установлен на 65.
Все эти мысли полезны, но мой инстинкт состоит в том, чтобы использовать
tagList
. Алгоритм работает следующим образом:
- Сохраните все ползунки в списке тегов
- Пусть n будет длиной списка тегов
- Заполнить все виджеты слайдера из списка тегов
- Если виджет слайдера изменяется (на сервере) под
observe {...}
, определите, какой виджет. - Пусть x будет 100 минус значение виджета, измененного из списка тегов (3) и (4).
- Установите для свойства value остальных ползунков в списке тегов значение
x/(n-1)
.
Надеюсь это поможет. Обновим код как можно скорее.
Я наткнулся на эту тему, пытаясь реализовать то же самое. Я разработал решение, основанное на псевдокоде, изложенном bmc выше, и решил поделиться им.
использую три разных
Это продемонстрировано ниже с использованием интерактивного документа Rmarkdown, но это также должно быть перенесено и в стандартные блестящие приложения.
---
title: "Force Sliders to Sum to 100"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
library(shiny)
```
```{r inputs}
sliderInput("s1", "Slider 1", min = 0, max = 100, value = 50, step = 0.1)
sliderInput("s2", "Slider 2", min = 0, max = 100, value = 25, step = 0.1)
sliderInput("s3", "Slider 3", min = 0, max = 100, value = 25, step = 0.1)
# when s1 changes, adjust other two to fill the remainder with the same ratio as they currently have
observeEvent(input$s1, {
remaining <- 100 - input$s1
p_remaining_s2 <- input$s2/(input$s2 + input$s3)
p_remaining_s3 <- input$s3/(input$s2 + input$s3)
updateSliderInput(inputId = "s2", value = remaining * p_remaining_s2)
updateSliderInput(inputId = "s3", value = remaining * p_remaining_s3)
})
# when s2 changes, do the same
observeEvent(input$s2, {
remaining <- 100 - input$s2
p_remaining_s1 <- input$s1/(input$s1 + input$s3)
p_remaining_s3 <- input$s3/(input$s1 + input$s3)
updateSliderInput(inputId = "s1", value = remaining * p_remaining_s1)
updateSliderInput(inputId = "s3", value = remaining * p_remaining_s3)
})
# when s3 changes, do the same
observeEvent(input$s3, {
remaining <- 100 - input$s3
p_remaining_s1 <- input$s1/(input$s1 + input$s2)
p_remaining_s2 <- input$s2/(input$s1 + input$s2)
updateSliderInput(inputId = "s1", value = remaining * p_remaining_s1)
updateSliderInput(inputId = "s2", value = remaining * p_remaining_s2)
})
```
```{r outputs}
renderTable({
data.frame(
Slider = c("1", "2", "3", "sum"),
Value = c(input$s1, input$s2, input$s3, input$s1 + input$s2 + input$s3)
)
})
```