R语言进阶:三维图形、分布与回归表解读

从多变量可视化到统计推断的直觉理解

尹俊贺

课前说明

第一次运行前,请先安装这几个扩展包。

install.packages(c("scatterplot3d", "plot3D", "plotrix"))

如果你已经安装过,就不用重复安装。

本节课的核心任务

今天这一讲不再重复二维图形,而是回答四个更进阶的问题:

  1. 当二维图只能看到两个变量时,我们怎样进入三维表达?
  2. 当我们面对一组数据时,怎样用统计量快速把握它的基本面貌?
  3. 为什么统计推断总是离不开“分布”,尤其是正态分布?
  4. 当我们看到一张回归表时,到底应该先看哪里,再看哪里?

本节课的逻辑链条

三维图形 用来帮助我们看到多变量结构。

描述性统计 用来帮助我们概括数据的中心、离散和分布。

分布 用来帮助我们理解为什么可以做统计推断。

回归表 用来帮助我们把“看起来有关系”变成“可以被解释的关系”。

为什么需要三维?

二维散点图一次只能展示两个变量。

但真实数据经常是这样的:

  • 油耗 mpg
  • 马力 hp
  • 车重 wt

如果我们只看 mpghp,就可能忽略 wt 的作用。

所以,三维图形的价值并不只是“更炫”,而是它开始逼近真实研究中的多变量思维。

三维散点图:先准备一个简单例子

set.seed(123)
x <- rnorm(100)
y <- rnorm(100)
z <- 0.6 * x + 0.4 * y + rnorm(100, sd = 0.5)

这里我们人为构造了三组数据:

  • x 可以理解为第一个解释变量
  • y 可以理解为第二个解释变量
  • zxy 共同决定

这样做的好处是,图形的结构会比较清晰,学生更容易看出三维关系。

三维散点图:画出来看看

scatterplot3d(
  x, y, z,
  pch = 16,
  color = "steelblue4",
  main = "3D Scatter Plot: z is influenced by x and y",
  xlab = "x",
  ylab = "y",
  zlab = "z"
)

三维散点图:这一页应该怎么理解

看到这张图时,学生至少要抓住三件事:

  • 每一个点代表一个观测值。
  • 横轴、纵轴、竖轴分别对应三个变量。
  • 点不是完全乱飞的,而是在围绕某种空间结构分布。

如果点大体贴近某个平面,通常意味着:

第三个变量可以由前两个变量较好地解释。

这就是回归模型最直观的几何直觉。

三维散点图:它和二维散点图有什么不同

二维散点图只能问:

xy 有没有关系?

三维散点图开始能问:

z 会不会同时受到 xy 的影响?

因此,三维图其实在做一件非常重要的事:

把“两个变量的相关”推进到“多个变量的联合关系”。

三维饼图:核心不是三维,而是“比例”

pie3D(
  x = c(25, 40, 20, 15),
  labels = c("A", "B", "C", "D"),
  explode = 0.08,
  main = "3D Pie Chart: Share of Each Category",
  labelcex = 0.9
)

三维饼图:应该怎么讲

这一页最容易产生一个误区:

3D 饼图看起来更复杂,所以信息更多。

其实不是。

饼图无论是否 3D,本质都在回答同一个问题:

  • 每一部分占整体的比例是多少?
  • 谁占比最大?
  • 谁占比最小?

所以这页最重要的话不是“它很高级”,而是:

3D 在这里主要是视觉效果,核心信息仍然是比例结构。

三类三维图:功能对比

图形 最适合回答的问题 核心单位
3D散点图 多个连续变量之间是否有结构关系
3D柱状图 两个类别维度下的数值差异 柱体
3D饼图 各部分占整体的比例 扇区

这一页的目的,是帮助学生形成“图形和任务匹配”的意识。

图形很好,但它仍然有局限

三维图能帮助我们形成直觉,但它们并不完美。

局限主要有三点:

  • 角度会影响判断
  • 视觉比较不够精确
  • 图不能直接告诉我们“这个关系是否可靠”

这就引出下一步:

我们需要用统计量把图形观察变成更精确的描述。

描述性统计:先从 summary() 开始

