数据清洗基本功:dplyr包的五虎将

咱们做质量控制分析,有个铁律:垃圾进,垃圾出。数据没洗干净,后面再漂亮的统计模型也是白搭。

我当年刚入行时,接手一批生产线的传感器数据。好家伙,几千行数据里,居然有列名带空格、缺失值写了个"9999"、日期格式混了三种。手工在Excel里改了三天,结果手一抖还拖歪了一列。从那以后,我就铁了心用R的dplyr包来管数据清洗。

今天咱们就聊dplyr里的五个核心函数——filter、select、mutate、arrange、summarise。掌握了这五个,你处理质控数据就能告别复制粘贴了。

dplyr五虎将:数据清洗全流程 原始数据 filter() 行过滤 select() 列选择 mutate() 创建/修改列 arrange() 排序 sum mar ise (聚合) 五个函数串联的典型流程 1. filter():按条件筛行(如:剔除不合格批次) 2. select():只保留需要的列(如:批次号、检测值、判定结果) 3. mutate():计算新指标(如:偏差 = 实测值 - 标准值) 4. arrange():按关键字段排序 → 5. summarise():分组汇总统计

4.1 filter():把垃圾行扔出去

filter() 的活儿很简单——按条件留下需要的行。就像在Excel里做筛选,但dplyr的语法更干净、可重复。

library(dplyr)
# 基础用法:筛选温度在20~30度之间的样本
clean_data <- raw_data %>%
  filter(temperature >= 20, temperature <= 30)

参数说明:

  • 第一个参数是数据框
  • 后续参数写逻辑条件,多个条件默认是 AND 关系
  • 如果想用 OR,写 |

我在项目中最常用的是这个场景:质量检测数据里经常有"预检记录"和"正式记录"混在一起。用filter(tag == "正式")一刀切掉预检数据,干净利落。

最佳实践:质控数据中,filter(!is.na(关键指标)) 这行代码能帮你过滤掉大量不完整记录。跑分析前,先确认缺失值如何处理。

多条件示例:

# 保留A类产品中,重量在100±2g且外观合格的行
valid_A <- products %>%
  filter(category == "A",
         weight >= 98, weight <= 102,
         appearance == "合格")
小技巧:写条件时,把字符串比较写在前面、数值比较写在后面。我一般先在脑子里过一遍"要排除什么",然后写出保留条件。这样逻辑更清晰。

4.2 select():别让无关列碍眼

数据框几百列的时候,你看得眼花?select() 就是你的"列减负工具"。

# 只保留批次号、检测时间和三个关键指标
narrow_data <- wide_data %>%
  select(batch_id, test_time, hardness, thickness, gloss)

常用写法一览:

写法 含义 例子
select(col1, col2) 保留指定列 select(batch, value)
select(-col1) 剔除指定列 select(-remark)
select(starts_with("质量")) 列名以"质量"开头 select(starts_with("qc_"))
select(contains("date")) 列名包含"date" select(contains("时间"))
select(where(is.numeric)) 只选数值型列 常用于批量计算

我记得有一次拿到供应商的数据,他们给了一个200多列的表格,里面还有5列叫"备注1"到"备注5"。我直接用select(-starts_with("备注"))清掉了,省了半小时手工删除。

注意:select()不会更改原始数据框。如果你想把结果保存下来,必须赋值。data <- data %>% select(...)才是真正覆盖。

4.3 mutate():自己动手造新指标

原始数据里没有你想要的计算字段?mutate() 就是你的"造列器"。

# 计算偏差百分比和判定结果
qc_data <- sample_data %>%
  mutate(
    deviation_pct = (measured - nominal) / nominal * 100,
    status = if_else(abs(deviation_pct) <= 5, "合格", "待复检")
  )

几点实战经验:

  • 新列可以引用同一条记录的其他列,也可以引用整个向量
  • 想保留旧列同时加新列,直接写;mutate()默认保留全部列
  • 如果只想保留新列,用 transmute()

我在处理SPC数据时,经常需要算 "这个批次偏离规格中心多少"。一个 mutate(偏移量 = 平均值 - 规格中心) 就搞定。要是手工在Excel里拉公式,几十个文件够你拉一天。

