如何使用spaCy Matcher为被分词为单个Token的序列创建规则匹配模式
如何使用spaCy Matcher为被分词为单个Token的序列创建规则匹配模式
嗨Tarran,我完全懂你遇到的困扰——spaCy的分词器把IQ=68这种连在一起的组合当成了单个token,导致你原本针对三个token设计的pattern2根本匹配不上对吧?别担心,有几个实用的解决方案,我给你一步步讲清楚:
方法一:用正则表达式匹配单个Token的文本
既然IQ=68被当成了一个完整的token,那我们直接针对这个单个token的文本写匹配规则就行。用TEXT属性搭配正则表达式,就能精准捕捉到这种包含IQ、等号(可选)和数字的格式:
import spacy from spacy.matcher import Matcher nlp = spacy.load("en_core_web_sm") matcher = Matcher(nlp.vocab) # 单个token的匹配模式:不区分大小写,匹配iq开头、等号可选、后跟数字的格式 pattern_single_token = [ {"TEXT": {"REGEX": r"^iq=?\d+$", "LOWER": True}} ] matcher.add("IQ_SCORE", [pattern_single_token]) # 测试文本 doc = nlp("Patient's IQ=68, previous assessment noted IQ 72 and iq90.") matches = matcher(doc) for match_id, start, end in matches: span = doc[start:end] print(f"匹配到IQ分数: {span.text}")
这个正则表达式^iq=?\d+$的意思是:以iq开头(不区分大小写),等号是可选的,后面跟着一个或多个数字,刚好能覆盖IQ=68、IQ72、iq90这类常见格式。
方法二:调整spaCy的分词规则,拆分组合Token
如果你希望从根源上解决这类问题——让spaCy把=单独拆分成一个token,那可以修改分词器的前缀规则,让等号被识别为单独的分词单元。这样你原来的pattern2就能正常工作了:
import spacy from spacy.matcher import Matcher from spacy.util import compile_prefix_regex nlp = spacy.load("en_core_web_sm") # 修改分词器的前缀规则,添加等号作为前缀 prefixes = list(nlp.Defaults.prefixes) prefixes.append("=") nlp.tokenizer.prefix_search = compile_prefix_regex(prefixes).search # 现在用你原来的pattern2就行 pattern2 = [{"LOWER": "iq"}, {"TEXT": "=", "OP": "?"}, {"IS_DIGIT": True}] matcher = Matcher(nlp.vocab) matcher.add("IQ_SCORE", [pattern2]) doc = nlp("Patient's IQ=68, previous assessment noted IQ 72.") matches = matcher(doc) for match_id, start, end in matches: span = doc[start:end] print(f"匹配到IQ分数: {span.text}")
不过要注意,调整分词规则可能会影响其他文本的分词结果,比如如果文本里有a=b这类内容,也会被拆成a、=、b,你需要确认这种拆分符合你的整体需求。
方法三:结合PhraseMatcher补充匹配
如果你的临床总结里有很多固定格式的IQ分数短语(比如IQ=68、IQ 75),还可以用PhraseMatcher来匹配这些固定短语,作为Matcher的补充:
import spacy from spacy.matcher import PhraseMatcher nlp = spacy.load("en_core_web_sm") matcher = PhraseMatcher(nlp.vocab) # 准备固定短语列表 iq_phrases = ["IQ=68", "IQ 72", "iq90"] phrase_patterns = [nlp(text) for text in iq_phrases] matcher.add("IQ_SCORE", phrase_patterns) doc = nlp("Patient's IQ=68, previous assessment noted IQ 72.") matches = matcher(doc) for match_id, start, end in matches: span = doc[start:end] print(f"匹配到IQ分数: {span.text}")
这种方法适合已知所有可能格式的场景,灵活性不如正则,但匹配速度会更快。
总结一下,如果你只是临时解决IQ=68这类单个token的匹配问题,方法一的正则方案最快捷;如果需要长期处理这类带符号的组合,方法二的分词规则调整更彻底;方法三则适合固定短语较多的情况。
备注:内容来源于stack exchange,提问作者Tarran
相关产品推荐
相关产品推荐