summary(mtcars[, c("mpg", "hp", "wt")])
      mpg              hp              wt       
 Min.   :10.40   Min.   : 52.0   Min.   :1.513  
 1st Qu.:15.43   1st Qu.: 96.5   1st Qu.:2.581  
 Median :19.20   Median :123.0   Median :3.325  
 Mean   :20.09   Mean   :146.7   Mean   :3.217  
 3rd Qu.:22.80   3rd Qu.:180.0   3rd Qu.:3.610  
 Max.   :33.90   Max.   :335.0   Max.   :5.424  

summary() 这一页到底要讲什么

summary() 最适合做“第一次看数据”。

它给出的信息虽然不算特别多,但非常关键:

  • Min.:最小值
  • 1st Qu.:第一四分位数
  • Median:中位数
  • Mean:均值
  • 3rd Qu.:第三四分位数
  • Max.:最大值

它帮助我们快速回答:

这个变量大致处在什么水平?

它的分布是集中还是分散?

它有没有特别大的极端值?

如何读 summary():以 mpg 为例

当你看到 mpg 的 summary 输出时,可以这样解释:

  • Mean 告诉我们样本的平均油耗水平。
  • Median 告诉我们中间位置的典型水平。
  • 如果 MeanMedian 差得比较多,往往意味着分布可能不对称。
  • MinMax 告诉我们数据范围。
  • 两个四分位数帮助我们理解“中间一半的数据大概落在哪里”。

这一步其实是在训练学生建立三个基本维度:

中心位置、离散程度、分布形状。

sapply():为什么它很实用

如果变量很多,一个个写 mean()sd() 会很慢。

sapply() 的作用就是:

对一个变量列表,重复做同一件事。

它不是新的统计学概念,而是一个“批量执行工具”。

sapply():一次性算多个均值和标准差

sapply(mtcars[, c("mpg", "hp", "wt")], mean)
      mpg        hp        wt 
 20.09062 146.68750   3.21725 
sapply(mtcars[, c("mpg", "hp", "wt")], sd)
       mpg         hp         wt 
 6.0269481 68.5628685  0.9784574 

sapply() 这一页应该怎么讲

这一页要让学生理解两层意思:

第一层,操作层面:

  • 我们可以一次性对多列做同样计算
  • 代码更短,效率更高

第二层,统计层面:

  • mean 反映平均水平
  • sd 反映波动程度

这正好对应描述性统计中的两个最基本问题:

数据大概在什么位置?

数据围绕这个位置波动得厉不厉害?

为什么要讲“分布”

到这里,学生已经知道:

  • 均值是中心
  • 标准差是离散

但统计学不会停在这里。

因为接下来我们会问:

这些统计量为什么有意义?

为什么我们可以根据样本去推断总体?

答案就和“分布”有关。

正态分布:统计学里最重要的基准分布

curve(
  dnorm(x, mean = 0, sd = 1),
  from = -4, to = 4,
  lwd = 3,
  col = "steelblue4",
  main = "Normal Distribution",
  xlab = "x",
  ylab = "Density"
)
abline(v = 0, lty = 2, col = "firebrick")

正态分布:先抓住两个参数

正态分布最核心的两个参数是:

  • mu:均值,决定曲线中心在哪里
  • sigma:标准差,决定曲线是“高而窄”还是“矮而宽”

所以正态分布可以理解为:

一个以均值为中心、由标准差控制离散程度的对称分布。

正态分布:学生必须知道的三个性质

  1. 左右对称
  2. 中间高、两边低
  3. 大多数数据集中在均值附近

这三点非常重要,因为它们帮助学生建立一个直觉:

真实世界中的很多误差项和很多平均后的统计量,都会“近似地”呈现这种形状。

经验法则:68% - 95% - 99.7%

在正态分布里:

  • 约 68% 的数据落在均值的 ±1 个标准差范围内
  • 约 95% 的数据落在均值的 ±2 个标准差范围内
  • 约 99.7% 的数据落在均值的 ±3 个标准差范围内

这条经验法则的意义在于:

标准差不只是“一个数字”,它对应了数据围绕中心的典型波动范围。

不是所有数据都正态:看一个右偏分布