# 多个转换一起做
spc_summary <- batch_stats %>%
  mutate(
    cp = (usl - lsl) / (6 * std_dev),
    cpk = min(
      (usl - mean) / (3 * std_dev),
      (mean - lsl) / (3 * std_dev)
    ),
    conclusion = case_when(
      cpk >= 1.67 ~ "优秀",
      cpk >= 1.33 ~ "良好",
      cpk >= 1.00 ~ "一般",
      TRUE         ~ "需改进"
    )
  )
提醒:用 case_when() 做多条件判定时,条件按顺序匹配,命中第一个就停止。把最严格的条件写在最前面。

4.4 arrange():给数据排排队

数据默认都是按录入顺序排列的。做质控分析时,按时间排序是家常便饭。

# 按生产日期升序,同一天内按批次号降序
ordered_data <- production_log %>%
  arrange(prod_date, desc(batch_no))

排序规则很简单:

  • 默认升序(从小到大、从早到晚)
  • 想降序:在外面套一层 desc()
  • 多个排序条件:依次写,越靠前的优先级越高

我碰到过一个坑:数据框的日期列存的是字符串("2024-01-15"这种),直接用arrange()排序会出现"2024-01-02"排在"2024-01-15"后面——因为字符串是按字典序比较的。得先mutate(date = as.Date(date))转成日期型再排序。

# 正确的日期排序姿势
sorted_by_date <- data %>%
  mutate(test_date = as.Date(test_date, format = "%Y-%m-%d")) %>%
  arrange(test_date)
习惯:我一般把 arrange() 放在清洗的倒数第二步。先筛干净、选好列、算好指标,最后排个序,方便肉眼检查。

4.5 summarise():一句话出统计结果

这是dplyr里最有"分析味"的函数。它的作用是:把多行数据压缩成一行统计量。

# 计算整体合格率、均值和标准差
overall_summary <- inspection_data %>%
  summarise(
    总批次 = n(),
    合格批次 = sum(result == "合格"),
    合格率 = 合格批次 / 总批次 * 100,
    平均强度 = mean(strength, na.rm = TRUE),
    强度标准差 = sd(strength, na.rm = TRUE)
  )

常用的汇总函数:

函数 含义
n() 计数(当前组有多少行)
sum() 求和
mean() 均值
sd() 标准差
min() / max() 最小值/最大值

你可能会问:如果我想按不同产品线分别算合格率呢?这就得配合 group_by() 了。这两个函数是天生搭档。

# 按生产线分组汇总
line_summary <- data %>%
  group_by(生产线) %>%
  summarise(
    批次数量 = n(),
    合格率 = mean(判定 == "合格", na.rm = TRUE) * 100,
    .groups = "drop"
  )
注意:不写 .groups = "drop",分组信息会保留在结果中。后续分析如果不小心再跟其他数据框操作,容易产生奇怪的问题。我建议每次都显式写出 .groups = "drop"。

4.6 案例:一个完整的清洗流程

来,咱们走一遍真实场景。假设你收到了三个月来每个批次的拉力测试数据,需要整理成按周分组的汇总报表。

# 原始数据 raw_data 包含:batch_id, prod_line, test_date,
# strength, thickness, result(合格/不合格)

clean_report <- raw_data %>%
  # 第1步:只保留最近90天的有效检测
  filter(!is.na(strength),
         test_date >= Sys.Date() - 90) %>%
  # 第2步:留有用字段
  select(prod_line, test_date, strength, result) %>%
  # 第3步:计算偏差指标
  mutate(
    周标签 = paste0(
      format(test_date, "%Y"),
      "-W",
      format(test_date, "%V")
    ),
    强度达标 = if_else(strength >= 50, TRUE, FALSE)
  ) %>%
  # 第4步:按产线和周排序
  arrange(prod_line, test_date) %>%
  # 第5步:分组汇总
  group_by(prod_line, 周标签) %>%
  summarise(
    检测数 = n(),
    平均强度 = mean(strength, na.rm = TRUE),
    强度标准差 = sd(strength, na.rm = TRUE),
    合格率 = mean(强度达标, na.rm = TRUE) * 100,
    .groups = "drop"
  )

你看,用了五个函数配上管道操作,整个清洗和汇总流程一目了然。别人改天想复现你的结果,只要跑一遍这段代码就行。比你在Excel里点来点去强太多了。

总结一下:数据清洗不是做苦力,而是把你的分析思路变成可执行的代码。dplyr的这五个函数,就是实现这个目标的最顺手工具。

好了,这一章就到这儿。数据洗干净了,下回咱们就能放心地画图做模型了。