Shiny 用于具有两个选择列表的区域分布图

huangapple go评论63阅读模式
英文:

Shiny for choropleth map with two selection lists

问题

I have df, and I wanted to create a shiny application that gets updated based on the value of two selection lists, one for picking an indicator, and the other for choosing the subgroup. I also create a function for produce the choropleth based on the values if its input. The function works fine when it is not run inside the shiny but return error within shiny (object of type ‘closure’).

library(dplyr)
library(readxl)
library(ggplot2)
library(sf)
library(rnaturalearth)

# Creating a test data frame
df <- tribble(
  ~Country, ~ISO3, ~Subgroup, ~Indicator, ~Value,
  "Jordan", "JOR", "Group1", "Ind1", 25.6,
  "Turkey", "TUR", "Group1", "Ind1", 56.4,
  "Jordan", "JOR", "Group1", "Ind2", 63.4,
  "Turkey", "TUR", "Group1", "Ind2", 87.5,
  "Jordan", "JOR", "Group2", "Ind1", 13.2,
  "Turkey", "TUR", "Group2", "Ind1", 22.6,
  "Jordan", "JOR", "Group2", "Ind2", 44.9,
  "Turkey", "TUR", "Group2", "Ind2", 78.5,
)

# Function to use for mapping an indicator
MapValue <- function(data, var, group){
  
  df <-  df %>%
    filter(Indicator == var & Subgroup == group)
  
  df_join <- rnaturalearth::ne_countries(returnclass = "sf") %>% 
    left_join(df, by = join_by(iso_a3 == ISO3))
  
  plot(df_join["Value"],
       border = 'grey80',
       pal = colorRampPalette(c("white", "darkgreen"))(100),
       breaks = c(0:100))
}

# Shiny 
ui <- fluidPage(
  titlePanel("Shiny for Indicators"), 
  
  sidebarLayout(

      selectInput(inputId = "var", 
                  label = "Choose a variable to display", 
                  choices = list("Ind1", "Ind2"), 
                  selected = "Ind1"),
      
      selectInput(inputId = "groups", 
                  label = "Choose a Subgroup to display", 
                  choices = list("Group1", "Group2"), 
                  selected = "Group1")
    ),
    
    mainPanel(plotOutput("map"))
  )
)

server <- function(input, output){
  output$map <- renderPlot({
    
    args$data <- df
    args$var <- switch(input$var,
                       "Ind1"= df[df$Indicator == "Ind1", ],
                       "Ind2" = df[df$Indicator == "Ind2",]
    )
    args$group <- switch(input$groups,
                         "Group1"= df[df$Subgroup == "Group1", ],
                         "Group2" = df[df$Subgroup == "Group2", ]
    )
    do.call(MapValue, args)
  })
}

shinyApp(ui = ui, server = server)
英文:

I have df, and I wanted to create a shiny application that gets updated based on the value of two selection lists, one for picking an indicator, and the other for choosing the subgroup. I also create a function for produce the choropleth based on the values if its input. The function works fine when it is not run inside the shiny but return error within shiny (object of type ‘closure’).

