如何用ggplot2或plotly复现hist()的混合直方图与柱状图效果?
嘿,我来帮你搞定这个需求!你要的温度区间对应居住总人数的交互式图表,不管用ggplot2还是plotly都能完美复刻,还能轻松整合到Shiny应用里。下面一步步给你演示:
核心思路说明
首先要明确:你需要的不是传统直方图(展示频率/密度),而是基于温度区间统计总人数后的柱状图——先把温度划分成区间,再计算每个区间内的居住总人数,最后用柱状图展示并标注数值,这就是你说的“混合直方图与柱状图”的本质。
用ggplot2实现基础图表(可直接整合到Shiny)
先从基础的静态图表开始,再扩展到交互式Shiny应用:
1. 数据预处理(模拟+统计)
先模拟一组类似的数据集,再按温度区间统计总人数:
library(dplyr) # 模拟温度和对应居住人数的数据(实际替换成你的真实数据) set.seed(123) temp_data <- data.frame( temperature = rnorm(500, mean = 20, sd = 5), # 温度数据 people = sample(1:10, 500, replace = TRUE) # 每个温度点对应的居住人数 ) # 自定义温度区间并统计总人数 temp_summary <- temp_data %>% mutate(temp_bin = cut(temperature, breaks = seq(5, 35, 5))) %>% # 5℃为间隔的区间 group_by(temp_bin) %>% summarise(total_people = sum(people)) %>% # 计算每个区间的总人数 ungroup()
2. 绘制带标注的柱状图
用ggplot2绘制柱状图,并在每个柱形上方标注对应的总人数:
library(ggplot2) p <- ggplot(temp_summary, aes(x = temp_bin, y = total_people)) + geom_col(fill = "#2196F3", alpha = 0.7) + # 绘制柱状图 geom_text(aes(label = total_people), vjust = -0.5, size = 4) + # 柱形上方标注数值 labs( x = "温度区间(℃)", y = "区间内居住总人数", title = "温度区间与居住总人数分布" ) + theme_minimal() + theme(plot.title = element_text(hjust = 0.5)) # 标题居中 print(p)
整合到Shiny应用(ggplot2版本)
把上面的代码改成交互式的Shiny应用,比如添加调整温度区间宽度的控件:
library(shiny) library(dplyr) library(ggplot2) ui <- fluidPage( titlePanel("温度区间居住人数交互式图表"), sidebarLayout( sidebarPanel( # 交互式控件:调整温度区间宽度 numericInput("bin_width", "温度区间宽度(℃)", value = 5, min = 1, max = 10) ), mainPanel( plotOutput("temp_plot") ) ) ) server <- function(input, output) { # 模拟数据(实际替换为你的真实数据集) temp_data <- reactive({ set.seed(123) data.frame( temperature = rnorm(500, mean = 20, sd = 5), people = sample(1:10, 500, replace = TRUE) ) }) # 动态统计区间总人数(根据用户选择的区间宽度) temp_summary <- reactive({ temp_data() %>% mutate(temp_bin = cut( temperature, breaks = seq(min(temperature()) - 1, max(temperature()) + 1, input$bin_width) )) %>% group_by(temp_bin) %>% summarise(total_people = sum(people)) %>% ungroup() }) # 生成交互式图表 output$temp_plot <- renderPlot({ ggplot(temp_summary(), aes(x = temp_bin, y = total_people)) + geom_col(fill = "#2196F3", alpha = 0.7) + geom_text(aes(label = total_people), vjust = -0.5, size = 4) + labs( x = "温度区间(℃)", y = "区间内居住总人数", title = "温度区间与居住总人数分布" ) + theme_minimal() + theme(plot.title = element_text(hjust = 0.5)) }) } shinyApp(ui, server)
用plotly实现(更适合Shiny交互式体验)
如果想要更流畅的交互式效果(比如hover显示详情、缩放等),可以用plotly实现,同样能整合到Shiny:
1. 静态plotly图表
library(plotly) # 基于之前的temp_summary数据绘制 p_plotly <- plot_ly(temp_summary, x = ~temp_bin, y = ~total_people, type = 'bar', marker = list(color = '#2196F3', opacity = 0.7)) %>% add_text(text = ~total_people, textposition = 'top center', textfont = list(size = 12)) %>% layout( title = list(text = "温度区间与居住总人数分布", x = 0.5), xaxis = list(title = "温度区间(℃)"), yaxis = list(title = "区间内居住总人数"), showlegend = FALSE ) p_plotly
2. Shiny + plotly版本
只需要把Shiny里的plotOutput换成plotlyOutput,renderPlot换成renderPlotly即可:
library(shiny) library(dplyr) library(plotly) ui <- fluidPage( titlePanel("温度区间居住人数交互式图表"), sidebarLayout( sidebarPanel( numericInput("bin_width", "温度区间宽度(℃)", value = 5, min = 1, max = 10) ), mainPanel( plotlyOutput("temp_plot") ) ) ) server <- function(input, output) { temp_data <- reactive({ set.seed(123) data.frame( temperature = rnorm(500, mean = 20, sd = 5), people = sample(1:10, 500, replace = TRUE) ) }) temp_summary <- reactive({ temp_data() %>% mutate(temp_bin = cut( temperature, breaks = seq(min(temperature()) - 1, max(temperature()) + 1, input$bin_width) )) %>% group_by(temp_bin) %>% summarise(total_people = sum(people)) %>% ungroup() }) output$temp_plot <- renderPlotly({ plot_ly(temp_summary(), x = ~temp_bin, y = ~total_people, type = 'bar', marker = list(color = '#2196F3', opacity = 0.7)) %>% add_text(text = ~total_people, textposition = 'top center', textfont = list(size = 12)) %>% layout( title = list(text = "温度区间与居住总人数分布", x = 0.5), xaxis = list(title = "温度区间(℃)"), yaxis = list(title = "区间内居住总人数"), showlegend = FALSE ) }) } shinyApp(ui, server)
关键注意点
- 为什么不用
hist():因为hist()默认统计的是区间内的观测数量,而你需要的是观测对应的人数总和,所以必须先手动用cut()划分区间,再用sum(people)统计总人数,最后用柱状图展示。 - 标注位置:
ggplot2里用vjust = -0.5把文字放在柱形上方,plotly里用textposition = 'top center'实现同样效果。 - 数据替换:把代码里的模拟数据
temp_data换成你自己的真实数据集即可。
内容的提问来源于stack exchange,提问作者G. Sozu
相关产品推荐
相关产品推荐

