如何使用 pandas 安全地分割带可选标注的分数字符串并提取第二部分

本文介绍在 pandas 中安全处理含空格分隔的分数字符串(如 `'20 m b'` 或 `'25'`),通过 `str.split()` 提取主数值与可选备注,避免因字段缺失导致的 `keyerror` 或 `valueerror`。

在数据清洗中,常遇到类似 '20 M B, 25, 21' 这样的混合字符串:每个逗号分隔项是一个“分数+可选字母标注”组合,目标是将每项拆成独立行,并分离出纯数字(score)和后续备注(notes)。直接使用 str.split(' ', 1, expand=True)[1] 会因某些项无空格而报错 "KeyError: 1"——因为 expand=True 仅返回实际存在的列数(单值时只有 [0]),索引 1 不存在。

推荐方案:用 reindex() 统一列结构
最简洁、健壮的做法是强制对 split(..., expand=True) 的结果进行列对齐,确保始终存在 [0] 和 [1] 列(缺失时自动填充 NaN):

# 步骤1:按逗号切分并展开为多行
df['score'] = df['score'].str.split(r',\s*')  # 使用正则处理空格,更鲁棒
df = df.explode('score').reset_index(drop=True)

# 步骤2:安全分割 score 字段 → 分离数值与备注
split_parts = df['score'].str.split(' ', n=1, expand=True).reindex(columns=[0, 1])
df['score'] = split_parts[0]
df['notes'] = split_parts[1]

⚠️ 注意:n=1 表示最多分割一次(保留 'M B' 作为整体),reindex(columns=[0, 1]) 是关键——它保证即使原字符串无空格(如 '25'),也会生成两列,其中 [1] 自动为 NaN,彻底规避索引错误。

? 进阶替代:正则一步提取(更语义化)
若原始格式较规范(数字后跟空格+非逗号字符),可用 str.extractall() 直接捕获结构化信息,无需手动 explode + split:

# 提取所有匹配项(自动处理多值),并关联原表其他列
pattern = r'(?P\d+)(?:\s+(?P[^,]+))?'
extracted = df['score'].str.extractall(pattern).droplevel('match')
result = df.drop('score', axis=1).join(extracted)

该正则含义:

  • (?P\d+):捕获连续数字(必选);
  • (?:\s+(?P[^,]+))?:非捕获组,匹配空格后任意非逗号字符(可选,命名 notes)。

✅ 优势:天然兼容无备注场景(notes 自动为 NaN),代码更声明式,且避免中间 explode 的内存开销。

? 总结建议

  • 日常使用优先选 reindex() 方案:逻辑清晰、易调试、兼容性强;
  • 数据量大或格式稳定时,推荐正则 extractall():性能更优、语义明确;
  • 始终用 n=1 限制 split() 次数,避免误切备注中的空格(如 'M B');
  • 替换 ", " 为 r',\s*' 可鲁棒处理 '20,25' 或 '20, 25' 等变体。