【问题标题】:creating a new column based on dynamically changing threshold conditions Shiny根据动态变化的阈值条件创建新列
【发布时间】:2020-01-16 12:35:10
【问题描述】:

我正在尝试创建一个闪亮的应用程序,用户从下拉框中选择一个变量,例如牙生长数据集中的剂量或补充,则变量中每个唯一元素的滑块从 1 到 100 可用,例如,如果选择剂量,则为 0.5、1、2。根据滑块上的变量 selected 和 selected 值,我想创建另一个二进制变量,例如足够的长度,即:

    if (input$group == "supp"){
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="VC" & ToothGrowth$len > input$VC)]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="VC" & ToothGrowth$len <= input$VC)]<-0
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="OJ" & ToothGrowth$len > input$OJ)]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="OJ" & ToothGrowth$len <= input$OJ)]<-0
    } else if (input$group == "dose"){
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="0.5" & ToothGrowth$len > input$"0.5")]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="0.5" & ToothGrowth$len <= input$"0.5")]<-0
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="1" & ToothGrowth$len > input$"1")]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="1" & ToothGrowth$len <= input$"1")]<-0
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="2" & ToothGrowth$len > input$"2")]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="2" & ToothGrowth$len <= input$"2")]<-0
    }

有没有一种方法可以做到这一点,而无需对所有可能性进行硬编码,因为一旦我开始工作,我会将其应用于比 toothgroup 更大的数据集,其中有许多变量和这些变量中的更多独特元素?

到目前为止,shinny 应用的完整代码是:

library(shiny)
library(ggplot2)
data("ToothGrowth")

ui<-shinyUI(
  fluidPage(
    fluidRow(
      column(width = 4, 
             selectInput("group", "Group:", 
                         c("Supp" = "supp",
                           "Dose" = "dose")),
             uiOutput("sliders"),
             tableOutput("summary")
      ),
      mainPanel(

        # Output: Histogram ----
        plotOutput(outputId = "distPlot")

      )
    )
  )
)

server <- shinyServer( function(input, output) { 

  dat<-reactive({
    as.character(unique(ToothGrowth[,input$group]))
  })

  #reactive code for referrals based on the slider for threshold----
  dat2 <- reactive({
    req(ToothGrowth)
    ToothGrowth$sufficient_length<-rep(0,nrow(ToothGrowth))
    if (input$group == "supp"){
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="VC" & ToothGrowth$len > input$VC)]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="VC" & ToothGrowth$len <= input$VC)]<-0
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="OJ" & ToothGrowth$len > input$OJ)]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$supp=="OJ" & ToothGrowth$len <= input$OJ)]<-0
    } else if (input$group == "dose"){
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="0.5" & ToothGrowth$len > input$"0.5")]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="0.5" & ToothGrowth$len <= input$"0.5")]<-0
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="1" & ToothGrowth$len > input$"1")]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="1" & ToothGrowth$len <= input$"1")]<-0
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="2" & ToothGrowth$len > input$"2")]<-1
      ToothGrowth$sufficient_length[which(ToothGrowth$dose=="2" & ToothGrowth$len <= input$"2")]<-0
    }
    return(ToothGrowth)
  })


  #Render the sliders
  output$sliders <- renderUI({
    # First, create a list of sliders each with a different name
    sliders <- lapply(1:length(dat()), function(i) {
      inputName <- dat()[i]
      sliderInput(inputName, inputName, min=0, max=100, value=10)
    })
    # Create a tagList of sliders (this is important)
    do.call(tagList, sliders)
  })

  output$distPlot <- renderPlot({
    ggplot(dat2(),aes(len,fill = as.factor(sufficient_length)))+
      geom_histogram(bins=20)

  })
})

shinyApp(ui, server) 

