如何用Shiny响应式函数绘制分位数分类的sf对象地图并解决报错
问题原因与解决代码
你的错误出在这句代码:
labels() <- labels()[1:length(labels()) - 1]
反应式对象(比如labels())不能在反应式上下文(reactive、render函数)外部直接赋值修改,这违反了Shiny的反应式规则,才会抛出"You tried to do something that can only be done from inside a reactive consumer"错误。
另外还有几个潜在问题:
- 用到了
imap_chr但没加载purrr包 income_grp存在NA值,需要从选择项中剔除- 标签里的
%是错误的,pop_est是人口估计数,不是百分比
以下是修正后的完整代码:
library(shiny) library(sf) library(tidyverse) # 包含dplyr、ggplot2、purrr等包 # 加载数据并预处理选择项 shp <- rnaturalearth::ne_countries(returnclass = "sf", scale = 'large') income_choices <- shp %>% pull(income_grp) %>% unique() %>% na.omit() %>% sort() ui <- bootstrapPage( tags$style(type = "text/css", "html, body {width:100%;height:100%}"), absolutePanel(top = 10, right = 10, selectInput("income", label = "选择收入分组", choices = income_choices) ), plotOutput("map", width = "100%", height = "100%"), ) server <- function(input, output, session) { shp_filtered <- reactive({ shp %>% filter(income_grp == input$income) }) quantiles <- reactive({ shp_filtered() %>% pull(pop_est) %>% quantile(probs = seq(0, 1, length.out = 4), na.rm = TRUE) %>% as.vector() }) # 在reactive内部处理标签,去掉最后一项 labels <- reactive({ q <- quantiles() imap_chr(q[-length(q)], function(x, idx){ paste0(round(x, 0), " – ", round(q[idx + 1], 0)) }) }) shp_final <- reactive({ shp_filtered() %>% mutate(pop_quantiles = cut(pop_est, breaks = quantiles(), labels = labels(), include.lowest = TRUE)) }) output$map <- renderPlot({ shp_final() %>% ggplot() + geom_sf(aes(fill = pop_quantiles), color = "white", size = 0.1) + scale_fill_viridis_d(option = "plasma", name = "人口估计数") + theme_void() + theme(legend.position = "bottom") }) } shinyApp(ui, server)
关键修改说明
- 把标签的截断逻辑放到
labels的reactive内部,通过q[-length(q)]直接生成正确长度的标签,避免外部修改反应式对象 - 加载
tidyverse包统一管理所需的工具包 - 预处理
income_grp的选择项,剔除NA并排序 - 修正标签文本,去掉错误的
%符号 - 优化地图样式,添加边界线、调整图例位置和颜色方案,更接近示例图效果
内容的提问来源于stack exchange,提问作者rez
相关产品推荐
相关产品推荐

