【问题标题】:Medical information extraction using Python使用 Python 提取医学信息
【发布时间】:2011-04-30 00:26:30
【问题描述】:

我是一名护士,我知道 python,但我不是专家,只是用它来处理 DNA 序列
我们得到了用人类语言编写的医院记录,我应该将这些数据插入到数据库或 csv 文件中,但它们超过 5000 行,这太难了。所有数据都以一致的格式写入让我给你看一个例子

11/11/2010 - 09:00am : He got nausea, vomiting and died 4 hours later

我应该得到以下数据

Sex: Male
Symptoms: Nausea
    Vomiting
Death: True
Death Time: 11/11/2010 - 01:00pm

另一个例子

11/11/2010 - 09:00am : She got heart burn, vomiting of blood and died 1 hours later in the operation room

我得到了

Sex: Female
Symptoms: Heart burn
    Vomiting of blood
Death: True
Death Time: 11/11/2010 - 10:00am

当我说 in 时,顺序不一致......所以 in 是一个关键字,之后的所有文本都是一个地方,直到我找到另一个关键字
在开始时,他或她确定性别,得到........接下来是一组症状,我应该根据分隔符拆分,可以是逗号、连字符或其他任何东西,但对于同一行是一致的 死了.....几个小时后也应该得到多少小时,有时病人还活着出院....等
也就是说,我们有很多约定,我认为如果我可以使用关键字和模式对文本进行标记,我就可以完成工作。所以,如果你知道一个有用的函数/模块/教程/工具,最好在 python 中执行此操作(如果不是 python,那么 gui 工具会很好)

一些信息:

there are a lot of rules to express various medical data but here are few examples
- Start with the same date/time format followed by a space followd by a colon followed by a space followed by He/She followed space followed by rules separated by and
- Rules:
    * got <symptoms>,<symptoms>,....
    * investigations were done <investigation>,<investigation>,<investigation>,......
    * received <drug or procedure>,<drug or procedure>,.....
    * discharged <digit> (hour|hours) later
    * kept under observation
    * died <digit> (hour|hours) later
    * died <digit> (hour|hours) later in <place>
other rules do exist but they follow the same idea

【问题讨论】:

  • 如果你能提供更多的例子,包括有不同命令的例子,或者病人生活/出院的例子,这可能会有所帮助。
  • 是否有有效症状的关键字列表?所有记录都会以“他”或“她”开头吗?所有记录都以相同格式的日期/时间开头吗?如果患者出院,记录中是否总是会出现“出院”字样以及“x 小时后”字样?
  • 好的,我在问题的底部添加了一些信息。
  • 我建议,无论您使用哪种方法,都不要让错误被忽视。处理数据并存储所有明确且清晰解析的内容,同时单独存储其他所有内容。浏览错误,增强解析器,再试一次。最后,如果还有一些记录很难在不破坏解析器的情况下解析,请自己解释它们。

标签: python parsing machine-learning nlp information-extraction


【解决方案1】:

这里有一些可能的方法可以解决这个问题 -

  1. 使用正则表达式 - 根据文本中的模式定义它们。匹配表达式,提取模式,然后重复所有记录。这种方法需要很好地理解数据的格式,当然还有正则表达式:)
  2. 字符串操作 - 这种方法相对简单。再次需要对数据的格式有一个很好的理解。这就是我在下面所做的。
  3. 机器学习 - 您可以定义所有规则并根据这些规则训练模型。在此之后,模型会尝试使用您提供的规则来提取数据。这是比前两种更通用的方法。也是最难实施的。

看看这是否适合你。可能需要一些调整。

new_file = open('parsed_file', 'w')
for rec in open("your_csv_file"):
    tmp = rec.split(' : ')
    date = tmp[0]
    reason = tmp[1]

    if reason[:2] == 'He':
        sex = 'Male'
        symptoms = reason.split(' and ')[0].split('He got ')[1]
    else:
        sex = 'Female'
        symptoms = reason.split(' and ')[0].split('She got ')[1]
    symptoms = [i.strip() for i in symptoms.split(',')]
    symptoms = '\n'.join(symptoms)
    if 'died' in rec:
        died = 'True'
    else:
        died = 'False'
    new_file.write("Sex: %s\nSymptoms: %s\nDeath: %s\nDeath Time: %s\n\n" % (sex, symptoms, died, date))