library(dplyr)
library(readxl)
library(ggplot2)
library(sf)
library(rnaturalearth)
# Creating a test data frame
df &lt;- tribble(
~Country, ~ISO3, ~Subgroup, ~Indicator, ~Value,
&quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group1&quot;, &quot;Ind1&quot;, 25.6,
&quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group1&quot;, &quot;Ind1&quot;, 56.4,
&quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group1&quot;, &quot;Ind2&quot;, 63.4,
&quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group1&quot;, &quot;Ind2&quot;, 87.5,
&quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group2&quot;, &quot;Ind1&quot;, 13.2,
&quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group2&quot;, &quot;Ind1&quot;, 22.6,
&quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group2&quot;, &quot;Ind2&quot;, 44.9,
&quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group2&quot;, &quot;Ind2&quot;, 78.5,
)
# Function to use for mapping an indicator
MapValue &lt;- function(data, var, group){
df &lt;-  df %&gt;%
filter(Indicator == var &amp; Subgroup == group)
df_join &lt;- rnaturalearth::ne_countries(returnclass = &quot;sf&quot;) %&gt;% 
left_join(df, by = join_by(iso_a3 == ISO3))
plot(df_join[&quot;Value&quot;],
border = &#39;grey80&#39;,
pal = colorRampPalette(c(&quot;white&quot;, &quot;darkgreen&quot;))(100),
breaks = c(0:100))
}
# Shiny 
ui &lt;- fluidPage(
titlePanel(&quot;Shiny for Indicators&quot;), 
sidebarLayout(
selectInput(inputId = &quot;var&quot;, 
label = &quot;Choose a variable to display&quot;, 
choices = list(&quot;Ind1&quot;, &quot;Ind2&quot;), 
selected = &quot;Ind1&quot;),
selectInput(inputId = &quot;groups&quot;, 
label = &quot;Choose a Subgroup to display&quot;, 
choices = list(&quot;Group1&quot;, &quot;Group2&quot;), 
selected = &quot;Group1&quot;)
),
mainPanel(plotOutput(&quot;map&quot;))
)
)
server &lt;- function(input, output){
output$map &lt;- renderPlot({
args$data &lt;- df
args$var &lt;- switch(input$var,
&quot;Ind1&quot;= df[df$Indicator == &quot;Ind1&quot;, ],
&quot;Ind2&quot; = df[df$Indicator == &quot;Ind2&quot;,]
)
args$group &lt;- switch(input$groups,
&quot;Group1&quot;= df[df$Subgroup == &quot;Group1&quot;, ],
&quot;Group2&quot; = df[df$Subgroup == &quot;Group2&quot;, ]
)
do.call(MapValue, args)
})
}
shinyApp(ui = ui, server = server)

答案1

得分: 1

这里有几个问题:

  1. 你应该将数据放在服务器函数中,而不是在外部仅定义它。

  2. MapValue() 函数有 data 作为参数,但你在函数中调用它时使用的是 df

  3. MapValue() 函数中的 groupvar 参数应该只是标识所需的组和指标的字符字符串,而不是组和指标的实际值。所以,你只需要使用 group = input$groupsvar = input$var 就可以了。

  4. 你将 args 用作列表,但你没有先初始化这个列表。所以,在开始填充 args 之前,你需要执行 args <- list()

此外,'closure' 类型的对象无法进行子集操作的错误通常发生在你认为某些东西被定义为数据,但 R 认为它是一个函数的情况下。在创建对象时,最好避免使用函数名称 - 这将使调试变得更容易一些。我的默认做法总是使用 dat。以下是一个完整的工作示例应用程序:

library(dplyr)
library(readxl)
library(ggplot2)
library(sf)
library(rnaturalearth)
library(shiny)

# 创建一个测试数据框

# 用于映射指标的函数
MapValue <- function(data, var, group){
  
  data <-  data %>%
    filter(Indicator == var & Subgroup == group)
  
  df_join <- rnaturalearth::ne_countries(returnclass = "sf") %>%
    left_join(data, by = join_by(iso_a3 == ISO3))
  
  plot(df_join["Value"],
       border = 'grey80',
       pal = colorRampPalette(c("white", "darkgreen"))(100),
       breaks = c(0:100))
}

# Shiny UI
ui <- fluidPage(
  titlePanel("Shiny for Indicators"), 
  
  sidebarLayout(
    
    selectInput(inputId = "var", 
                label = "Choose a variable to display", 
                choices = list("Ind1", "Ind2"), 
                selected = "Ind1"),
    
    selectInput(inputId = "groups", 
                label = "Choose a Subgroup to display", 
                choices = list("Group1", "Group2"), 
                selected = "Group1")
  ),
  
  mainPanel(plotOutput("map"))
)

# Shiny 服务器
server <- function(input, output){
  dat <- tribble(
    ~Country, ~ISO3, ~Subgroup, ~Indicator, ~Value,
    "Jordan", "JOR", "Group1", "Ind1", 25.6,
    "Turkey", "TUR", "Group1", "Ind1", 56.4,
    "Jordan", "JOR", "Group1", "Ind2", 63.4,
    "Turkey", "TUR", "Group1", "Ind2", 87.5,
    "Jordan", "JOR", "Group2", "Ind1", 13.2,
    "Turkey", "TUR", "Group2", "Ind1", 22.6,
    "Jordan", "JOR", "Group2", "Ind2", 44.9,
    "Turkey", "TUR", "Group2", "Ind2", 78.5,
    
  )
  
  output$map <- renderPlot({
    args <- list()
    args$data <- dat 
    args$var <- input$var
    args$group <- input$groups
    do.call(MapValue, args)
  })
}

