R Shiny - Is it possible to nest reactive functions?

不打扰是莪最后的温柔 提交于 2021-02-11 12:40:23

问题


In R-Shiny. Trying to break up a really long reactive function (thousands of lines!). Hypothetically, is it possible to nest conditional reactive functions, something similar to:

STATE_filter <- reactive({
 
   if(input$selectcounty ends with "-AL") {
    run AL_filter()
  }
  else if (input$selectstate ends with "-AR"){
    run AR_filter()
  }
  else {
    return("ERROR")
  }
})

EDIT

Non-hypothetically, I'm trying to create a nested reactive filtering function based on user select inputs of U.S. counties. Upon their selection of county, a circlepackeR graph should pop up in a modal dialog box. This is the data I am using:

dput(head(demographics))
structure(list(NAME = c("Autauga-AL", "Baldwin-AL", "Barbour-AL", 
"Bibb-AL", "Blount-AL", "Bullock-AL"), STATE_NAME = c("AL", "AL", 
"AL", "AL", "AL", "AL"), gender = structure(c(2L, 2L, 2L, 2L, 
2L, 2L), .Label = c("female", "male"), class = "factor"), hispanic = structure(c(2L, 
2L, 2L, 2L, 2L, 2L), .Label = c("hispanic", "nonhispanic"), class = "factor"), 
    race = structure(c(6L, 6L, 6L, 6L, 6L, 6L), .Label = c("asian", 
    "black", "islander", "native", "two or more", "white"), class = "factor"), 
    makeup = structure(c(2L, 2L, 2L, 2L, 2L, 2L), .Label = c("in combination", 
    "one race", "two or more"), class = "factor"), r_count = c(456L, 
    1741L, 114L, 96L, 320L, 44L), pathString = c("world/male/nonhispanic/white/one race", 
    "world/male/nonhispanic/white/one race", "world/male/nonhispanic/white/one race", 
    "world/male/nonhispanic/white/one race", "world/male/nonhispanic/white/one race", 
    "world/male/nonhispanic/white/one race")), row.names = c(NA, 
6L), class = "data.frame")

Here's an example of the reactive function I'm using below. It's a small subset of 10,000 + lines, and I want to "nest" it by splitting the lines by state (AL for Alabama, AR for Arkansas) first so it's a cleaner piece of code.

demographics_filter <- reactive({
   if(input$selectcounty == "Autauga-AL") {
    race_autauga <- subset.data.frame(demographics, NAME=="Autauga-AL")
    nodes_autauga <- as.Node(race_autauga)
  } 
  else if(input$selectcounty== "Baldwin-AL") {
    race_baldwinAL <-subset.data.frame(demographics, NAME=="Baldwin-AL")
    nodes_baldwinAL<- as.Node(race_baldwinAL)
  } 
 else if(input$selectcounty== "Ashley-AR") {
    race_AshleyAR <-subset.data.frame(race, NAME=="Ashley-AR")
    nodes_AshleyAR<- as.Node(race_AshleyAR)
  }
  else {
    return("ERROR!")
  }
})

And finally, here's the graph in my server that's utilizing this function:

     output$circle_graph_of_demographics <- renderCirclepackeR({
      circlepackeR(demographics_filter(), size = "r_count"
    })  

回答1:


Speaking personally, if a single function/reactive is 1000s of lines long, there's definitely room for improvement through refactorisation!

One thing I find strange about the demographics_filter reactive you've given us is that it returns NULL in the case of valid data and "ERROR!" in the case of invalid data, so I'm not sure how you can use it successfully in output$circle_graph_of_demographics. If you don't need it to return anything, then perhaps an eventReactive(input$selectcounty, {...}) would be more appropriate?

It looks as if you need to create both a (set of) nodes and a (set of) filtered data frames based on changes to the value of input$selectcounty. It's not clear why you need a node and subset for, say, Autauga-Al when input$selectcounty is, say, Baldwin-AR, which is why I've put "set of" in brackets.

Based on what you've told us (without a MWE, it's impossible to be sure exactly what will suit your needs), I would do something like:

demographics_filter <- reactive({
  req(input$selectcounty)
  subset.data.frame(demographics, NAME==input$selectcounty)
})

demographics_node <- reactive({
  as.Node(demographics_filter())
})

which should provide a compact solution that is robust with respect to changes in county and state names. If I understand you correctly, this replaces your many-thousands-of-lines with just seven. Obviously, you may need to refactor the rest of your code to take account of your changes.

If you do need sets of filtered data frames and nodes, then I'd do something like this:

v <- reactiveValues(
       demographics_filter=list(),
       demographics_nodes=list()
     )

eventReactive(input$selectcounty, {
  req(input$selectcounty)
  v$demographics_filter[[input$selectcounty]] <- subset.data.frame(demographics, NAME==input$selectcounty)
  v$demographics_node[[input$selectcounty]] <- as.Node(v$demographics_filter[[input$selectcounty]])
})

Again, it's a compact, robust solution, and you may need to refactor your code elsewhere to take account of the changes.

All my code is untested because I don't have a MWE to work with.




回答2:


Got it!

yes, you (I) can nest reactive functions.

### ALABAMA FILTER
al_filter <- reactive({
  if(input$selectcounty == "Autauga-AL") {
    demographics_autauga <- subset.data.frame(demographics, NAME=="Autauga-AL")
    nodes_autauga <- as.Node(demographics_autauga)
  } 
  else {
    return("ERROR2")
  }
})

##### ARKANSAS FILTER
ar_filter <- reactive ({
  if(input$selectcounty== "Arkansas-AR") {
    demographics_ArkansasAR <-subset.data.frame(demographics, NAME=="Arkansas-AR")
    nodes_ArkansasAR<- as.Node(demographics_ArkansasAR)
  }   
  else {
    return("ERROR2")
  }
})

##### STATES FILTER
demographics_filter <- reactive({
   if(grepl("-AL", input$selectcounty)){
    return(al_filter())
  }
  else if (grepl("-AR", input$selectcounty)){
    return (ar_filter())
  }
  else {
    return(" ERROR")
  }
})


来源:https://stackoverflow.com/questions/63351401/r-shiny-is-it-possible-to-nest-reactive-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!