Ech 记录以换行符分隔 \n & 因为您没有提到一个患者记录是 2 个换行符分隔 \n\n 与另一个。

稍后: @Nurse 你最后做了什么?只是好奇。

【讨论】:

  • 我正要发表评论,基本上是这样说的:它看起来像string.split(...),一个简单的状态机(像这个)会给你带来最大的收益。
  • 就是这样。基本的字符串咀嚼。如果您的记录符合您所说的模式。那么这应该是开箱即用的。但是如果出现一些差异(因为我不知道数据)。您可能需要稍微调整一下以匹配您的数据。
  • 如果它是一致的,我会这样做。有很多规则,它们不以相同的顺序存在。如果你明白我的意思,我们不能线性处理它。
  • @Nurse 那么最好提及所有规则/案例。在不了解所有规则的情况下,人们无法向您推荐完美的解决方案。
  • 您的输出是否仅包含“性别”、“症状”、“死亡”、“死亡时间”字段?还是有时需要输出其他信息,比如治疗?
【解决方案2】:

这使用dateutil 来解析日期(例如'11/11/2010 - 09:00am'),并使用parsedatetime 来解析相对时间(例如'4 小时后'):

import dateutil.parser as dparser
import parsedatetime.parsedatetime as pdt
import parsedatetime.parsedatetime_consts as pdc
import time
import datetime
import re
import pprint
pdt_parser = pdt.Calendar(pdc.Constants())   
record_time_pat=re.compile(r'^(.+)\s+:')
sex_pat=re.compile(r'\b(he|she)\b',re.IGNORECASE)
death_time_pat=re.compile(r'died\s+(.+hours later).*$',re.IGNORECASE)
symptom_pat=re.compile(r'[,-]')

def parse_record(astr):    
    match=record_time_pat.match(astr)
    if match:
        record_time=dparser.parse(match.group(1))
        astr,_=record_time_pat.subn('',astr,1)
    else: sys.exit('Can not find record time')
    match=sex_pat.search(astr)    
    if match:
        sex=match.group(1)
        sex='Female' if sex.lower().startswith('s') else 'Male'
        astr,_=sex_pat.subn('',astr,1)
    else: sys.exit('Can not find sex')
    match=death_time_pat.search(astr)
    if match:
        death_time,date_type=pdt_parser.parse(match.group(1),record_time)
        if date_type==2:
            death_time=datetime.datetime.fromtimestamp(
                time.mktime(death_time))
        astr,_=death_time_pat.subn('',astr,1)
        is_dead=True
    else:
        death_time=None
        is_dead=False
    astr=astr.replace('and','')    
    symptoms=[s.strip() for s in symptom_pat.split(astr)]
    return {'Record Time': record_time,
            'Sex': sex,
            'Death Time':death_time,
            'Symptoms': symptoms,
            'Death':is_dead}


if __name__=='__main__':
    tests=[('11/11/2010 - 09:00am : He got nausea, vomiting and died 4 hours later',
            {'Sex':'Male',
             'Symptoms':['got nausea', 'vomiting'],
             'Death':True,
             'Death Time':datetime.datetime(2010, 11, 11, 13, 0),
             'Record Time':datetime.datetime(2010, 11, 11, 9, 0)}),
           ('11/11/2010 - 09:00am : She got heart burn, vomiting of blood and died 1 hours later in the operation room',
           {'Sex':'Female',
             'Symptoms':['got heart burn', 'vomiting of blood'],
             'Death':True,
             'Death Time':datetime.datetime(2010, 11, 11, 10, 0),
             'Record Time':datetime.datetime(2010, 11, 11, 9, 0)})
           ]

    for record,answer in tests:
        result=parse_record(record)
        pprint.pprint(result)
        assert result==answer
        print

产量:

{'Death': True,
 'Death Time': datetime.datetime(2010, 11, 11, 13, 0),
 'Record Time': datetime.datetime(2010, 11, 11, 9, 0),
 'Sex': 'Male',
 'Symptoms': ['got nausea', 'vomiting']}

