自然语言结构化处理工具-Apache UIMA Ruta使用(三)项目实战

本文介绍了在医疗领域中,如何通过Ruta脚本处理包含大量否定表述和复杂结构的门诊病历,以准确识别符合条件的症状并统计。脚本利用词表、正则表达式和逻辑判断,解决文本处理中的问题,实现对发热、呼吸道症状等关键信息的筛选和综合征分类。
摘要由CSDN通过智能技术生成

一、项目背景

项目要求,对医生书写的门诊病历及住院病程记录进行挖掘,找出符合条件的出现一定症状的患者,统计每日人数,所给的规则是这样的:

同时还含有补充条件:

常见的门诊病历是下面这样的:

主诉:患者XXX,男,69岁,因“反复气促、发热、咳嗽咳痰12天,加重1天”于2023-05-31 17 :40收入本区,伴有紫癜。

首记

患者基本信息:患者男,69岁。现病史:患者家属代诉患者于12前无明显诱因出现气促、发热,具体热型不详,伴有咳嗽咳痰,无恶心,无呕吐,

偶有胸闷,无胸痛,无腹痛腹泻,自服退热药物,未见明显好转,6天前患者症状加重,在家属陪同下至“XXXXXXXXXX医院”就诊,

行胸部CT检查示:

1.左肺上叶下舌段肿块影,不除外肿瘤病变,建议完善增强扫描及穿刺活检明确性质;纵膈内数枚增大或肿大淋巴结;

2.肺气肿伴肺大泡;双肺散在纤维化、间质性炎症,双侧胸膜增厚,建议治疗后复查;

心包少量积液,主动脉弓及左冠状动脉钙化斑;

左胸壁少许积气。

予高流量氧疗,予抗感染、化痰、维持内环境平衡等对症治疗后,症状好转后出院,现患者气促、咳嗽加重,今日遂来我院就诊

发育正常,营养中等,自主体位,步态不稳,表情痛苦,神志清楚,对答切题,查体合作。

全身浅表淋巴结未扪及肿大。无畸形。眉毛无脱落,眼睑无水肿、下垂、倒睫,

睑结膜无苍白、充血,球结膜无充血、水肿,巩膜无黄染,角膜透明,眼球运动灵活,无凸出、凹陷、震颤、运动障碍,

双侧瞳孔等大等圆,直径约3-4mm,对光反射灵敏,辐辏反射正常。耳廓正常,无畸形,无结节,无肿胀,外耳道未见异常分泌物 ,

粗测听力无下降,乳突区无压痛。鼻部个形无异常,无鼻翼扇动,鼻腔无异常分泌物,鼻中隔无偏曲,鼻窦区无压痛。

口唇发绀,口腔黏膜未见出血点及溃疡,伸舌

居外院胸部CT检查示:1.左肺上叶下舌段肿块影,不除外肿瘤病变,建议完善增强扫描及穿刺活检明确性质;纵膈内数枚增大或肿大淋巴结;

2.肺气肿伴肺大泡;双肺散在纤维化、间质性炎症,双侧胸膜增厚,建议治疗后复查;心包少量积液,主动脉弓及左冠状动脉钙化斑;左胸壁少许积气。

鉴别诊断:无

诊疗计划:1、重症监护、告病重。

2、完善相关辅助检查:三大常规、肝肾功能、生化检查、胸片、心电图等。

3、具体治疗用药方案:予抗感染、雾化稀释痰液促进痰液排出、维持酸碱电解质平衡及营养支持等治疗。

4.根据病情变化及时调整方案。

5.请示上级医师指导下一步诊疗方案。记录日期:2023-05-31 21:30

发热门诊

 二、面对的问题

1、首先可以较为明显的看到,这段文本描述中出现了很多否定性质的表述,比如“无”、“未见”等

2、有些否定表述是会夸标点的,比如“球结膜无充血、水肿”,

3、文章有比较明显的段落区分,有比较清晰的层次结构,

4、从文章中可以拿到就诊时间和患者症状的出现时间,如咳嗽咳痰12天

这种情况下,我们仅仅使用字符串匹配、或者正则表达式的话,一个是表达能力有限,另外一个是处理逻辑会跟代码耦合,不方便随时调整

三、脚本示例

前两章有对Ruta脚本有做简单介绍,

这里我把我这边处理这个问题的脚本放过来:

PACKAGE com.ll.syndrome;

//规则运行的主入口
TYPESYSTEM SyndromeTypeSystem;

DECLARE Annotation CaesuraSign;

