R Shiny 中的反应式桑基图,以多个数据帧作为输入

Reactive Sankey Diagram in R Shiny with multiple dataframes as input

这是我第一次创建仪表板,我 运行 遇到了一个我似乎无法解决的问题。我创建了一个桑基图,我希望能够通过不同的数据帧交互地更改其内容(在本例中:level_1、level_2、level_3)。我只用常规图练习过这个,其中输入来自一个数据框中的变量,这是我在这段代码中的起点(例如,我有一个 df$country,所以我在我的中使用 input$country plot --> 然后我可以在仪表板侧边栏中选择不同的国家,以更改 plot 的内容)。当输入必须来自单独的数据帧时,我不知道该怎么做。

我的代码:(在app.R)

level_1 <- as.data.frame(matrix(sample(seq(0,40), 15, replace=T ), 3, 5))
level_2 <- as.data.frame(matrix(sample(seq(0,40), 20, replace=T ), 4, 5))
level_3 <- as.data.frame(matrix(sample(seq(0,40), 25, replace=T ), 5, 5))

levels <- list(level_1, level_2, level_3)

ui <- dashboardPage(
    dashboardHeader(title = "title"),
    
    dashboardSidebar(
        selectInput("in_levels", "Levels", choices = levels) 

        
    ),
    dashboardBody(
        fluidRow(sankeyNetworkOutput("widget1"))
    )
)

server <- function(input, output) { 
    
    links <- input$in_levels %>%
        rownames_to_column(var="source") %>% 
        gather(key="target", value="value", -1) %>%
        filter(value != 0)
    
    nodes <- data.frame(
        name=c(as.character(links$source), as.character(links$target)) %>% 
            unique()
    )
    
    links$IDsource <- match(links$source, nodes$name)-1 
    links$IDtarget <- match(links$target, nodes$name)-1
    
    output$widget1 <- renderSankeyNetwork({
        sankeyNetwork(Links = links, Nodes = nodes,
                      Source = "IDsource", Target = "IDtarget",
                      Value = "value", NodeID = "name", fontSize = 14, nodeWidth = 60,
                      fontFamily = "Arial", iterations = 0, sinksRight=TRUE)
    })
}

shinyApp(ui, server)

我认为创建一个包含所有数据帧的 list()、levels 可能会有所帮助,但这不起作用。我收到此错误:

Error : Can't access reactive value 'in_levels' outside of reactive consumer.
i Do you need to wrap inside reactive() or observer()?

我已经在 google 上搜索了 reactive() 和 observer() 以试图找出我的下一步应该是什么,但我还没有找到解决方案。如果有人可以就如何继续、进行更改或阅读一些可以增加我的理解的内容给我建议,我将不胜感激。

提前致谢!

如果您想访问服务器中的任何 input 值,您需要使用反应式上下文。 shiny 不允许您做其他事情,但即使这样做了,如果更新了 input 值,服务器端代码也不会更新以反映更改。由于您希望 linksnodes 都是动态的并且彼此依赖,一个简洁的解决方案可能是将两个对象存储在列表中,如下所示:

server <- function(input, output) { 
    
    plot_data <- reactive({

      # Perform all your computation inside this reactive!    

      links <- input$in_levels %>%
        rownames_to_column(var="source") %>% 
        gather(key="target", value="value", -1) %>%
        filter(value != 0)
    
      nodes <- data.frame(
        name = c(as.character(links$source), as.character(links$target)) %>% 
            unique()
      )
    
      links$IDsource <- match(links$source, nodes$name)-1 
      links$IDtarget <- match(links$target, nodes$name)-1

      # Return the data in a list
      list(links = links, nodes = nodes)

    })
    
    # Access the datasets by calling the reactive and then treating as a normal list
    output$widget1 <- renderSankeyNetwork({
        sankeyNetwork(Links = plot_data()$links, Nodes = plot_data()$nodes,
                      Source = "IDsource", Target = "IDtarget",
                      Value = "value", NodeID = "name", fontSize = 14, nodeWidth = 60,
                      fontFamily = "Arial", iterations = 0, sinksRight=TRUE)
    })
}

这是未经测试的,因为我当前的 R 版本不支持 network3d 包。

反应性的概念很棘手,但如果您是 shiny 的新手,chapter 3 of Mastering Shiny 应该很有启发性。