curve(
  dlnorm(x, meanlog = 0, sdlog = 0.5),
  from = 0.001, to = 4,
  lwd = 3,
  col = "darkorange3",
  main = "Right-Skewed Distribution",
  xlab = "x",
  ylab = "Density"
)

为什么要把右偏分布拿出来对比

因为学生很容易误以为:

所有变量都差不多是钟形曲线。

其实不是。

右偏分布常见于:

  • 收入
  • 财富
  • 企业规模
  • 房价

这些变量经常表现为:

  • 大多数观测值在左边
  • 少数特别大的值把右尾拉长

这时往往会出现:

mean > median

正态分布和统计推断有什么关系

这是这部分最重要的一页。

我们后面要讲:

  • t
  • p
  • 回归表里的显著性

这些概念之所以能成立,不是凭空来的,而是因为统计推断依赖一个前提:

很多估计量在大样本下会近似服从正态分布,或者至少可以用对称分布来逼近。

所以“分布”不是装饰,而是推断的基础。

分类统计:table()

table(mtcars$cyl)

 4  6  8 
11  7 14 

table() 这一页要怎么讲

table() 的任务非常单纯:

统计每个类别出现了多少次。

这一步虽然简单,但很重要,因为只要变量是分类变量,第一件事往往就是先数频数。

例如这里:

  • 4 缸车有多少台
  • 6 缸车有多少台
  • 8 缸车有多少台

比例统计:prop.table()

prop.table(table(mtcars$cyl))

      4       6       8 
0.34375 0.21875 0.43750 

为什么要从频数走到比例

因为单看频数,有时不够直观。

比例能更直接回答:

  • 某一类占总体多少
  • 结构是否失衡

所以可以给学生强调一句:

table() 是“数量”,prop.table() 是“结构”。

交叉列联表:xtabs()

xtabs(~ cyl + gear, data = mtcars)
   gear
cyl  3  4  5
  4  1  8  2
  6  2  4  1
  8 12  0  2

xtabs() 应该怎么解释

这一页是分类变量分析的进阶。

它不再只是看一个变量,而是在问:

两个分类变量之间有没有结构关系?

这里的例子是:

  • 不同的气缸数
  • 对应不同的档位数

所以 xtabs() 的本质就是:

把两个分类变量交叉起来,看看每个组合里有多少观测值。

从列联表过渡到“关系”

如果一个列联表里不同类别组合非常不均匀,往往说明:

  • 这两个分类变量可能存在某种关联

虽然今天这节课不展开卡方检验,但你可以让学生先形成直觉:

结构上的不均匀,本身就提示了变量之间可能有关系。

现在进入回归分析

到这里为止,我们已经做了三件事:

  1. 用图形看关系
  2. 用描述性统计看基本特征
  3. 用分布理解推断基础

接下来,我们要把这些内容合到一起,进入最常见的统计表:

回归表

建立一个简单回归模型

model <- lm(mpg ~ hp + wt, data = mtcars)
summary(model)

Call:
lm(formula = mpg ~ hp + wt, data = mtcars)

Residuals:
   Min     1Q Median     3Q    Max 
-3.941 -1.600 -0.182  1.050  5.854 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 37.22727    1.59879  23.285  < 2e-16 ***
hp          -0.03177    0.00903  -3.519  0.00145 ** 
wt          -3.87783    0.63273  -6.129 1.12e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.593 on 29 degrees of freedom
Multiple R-squared:  0.8268,    Adjusted R-squared:  0.8148 
F-statistic: 69.21 on 2 and 29 DF,  p-value: 9.109e-12

先不要急着看全部输出

summary(model) 的输出很多,第一次看往往会乱。

所以要教学生一个顺序:

  1. 先看模型里有哪些变量
  2. 再看每个变量的系数方向和大小
  3. 再看显著性
  4. 最后再看 R^2

换句话说,不能“每个数字都平等地看”。

把回归表整理成更易读的形式

reg_mat <- summary(model)$coefficients
reg_tab <- data.frame(
  Variable  = rownames(reg_mat),
  Estimate  = round(reg_mat[, 1], 3),
  Std_Error = round(reg_mat[, 2], 3),
  t_value   = round(reg_mat[, 3], 3),
  p_value   = round(reg_mat[, 4], 4),
  row.names = NULL
)
reg_tab
     Variable Estimate Std_Error t_value p_value
