数据清洗基础:重复值处理、格式统一、数据类型转换
数据清洗,说白了就是「擦干净黑板再写题」。原始组学数据里,重复、错位、格式混乱的情况太常见了。我刚开始做生物信息管线时,被一份基因组注释文件坑了三天——里面居然有 30% 的重复行。从那以后,清洗成了我管线里的头号步骤。
这一章我们聚焦三个最脏的环节:重复值处理、格式统一、数据类型转换。先把它们搞定,后续的特征工程才站得住。
3.1 重复值处理:别让数据「自我复制」
重复值是最常见的「脏东西」。它会让统计量失真,让模型看过的东西被反复加权。我个人习惯在处理管线里单独写一个 dedup 步骤,绝不含糊。
3.1.1 完全重复 vs 部分重复
- 完全重复:整行一模一样。直接删,几乎没争议。
- 部分重复:只有主键或关键列相同。你需要决定「保留哪一行」,或「合并信息」。
用 Python 处理重复值,pandas 是最顺手的选择:
import pandas as pd
# 加载原始表达矩阵
df = pd.read_csv("expression_matrix.csv", sep="\t")
# 查看重复行数量
print(f"重复行数(完全重复): {df.duplicated().sum()}")
# 删除完全重复行
df_clean = df.drop_duplicates()
# 按基因ID去重,保留均值(部分重复场景)
df_dedup = df.groupby("gene_id", as_index=False).mean()
3.1.2 避坑指南:别把「合法重复」删了
我曾经犯过一个低级错误。一份时间序列的蛋白质组数据里,同一个蛋白在不同时间点有不同丰度值。我直接用 drop_duplicates() 把时间戳给干掉了,结果把整个动态变化都删没了。
你应该这样思考:
- 重复的是「实验批次」还是「测量值」?
- 如果是批次,保留最新的。
- 如果是生物学重复,取均值或中位数。
- 如果是技术重复,先评估一致性,再决定聚合方式。
3.2 格式统一:让数据「说同一种语言」
格式不统一是另一个让人头疼的问题。同一个样本名,一个文件写 Sample_001,另一个写 sample-001,第三个写 S001。合并的时候直接崩。
3.2.1 命名规范:小写 + 下划线
我个人习惯:所有列名、样本名、分组名统一转为 小写,空格替换为下划线。这是个简单的规则,但能避免无数的匹配失败。
# 统一列名格式
df.columns = df.columns.str.lower().str.replace(" ", "_")
# 统一样本名格式
df["sample_id"] = df["sample_id"].str.lower().str.replace("-", "_")
3.2.2 日期与数值格式
日期格式的混乱我遇到太多了。有人用 2024-01-15,有人用 01/15/2024,还有人用 15-Jan-2024。你想想看,如果你不统一,时间序列怎么对齐?
我建议一步到位:
# 统一日期为 ISO 格式
df["date"] = pd.to_datetime(df["date"], errors="coerce").dt.strftime("%Y-%m-%d")
数值列也要小心。很多时候从实验仪器导出的文件里,数字里混了中文逗号、单位、甚至空格。
# 去除千分位逗号、单位后缀
df["intensity"] = df["intensity"].str.replace(",", "").str.replace(" ng/ml", "").astype(float)
errors="coerce" 时,无法转换的值会变成 NaN。请务必检查转换后的缺失数量,如果太多,说明原始格式有大问题。
3.3 数据类型转换:让机器「读得懂」
数据类型不对,模型根本没法训练。绝大多数机器学习库只吃数值类型。类别、字符串、布尔值都需要转。
3.3.1 常见类型映射
| 原始类型 | 常见问题 | 转换目标 | 转换方法 |
|---|---|---|---|
| 整数列存为字符串 | 误含空格 | int64 |
astype(int) |
| 小数存为字符串 | 带单位 | float64 |
pd.to_numeric() |
| 分类变量 | 字符型 | category |
astype('category') |
| 布尔值 | “Y/N”字符串 | bool |
map({'Y':True, 'N':False}) |
3.3.2 代码实战:一条管线搞定
# 确认当前各列类型
print(df.dtypes)
# 批量转换数值列
num_cols = ["expression_value", "p_value", "fold_change"]
for col in num_cols:
df[col] = pd.to_numeric(df[col], errors="coerce")
# 分类列转换
cat_cols = ["tissue_type", "condition"]
for col in cat_cols:
df[col] = df[col].astype("category")
# 检查转换结果
print(df.dtypes)
object 类型的列,除非它是真正的自由文本。
3.4 本章小结:干净数据是模型好坏的命门
重复值、格式、类型——这三个问题看似基础,但不处理干净,后面全白搭。我个人的准则是:数据清洗耗时不能少于整个管线开发的 30%。否则,你就是在劣质品上雕花。
现在,你的数据应该是:
- 无完全重复行,部分重复已按规则聚合
- 所有命名格式统一为小写+下划线
- 所有类型都已转换为合适的数值或类别