shinyApp(ui = ui, server = server)
英文:

There were a couple of things going on here:

  1. You should put the data in the server function, rather than just defining it outside.

  2. The MapValue() function had data as an argument, but you were calling it in the function as df

MapValue &lt;- function(data, var, group){
  
  df &lt;-  df %&gt;%
    filter(Indicator == var &amp; Subgroup == group)
  
...
}

you can fix this this either by using MapValue &lt;- function(df, var, group) or by using data instead of df in the body of the function.

  1. The MapValue() function group and var arguments should just be character strings identifying the desired group and indicator, not the values of the group and indicators themselves. So, all you need to do is use group = input$groups and var = input$var and it should work.

  2. You're using args as a list, but you don't initialize the list first. So, before you start filling up args you need to do args &lt;- list().

One other point, the object of type &#39;closure&#39; is not subsettable error generally happens when you think something is defined as data, but R thinks it is a function. Both df and args are functions in R. When making objects, the best practice would be to stay away from function names - that will make the debugging a bit easier. My default is always dat. See below for a full working App.


Full App

library(dplyr)
library(readxl)
library(ggplot2)
library(sf)
library(rnaturalearth)
library(shiny)

# Creating a test data frame

# Function to use for mapping an indicator
MapValue &lt;- function(data, var, group){
  
  data &lt;-  data %&gt;%
    filter(Indicator == var &amp; Subgroup == group)
  
  df_join &lt;- rnaturalearth::ne_countries(returnclass = &quot;sf&quot;) %&gt;% 
    left_join(data, by = join_by(iso_a3 == ISO3))
  
  plot(df_join[&quot;Value&quot;],
       border = &#39;grey80&#39;,
       pal = colorRampPalette(c(&quot;white&quot;, &quot;darkgreen&quot;))(100),
       breaks = c(0:100))
}

# Shiny 
ui &lt;- fluidPage(
  titlePanel(&quot;Shiny for Indicators&quot;), 
  
  sidebarLayout(
    
    selectInput(inputId = &quot;var&quot;, 
                label = &quot;Choose a variable to display&quot;, 
                choices = list(&quot;Ind1&quot;, &quot;Ind2&quot;), 
                selected = &quot;Ind1&quot;),
    
    selectInput(inputId = &quot;groups&quot;, 
                label = &quot;Choose a Subgroup to display&quot;, 
                choices = list(&quot;Group1&quot;, &quot;Group2&quot;), 
                selected = &quot;Group1&quot;)
  ),
  
  mainPanel(plotOutput(&quot;map&quot;))
)


server &lt;- function(input, output){
  dat &lt;- tribble(
    ~Country, ~ISO3, ~Subgroup, ~Indicator, ~Value,
    &quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group1&quot;, &quot;Ind1&quot;, 25.6,
    &quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group1&quot;, &quot;Ind1&quot;, 56.4,
    &quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group1&quot;, &quot;Ind2&quot;, 63.4,
    &quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group1&quot;, &quot;Ind2&quot;, 87.5,
    &quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group2&quot;, &quot;Ind1&quot;, 13.2,
    &quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group2&quot;, &quot;Ind1&quot;, 22.6,
    &quot;Jordan&quot;, &quot;JOR&quot;, &quot;Group2&quot;, &quot;Ind2&quot;, 44.9,
    &quot;Turkey&quot;, &quot;TUR&quot;, &quot;Group2&quot;, &quot;Ind2&quot;, 78.5,
    
  )
  
  output$map &lt;- renderPlot({
    args &lt;- list()
    args$data &lt;- dat 
    args$var &lt;- input$var
    args$group &lt;- input$groups
    do.call(MapValue, args)
  })
}

shinyApp(ui = ui, server = server)

huangapple
  • 本文由 发表于 2023年5月14日 17:40:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76246792.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定