【问题讨论】:

  • 当然,这个例子可以压缩到+((dose_selected &amp;&amp; ((dose == 1 &amp;&amp; len &gt; 1_dose_slider_value) || (dose == 2 &amp;&amp; len &gt; 2_dose_slider_value))) || (supp_selected &amp;&amp; ((supp == OJ &amp;&amp; len &gt; OJ_supp_slider_value) || (supp == VC &amp;&amp; len &gt; VC_supp_slider_value))))(为转储道歉),但我不知道这是否有多大帮助。如果没有更多的上下文就很难确定(如果有真正的代码会很好)。此外,扩展到更大的数据集是模糊的,是否意味着更多的行或不同的列/类别?
  • 顺便说一句:使用&amp; 表明您正在处理值向量,而不是单例。使用&amp; 的一个后果是它不会使用短路逻辑。如果你确实有一个值向量,那么更完整的例子是更合理的。如果不是,那么,了解&amp; and &amp;&amp; 之间的区别也许会有所帮助。
  • 抱歉,迄今为止我忘记包含闪亮应用程序的代码 - 我现在已经包含它。我想避免的事情是显式编码dosedose==1 等,而是尝试使这种编码更通用于选择的下拉选项,例如dose, supp,... 在更大的数据集中会有不同的、更多的列和每列更多的类别。
  • (1) 你打算用sufficient_length做什么?在这种情况下,它似乎应该是 nrow(ToothGrowth) 长(所以你使用 &amp; 是正确的)。 (2) 你说"if dose == 1",但是用户没有办法选择做什么。 (3) 最后,您能否在给定一组特定用户选择的情况下包含sufficient_length 的外观?
  • 我现在添加了更详细的代码和如何在 ggplot 中使用 sufficient_length 的示例。主要是找出是否有一种很好的方法来计算所有组合的sufficient_length,而无需使用上面的 else if 语句进行硬编码。

标签: r shiny


【解决方案1】:

试试这个技巧,它(我认为)对关卡数量很有效。

  dat2 <- reactive({
    req(input$group)

    ToothGrowth$sufficient_length <- 
      +apply(
        outer(ToothGrowth$len, mapply(`[[`, list(input), dat()), FUN=`>`) &
          outer(ToothGrowth[[input$group]], dat(), `==`),
        1, any)

    return(ToothGrowth)
  })