//将需要用到的此表引入进来
WORDLIST KeyWord_Negative = '/wordList/KeyWord_Negative_Expression.txt';
WORDLIST KeyWord_Fever = '/wordList/KeyWord_Fever.txt';
WORDLIST KeyWord_Respiratory = '/wordList/KeyWord_Respiratory.txt';
WORDLIST KeyWord_Bleeding = '/wordList/KeyWord_Bleeding.txt';
WORDLIST KeyWord_Rash = '/wordList/KeyWord_Rash.txt';
WORDLIST KeyWord_Diarrhea = '/wordList/KeyWord_Diarrhea.txt';
WORDLIST KeyWord_Meningitis_1 = '/wordList/KeyWord_Meningitis_1.txt';
WORDLIST KeyWord_Meningitis_2 = '/wordList/KeyWord_Meningitis_2.txt';
WORDLIST KeyWord_Meningitis_3 = '/wordList/KeyWord_Meningitis_3.txt';
WORDLIST KeyWord_Meningitis_4 = '/wordList/KeyWord_Meningitis_4.txt';
WORDLIST KeyWord_Punctuation = '/wordList/KeyWord_Punctuation.txt';
WORDLIST KeyWord_UselessExpression = '/wordList/KeyWord_UselessExpression.txt';
WORDLIST KeyWord_DateUnit = '/wordList/KeyWord_DateUnit.txt';

//将文档中词表里的词汇初步标记为对应的类型
Document{-> MARKFAST(Negative,KeyWord_Negative)};
Document{-> MARKFAST(Fever,KeyWord_Fever)};
Document{-> MARKFAST(Respiratory,KeyWord_Respiratory)};
Document{-> MARKFAST(Bleeding,KeyWord_Bleeding)};
Document{-> MARKFAST(Rash,KeyWord_Rash)};
Document{-> MARKFAST(Diarrhea,KeyWord_Diarrhea)};
Document{-> MARKFAST(Meningitis01,KeyWord_Meningitis_1)};
Document{-> MARKFAST(Meningitis02,KeyWord_Meningitis_2)};
Document{-> MARKFAST(Divide,KeyWord_Punctuation)};
Document{-> MARKFAST(UselessExpression,KeyWord_UselessExpression)};
Document{-> MARKFAST(DateUnit,KeyWord_DateUnit)};



(NUM DateUnit){->MARK(Duration),Duration.Number=NUM,Duration.Unit=DateUnit};
//将无需 无力中的无取消标记为否定词
Negative{AND(REGEXP("无"),PARTOF(UselessExpression))->UNMARK(Negative)};
//将无用描述中包含的发热取消标记为Fever类型
Fever{AND(REGEXP("发热"),PARTOF(UselessExpression))->UNMARK(Fever)};

