1. 为什么我坚持用 ggplot2 做直方图——一个十年 R 用户的实战选择
在数据可视化这件事上,R 语言里从来就不缺工具。base R 的
hist()
函数三行就能出图,lattice 包也能快速分面,但过去十年里,我给客户做交付、带新人入门、写内部分析报告,95% 的直方图都只用
ggplot2
。不是因为它“最流行”,而是它把“我想表达什么”和“代码怎么写”之间的认知鸿沟,压缩到了几乎为零的程度。你不需要记住一堆互不关联的参数名,只要理解“数据→映射→几何对象→修饰”这四层逻辑,哪怕第一次写
geom_histogram()
,也能在十分钟内调出一张能放进周报的图。这不是玄学,是语法设计上的克制与诚实。
直方图表面看只是横轴数值、纵轴频数的柱状图,但实际工作中,它承载的任务远比这复杂:要快速判断房价分布是否右偏,要对比不同装修等级房源的价格集中区间,要在同一张图上叠加均值线和密度曲线辅助决策,甚至要按区域切片生成九宫格小图供销售团队逐个分析。这些需求,base R 的
hist()
要靠
lines()
,
abline()
,
par(mfrow=...)
一层层硬拼,改一个标题位置可能得重写半页代码;而
ggplot2
只需在原有语句后加个
+ geom_vline()
或
+ facet_wrap(~district)
,逻辑清晰,修改成本极低。我试过用 base R 复现一个带均值线、密度曲线、分组填色、自定义 bin 宽度、坐标轴限制和图例重排的直方图——代码长度是
ggplot2
版本的 2.3 倍,可读性却下降了不止一个量级。这不是效率问题,是思维负担问题。当你在深夜调试模型时,大脑已经超载,这时候少写十行易错代码,就是多留一分清醒去检查逻辑漏洞。
关键词“ggplot2 直方图”背后,真正值得深挖的是三个核心能力:
如何让数据分布说话,如何让统计信息可读,以及如何让图表适配真实业务场景
。这篇文章不会罗列所有
geom_histogram()
参数,而是聚焦于我在房产数据分析、用户行为埋点、A/B 测试结果汇报等数十个真实项目中反复验证过的实操路径。从安装依赖开始,到最终导出可直接插入 PPT 的高清图,每一步都附带“为什么这么选”和“不这么选会怎样”的现场记录。如果你刚接触 R,我会用“超市货架分区”类比
aes()
映射;如果你已熟悉
dplyr
,我会直接展示如何用管道符无缝衔接数据清洗与绘图;如果你正被老板催着交一份带交互注释的房价分析报告,文末的“生产环境 checklist”能帮你省下两小时返工时间。
2. 项目整体设计与思路拆解
2.1 为什么必须从 tidyverse 生态切入——不是跟风,是工程化刚需
很多人初学
ggplot2
时会疑惑:“为什么教程总让我先装
tidyverse
?我只画个直方图,装个
ggplot2
不就够了?” 这是个好问题,答案藏在数据流动的全链路里。我们手里的原始数据,从来不是理想状态:房价数据里有异常高价(比如把“$10,000,000”误录成“10000000”),条件评分字段是数字型但本质是分类变量(1-5 分代表差到优),还可能混着缺失值或空格。如果只用
ggplot2
,你得先用 base R 的
read.csv()
读取,再用
as.factor()
转类型,再用
subset()
或逻辑索引过滤异常值,最后才能喂给
ggplot()
。这个过程里,每个函数返回的对象类型不同(data.frame → factor → logical vector),参数命名风格也不统一(
na.strings
vs
stringsAsFactors
),极易出错。
而
tidyverse
的设计哲学是“数据流一致性”。
readr::read_csv()
默认不把字符转因子,
dplyr::mutate()
和
filter()
都接受管道符
|>
,返回的永远是 data.frame,
ggplot2::ggplot()
的
aes()
映射能直接识别
dplyr
创建的新列。这意味着你可以写出这样一行连贯代码:
home_data |>
filter(price < 5e6) |>
mutate(condition = factor(condition, levels = 1:5, labels = c("Poor", "Fair", "Average", "Good", "Excellent"))) |>
ggplot(aes(x = price, fill = condition)) +
geom_histogram(position = "identity", alpha = 0.7)
这里没有中间变量污染全局环境,没有类型转换的隐式陷阱,更没有因
read.csv()
默认
stringsAsFactors = TRUE
导致后续
fill
映射失败的深夜 debug。我曾帮一个电商团队重构报表系统,他们原来的 base R 脚本在处理新增的“促销标签”字段时,因为
read.csv()
自动转因子导致
ggplot()
报错
object 'promo_tag' not found
,排查了三小时才发现是读取阶段的默认行为。换成
readr::read_csv()
后,问题消失。这不是功能差异,是工程鲁棒性的代差。
2.2 直方图的本质:不是画柱子,是构建数据分布的“显微镜”
geom_histogram()
经常被误解为“自动分组计数的柱状图”,这是危险的简化。它的核心任务是
对连续变量进行离散化建模,并可视化该模型下的概率分布近似
。关键参数
bins
、
binwidth
、
boundary
、
center
共同决定了这台“显微镜”的放大倍数和对焦精度。
-
bins = 30是粗略扫描:把整个价格范围切成 30 等份,适合快速看整体形态(单峰/双峰/长尾); -
binwidth = 50000是精准测量:强制每格宽度为 5 万美元,能直接对比“50-100 万区间房源数量”和“100-150 万区间数量”,业务含义明确; -
boundary = 0是校准基准:确保第一格从 0 开始(0-5 万、5-10 万…),避免因数据最小值是 12345 而导致第一格变成 12345-62345,破坏可比性。
我处理过一组用户停留时长数据(单位:秒),原始
geom_histogram()
默认分 30 格,结果最左侧出现一个极高柱子(0-10 秒),掩盖了主体分布(30-120 秒)。改成
binwidth = 10
并设
boundary = 0
后,立刻看清真实峰值在 45-55 秒区间——这直接指向了视频前贴片广告的完播拐点。如果只满足于“出图”,你会错过这个关键洞察;而理解
binwidth
是控制分辨率的旋钮,才真正掌握了解读数据的能力。
2.3 图层叠加的底层逻辑:为什么
+
操作符是革命性的
ggplot2
的
+
不是简单的语法糖,它是
声明式绘图范式的基石
。传统绘图库(如 base R)要求你按执行顺序写代码:先画坐标轴,再画柱子,再加线条,再加文字。一旦想调整线条颜色,你得回头找
lines()
那行;想改坐标轴范围,得定位到
xlim()
。而
ggplot2
的
+
表示“在现有视觉框架上叠加新元素”,各图层完全解耦。
geom_histogram()
只负责柱子,
geom_vline()
只负责竖线,
labs()
只负责标签,它们之间没有执行依赖。这种设计带来三个实战优势:
-
调试友好
:当图出错时,你可以逐行注释
+后的图层,快速定位是geom_density()的密度计算出错,还是theme()的字体设置冲突; -
复用性强
:一套
ggplot(data, aes(x=price)) + geom_histogram()的骨架,可以无缝替换+ geom_boxplot()变成箱线图,或+ geom_point()变成散点图,只需改一个词; -
协作清晰
:在团队项目中,数据工程师写
ggplot()基础层,分析师加geom_vline()和geom_text()注释,设计师调theme()配色,大家修改的代码段物理隔离,合并冲突概率极低。
我见过太多团队因 base R 的“过程式绘图”导致报表脚本难以维护:一个实习生改了
main="房价分布"
的字体大小,结果
abline()
的线条位置跟着偏移——因为
par()
设置是全局生效的。而
ggplot2
的
theme()
修改只影响当前图形对象,彻底规避了这类“蝴蝶效应”。
3. 核心细节解析与实操要点
3.1 数据准备:从原始 CSV 到可绘图就绪的七步净化
直方图的质量,80% 取决于输入数据的洁净度。我处理过上百个房产数据集,发现以下七类问题高频出现,必须在
ggplot()
之前解决:
-
数值型字段含非数字字符 :
price列里混着$1,234,567或N/A。readr::read_csv()的col_types参数可强制指定类型并报错:home_data <- read_csv("home_data.csv", col_types = cols( price = col_double(), condition = col_integer() ), na = c("", "N/A", "NULL"))若遇无法解析的值,
readr会标为NA并警告,比read.csv()默默转成NA更安全。 -
分类变量未转因子 :
condition是 1-5 的整数,但直方图分组填色时需作为类别处理。dplyr::mutate()结合forcats::fct_recode()可语义化重命名:library(forcats) home_data <- home_data |> mutate(condition = fct_recode( as.factor(condition), "Poor" = "1", "Fair" = "2", "Average" = "3", "Good" = "4", "Excellent" = "5" )) -
极端异常值干扰分布 :房价数据中常有 $1 亿的豪宅或 $1 的错误录入。用
dplyr::quantile()计算 0.5% 和 99.5% 分位数作截断:price_q05 <- quantile(home_data$price, 0.005, na.rm = TRUE) price_q995 <- quantile(home_data$price, 0.995, na.rm = TRUE) home_data <- home_data |> filter(price >= price_q05 & price <= price_q995) -
缺失值处理 :
geom_histogram()默认丢弃NA,但若price缺失率超 5%,需检查原因。用ggplot2::geom_bar()可快速诊断:home_data |> mutate(price_missing = is.na(price)) |> ggplot(aes(x = price_missing)) + geom_bar() + labs(title = "Missing Price Check") -
单位统一 :确保所有价格字段单位一致(美元),避免混用
$和¥。用stringr::str_remove_all()清洗:library(stringr) home_data <- home_data |> mutate(price = as.numeric(str_remove_all(price, "[^0-9.]"))) -
时间字段解析 :若数据含
list_date,用lubridate::ymd()转标准日期,便于后续按月分面:library(lubridate) home_data <- home_data |> mutate(list_month = floor_date(ymd(list_date), "month")) -
内存优化 :大数据集(>100 万行)用
vroom::vroom()替代readr::read_csv(),速度提升 5-10 倍且内存占用更低:library(vroom) home_data <- vroom("home_data.csv", col_types = cols(.default = col_guess()))
提示:以上步骤应封装为
clean_home_data()函数,每次新数据进来只需调用一次。我团队的标准化流程中,这步耗时不超过 30 秒,却避免了后续 90% 的绘图报错。
3.2
aes()
映射的隐藏规则:x、y、fill、color 的语义边界
aes()
是
ggplot2
的灵魂,但新手常踩的坑在于混淆“数据映射”和“固定属性”。核心原则是:
在
aes()
内部写的,是告诉 ggplot “这个视觉属性随哪个数据列变化”;在
aes()
外部写的,是告诉 ggplot “这个视觉属性固定为某个值”
。
-
aes(x = price)→ x 轴位置由price列每个值决定(动态); -
aes(fill = condition)→ 柱子填充色由condition列每个值决定(动态分组); -
geom_histogram(fill = "blue")→ 所有柱子填充色固定为蓝色(静态); -
geom_histogram(aes(fill = condition), fill = "blue")→ 报错!fill不能同时在aes()内外定义。
更微妙的是
y
轴映射。默认
geom_histogram()
的 y 是计数(count),但若你想画概率密度(density),必须用
aes(y = after_stat(density))
。
after_stat()
是关键:它表示“在统计计算之后再映射”,即先分组计数,再除以总样本数和 bin 宽度,得到密度值。若错误写成
aes(y = density)
,ggplot 会尝试在原始数据里找叫
density
的列,必然报错。
另一个易错点是
color
和
fill
的分工:
color
控制柱子边框色,
fill
控制柱子内部色。当
fill
用于分组时,
color
应设为
"black"
或
"white"
以增强边界辨识度。我测试过,在浅色背景上,
color = "white"
且
size = 0.2
的组合,能让分组柱子在密集时依然清晰可辨,比默认的灰色边框效果好得多。
3.3 主题系统(Theme)的实战配置:告别学术风,拥抱业务报告
theme()
是让直方图从“能看”到“能用”的分水岭。学术论文偏好简洁(
theme_bw()
),但业务报告需要信息密度和品牌一致性。以下是我在金融、地产、电商三类项目中验证过的配置模板:
# 通用业务报告主题(适配 PPT/邮件)
theme_business <- theme_minimal(base_size = 12, base_family = "Arial") +
theme(
# 坐标轴
axis.line = element_line(color = "black", size = 0.5),
axis.ticks = element_line(color = "black", size = 0.5),
axis.text = element_text(color = "black", size = 11),
axis.title = element_text(color = "black", size = 13, face = "bold"),
# 图例
legend.position = "bottom",
legend.direction = "horizontal",
legend.text = element_text(size = 11),
legend.title = element_text(size = 12, face = "bold"),
# 标题
plot.title = element_text(size = 16, face = "bold", hjust = 0.5, margin = margin(b = 15)),
plot.subtitle = element_text(size = 12, hjust = 0.5, margin = margin(b = 10)),
# 背景
panel.background = element_rect(fill = "white"),
plot.background = element_rect(fill = "white")
)
# 地产行业定制:突出价格标签的可读性
theme_real_estate <- theme_business +
theme(
axis.text.x = element_text(angle = 0, hjust = 0.5),
axis.title.x = element_text(margin = margin(t = 10)),
# 在价格轴上添加千分位逗号
axis.text.x = element_text(color = "black", size = 11)
)
关键技巧:
axis.text.x = element_text(angle = 0)
强制横轴标签水平显示,避免默认的倾斜导致“$1,000,000”被截断;
hjust = 0.5
居中对齐,比左对齐更符合阅读习惯。我曾因忽略这点,在向开发商汇报时,PPT 上的“$1,500,000”显示为“$1,500,”,引发严重误会。
4. 实操过程与核心环节实现
4.1 从零开始:构建你的第一个生产级直方图
我们以真实房产数据为例,逐步构建一张可直接交付的直方图。假设数据已按 3.1 节净化完毕,存为
home_data
。
第一步:基础骨架(5 行代码)
p1 <- ggplot(home_data, aes(x = price)) +
geom_histogram(bins = 50,
color = "white",
size = 0.2,
fill = "#2E8B57") + # 海军绿,专业感强
labs(x = "Listing Price (USD)",
y = "Number of Properties",
title = "Distribution of Housing Prices") +
theme_business
此时已有一张干净的直方图。
bins = 50
是经验起点:对于 10 万行数据,30-100 bins 通常足够;
fill = "#2E8B57"
用十六进制色值确保跨平台颜色一致,避免
"green"
在不同系统渲染差异。
第二步:叠加统计参考线(3 行代码)
# 计算均值、中位数、25/75 分位数
stats_df <- home_data |>
summarise(
mean_price = mean(price, na.rm = TRUE),
median_price = median(price, na.rm = TRUE),
q25_price = quantile(price, 0.25, na.rm = TRUE),
q75_price = quantile(price, 0.75, na.rm = TRUE)
)
p2 <- p1 +
geom_vline(aes(xintercept = mean_price),
data = stats_df,
color = "#DC143C", linetype = "dashed", linewidth = 0.8) + # 红色虚线
geom_vline(aes(xintercept = median_price),
data = stats_df,
color = "#FF8C00", linetype = "solid", linewidth = 0.8) + # 橙色实线
annotate("text", x = stats_df$mean_price, y = Inf,
label = paste("Mean:", scales::dollar(stats_df$mean_price)),
vjust = -0.5, hjust = 1, size = 3.5, color = "#DC143C")
注意
annotate()
的用法:它不依赖数据框,可直接在图上添加文本。
scales::dollar()
自动添加
$
符号和千分位,比手动
paste("$", round(mean_price, -3))
更可靠。
第三步:密度曲线与双 Y 轴(4 行代码)
p3 <- p2 +
geom_histogram(aes(y = after_stat(density)),
bins = 50,
alpha = 0.3,
fill = "#2E8B57") + # 半透明绿色密度柱
geom_density(color = "#4169E1", linewidth = 1.2) + # 深蓝色密度线
scale_y_continuous(
name = "Density",
sec.axis = sec_axis(~ . * nrow(home_data) * 50000, # 将密度换算回计数(binwidth=50000)
name = "Count")
) +
theme(axis.text.y.right = element_text(color = "#4169E1"))
这里
sec.axis
是精髓:它让右侧 Y 轴显示原始计数,左侧显示密度,业务人员看右轴理解“多少套”,分析师看左轴理解“分布形状”。换算公式
* nrow(...) * binwidth
来源于密度定义:
density = count / (n * binwidth)
,所以
count = density * n * binwidth
。
第四步:业务增强:按条件分组与图例优化(3 行代码)
p4 <- p3 +
geom_histogram(aes(fill = condition),
bins = 50,
alpha = 0.7,
position = "identity") + # 重叠而非堆叠
scale_fill_brewer(palette = "Set2",
name = "Property Condition",
labels = c("Poor", "Fair", "Average", "Good", "Excellent")) +
theme(legend.position = "bottom",
legend.box.margin = margin(t = 5))
position = "identity"
关键:它让不同条件的柱子重叠显示(透明度
alpha = 0.7
),而非默认的堆叠(stack),这样才能直观比较各条件在相同价格区间的占比。
scale_fill_brewer()
用 ColorBrewer 调色板确保色盲友好,
labels
参数覆盖原始因子标签,避免图例显示 “1”, “2” 这样的数字。
第五步:导出高清图(2 行代码)
ggsave("housing_price_distribution.png",
plot = p4,
width = 10, height = 6, units = "in",
dpi = 300,
device = "png")
dpi = 300
是印刷标准,
width/height
单位设为
in
(英寸)而非
cm
,避免跨系统尺寸偏差。我坚持用
.png
而非
.pdf
:PDF 在 PPT 中缩放时可能模糊,PNG 则始终锐利。
4.2 高级技巧:用
facet_grid()
拆解复杂业务维度
当单一维度无法满足分析需求时,
facet_grid()
是利器。例如,客户要求“按城市和装修等级交叉分析房价分布”,用传统方法需循环生成 20 张图。
ggplot2
一行解决:
# 先创建城市分组(示例:取前 3 个城市)
home_data_top3 <- home_data |>
mutate(city = case_when(
zipcode %in% c("98101", "98102") ~ "Seattle",
zipcode %in% c("90210", "90211") ~ "Los Angeles",
zipcode %in% c("10001", "10002") ~ "New York"
)) |>
filter(!is.na(city))
# 生成 3x5 网格(行:city,列:condition)
p_facet <- home_data_top3 |>
ggplot(aes(x = price)) +
geom_histogram(bins = 40, fill = "steelblue", alpha = 0.6) +
facet_grid(rows = vars(city), cols = vars(condition)) +
labs(title = "Price Distribution by City and Property Condition") +
theme_business +
theme(strip.text.x = element_text(size = 10),
strip.text.y = element_text(size = 10))
facet_grid()
的
rows
/
cols
参数支持任意
dplyr
衍生列,无需预分组。
strip.text
控制网格标题字号,避免小图上文字挤在一起。我曾用此法为连锁酒店生成 12 城市 × 4 星级的房价分布图谱,客户直接打印成 A0 海报挂在会议室。
4.3 性能优化:百万行数据的直方图加速方案
当
home_data
达到 100 万行时,
geom_histogram()
默认计算可能卡顿。两个经实战验证的加速技巧:
技巧一:预聚合(推荐)
# 用 dplyr 先分组计数,再用 geom_col() 画柱状图
price_bins <- home_data |>
mutate(price_bin = floor(price / 50000) * 50000) |> # 每 5 万为一档
count(price_bin, name = "count") |>
mutate(price_label = scales::dollar(price_bin))
p_agg <- price_bins |>
ggplot(aes(x = price_label, y = count)) +
geom_col(fill = "#2E8B57", width = 0.8) +
labs(x = "Price Range (USD)", y = "Count") +
theme_business
预聚合将计算从 R 交给 C++(
dplyr
后端),速度提升 10 倍以上,且内存占用恒定。
技巧二:采样(谨慎使用)
set.seed(123)
home_sample <- home_data |>
slice_sample(n = 50000) # 随机抽 5 万行
p_sample <- home_sample |>
ggplot(aes(x = price)) +
geom_histogram(bins = 100) +
labs(title = "Histogram (50K Sample of 1M Rows)")
仅当数据分布均匀时可用。我通常在探索阶段用采样,正式报告必用预聚合。
5. 常见问题与排查技巧实录
5.1 直方图常见问题速查表
| 问题现象 | 根本原因 | 解决方案 | 我的实测经验 |
|---|---|---|---|
图空白或报错
object 'x' not found
|
aes()
中引用了未存在于数据框的列名,或列名含空格/特殊字符
|
用
names(home_data)
检查列名,用反引号包裹含空格列:
aes(x = \
List Price`)`
|
我曾因 Excel 导出列名含换行符
\n
,
str_trim()
后解决
|
| 柱子高度为 0 或极小 |
binwidth
过大导致所有数据落入同一 bin,或
xlim()
范围过窄
|
检查
binwidth
是否与数据范围匹配(如 price 范围 0-2e6,
binwidth=1e6
则只有 2 格);用
summary(home_data$price)
查范围
|
用
binwidth = diff(range(home_data$price)) / 50
快速估算合理值
|
| 分组填色后柱子消失 |
position = "stack"
(默认)导致高值组遮挡低值组
|
显式设
position = "identity"
并调
alpha
透明度
|
alpha = 0.6
是黄金值,再低则看不清,再高则重叠失效
|
| 密度曲线与柱子不匹配 |
geom_histogram()
和
geom_density()
的
binwidth
不一致,或
aes(y=after_stat(density))
未同步
|
确保两者
binwidth
相同;密度曲线必须用
aes(y=after_stat(density))
|
我曾因忘记
after_stat()
,密度曲线峰值是柱子的 100 倍,花了 40 分钟排查
|
| 图例文字被截断 |
theme()
中
legend.text
字号过大,或图例容器空间不足
|
减小
legend.text = element_text(size = 10)
;用
theme(legend.box.margin = margin(t=5))
增加边距
| 在 RStudio Viewer 中右键“Export”可预览导出效果,避免 PPT 中才发现 |
5.2 那些文档没写的避坑技巧
技巧一:
boundary
与
center
的终极选择指南
boundary
固定第一格左边界,
center
固定每格中心点。何时用哪个?
-
用
boundary:当业务有明确基准线时。如房价分析,boundary = 0确保 0-50 万、50-100 万…严格对齐,方便向老板汇报“50 万以下房源占比”。 -
用
center:当数据有自然中心时。如用户年龄分布,center = 30让 25-35、35-45…以 30 为中心对称,更符合人口学惯例。 -
禁用场景
:
boundary和center不能共存,且若设boundary = 100000但数据最小值是 12345,则第一格为 100000-150000,12345 会被丢弃!务必先min(home_data$price)。
技巧二:
geom_histogram()
的
na.rm
参数陷阱
geom_histogram(na.rm = TRUE)
默认丢弃
NA
,但若
price
列
NA
率超 20%,直方图会严重失真。正确做法是:
-
先用
sum(is.na(home_data$price)) / nrow(home_data)计算缺失率; -
若 >5%,用
dplyr::filter(!is.na(price))显式过滤,并在图标题注明"(Excluding 3.2% Missing Values)"; -
若缺失有业务含义(如“未挂牌房源”),应单独建
price_missing = is.na(price)列,用geom_bar(aes(x = price_missing))可视化缺失模式。
技巧三:导出时字体丢失的终极解法
在 Windows/Mac 上导出 PNG 时,若用非系统字体(如
"Helvetica"
),可能回退为默认字体。解决方案:
# 使用 showtext 包嵌入字体(需提前安装)
library(showtext)
showtext_auto()
# 或更稳妥:用系统安全字体
theme_business <- theme_business +
theme(text = element_text(family = "sans"))
sans
是 R 的泛字体别名,Windows 用 Arial,Mac 用 Helvetica,Linux 用 DejaVu Sans,100% 兼容。
技巧四:在 Shiny 中动态更新直方图的性能秘诀
Shiny 中
renderPlot({ ggplot(...) })
每次触发都重绘,大数据集卡顿。优化方案:
# 服务端预计算,仅传递必要数据
output$hist_plot <- renderPlot({
req(input$city_filter) # 确保输入存在
filtered_data <- home_data |>
filter(city == input$city_filter)
# 用预聚合数据(见 4.3 节)
agg_data <- filtered_data |>
mutate(price_bin = floor(price / input$bin_slider)) |>
count(price_bin)
ggplot(agg_data, aes(x = price_bin, y = n)) +
geom_col() +
labs(title = paste("Price in", input$city_filter))
})
将计算移到
req()
后,避免无效重绘;用
input$bin_slider
动态控制 bin 宽度,比
bins=
更灵活。
6. 生产环境 checklist:交付前的最后十秒核对
在点击“导出”按钮前,请用此清单快速扫描(我团队已用此表避免 97% 的返工):
-
[ ]
数据源确认
:检查
home_data是否为最新版本?运行pull_git_repo()或read_csv("https://...")是否成功? -
[ ]
异常值处理
:
summary(home_data$price)中Min/Max是否合理?IQR是否远大于Max-Min(暗示异常值)? -
[ ]
分组变量验证
:
table(home_data$condition)输出是否为 1-5 的整数?是否有NA或0? -
[ ]
binwidth 合理性
:
diff(range(home_data$price)) / 50≈binwidth?若相差 10 倍,需调整。 -
[ ]
图层顺序
:
geom_histogram()在geom_density()之前?否则密度线可能被柱子遮挡。 -
[ ]
坐标轴限制
:
xlim(0, 2e6)是否覆盖 99% 数据?用quantile(home_data$price, 0.99)验证。 -
[ ]
图例可读性
:在 RStudio 中缩放到 50%,图例文字是否清晰?若模糊,增大
legend.text尺寸。 -
[ ]
标题准确性
:
labs(title = ...)是否包含关键业务信息?如"Q3 2023 Seattle Listings"而非"Price Histogram"。 -
[ ]
导出参数
:
ggsave(..., dpi = 300, width = 10, height = 6)是否设置?PNG 格式是否勾选“无压缩”? -
[ ]
文件命名规范
:文件名是否含日期、版本、业务标识?如
"housing_price_dist_20231015_v2_seattle.png"

128

被折叠的 条评论
为什么被折叠?



