科研数据获取与分析 03:元数据清洗与对齐——把脏数据变成可信数据集

· 约 5 分钟 ·
本文目录(12 节)

前两课定义了采集流程,这一课把不同来源的记录整理成能共同使用的数据表。关键不只是去掉空值,还要解释一行代表什么、同一篇论文怎样合并、冲突字段保留哪一份。只有这些规则明确,后面的统计和建模才有稳定的输入。

前置阅读:Pandas 数据分析与可视化(本篇的所有操作都是那篇的进阶应用)、系列二第 1-2 课(数据来源)。

先盘家底:数据质量审计

清洗之前先量化”有多脏”。对 raw 层跑一次审计,每个字段看三个数:缺失率、异常值、来源间不一致率:

import pandas as pd

raw = pd.read_json("data/raw/papers_2026-08-28.jsonl", lines=True)

audit = pd.DataFrame({
    "missing_rate": raw.isna().mean(),
    "unique_count": raw.nunique(),
    "dtype": raw.dtypes,
})
print(audit.sort_values("missing_rate", ascending=False))

# 按来源看质量差异
print(raw.groupby("source")[["year", "authors", "venue"]]
          .apply(lambda g: g.isna().mean()))

审计时应按来源分别查看缺失和格式差异。例如预印本可能尚无正式发表 venue,网页里的年份也可能写成“in press”。这些情况需要不同处理:尚未发生的发表信息不能凭空补齐,无法解析的年份应保留原值并标记。先处理影响主键、切分和标签的字段,再决定其他字段是否保留;缺失率高本身并不是删除字段的充分理由。

字段级清洗:每个字段一条规则

标题:规范化用于去重,原样用于展示

import re
import unicodedata

def normalize_title(t: str) -> str:
    t = unicodedata.normalize("NFKD", t)      # 统一 Unicode(全角→半角等)
    t = t.lower().strip()
    t = re.sub(r"[^a-z0-9一-鿿 ]", " ", t)     # 标点变空格
    return re.sub(r"\s+", " ", t).strip()

df["title_norm"] = df["title"].map(normalize_title)

注意保留两列:title(原始,展示用)和 title_norm(规范化,匹配用)。规范化是有损操作,原始值必须留底。

年份: coercion + 合理性边界

df["year"] = pd.to_numeric(df["year"], errors="coerce")
# 科研论文的合理范围;范围外的当缺失处理,而不是乱猜
df.loc[~df["year"].between(1950, 2026), "year"] = pd.NA

作者:从四种格式到一种

arXiv 给 ["Ashish Vaswani", ...] 列表,Crossref 给 {"given": "Ashish", "family": "Vaswani"} 对象列表,网页给分号连接的字符串。统一成”名 姓”字符串列表:

def normalize_authors(value, source):
    if value is None or (isinstance(value, float) and pd.isna(value)):
        return []
    if source == "crossref":
        return [f"{a.get('given', '')} {a.get('family', '')}".strip()
                for a in value]
    if source == "conf_page":
        return [n.strip() for n in str(value).split(";") if n.strip()]
    return list(value)   # arxiv / openalex 已是列表

df["authors"] = [normalize_authors(v, s) for v, s in zip(df["authors"], df["source"])]

机构名同理,但要更狠一点:MIT、Massachusetts Institute of Technology、M.I.T. 是同一所——建一个别名映射表手工维护 Top 100 机构,剩下的保留原样。别试图全自动归一,机构名的变体数量超出想象,小映射表 + 人工维护是性价比最高的方案。

重复合并:四条记录的实体对齐

同一篇论文可能以”arXiv 预印本 + OpenAlex 记录 + Crossref 正式版 + 会议名单”四种身份出现。合并策略分两档:

强匹配(自动合):DOI 相同,或 arXiv ID 相同。零误判,直接合。

弱匹配(规则合):title_norm 完全相同 + 第一作者姓氏相同 + 年份差 ≤ 1。三个条件同时满足才合——只凭标题合并会把同名综述论文(“A Survey on X”满地都是)错误合并。