1 (Intercept)   37.227     1.599  23.285  0.0000
2          hp   -0.032     0.009  -3.519  0.0015
3          wt   -3.878     0.633  -6.129  0.0000
r2_value <- round(summary(model)$r.squared, 3)
adj_r2   <- round(summary(model)$adj.r.squared, 3)
n_value  <- nobs(model)
cat("R-squared =", r2_value, "\n")
R-squared = 0.827 
cat("Adjusted R-squared =", adj_r2, "\n")
Adjusted R-squared = 0.815 
cat("N =", n_value, "\n")
N = 32 

回归表里最重要的第一列:Coefficient / Estimate

系数的任务是回答:

当这个变量增加 1 个单位时,结果变量平均会变化多少?

例如,如果 wt 的系数是负的,就说明:

  • 在其他变量保持不变的条件下
  • 车重越高
  • mpg 越低

所以系数同时给出两类信息:

  • 方向:正还是负
  • 大小:变化幅度是多少

第二列:Std. Error

标准误不是“另一个系数”,它反映的是:

这个系数估计得稳不稳。

如果标准误很大,说明:

  • 这个系数在不同样本里可能波动较大
  • 我们对这个估计的把握就没有那么强

    所以标准误是“系数的稳定性信息”。

第三列:t 值

回归输出里的 t value 本质上就是:

\[ t = \frac{\hat{\beta}}{SE(\hat{\beta})} \]

可以把它理解为:

系数相对于自身噪音有多大。

如果绝对值比较大,通常意味着这个结果不像是纯随机波动造成的。

第四列:p 值

p value 经常让初学者最紧张,但其实你可以把它讲得很直白:

如果这个变量实际上没有作用,那么像现在这样极端的结果出现的概率有多大?

因此:

  • p 很小:更不容易是随机巧合
  • p 较大:我们就不能很有把握地说它有作用

课堂上完全没必要把假设检验的全过程都展开,但一定要讲清:

p 值不是“作用大小”,而是“证据强度”。

R^2:整体解释力

R^2 的问题不是“单个变量怎么样”,而是:

整个模型对结果变量解释得怎么样?

例如,R^2 = 0.75 可以理解为:

  • 模型解释了大约 75% 的样本波动

它告诉我们模型整体拟合得好不好,但它不能代替对单个变量系数的解释。

读一行回归结果:必须训练这个能力

假设我们看到这样一行:

  • wt = -3.878
  • Std.Error = 0.633
  • t = -6.129
  • p < 0.001

标准读法应该是:

在控制马力之后,车重对油耗具有显著负向影响。车重每增加 1 个单位,油耗指标 mpg 平均下降约 3.878 个单位,而且这个结果具有很强的统计证据。

这一句非常重要,因为它把:

  • 方向
  • 大小
  • 可信度

三个层面合在了一起。

一张论文风格的表,应该先看什么

如果你把来龙去脉都压缩进一张论文表,最建议学生按这个顺序阅读:

  1. 先看因变量是谁
  2. 再看核心自变量的系数符号
  3. 再看系数大小是否有实际意义
  4. 再看星号或 p 值
  5. 最后看 R^2 和样本量 N

不要一上来盯着星号,也不要只看 R^2

学生最常犯的三个错误

第一,认为“显著”就等于“影响很大”。

  • 错。显著只表示证据较强,不等于效果一定大。

第二,认为 R^2 高就说明模型一定正确。

  • 错。R^2 只是拟合度,不代表因果识别完全没问题。

第三,认为系数就是因果关系。

  • 也错。回归首先给出的是条件相关关系,是否能解释为因果,要看研究设计。

#回归表

# 如果没安装,先取消注释运行一次
# install.packages("stargazer")

library(stargazer)

# 使用内置数据
data(mtcars)

# 建立两个模型(方便讲“控制变量”)
model1 <- lm(mpg ~ hp, data = mtcars)
model2 <- lm(mpg ~ hp + wt, data = mtcars)

# 输出回归表(HTML格式适合PPT)
stargazer(model1, model2,
          type = "html",
          title = "Regression Results",
          dep.var.labels = "Miles per Gallon (mpg)",
          covariate.labels = c("Horsepower", "Weight"),
          digits = 3,
          star.cutoffs = c(0.1, 0.05, 0.01),
          align = TRUE)
