PySpark如何实现字符串列的整词及复合词组子串匹配
PySpark 整词/完整词组匹配实现方案
现有两种方案的核心缺陷是没有同时兼顾「子串边界校验」和「连续短语匹配」两个要求,以下两种经过验证的方案可以直接使用:
- 基于正则边界匹配的方案:无额外依赖,性能好,适合绝大多数通用场景
- 基于分词+n-gram的方案:精度更高,适合复杂文本场景
方案1:内置正则匹配(推荐优先使用)
利用Spark内置的rlike正则匹配能力,通过自定义边界规则避免子串误判,同时天然支持任意长度的连续词组匹配,不需要提前拆分句子。
正则规则逻辑:
- 自动转义候选词中的正则特殊字符,避免符号导致匹配异常
- 在候选词前后增加边界断言:候选词的前/后位置,要么是句子的开头/结尾,要么是非字母数字的分隔符(空格、标点等),从规则上杜绝长单词内部的子串误匹配
示例代码:
from pyspark.sql import functions as F # 构造测试数据集 df = spark.createDataFrame([ ("su", "We saw the survivors."), # 预期不匹配:su是survivors的子串 ("su", "We saw su yesterday."), # 预期匹配:su是独立单词 ("Roman emperor", "He was a Roman emperor."), # 预期匹配:是完整连续词组 ("Roman emperor", "He was a Roman empire emperor.") # 预期不匹配:词组中间插入了其他词 ], ["candidate", "sentence"]) # 构造匹配表达式 match_logic = F.expr(r""" rlike( sentence, concat( '(^|[^a-zA-Z0-9])', regexp_replace(candidate, '([\\\\.\\\\+\\\\*\\\\?\\\\[\\\\^\\\\]\\\\$\\\\(\\\\)\\\\{\\\\}\\\\|\\\\\\\\])', '\\\\$1'), '($|[^a-zA-Z0-9])' ) ) """) # 执行匹配 df.withColumn("is_match", match_logic).show(truncate=False)
运行输出:
+-------------+------------------------------------+--------+ |candidate |sentence |is_match| +-------------+------------------------------------+--------+ |su |We saw the survivors. |false | |su |We saw su yesterday. |true | |Roman emperor|He was a Roman emperor. |true | |Roman emperor|He was a Roman empire emperor. |false | +-------------+------------------------------------+--------+
适配提示:如果你的业务场景需要匹配带连字符、撇号的词汇(例如
mother-in-law、don't),只需要调整边界规则中的字符排除范围,把需要保留的词内字符加入允许范围即可。
方案2:分词+n-gram匹配(高精度场景适用)
如果你的文本存在复杂语言特征(比如英文时态/复数变形、中文无空格分隔、特殊专业术语),可以用分词+n-gram的方案实现更精准的匹配:
- 对句子做标准化预处理:统一大小写、去除标点、分词、词形还原(比如把
survivors还原为survivor) - 统计每个候选词的分词长度n,对句子的分词结果生成所有连续n个词组成的n-gram片段数组
- 把候选词做和句子一致的标准化处理后,用
array_contains判断候选词是否存在于对应n-gram数组中即可
这个方案可以解决词形变化带来的匹配漏判问题,但是需要引入Spark MLlib的分词、n-gram生成相关组件,性能比纯正则方案稍低,适合对匹配精度要求极高的场景。
内容的提问来源于stack exchange,提问作者A.M.
相关产品推荐
相关产品推荐

