散点图展示什么
散点图展示两个数值变量之间的关系:每个观测值变成一个点,它的第一个值决定在横轴上的位置,第二个值决定在纵轴上的位置。如果两个变量一起变化,这些点就会形成某种图案;如果没有关系,你得到的就是一团没有形状的点云。在拟合任何模型之前,这都是标准的第一步观察。R 画散点图用的就是 plot() 指南中介绍过的 plot() 函数。
我们用 mtcars,一个包含 32 辆汽车的内置数据集,来回答一个物理问题:车越重,发动机功率越大吗?
画图
wt 是重量,单位是千磅,hp 是马力:
plot(mtcars$wt, mtcars$hp,
main = "Horsepower vs. weight",
xlab = "Weight (1000 lbs)",
ylab = "Horsepower",
pch = 19,
col = "steelblue")
画出来的图是:三十二个实心点,从图的低角(轻车,重量约 1.5,马力约 60 至 90)向上延伸到重量超过 5、马力超过 200 的重车。上升趋势一目了然,但并不整齐:在任何一个重量上,点都分布在相当宽的马力范围内。
样式设置用的是常见的基础绘图工具:pch = 19 画实心圆(默认的空心圆在截图里几乎看不见),col 设置颜色,点需要更大的话就用 cex = 1.3。要按第三个分类变量给点上色,就用因子去索引一个颜色向量:col = c("tomato", "steelblue", "darkgreen")[factor(mtcars$cyl)] 会让每种气缸数都有自己的颜色。
怎么读:方向、强度、形状
每次看散点图,都按顺序问三个问题:
- 方向。 沿着横轴看过去,点是上升的(正相关)还是下降的(负相关)?这里是上升的:越重功率越大。如果换成
mpg对wt作图,点云就是下降的:越重越费油。 - 强度。 这些点有多紧密地贴着同一条路径?像铅笔线一样细的带子是强关系;松散的一片是弱关系。这团点云属于中等紧密。
- 形状与意外。 这条路径是直的还是弯的?有没有聚集的点群,或者离其他点都很远的点?在
mtcars中,Maserati Bora 明显高出其他车一截:中等重量,却有 335 马力。这样一个点就可能明显拉偏拟合的直线,这正是为什么要先看再拟合。
添加趋势线
散点图陈述了一种关系;穿过它的一条线则把这个结论概括出来。拟合一个线性模型,直接交给 abline():
plot(mtcars$wt, mtcars$hp,
pch = 19, col = "steelblue",
xlab = "Weight (1000 lbs)", ylab = "Horsepower")
abline(lm(hp ~ wt, data = mtcars), col = "tomato", lwd = 2)
lm(hp ~ wt) 拟合最小二乘直线(公式读作“用 wt 解释 hp”,纵轴变量写在 ~ 前面),abline() 把它画在图上。这条线的斜率大约是每千磅 46 马力。这个模型的含义,以及怎么读它的汇总结果,是线性回归要讲的内容。
如果你不想预设关系是一条直线,lowess() 会画一条跟着数据走的平滑曲线:
lines(lowess(mtcars$wt, mtcars$hp), col = "darkgreen", lwd = 2, lty = 2)
当 lowess 曲线和直线大致吻合时,用线性关系来概括是合理的。当曲线明显弯离直线时,说明关系是非线性的,用直线来描述就会产生误导。
用 cor() 核对数字
图给你形状;cor() 用一个数字给你强度。这一步只有文本输出,所以可以在这里运行:
重量和马力的相关系数约为 0.66,就是那团中等紧密、向上的点云的数字版本。矩阵还告诉你,mpg 和这两个变量都有很强的负相关(和重量的相关系数约为 −0.87)。不过要记住先后顺序:先画图,再算系数。一个 r 值可能掩盖一条曲线,也可能被一个离群值抬高,它误导人的几种经典方式见相关性。
散点图矩阵:pairs()
当有好几个数值列时,手动画出每一对就很烦了。pairs() 一次调用就能搞定:
pairs(mtcars[, c("mpg", "wt", "hp")],
pch = 19, col = "steelblue")
结果是一个 3 × 3 的网格:变量名沿着对角线排列,每个非对角线面板都是一对变量的散点图,mpg 对 wt、mpg 对 hp、wt 对 hp,每一对都会出现两次,坐标轴互换。这是初步分析新数据集最快的方法:看一眼就知道哪些变量对有关系、哪些关系是弯曲的、离群值藏在哪里。像这里一样先取列的子集:超过六七个变量后,面板会小到看不清。
ggplot2 写法
在 ggplot2 中,散点图加拟合线就是两个图层:
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = hp)) +
geom_point(color = "steelblue", size = 2) +
geom_smooth(method = "lm", color = "tomato") +
labs(title = "Horsepower vs. weight",
x = "Weight (1000 lbs)", y = "Horsepower")
geom_smooth(method = "lm") 相当于 abline(lm(...)),还附送一个福利:线周围有一条阴影表示的置信带。不设置 method 的话,它会改为拟合一条 loess 曲线,也就是 ggplot2 中对应 lowess() 的功能。快速看一眼的话,基础 R 打字更少;一旦你想按分组给点上色并自动生成图例,ggplot2 就更胜一筹。
要点回顾
- 用两个数值向量调用
plot(x, y)就是散点图;pch = 19加上坐标轴标签,就能拿得出手。 - 在计算任何东西之前,先读方向、强度和形状,并找出离群值。
abline(lm(y ~ x, data = df))添加回归线;lines(lowess(x, y))添加一条不预设为直线的曲线。cor()把图中看到的东西量化;图则让这个数字保持诚实。pairs(df[, cols])一次画出所有两两之间的散点图,是初步分析新数据集最快的方法。
下一篇:条形图,离开数值变量对,转而比较各个类别的计数。
常见问题
R 中怎么画散点图?
用两个数值向量调用 plot(x, y),例如 plot(mtcars$wt, mtcars$hp)。每个观测值变成一个点。加上 pch = 19 画实心点,用 main、xlab、ylab 加标签。
R 中怎么给散点图添加回归线?
拟合模型后把它交给 abline():abline(lm(hp ~ wt, data = mtcars)) 会在已有的图上画出最小二乘直线。注意公式的顺序:纵轴变量写在 ~ 前面。
R 中怎么一次画出多对变量的散点图?
pairs(df) 会画出散点图矩阵:每一对列都有一个小面板。先取子集,比如 pairs(mtcars[, c("mpg", "wt", "hp")]),因为超过六七列之后,面板会小得看不清。
散点图能告诉你哪些相关系数告诉不了你的东西?
形状。相关系数只是一个数字,一条干净的直线、一条曲线,或者一团带有一个极端离群值的点云,可能得到完全相同的相关系数。散点图能直接显示弯曲、聚集和离群值,所以要先画图,再计算 cor()。