英文:
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 <- 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)
答案1
得分: 1
这里有几个问题:
-
你应该将数据放在服务器函数中,而不是在外部仅定义它。
-
MapValue()
函数有data
作为参数,但你在函数中调用它时使用的是df
。 -
MapValue()
函数中的group
和var
参数应该只是标识所需的组和指标的字符字符串,而不是组和指标的实际值。所以,你只需要使用group = input$groups
和var = input$var
就可以了。 -
你将
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:
-
You should put the data in the server function, rather than just defining it outside.
-
The
MapValue()
function haddata
as an argument, but you were calling it in the function asdf
MapValue <- function(data, var, group){
df <- df %>%
filter(Indicator == var & Subgroup == group)
...
}
you can fix this this either by using MapValue <- function(df, var, group)
or by using data
instead of df
in the body of the function.
-
The
MapValue()
functiongroup
andvar
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 usegroup = input$groups
andvar = input$var
and it should work. -
You're using
args
as a list, but you don't initialize the list first. So, before you start filling upargs
you need to doargs <- list()
.
One other point, the object of type 'closure' 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 <- 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 <- 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){
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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论