Python 正则表达式否定回顾

Python Regex Negative Lookbehind

我有一个很大的 CT 扫描结果和印象数据库。我正在尝试构建一个正则表达式,该正则表达式搜索整数或浮点数,后跟 'mm' ,它与前面或后面的单词 'nodule' 相邻。到目前为止,这是我的正则表达式:

nodule_4mm_size = "(?s).*?([0-4]*\.*[0-9]+\s*[mM]{2})[\w\W]{0,24}[Nn]odule|(?s)[Nn]odule[\w\W]{0,24}.*?([0-4]*\.*[0-9]+\s*[mM]{2})”

但是,我需要确保这些发现之前没有之前或之前的测量结果。放射科医生指的是以前的扫描。所以我正在尝试消极的回顾,就像这样:

(?<!previously measured)\?[Nn]odule[\w\W]{0,24}[^\.\d]([0-4]\s*[mM]{2}|[0-3]\.[0-9]\s*[mM]{2}|4\.0+\s*[mM]{2})

但是,我无法让它工作。以下面这段话为例。

"For example, the largest nodule which is located in the right lower lobe and currently measures 4.4 mm (image #82, series 3) previously measured 3.6 mm on 09/01/2011."

在这种情况下,我希望正则表达式命中 4.4 毫米而不是 3.6 毫米。此外,如果发现多个命中,我只想保留找到的最大尺寸。例如,

"For example, the largest nodule which is located in the right lower lobe and currently measures 4.4 mm (image #82, series 3) previously measured 3.6 mm on 09/01/2011. Another nodule was found measuring 2.2 mm.

在这种情况下,我想确保只识别出 4.4 毫米。

任何帮助将不胜感激。就是不能让这种消极的回顾工作!谢谢!

两种可能:

1) 使用回顾:

(?<!previously measured )(?<![0-9.])([0-9]+(?:\.[0-9]+)?) ?mm

第一个检查 "previously measured " 是否不在数字之前,第二个检查数字之前是否没有数字或点(否则点之后的 4 将匹配。请记住正则表达式engine returns 左边第一个结果)。

2) 使用捕获组:

previously measured [0-9]+(?:\.[0-9]+)? ?mm|([0-9]+(?:\.[0-9]+)?) ?mm

思路是匹配你之前想避免的。当capture group 1存在时,你就得到了一个结果。

关于最大的数,用re.findall的方法,然后取最大的结果(正则表达式解决不了这种事情)。

如果附近需要nodule字,可以试试:

(?:((?<!previously measured\s)\d+.\d+\s*mm)(?:[^.?!\n]*?)?nodule|nodule(?:[^.?!\n]*?((?<!previously measured\s)\d+.\d+\s*mm))?)

DEMO

它将匹配如果:

  • 结节与mm值在同一句话中([^.?!\n] 应该阻止它,但是像 Mr.、decimals 等词会干扰 匹配),你可以用 .+? (DEMO) 替换它,但是它可以在句子之间匹配
  • 值在单词nodule之前或之后(在这个排序中,如果有 是之前的值,先匹配),
  • 值将分组捕获:之前 - \1,之后 - \2,
  • 它应该与 g 和 i 模式一起使用

其他类似的解决方案是:

(?=((?<!previously measured\s)\d+.\d+ mm)[^.?!]+nodule)|(?=nodule[^.?!]+((?<!previously measured\s)\d+\.\d+ mm))

DEMO

仅基于环视,它不会直接匹配文本而是零长度位置,并将值捕获到组中。

让我们分解一下,保留相关部分。现在你有两个选择:

选项 1(数字后跟“nodule”):

([0-4]\.\d+\s*[mM]{2})[\s\S]{0,24}[Nn]odule

选项 2(“nodule”后跟数字):

[Nn]odule[\s\S]{0,24}([0-4]\.\d+\s*[mM]{2})

您应该知道正则表达式引擎是 greedy。这意味着 [\s\S]{1,24} 会尽可能匹配,匹配不一定最接近“nodule”的数字。例如,

Pattern: [Nn]odule[\s\S]{0,24}([0-4]\.\d+\s*[mM]{2})

Text: ... nodule measured 1.4 mm. Another 3.2 mm ...
                                          ^    ^
                                          |    |
          matches this second occurence.  +----+

要解决此问题,请在量词后添加额外的 ? 使其成为 lazy。因此,不要使用 [\s\S]{0,24},而是使用 [\s\S]{0,24}?.


For example, the largest nodule which is located in the right lower lobe and currently measures 4.4 mm

此处的示例中“nodule”由超过 24 个字符分隔。您应该增加中间的字符数。也许 [\s\S]{0,70}?.


So I am trying a negative lookbehind

Lookbehinds 仅断言紧接在特定位置之前的文本。为避免这种情况,我建议匹配文本“previously measured”,并在其周围消耗一些字符。那么,您怎么知道不考虑这些情况呢?简单,不要创建捕获。所以你会匹配类似

的东西
[\s\S]{0,10}previously measured[\s\S]{0,10}

并放弃匹配,因为它没有 return 任何组。此外,您可以在此处包括不同的例外情况:

[\s\S]{0,10}(?:previously measured|previous scan|another patient|incorrectly measured)[\s\S]{0,10}

if multiple hits are found I would like to only keep the largest size found

你不能用正则表达式来做到这一点。循环您的代码以找到最大的。


结果:

有了这些条件,我们有:

[\s\S]{0,10}previously measured[\s\S]{0,10}|([0-4]\.\d+\s*[mM]{2})[\s\S]{0,70}?[Nn]odule|[Nn]odule[\s\S]{0,70}?([0-4]\.\d+\s*[mM]{2})

DEMO


要检查的额外条件

也许,为了减少误报,以下选项之一变得有用:

  1. 不允许在换行符后进行匹配。
  2. 如果“nodule”和数字之间有句号,则不匹配。
  3. 寻找接近小节的日期。

关于这个问题,我最终使用 nltk 模块将报告标记为单个句子。适用于所有实例的最终正则表达式是:

nodule_search = "[\s\S]{0,10}(?:previously measured|compared to )[\s\S]{0,10}|(\d[\.,]\d+|\d+|\d\d[\.,]\d+)\s*[mM]{2}[\s\S]{0,40}?[Nn]odule|[Nn]odule[\s\S]{0,40}?(\d[\.,]\d+|\d+|\d\d[\.,]\d+)\s*[mM]{2}"

所以在这种情况下,我最终没有做负面回顾,而是做了一个捕获组。

感谢大家的意见。