Regression Results
Dependent variable:
Miles per Gallon (mpg)
(1) (2)
Horsepower -0.068*** -0.032***
(0.010) (0.009)
Weight -3.878***
(0.633)
Constant 30.099*** 37.227***
(1.634) (1.599)
Observations 32 32
R2 0.602 0.827
Adjusted R2 0.589 0.815
Residual Std. Error 3.863 (df = 30) 2.593 (df = 29)
F Statistic 45.460*** (df = 1; 30) 69.211*** (df = 2; 29)
Note: p<0.1; p<0.05; p<0.01

📄 Slide 1:回归表整体结构

## 📊 回归表怎么看?

> 一张回归表,本质是在回答三个问题:

1. 哪些变量影响了结果?
2. 影响方向是什么?影响有多大?
3. 这个结果是否可靠?

---

我们主要关注三类信息:

- 系数(Coefficient):影响方向和大小  
- 标准误(Standard Error):不确定性  
- 显著性(Significance):是否可信  

---

同时也要看模型整体:

- R²:解释力  
- 样本量(N)  
- F统计量:整体显著性  

📄 Slide 2:核心变量(Horsepower + Weight)

## 🔍 核心变量解释

Horsepower(马力):

- Model (1): -0.068*** (0.010)
- Model (2): -0.032*** (0.009)

👉 解释:

马力增加 → 油耗下降(负向关系)

👉 关键点:

加入车重后,影响变小  
→ 存在遗漏变量偏误

---

Weight(车重):

- -3.878*** (0.633)

👉 解释:

车重每增加1单位,mpg下降约3.878  

👉 结论:

车重的影响强于马力

📄 Slide 3:标准误 + 显著性

## 📉 不确定性与显著性

标准误(Standard Error):

- (0.010), (0.009), (0.633)

👉 含义:

估计的不确定性  
数值越小 → 结果越稳定  

---

显著性(***):

- *** → p < 0.01

👉 含义:

结果不是随机的  

---

t 值直觉:

t ≈ 系数 / 标准误  

例如:

-0.032 / 0.009 ≈ -3.5 → 显著

📄 Slide 4:模型整体(R² + 误差)

## 📈 模型整体表现

R²:

- Model (1): 0.602  
- Model (2): 0.827  

👉 含义:

模型解释了多少数据变化  

👉 关键:

加入车重 → 解释力明显提升  

---

Residual Std. Error:

- 3.863 → 2.593  

👉 含义:

预测误差  

👉 结论:

模型 (2) 更准确

📄 Slide 5:其他信息(Constant + F)

## 🧩 其他信息

Constant(常数项):

- 30.099*** / 37.227***

👉 含义:

当所有变量为0时的基准值  

👉 一般不是重点  

---

F Statistic:

- 45.460*** / 69.211***

👉 含义:

模型整体是否有效  

👉 结论:

两个模型整体显著

:最终总结(最重要)

🧠 如何总结一张回归表?

👉 标准表达:

马力和车重对油耗都有显著负向影响,
在加入车重后,模型解释力显著提升(R²从0.60到0.83),
同时马力的影响减弱,说明车重是一个重要的控制变量。

🎯 一句话总结

回归表回答的是:

  • 谁在影响结果
  • 影响有多大
  • 这个结论是否可信

把今天的内容串起来

现在回头看,我们今天做的其实是同一件事的四个层次:

  • 三维图形:让你先“看到”多变量结构
  • 描述性统计:让你快速概括数据
  • 分布:让你理解统计推断为什么成立
  • 回归表:让你把关系写成可以解释的数字

所以它们不是分散的知识点,而是一条连续的学习路径。

结论页

今天最重要的收获可以概括成三句话:

  1. 当变量不止两个时,三维图帮助我们形成多变量直觉。
  2. 当我们想快速理解数据,summary()sapply()table()xtabs() 是最实用的入口。
  3. 当我们看到回归表时,先看系数方向和大小,再看显著性,最后看整体解释力。

Q&A

你们现在最应该尝试做的一件事,不是背定义,而是练习把一张回归表“翻译成一句完整的人话”。