2010-10-25 8 views
13

Sono un'infermiera e conosco Python ma non sono un esperto, l'ho appena usata per elaborare sequenze di DNA
Abbiamo documenti ospedalieri scritti in lingue umane e io dovrei inserire questi dati in un database o file CSV ma sono più di 5000 linee e questo può essere così difficile. Tutti i dati sono scritti in un formato coerente Lasciate che vi mostri un esempioEstrazione di informazioni mediche usando Python

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

dovrei ottenere i seguenti dati

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

Un altro esempio

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

E io ottenere

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

th L'ordine non è coerente quando dico in ....... quindi è una parola chiave e tutto il testo dopo è un posto finché non trovo un'altra parola chiave
All'inizio Lui o Lei determina il sesso, ha ottenuto ... ..... tutto ciò che segue è un gruppo di sintomi che dovrei dividere in base al separatore che può essere una virgola, hypen o qualsiasi altra cosa ma è coerente per la stessa riga
morirà ..... ore più tardi dovrebbe anche ottenere come molte ore, a volte il paziente è stil vivo e scaricato .... ecc.
Vale a dire che abbiamo un sacco di convenzioni e penso che se riesco a tokenizzare il testo con parole chiave e modelli posso fare il lavoro. Quindi, per favore, se si conosce un utile funzione/modules/tutorial/strumento per farlo preferibilmente in python (se non python così uno strumento GUI sarebbe bello)

Alcuni poche informazioni:

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 
+3

Può essere utile se è possibile fornire altri esempi, compresi quelli in cui ci sono ordini diversi o quando un paziente vive/è dimesso. – GWW

+1

Esiste un elenco di parole chiave che sono sintomi validi? Tutti i record inizieranno con "lui" o "lei"? Tutti i record iniziano con la data/ora nello stesso formato? Se il paziente è dimesso, il record avrà sempre la parola "scaricata" seguita da "x ore dopo"? – philosodad

+0

ok ho aggiunto alcune informazioni in fondo alla domanda. – Nurse

risposta

9

Questo utilizza dateutil per analizzare la data (ad esempio, '11/11/2010 - 09:00 '), e parsedatetime per analizzare il tempo relativo (ad esempio '4 ore più tardi'):

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 

rendimenti :

{'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']} 

Nota: prestare attenzione alle date di analisi. L'8/9/2010 indica il 9 agosto o l'8 settembre? Tutti i detentori del record usano la stessa convenzione? Se scegli di utilizzare dateutil (e penso davvero che sia l'opzione migliore se la stringa della data non è strutturata rigidamente) assicurati di leggere la sezione su "Format precedence" nello dateutil documentation in modo che tu possa (si spera) risolvere '8/9/2010 'correttamente. Se non è possibile garantire che tutti i mantenitori di record utilizzino la stessa convenzione per specificare le date, i risultati di questo script dovrebbero essere controllati manualmente. Potrebbe essere saggio in ogni caso.

+2

+1 Just per lo sforzo nel fare tutto questo –

9

Ecco alcuni possibile modo è possibile risolvere questo -

  1. Utilizzando espressioni regolari - definirli secondo gli schemi nel testo. Abbina le espressioni, estrai pattern e ripeti per tutti i record. Questo approccio richiede una buona comprensione del formato in cui i dati sono & ovviamente espressioni regolari :)
  2. Manipolazione stringa - Questo approccio è relativamente più semplice. Di nuovo, è necessaria una buona comprensione del formato in cui si trovano i dati. Questo è quello che ho fatto sotto.
  3. Apprendimento automatico - È possibile definire tutte le regole & formando un modello su queste regole. Dopo questo il modello tenta di estrarre i dati usando le regole che hai fornito. Questo è un approccio molto più generico rispetto ai primi due. Anche il più difficile da implementare.

Vedere se questo lavoro per voi. Potrebbe essere necessario qualche aggiustamento.

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 record è a capo separato \n & poiché lei non ha citato un record del paziente è di 2 nuove righe separate \n\n dall'altro.

LATER: @Nurse cosa hai fatto? Solo curioso.

+0

Stavo per commentare e dire praticamente questo: assomiglia a 'stringa.split (...)' e una semplice macchina a stati (come questa) ti darà il massimo per il tuo dollaro. –

+0

questo è tutto. Munching di stringhe di base. Se i tuoi registri sono nello schema, tu dici di essere. Allora questo dovrebbe funzionare fuori dagli schemi. Ma se sorgono alcune discrepanze (poiché non conosco i dati).Potrebbe essere necessario modificarlo un po 'per abbinare i dati. –

+0

Lo farei in questo modo se è coerente .. ci sono un sacco di regole e non esistono nello stesso ordine. Non possiamo elaborarlo linearmente se sai cosa intendo. – Nurse

3

Forse questo può aiutare anche voi, non è testato

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':''} 
1

Sarebbe relativamente facile da fare la maggior parte del trattamento per quanto riguarda il sesso, data/ora, ecc, come quelli prima di voi hanno dimostrato , dal momento che puoi davvero definire una serie di parole chiave che indicherebbero queste cose e utilizzerebbero quelle parole chiave.

Tuttavia, la questione dell'elaborazione dei sintomi è leggermente diversa, in quanto un elenco definitivo di parole chiave che rappresentano i sintomi sarebbe difficile e molto probabilmente impossibile.

Ecco la scelta che devi fare: l'elaborazione di questi dati rappresenta davvero abbastanza lavoro da trascorrere giorni a scrivere un programma per farlo per me? Se è così, allora dovresti esaminare l'elaborazione del linguaggio naturale (o l'apprendimento automatico, come qualcuno prima di me ha detto). Ho sentito cose molto positive su nltk, un toolkit di linguaggio naturale per Python. Se il formato è coerente come dici tu, l'elaborazione del linguaggio naturale potrebbe non essere troppo difficile.

Ma, se non si è disposti a dedicare il tempo e gli sforzi per affrontare un problema CS veramente difficile (e credetemi, l'elaborazione del linguaggio naturale è), allora si dovrebbe fare gran parte dell'elaborazione in Python analizzando le date , pronomi specifici per genere, ecc. e inserire manualmente nelle parti più difficili (es. sintomi).

Anche in questo caso, dipende dal fatto che la soluzione programmatica o manuale richieda meno tempo a lungo termine.

+0

ma se capisco il formato, i sintomi sarebbero solo delle stringhe delimitate tra una parola chiave 'ottenuto' e il successivo 'e'. – philosodad

+0

Speriamo che sia vero, nel qual caso potresti semplicemente usare normale elaborazione di stringhe o re gex. –

+0

Questo è qualcosa su cui stavo facendo una ricerca. Ho provato già nltk ma la documentazione è molto tecnica e non riesco a ottenerla. In realtà non mi interessa passare un mese o due a sviluppare uno strumento che mi aiuterà a lungo termine. Ho circa 7000-10000 record da inserire in un database ogni mese, quindi investire un po 'di tempo per imparare non sarebbe una perdita di tempo. – Nurse

Problemi correlati