数据探索基础:summary、str、head、glimpse、基本统计量
拿到一个新数据集,你第一件事会做什么?
我见过不少新手直接就开始建模,结果跑出来的模型连自己都看不懂——说白了就是没摸透数据。我个人习惯在特征工程之前,先花 10 分钟做一轮快速数据探索。这步做充分了,后面能省下大量返工时间。
数据探索的整体流程
把数据当成一个陌生人,你要先看它的“长相”(行数、列名、数据类型),再看它的“性格”(缺失情况、分布、异常值)。通常我是按这个顺序来的:
- 用
head()瞄一眼前几行,确认列名和值对不对。 - 用
str()看结构,有没有因子,有没有字符型。写代码。 - 换成
glimpse()再扫一遍,视觉上更舒服。 - 用
summary()拿到五数摘要和频率表。 - 用基本统计量函数(
mean、median、sd、quantile)深入分析感兴趣的特征。
下面这张图把这个流程画出来了,你可以存起来当检查表。
head(data) ; str(data) ; glimpse(data) ; summary(data)。这样整数据的大致轮廓就有了。
逐个工具实战拆解
1. head() —— 先看看样子
head() 默认给你前 6 行数据。我觉得它最大的用处是快速确认列名、单位、是否读对了分隔符。有一次我在项目里读 CSV 时忘了加 stringsAsFactors=FALSE,结果 head() 一看,全是整数,吓得我赶紧改参数。
head(iris)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5.1 3.5 1.4 0.2 setosa
# 2 4.9 3.0 1.4 0.2 setosa
# 3 4.7 3.2 1.3 0.2 setosa
# 想看更多行?
head(iris, n = 10)
2. str() —— 结构解剖刀
str() 会告诉你每列的数据类型、因子水平的前几个值、行数。我特别依赖它来检查是不是有“伪数值”列——比如价格字段因为混了逗号被读成了字符型。这东西很隐蔽,但 str() 一眼就能揪出来。
str(iris)
# 'data.frame': 150 obs. of 5 variables:
# $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 ...
# $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 ...
# $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 ...
# $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 ...
# $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 ...
str()显示年龄列是字符型,没在意直接建模,结果模型预测全是负数。原因是有些样本写了“18岁”,读成字符了。自那以后我对数据类型格外敏感。
3. glimpse() —— 更清爽的结构查看器
glimpse() 来自 dplyr 包,其实是 str() 的“颜值升级版”。它会横向排列每列,直接展示前几个值,看起来像一张漂亮的数据卡片。我个人在交互式探索时更爱用它,因为眼睛不用上下滚动。
library(dplyr)
glimpse(iris)
# Rows: 150
# Columns: 5
# $ Sepal.Length 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, ...
# $ Sepal.Width 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, ...
# $ Petal.Length 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, ...
# $ Petal.Width 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.2, ...
# $ Species setosa, setosa, setosa, setosa, setosa, setosa, set...
4. summary() —— 一页纸的统计快照
这是我最频繁调用的函数,没有之一。对数值列它会输出最小值、第一四分位、中位数、均值、第三四分位、最大值。对因子它会输出各类的频数。连续看几列,异常值(比如负数当正数)、缺失值(NA 的数量)马上现形。
summary(iris)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
# 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
# Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
# Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
# 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
# Max. :7.900 Max. :4.400 Max. :6.900
你看,Petal.Length 的最小值是 1.0,最大值是 6.9,跨度很大——对于特征工程来说,这种变量可能需要标准化或对数变换。
5. 基本统计量函数 —— 深入细节
有时候 summary() 给的粒度还不够。比如我想知道标准差,或者指定百分位数。那就用下面这些核心函数:
mean(iris$Sepal.Length, na.rm = TRUE) # 5.843333
median(iris$Sepal.Length) # 5.8
sd(iris$Sepal.Length) # 0.8280661
quantile(iris$Sepal.Length, probs = c(0.1, 0.9)) # 10% 和 90% 分位点
# 10% 90%
# 4.8 6.9
NA 出现,mean()、sd() 这些函数必须加上 na.rm = TRUE,否则返回 NA。这是新手最容易犯的错——我当年因为这个被代码折磨了一整天,后来把“na.rm”直接焊在了条件反射里。
工具对比一览表
选哪个工具,取决于你想看什么。下面这张表帮你快速决策:
| 函数 | 最适合的场景 | 输出特点 |
|---|---|---|
head() |
快速看前几行数据,确认导入是否正确 | 数据框的前 n 行,默认 6 行 |
str() |
检查每列数据类型、因子水平、缺失情况 | 列名 + 类型 + 前几个值,紧凑存储 |
glimpse() |
交互式探索,更直观的数据结构展示 | 类似 str,但横向布局,值更完整 |
summary() |
数值变量的五数汇总+均值,因子变量频数 | 每列一个分节,数值列六数,因子列计数 |
mean / median / sd / quantile |
针对单列做精确统计,自定义分位点 | 返回单个数值或向量 |
summary(df) 然后翻看“Console”窗口,比在 View() 里点来点去快得多。你可以试试把常用探索命令写成一个短的函数,比如 explore <- function(x) { list(head= head(x), str= str(x), summary= summary(x)) },一键搞定。
实战综合演练
假设我们拿到了 airquality 这个内置数据集(纽约空气质量数据)。按刚才的流程跑一遍:
data(airquality)
head(airquality)
str(airquality)
glimpse(airquality)
summary(airquality)
你会看到 summary() 输出里 Ozone 列有 37 个 NA——这直接告诉你缺失值严重,后续必须处理。另外 Solar.R 也有 7 个 NA。很多人在特征工程早期忽略了这些,等建模才发现模型报错,白折腾。
这就是数据探索的价值。
好了,这一轮探索下来,你应该对数据集的骨架和血型都摸清了。接下来再做缺失值填充、异常值处理、特征构造,心里就有底了。