Introduction
散点图是两个连续变量最直接的入口:一行数据一个点,横轴一个变量,纵轴另一个。先看点云有没有方向、弯曲、分团、离群和重叠,再决定要不要相关系数或拟合模型。
先画干净的散点,别一上来就加拟合线——线会替读者先把点云总结掉,弯曲、分团和离群反而容易被略过。要模型时再去 scatter_fit。
分组也不是散点图的必需部分。没有真实分组就画普通散点;有分组时,再从形状、颜色或两者共同编码里挑一种。
读的时候按这个顺序:
- 先看整体形状。 点云是上升、下降、弯曲,还是没有明显方向。
- 看组内聚集。 同一种颜色是否聚在相近区域,组间是分开还是重叠。
- 再看离散程度。 同一组内部的点散得多宽,有没有次级结构。
- 找离群点。 尤其是远离本组主体、可能改变后续相关或回归结果的那些。
Example Data
散点图要的输入是:2 个连续变量,同一批观测,一行一个观测;可选再加 1 个分组变量。两个变量必须来自同一行,否则连成对都谈不上。
iris 是 150 朵花分属三个物种。这里用花瓣长和花瓣宽——三个物种会形成清楚但并非完全无重叠的点群,正适合演示分组。
library(ggplot2)
library(ggpubr)
library(biopalette)
# 公开地址,和数据集页上「下载 CSV」给的是同一个 —— 不写仓库相对路径:
# 那个目录不进仓库,读者 clone 下来也没有这个文件,这段代码就跑不了
d <- read.csv("https://assets.evanzhou.org/tessera/csv/iris.csv")
# 分组变量必须是 factor。写成 1/2/3 那种数值编码时也要先转 —— 否则会得到
# 一根连续色条,图能画出来,只是图例把三个组说成了一个连续量
d$Species <- factor(
d$Species,
levels = c("setosa", "versicolor", "virginica")
)
Palettes
三个物种是三个无序类别,用定性色板 walter_white2 里区分较清楚的青、橄榄和橙三色。只有三个真实分组就只取三色,不为了展示完整五色增加虚假类别。
浅色点在白底上容易消失,所以颜色版统一用带深色细边的圆点。边线只负责保持轮廓,不编码第二个变量。
形状是另一套通道,但它只在和颜色映射同一个变量时才该出现。颜色和形状分别映射两个不同分组,读者要同时解两套图例,信息密度很快就失控。
walter_white2 <- get_palette("walter_white2", type = "qualitative")
species_colors <- setNames(
walter_white2[c(1, 2, 4)],
levels(d$Species)
)
# 21/22/24 是带边框的圆、方、三角:fill 上色、color 画边,两者能分开控制
species_shapes <- setNames(c(21, 22, 24), levels(d$Species))
# 五张图共用同一套坐标范围和版式,轴一致才横着比得了
scatter_scales <- list(
scale_x_continuous(
breaks = 1:7,
limits = c(0.9, 7.1),
expand = expansion(mult = c(0, 0))
),
scale_y_continuous(
breaks = seq(0, 2.5, 0.5),
limits = c(-0.05, 2.55),
expand = expansion(mult = c(0, 0))
)
)
scatter_theme <- theme_pubr(base_size = 13, legend = "right") +
theme(
panel.grid.major = element_line(color = "#E5E3DC", linewidth = 0.35),
panel.grid.minor = element_blank(),
axis.line = element_line(color = "#333330", linewidth = 0.45),
axis.ticks = element_line(color = "#333330", linewidth = 0.4),
plot.title = element_text(face = "bold", size = 15, hjust = 0),
legend.title = element_text(face = "bold"),
legend.key.height = grid::unit(5, "mm"),
strip.background = element_blank(),
strip.text = element_text(face = "bold"),
plot.margin = margin(14, 16, 12, 12)
)
Recipe
| No. | Method | Input Data | Palettes |
|---|---|---|---|
| 1 | ggplot2 |
d |
— |
| 2 | ggplot2 |
d |
species_shapes |
| 3 | ggplot2 |
d |
species_colors |
| 4 | ggplot2 |
d |
species_colors / species_shapes |
| 5 | ggplot2 |
size_demo |
species_colors |
1 · 普通散点
没有分组要表达时,只画点。不要为了"丰富"画面主动加颜色或形状。
#| fig: basic
#| fig-width: 7.4
#| fig-height: 5.1
ggplot(d, aes(Petal.Length, Petal.Width)) +
# alpha 不是装饰:点一重叠,只有半透明才看得出那里叠了几个
geom_point(size = 2.7, alpha = 0.72, color = "#333330") +
scatter_scales +
labs(
title = "Iris petal measurements",
x = "Petal length (cm)",
y = "Petal width (cm)"
) +
scatter_theme