{'Death': True,
 'Death Time': datetime.datetime(2010, 11, 11, 10, 0),
 'Record Time': datetime.datetime(2010, 11, 11, 9, 0),
 'Sex': 'Female',
 'Symptoms': ['got heart burn', 'vomiting of blood']}

注意:请小心解析日期。 “8/9/2010”是指 8 月 9 日还是 9 月 8 日?所有的记录员都使用相同的约定吗?如果您选择使用 dateutil(我真的认为如果日期字符串的结构不严格,这是最好的选择)请务必阅读 dateutil documentation 中有关“格式优先级”的部分,以便您可以(希望)解决 '8/ 9/2010' 正确。 如果您不能保证所有记录保管人都使用相同的约定来指定日期,则必须手动检查此脚本的结果。无论如何,这可能是明智的。

【讨论】:

    【解决方案3】:

    也许这对你也有帮助,它没有经过测试

    import collections
    import datetime
    import re
    
    retrieved_data = []
    
    Data = collections.namedtuple('Patient', 'Sex, Symptoms, Death, Death_Time')
    dict_data = {'Death':'',
                 'Death_Time':'',
                 'Sex' :'',
                 'Symptoms':''}
    
    
    with open('data.txt') as f:
         for line in iter(f.readline, ""):
    
             date, text = line.split(" : ")
             if 'died' in text:
                 dict_data['Death'] = True
                 dict_data['Death_Time'] = datetime.datetime.strptime(date, 
                                                                     '%d/%m/%Y - %I:%M%p')
                 hours = re.findall('[\d]+', datetime.text)
                 if hours:
                     dict_data['Death_Time'] += datetime.timedelta(hours=int(hours[0]))
             if 'she' in text:
                dict_data['Sex'] = 'Female'
             else:
                dict_data['Sex'] = 'Male'
    
             symptoms = text[text.index('got'):text.index('and')].split(',')
    
             dict_data['Symptoms'] = '\n'.join(symptoms) 
    
             retrieved_data.append(Data(**dict_data))
    
             # EDIT : Reset the data dictionary.
             dict_data = {'Death':'',
                 'Death_Time':'',
                 'Sex' :'',
                 'Symptoms':''}
    

    【讨论】:

      【解决方案4】:

      就性别、日期/时间等进行大部分处理会相对容易,正如您之前所展示的那样,因为您实际上只需定义一组关键字来指示这些内容并使用那些关键字。

      但是,处理症状的问题有点不同,因为代表症状的关键字的明确列表很困难,而且很可能是不可能的。

      这是您必须做出的选择:处理这些数据真的代表了足够的工作来花几天时间编写一个程序来为我做这件事吗?如果是这种情况,那么您应该研究自然语言处理(或机器学习,正如我之前的某人所说)。我听说过关于nltk 的好消息,这是一个用于 Python 的自然语言工具包。如果格式和你说的一样一致,自然语言处理可能不会太难。

      但是,如果您不愿意花费时间和精力来解决一个真正困难的 CS 问题(相信我,自然语言处理是),那么您应该通过解析日期在 Python 中完成大部分处理,特定性别的代词等,并手动输入较难的部分(例如症状)。

      同样,从长远来看,这取决于您是否认为程序化或手动解决方案会花费更少的时间。

      【讨论】:

      • 但如果我理解格式,症状将只是关键字“得到”和下一个“和”之间的任何描述的字符串。
      • 希望这是真的,在这种情况下,您可以使用普通字符串处理或正则表达式。
      • 这是我正在搜索的内容。我已经尝试过 nltk,但是文档非常技术性,我无法获得它。实际上,我不介意花一两个月来开发一个从长远来看对我有帮助的工具。我每个月有大约 7000-10000 条记录要插入到数据库中,因此投入一些时间学习不会浪费时间。
      • @Nurse 您好,您能解决您的问题吗?你能分享更多关于你这个项目的信息吗?
      猜你喜欢
      • 1970-01-01
      • 2012-12-26
      • 2018-11-15
      • 1970-01-01
      • 2019-10-06
      • 2022-01-03
      • 2021-06-04
      • 1970-01-01
      相关资源
      最近更新 更多