df = df.sort_values("source_priority")   # crossref > openalex > arxiv > conf
merged = df.groupby("merge_key", as_index=False).agg({
    "title": "first",          # 高优先级来源的标题
    "year": "max",             # 正式发表年份优先
    "venue": lambda s: s.dropna().iloc[0] if s.notna().any() else pd.NA,
    "citations": "max",        # 引用数取最大(各来源统计口径不同,取最全的)
    "source": lambda s: "|".join(sorted(set(s))),   # 保留全部来源痕迹
})

source 合并成 "crossref|openalex" 这种形态,来源痕迹永远不能丢——数据出问题时它是唯一的排查线索。

质量门:清洗结果要过验收

清洗不是跑完脚本就结束,要有一组硬指标验收:

def quality_gate(df):
    checks = {
        "总行数": len(df),
        "paper_id 重复率": df["paper_id"].duplicated().mean(),
        "title 缺失率": df["title"].isna().mean(),
        "year 缺失率": df["year"].isna().mean(),
        "作者为空的占比": (df["authors"].str.len() == 0).mean(),
    }
    assert checks["paper_id 重复率"] == 0, "主键仍有重复"
    assert checks["title 缺失率"] < 0.001
    return checks

断言失败的字段打回重洗。这套门在每次数据更新时重跑——清洗规则对新数据失效是常态,门拦住的就是”规则悄悄失效”这件事。

落地:写进 cleaned 层

merged.to_parquet("data/cleaned/papers_2026-08-28.parquet",
                  compression="snappy", index=False)

带日期的文件名 + Parquet 格式,遵循大数据管理一篇的分层纪律。raw → cleaned 的转换脚本进 Git,任何人可以重放。

踩坑排查

症状原因处理
合并后论文数异常少弱匹配条件太严/太松抽样人工检查 50 对,调条件
标题规范化后中文没了正则只保留 a-z字符类里加 一-鿿
year 大量缺失某来源日期字段路径变了回查 raw 列,契约测试报警
同一作者被拆成两个人姓名格式未归一(“Vaswani, A.” vs “Ashish Vaswani”)姓氏+名字首字母做宽松匹配键
cleaned 表行数每天波动大采集层不稳定查上游采集日志,清洗层不背锅
merge 后 citations 倒退取 max 掩盖了来源口径差异保留各来源原值,另算 merged 值

作品集证据

本课产出:一份数据质量审计报告、一套字段级清洗规则、强/弱两档实体对齐逻辑、可重复执行的质量门。“我设计过四来源异构数据的清洗与实体对齐方案,误判率通过抽样验证”——这句是数据分析岗面试的硬通货。

练习

  1. 对 raw 数据跑一次完整质量审计,按缺失率排序输出报告。
  2. 实现弱匹配合并规则,抽样 50 对人工核对,计算精确率和召回率。
  3. 为 Top 20 机构建别名映射表,量化映射前后机构分布的变化。
  4. 把质量门加进清洗脚本,故意构造一份带重复 paper_id 的输入验证断言触发。

面试常问

Q:多来源数据合并的优先级怎么定? 按来源可信度定字段级优先级:Crossref 的出版信息最权威,OpenAlex 的引用关系最全,arXiv 的预印本时间最早,网页数据兜底。合并时保留全部来源痕迹,冲突字段按优先级取信,且优先级规则写进文档。

Q:清洗规则怎么验证有效性? 三件套:抽样人工核对(量化误判率)、质量门硬指标(重复率、缺失率断言)、规则单测(边界输入的输出固定)。只靠”跑完看结果差不多”的清洗不可信。

Q:原始数据为什么要原样保留? 清洗规则会迭代。留 raw 层意味着规则改进后可以不重新采集就重放清洗;同时 raw 是数据出问题时唯一的对账依据。存储成本远低于重采成本。

Q:实体对齐的精确率和召回率怎么权衡? 科研数据场景宁缺毋滥:错误合并(两篇变一篇)比漏合并(一篇变两篇)危害大,前者污染所有下游统计。所以弱匹配设多重条件保精确率,召回率的损失用”来源痕迹保留”来兜底排查。


下一课:科研数据获取与分析 04:探索性数据分析与 Plotly 可视化。

💬 评论

这套方案在哪一步卡住了?也欢迎补充你的实现方式或踩坑经验。

正在连接评论服务,首次访问可能需要几秒…