演练,假设选择了dose,滑块设置为 30、20 和 10,分别代表“0.5”、“1”和“2”。

  1. 相当于逐字逐句的ToothGrowth$dose,而是以编程方式从选定的group 中获取级别。

    ToothGrowth[[input$group]]
    #  [1] 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
    # [20] 1.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5
    # [39] 0.5 0.5 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0
    # [58] 2.0 2.0 2.0
    
  2. 我们想看看$len 是否大于所有滑块的值,所以outer 命令给了我们一个nrow(ToothGrowth) 行和3 列的矩阵(3 因为@ 987654329@有三个元素,$dose的三个层次。第1列代表第一个滑块("0.5"qhs@doseselected选择),第2列代表第二个滑块("1"),而第3列表示第三个滑块("2")。

    ToothGrowth$len
    #  [1]  4.2 11.5  7.3  5.8  6.4 10.0 11.2 11.2  5.2  7.0 16.5 16.5 15.2 17.3 22.5
    # [16] 17.3 13.6 14.5 18.8 15.5 23.6 18.5 33.9 25.5 26.4 32.5 26.7 21.5 23.3 29.5
    # [31] 15.2 21.5 17.6  9.7 14.5 10.0  8.2  9.4 16.5  9.7 19.7 23.3 23.6 26.4 20.0
    # [46] 25.2 25.8 21.2 14.5 27.3 25.5 26.4 22.4 24.5 24.8 30.9 26.4 27.3 29.4 23.0
    mapply(`[[`, list(input), dat())
    # [1] 30 18 10
    head(outer(ToothGrowth$len, mapply(`[[`, list(input), dat()), FUN=`>`))
    #       [,1]  [,2]  [,3]
    # [1,] FALSE FALSE FALSE
    # [2,] FALSE FALSE  TRUE
    # [3,] FALSE FALSE FALSE
    # [4,] FALSE FALSE FALSE
    # [5,] FALSE FALSE FALSE
    # [6,] FALSE FALSE FALSE
    

    那里的TRUE 表示$len 的第二个值(11.5)大于第三个滑块值(根据我的设置,它是10)。

  3. mapply 是获取多个input$ 元素值的技巧。通常,如果我们有一个名为list,我们可以使用单个[ 来索引多个值,但这不适用于特殊的input$ 对象。虽然我想使用sapply(dat(),[[, x = input),但这不起作用(没有在shiny 的东西中实现,因为谁想要/需要这样访问它并不奇怪)。所以我使用mapply 来解决这个问题。

    mapply(`[[`, list(input), dat())
    # [1] 30 20 10
    
  4. 现在我们有一个 60x3 的矩阵(来自项目符号 2),我们需要一个类似的矩阵来指示该行的 $dose 是否等于列的级别。在上一个项目符号中,TRUE 表示值 11.5(第 2 行)和 $dose"2"(第 3 列,滑块 3)。因此,现在我们将$dose 与可用级别进行outer 比较。

    dat()
    # [1] "0.5" "1"   "2"  
    head(outer(ToothGrowth[[input$group]], dat(), `==`))
    #      [,1]  [,2]  [,3]
    # [1,] TRUE FALSE FALSE
    # [2,] TRUE FALSE FALSE
    # [3,] TRUE FALSE FALSE
    # [4,] TRUE FALSE FALSE
    # [5,] TRUE FALSE FALSE
    # [6,] TRUE FALSE FALSE
    
  5. 从这里开始,我们取两个 60x3 矩阵并进行元素与:

    head(outer(ToothGrowth[[input$group]], dat(), `==`) &
          outer(ToothGrowth$len, mapply(`[[`, list(input), dat()), FUN=`>`))
    #       [,1]  [,2]  [,3]
    # [1,] FALSE FALSE FALSE
    # [2,] FALSE FALSE FALSE
    # [3,] FALSE FALSE FALSE
    # [4,] FALSE FALSE FALSE
    # [5,] FALSE FALSE FALSE
    # [6,] FALSE FALSE FALSE
    tail(outer(ToothGrowth[[input$group]], dat(), `==`) &
          outer(ToothGrowth$len, mapply(`[[`, list(input), dat()), FUN=`>`))
    #        [,1]  [,2] [,3]
    # [55,] FALSE FALSE TRUE
    # [56,] FALSE FALSE TRUE
    # [57,] FALSE FALSE TRUE
    # [58,] FALSE FALSE TRUE
    # [59,] FALSE FALSE TRUE
    # [60,] FALSE FALSE TRUE
    

    (好吧,那里没什么意思,只是想我会同时显示头部和尾部以证明某些行匹配。)

  6. apply 采用一个矩阵(两个矩阵的元素 AND,并将函数 (any) 应用于每一行(1,应用函数的边距)。

验证值是否相同:

## my code, assigned elsewhere for now
ind <- +apply(
  outer(ToothGrowth$len, mapply(`[[`, list(input), dat()), FUN=`>`) &
    outer(ToothGrowth[[input$group]], dat(), `==`),
  1, any)
## your code
ToothGrowth$sufficient_length[which(ToothGrowth$dose=="0.5" & ToothGrowth$len > input$"0.5")]<-1
ToothGrowth$sufficient_length[which(ToothGrowth$dose=="0.5" & ToothGrowth$len <= input$"0.5")]<-0
ToothGrowth$sufficient_length[which(ToothGrowth$dose=="1" & ToothGrowth$len > input$"1")]<-1
ToothGrowth$sufficient_length[which(ToothGrowth$dose=="1" & ToothGrowth$len <= input$"1")]<-0
ToothGrowth$sufficient_length[which(ToothGrowth$dose=="2" & ToothGrowth$len > input$"2")]<-1
ToothGrowth$sufficient_length[which(ToothGrowth$dose=="2" & ToothGrowth$len <= input$"2")]<-0

all(ind == ToothGrowth$sufficient_length)
# [1] TRUE

(顺便说一句:这个例子中的req(ToothGrowth) 是完全没有必要的,因为ToothGrowth 是一个静态数据集。通常,req 用于反应值,以确保它在当前反应状态下是“真实的”。这种情况经常发生,例如在启动时某些输入尚未完全定义,因此可能返回为NULL。所以你应该在input$... 上使用req 或服务器组件中的一些反应数据。 )

【讨论】:

  • 谢谢 - 这是一个非常整洁的解决方案!
猜你喜欢
  • 1970-01-01
  • 2020-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-27
  • 2020-07-18
相关资源
最近更新 更多