Pandas 字符串操作:向量化文本清洗
Updated on
文本列驱动搜索、分群和特征工程,但许多流水线仍然逐行循环或混用 None/NaN,导致筛选错误和代码缓慢。
- 问题: 大小写、空白符和部分匹配混乱,使得 join 和分析结果不可靠。
- 痛点: 按行的 Python 循环很慢,而不一致的缺失值(
NonevsNaN)会让比较逻辑失效。 - 解决方案: 使用 pandas 的字符串 dtype,配合向量化
.str方法、显式的正则控制,以及安全的 split / explode,让文本处理既快又可预测。
Want an AI agent that understands your pandas text cleaning and notebook context?
RunCell is a JupyterLab AI agent that can read your code, analyze DataFrames, understand notebook context, debug errors, and even generate & execute code for you. It works directly inside JupyterLab—no switching windows or copy-pasting.
👉 Try RunCell: runcell.dev (opens in a new tab)
快速参考
| 目标 | 方法 | 示例 |
|---|---|---|
| 规范大小写/空白符 | .str.casefold(), .str.strip() | s.str.strip().str.casefold() |
| 字面替换(快速) | .str.replace(..., regex=False) | s.str.replace("-", " ", regex=False) |
| 正则提取 / 查找 | .str.extract(), .str.contains() | s.str.extract(r"(\d{4})") |
| 拆分为多列 | .str.split(expand=True) | s.str.split("/", expand=True, n=2) |
| 拆分为多行 | .str.split().explode() | df.assign(tag=df["tags"].str.split(",")).explode("tag") |
| 长度/计数 | .str.len(), .str.count() | s.str.count(r"\d") |
从合适的字符串 dtype 开始
import pandas as pd
df["title"] = df["title"].astype("string[python]")- 使用
stringdtype(或在安装 Arrow 时使用"string[pyarrow]")会将缺失值统一为<NA>,这与.str方法和比较运算更兼容。 - 尽量避免混用 Python 对象;
object列在文本操作上更慢,也更不一致。
核心清洗模式
df["clean_name"] = (
df["name"]
.str.strip()
.str.replace(r"\s+", " ", regex=True)
.str.title()
)- 对于简单的字面替换,使用
regex=False,可以避免被当作正则解析,同时提升速度。 .str.normalize("NFKC")可以统一 Unicode 变体,在处理用户输入匹配时很有用。
去噪与统一大小写
df["search_key"] = df["product"].str.casefold().str.replace("-", " ", regex=False)casefold 比 lower 更适合国际化文本比较。
筛选与提取
# 精确的前缀/后缀检查
promo_rows = df[df["code"].str.startswith("PROMO", na=False)]
# 带 NA 安全处理的正则 contains
has_year = df["description"].str.contains(r"\b20\d{2}\b", regex=True, na=False)
# 捕获分组到列
df[["year", "country"]] = df["id"].str.extract(r"(?P<year>\d{4})-(?P<country>[A-Z]{2})")- 在过滤时设置
na=False,将缺失值视为“不匹配”,避免布尔索引崩溃。 - 在
extract中使用命名分组,可以直接得到语义清晰的列名,无需额外重命名。
Split、Expand 和 Explode
# 拆分成多列
df[["city", "state"]] = df["location"].str.split(",", n=1, expand=True)
# 将类似列表的列拆分成多行
tags = df.assign(tag=df["tags"].str.split("|"))
tags = tags.explode("tag").assign(tag=lambda t: t["tag"].str.strip())- 使用
expand=True保持列对齐;当单元格中包含多个 token 需要变为多行时,优先使用explode。 - 在 join 或聚合之前,对拆出来的片段做清洗(
str.strip,str.casefold)。
统计与度量文本
df["word_count"] = df["abstract"].str.split().str.len()
df["digit_count"] = df["code"].str.count(r"\d").str.len()会正确处理<NA>;当输入为stringdtype 时,结果为Int64。- 如果下游数值流水线需要普通整数,可以结合
fillna(0)做填充。
性能与安全提示
- 优先使用向量化的
.str方法而不是apply(lambda ...);前者更快且更好地处理 NA。 - 仅需字面替换时显式指定
regex=False,减少正则开销并避免意外匹配。 - 对于大文本列,可考虑使用
string[pyarrow]以减少内存占用并加速常见操作(需要安装pyarrow)。 - 尽量只对关键字段做一次标准化(strip + casefold),然后复用这些规范化键来做 join 或去重,而不是多处重复计算。
向量化字符串操作能让文本清洗简洁且高效。可以将其与 pandas-data-cleaning 配合进行更广泛的数据质量检查,并在规范化键准备好之后,结合 pandas-merge-join 实现可靠的 join。