DEBUG.WIKI
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
OnlineSIM:在线接收短信,虚拟号码租用
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →

pandas 3 升级后正则筛选少了行,检查字符串后端的匹配规则

pandas 3 升级后同一正则漏匹配末尾换行,核对 Arrow 与 Python 字符串路径,按既有业务规则固定匹配方式。

同一列文本、同一个 r"a$",升级 pandas 后,末尾带换行的 "a\n" 不再命中;把列转成 object 又能命中。这时先看字符串存储后端。pandas 3 默认推断出的字符串列,在安装了 PyArrow 时可由 Arrow 存储,部分正则操作会进入 RE2;object 字符串列使用的则是 Python re。两者对某些模式的解释不同,结果可能直接改变而不抛异常。

这个分支针对 pandas 3.0 的字符串迁移。可核对到的 3.0.6 实现仍保留 Arrow 正则路径,不能假定装上较新的补丁版就会让所有表达式与 Python re 完全一致。也不要把所有筛选数量变化归因于后端,缺失值、输入清洗和模式本身仍需单独核对。

先保留真正漏掉的文本

普通打印很容易把末尾换行隐藏起来。先从原始数据中选出一条漏匹配的非空值,用 repr() 查看,再记录列的 dtype 和底层数组类型:

import pandas as pd

s = pd.Series(["a", "a\n", None], dtype="str")
print(pd.__version__)
print(s.dtype)
print(type(s.array))
print([repr(value) for value in s.tolist()])

arrow_result = s.str.contains(r"a$", na=False)
python_result = s.astype(object).str.contains(r"a$", na=False)
print(arrow_result.tolist())
print(python_result.tolist())

这是用来比较两条路径的小型示例,dtype="str" 是否实际采用 Arrow,取决于当前安装和配置。两份结果显式使用相同的 na=False,避免把缺失值默认规则的差异混进正则比较。

Python 的 $ 可以匹配字符串结尾之前的最后一个换行位置;RE2 的相应规则并不与它完全相同。如果只有 "a\n" 这一类值出现差异,且底层确实进入 Arrow,才符合这里的条件。没有安装 PyArrow、两个结果完全相同,或者差异发生在另一种模式上时,继续检查实际环境与输入。

需要保留 Python 规则时,只切换这次匹配

如果既有业务明确依赖 Python re,可以在产生布尔掩码时临时转为 object,把结果用于原表:

mask = df["message"].astype(object).str.contains(r"a$", na=False)
selected = df.loc[mask]

message 是示例列名。这种写法保留原表的字符串列,只为这次匹配选择 Python 路径。不要为了修复一个筛选条件,把整张表所有列永久转换成 object,再让后续导出、聚合和类型检查跟着改变。

转换会带来额外对象和计算开销,应使用应用实际的数据规模评估。这里的目的在于固定匹配语义,不能预先承诺性能不变。

如果业务规则本来就是“只匹配整段内容”,可以重新审查是否应使用 fullmatch();如果业务允许忽略末尾换行,应在明确输入规范后做清洗。两种做法都会改变规则,不能只因为新旧结果不同就直接删掉换行或替换锚点。

别按报错清单推断所有正则都走同一个引擎

部分方法会对后行断言、反向引用等不受 RE2 支持的结构回退到 Python。某个复杂表达式能用,并不证明另一个简单表达式也走 Python。直接声明 Arrow dtype 与普通字符串 dtype 的具体调用路径也可能不同。

不要给字符串方法添加未经当前版本支持的 engine="python" 参数,也不要照搬尚未落地的全局正则选项。需要兼容多个部署环境时,把 dtype、PyArrow 是否安装和预期匹配结果一并固定,比仅锁定 pandas 主版本更可靠。

复核样本至少包括不带换行、带一个末尾换行、空串、缺失值,以及业务确实会收到的非 ASCII 文本。比较的是具体命中位置和行号,不能只看最终数量相同。保留原模式与输入样本;若新规则已驱动删除或导出,恢复代码不会自动补回那些结果,应根据保留的输入重新核对受影响的任务。