//主诉:患者胸闷、气促较前稍好转,轻微活动无明显气促,咳嗽、咳痰较前好转,无畏寒、发热。
//将从否定词开始 到 标点符号结束的部分 标记为否定语句,上述例句中,将 无明显气促  无畏寒 识别为否定句
(Negative #){->NegativeSentence} Divide;
//否定语句后紧跟、号的,扩充否定句的范围。上述例句中 发热前用、连接,也在被否定的范围内
//NegativeSentence{->SHIFT(NegativeSentence,1,3)} CaesuraSign # Divide;
//将包含在否定句中的症状也取消标记 无畏寒、发热一句中,发热本是被标记为Fever的,
//因其处在否定句中,故而取消Ferver标记,说明患者没有发热
Fever{PARTOFNEQ(NegativeSentence)->UNMARK(Fever)};
Respiratory{PARTOFNEQ(NegativeSentence)->UNMARK(Respiratory)};
Bleeding{PARTOFNEQ(NegativeSentence)->UNMARK(Bleeding)};
Rash{PARTOFNEQ(NegativeSentence)->UNMARK(Rash)};
Diarrhea{PARTOFNEQ(NegativeSentence)->UNMARK(Diarrhea)};
Meningitis01{PARTOFNEQ(NegativeSentence)->UNMARK(Meningitis01)};
Meningitis02{PARTOFNEQ(NegativeSentence)->UNMARK(Meningitis02)};
Meningitis03{PARTOFNEQ(NegativeSentence)->UNMARK(Meningitis03)};
Meningitis04{PARTOFNEQ(NegativeSentence)->UNMARK(Meningitis04)};
UselessExpression{REGEXP("建议")} # Fever{->UNMARK(Fever)};
UselessExpression{REGEXP("建议")} # Respiratory{->UNMARK(Respiratory)};
UselessExpression{REGEXP("建议")} # Bleeding{->UNMARK(Bleeding)};
UselessExpression{REGEXP("建议")} # Rash{->UNMARK(Rash)};
UselessExpression{REGEXP("建议")} # Diarrhea{->UNMARK(Diarrhea)};


Document{AND(CONTAINS(Fever),CONTAINS(Respiratory))->MARK(Syndrome01)};
Document{AND(CONTAINS(Fever),CONTAINS(Bleeding))->MARK(Syndrome02)};
Document{AND(CONTAINS(Fever),CONTAINS(Rash))->MARK(Syndrome03)};
Document{AND(CONTAINS(Diarrhea))->MARK(Syndrome04)};
Document{AND(CONTAINS(Meningitis01),
             CONTAINS(Meningitis02),
             CONTAINS(Meningitis03),
             CONTAINS(Meningitis04)
             )->MARK(Syndrome05)};

包名上面脚本有了,在包上右键新建Ruta文件,名字为Main.ruta

然后在descriptor包上右键:

 名字叫:SyndromeTypeSystem.xml

在这个文件上右键,选择上图 所示编辑器,会有这个画面弹出

 就是已纯文本的方式查看编辑这个文件,可以全选文件,把内容都删了,把下面这些文本粘进去

<?xml version="1.0" encoding="UTF-8"?>
<typeSystemDescription
    xmlns="http://uima.apache.org/resourceSpecifier">
    <name>SyndromeTypeSystem</name>
    <description/>
    <version/>
    <vendor/>
    <imports>
        <import location="BasicTypeSystem.xml"/>
    </imports>
    <types>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Fever</name>
            <description>发热</description>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Negative</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Respiratory</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Divide</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.NegativeSentence</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.DateUnit</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.UselessExpression</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Duration</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
            <features>
                <featureDescription>
                    <name>Number</name>
                    <description/>
                    <rangeTypeName>uima.tcas.Annotation</rangeTypeName>
                </featureDescription>
                <featureDescription>
                    <name>Unit</name>
                    <description/>
                    <rangeTypeName>com.ll.hiws.RutaType.DateUnit</rangeTypeName>
                </featureDescription>
            </features>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Fever1</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Fever2</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Bleeding</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Syndrome01</name>
            <description/>
            <supertypeName>uima.tcas.DocumentAnnotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Syndrome02</name>
            <description/>
            <supertypeName>uima.tcas.DocumentAnnotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Rash</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Syndrome03</name>
            <description/>
            <supertypeName>uima.tcas.DocumentAnnotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Syndrome04</name>
            <description/>
            <supertypeName>uima.tcas.DocumentAnnotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Syndrome05</name>
            <description/>
            <supertypeName>uima.tcas.DocumentAnnotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Diarrhea</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Meningitis01</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Meningitis02</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Meningitis03</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
        <typeDescription>
            <name>com.ll.hiws.RutaType.Meningitis04</name>
            <description/>
            <supertypeName>uima.tcas.Annotation</supertypeName>
        </typeDescription>
    </types>
</typeSystemDescription>

然后ctrl + S保存就可以了

这时点source旁边的TypeSystem就可以切换到类型视角,看到我自己定义的标签类

在Resource下新建WordList包,新建上脚本中需要的词表文件即可,词表文件,词表文件有很多种格式,我这里是一行一个词汇

这样就能把Main脚本运行起来了。

四、Main脚本解释

整个流程还是比较简单的。

第四行是导入要用到的类型系统,就是我们刚刚新建的TypeDescriptor文件,

这里是导入词表文件

这里是将词表中出现的词汇,在文档中标记为对应的标签

38行前文有提到找到时间表达式,提取时间跟单位

我们在词表KeyWord_Negative_Expression.txt中定义了一些否定表达式,并将文档中的否定表达式标记为了Negative,

并将KeyWord_Fever.txt中的“发热”标记为了Fever

 但是像无力这种,属于症状,不倾向于表示否定。要处理的发热门诊科室,也不能作为我们要抓取的症状,

 所以在这里进行了修正

 这一行,把从否定词到Divide都包括进去,标记为否定语句

这里的Divide是自定义标签类,词表是这个

 没有顿号,所以,“无发热、畏寒”就都被包含在一个否定句中了,通过查看输出结果,可以验证这点

 这里可以看到“无明显诱因出现气促、发热,”被识别成了否定句,因为无被识别成为了否定词,怎么修正呢?在KeyWord_UselessExpression.txt中加一个行就可以了,像这样。

清除测试结果再测试 ,结果就像下面这样。

 KeyWord_UselessExpression.txt是误导性表述词表

 51到60  含义是当左花括号{前的标签是否定句标签的一部分的时候,去掉这个标签

后续是把所有建议之后的要找的标签都给取消掉

 67含义是说 当这个文档中既包含Ferver发热标签,又包含呼吸道症状标签Respiratory的时候,把整个文档加上Syndrome01标签

68是说当这个文档中既包含Ferver发热标签,又包含出血Bleeding标签,就把整个文档标记问Syndrome02标签

 可以看到,这个患者发热咳嗽,被标记为了Syndrome01

后续会跟大家分享,如何将Ruta跟项目代码集成

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值