Вопрос по python – Pyparsing: извлечение переменной длины, переменное содержимое, переменная пробельная подстрока

2

Мне нужно извлечь оценки Глисона из плоского файла заключительных диагностических записей простатэктомии. Эти оценки всегда имеют слово Gleason и два числа, которые складываются в другой номер. Люди печатали их более двух десятилетий. Различные условные обозначения пробелов и модификаторов включены. Ниже приведена моя форма Бэкуса-Наура и два примера записей. Только для простатэктомий мы рассматриваем более тысячи случаев.

Я использую pyparsing, потому что я изучаю python, и у меня нет приятных воспоминаний о моем очень ограниченном воздействии регулярных выражений.

Мой вопрос: как я могу извлечь эти оценки Глисона, не анализируя каждый дополнительный необязательный фрагмент данных, который может или не может быть в этих окончательных диагнозах?

num = Word(nums)
record ::= accessionDate + accessionNumber + patMedicalRecordNum + finalDxText
accessionDate ::= num + "/" + num + "/" num
accessionNumber ::= "S" + num + "-" + num
patMedicalRecordNum ::= num + "/" + num + "-" + num + "-" + num
finalDxText ::= listOfParts + optionalComment + optionalpTNMStage
listOfParts ::= OneOrMore(part)
part ::= <multiline idiosyncratic freetext which may contain a Gleason score I want> + optionalpTNMStage
optionalComment ::= <multiline idiosyncratic freetext which may contain a Gleason score I don't want>
optionalpTNMStage ::= <multiline idiosyncratic freetext which may contain a Gleason score I don't want>


01/01/11  S11-55555 20/444-55-6666 A.  PROSTATE AND SEMINAL VESICLES, PROSTATECTOMY:                           
                                   -  ADENOCARCINOMA.                                                      

                                   TOTAL GLEASON SCORE:  GLEASON 5+4=9                                     
                                   TUMOR LOCATION:  BILATERAL                                              
                                   TUMOR QUANTITATION:  15% OF PROSTATE INVOLVED BY TUMOR                  
                                   EXTRAPROSTATIC EXTENSION:  PRESENT AT RIGHT POSTERIOR                   
                                   SEMINAL VESICLE INVASION:  PRESENT                                      
                                   MARGINS:  UNINVOLVED                                                    
                                   LYMPHOVASCULAR INVASION:  PRESENT                                       
                                   PERINEURAL INVASION:  PRESENT                                           
                                   LYMPH NODES (SPECIMENS B AND C):                                        
                                      NUMBER EXAMINED:  25                                                 
                                      NUMBER INVOLVED:  1                                                  
                                      DIAMETER OF LARGEST METASTASIS:  1.7 mm                              
                                   ADDITIONAL FINDINGS:  HIGH-GRADE PROSTATIC INTRAEPITHELIAL NEOPLASIA,   
                                      ACUTE AND CHRONIC INFLAMMATION, INTRADUCTAL EXTENSION OF INVASIVE    
                                      CARCINOMA                                                            

                                   PATHOLOGIC STAGE:  pT3b N1 MX                                           

                               B.  LYMPH NODES, RIGHT PELVIC, EXCISION:                                    
                                   -  ONE OF SEVENTEEN LYMPH NODES POSITIVE FOR METASTASIS (1/17).         

                               C.  LYMPH NODES, LEFT PELVIC, EXCISION:                                     
                                   -  EIGHT LYMPH NODES NEGATIVE FOR METASTASIS (0/8).                     
01/02/11  S11-4444 20/111-22-3333 PROSTATE AND SEMINAL VESICLES, PROSTATECTOMY:                               
                                  - ADENOCARCINOMA.                                                        
                                    GLEASON SCORE:  3 + 3 = 6 WITH TERTIARY PATTERN OF 5.                                             
                                    TUMOR QUANTITATION:  APPROXIMATELY 10% BY VOLUME.                      
                                    TUMOR LOCATION:  BILATERAL.                                            
                                    EXTRAPROSTATIC EXTENSION:  NOT IDENTIFIED.                             
                                    MARGINS:  NEGATIVE.                                                    
                                    PERINEURAL INVASION:  IDENTIFIED.                                      
                                    LYMPH-VASCULAR INVASION:  NOT IDENTIFIED.                              
                                    SEMINAL VESICLE/VASA DEFERENTIA INVASION: NOT IDENTIFIED.              
                                    LYMPH NODES:  NONE SUBMITTED.                                          
                                    OTHER:  HIGH GRADE PROSTATIC INTRAEPITHELIAL NEOPLASIA.                
                               PATHOLOGIC STAGE (pTNM):  pT2c NX.                                       

Полное раскрытие: я врач, занимающийся исследованиями; это моя первая настоящая работа с питоном. Я прочитал Lutz Learning Python, Shaw Learning Python «Трудный путь» и проработал различные наборы задач. Я рассмотрел многочисленные вопросы, связанные с pyparsing, на этом форуме, вики-сайте pyparsing, и я купил и прочитал «Начало работы с Pyparsing» г-на МакГуайра. Возможно, я задаю вопрос, когда мне действительно нужно сказать, что я стою на «смертельной спирали разочарования, которая так часто встречается, когда вам приходится писать парсеры». (МакГвайр, 17)? Я не знаю. Пока я просто рад работать над тем, что на самом деле может быть реальным проектом.

Обработка естественного языка сложно! Можете ли вы сделать некоторые упрощающие предположения? (Например, интересующий вас балл всегдаfirst Оценка Глисона, и всегда приходит в формеGleason i+j=k) Katriel
да, это верные предположения. Niels

Ваш Ответ

3   ответа
2

руктуру pyparsing для части num + num = num, вы сможете использовать SkipTo для пропуска чего-либо между & quot; Gleason & quot; и это. Примерно так (непроверенный псевдо-разбор):

score = num + "+" + num + "=" num
Gleason = "Gleason" + SkipTo(score) + score

PyParsing по умолчанию пропускает все пробелы, и с помощью SkipTo вы можете пропустить все, что не соответствует желаемому формату.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Niels
Error: User Rate Limit Exceeded Niels
Error: User Rate Limit Exceeded
2

ых Глисона.

from pyparsing import *
num = Word(nums)
accessionDate = Combine(num + "/" + num + "/" + num)("accDate")
accessionNumber = Combine("S" + num + "-" + num)("accNum")
patMedicalRecordNum = Combine(num + "/" + num + "-" + num + "-" + num)("patientNum")
gleason = Group("GLEASON" + Optional("SCORE:") + num("left") + "+" + num("right") + "=" + num("total"))
assert 'GLEASON 5+4=9' == gleason
assert 'GLEASON SCORE:  3 + 3 = 6' == gleason

patientData = Group(accessionDate + accessionNumber + patMedicalRecordNum)
assert '01/02/11  S11-4444 20/111-22-3333' == patientData

partMatch = patientData("patientData") | gleason("gleason")

lastPatientData = None
for match in partMatch.searchString(data):
    if match.patientData:
        lastPatientData = match
    elif match.gleason:
        if lastPatientData is None:
            print "bad!"
            continue
        print "{0.accDate}: {0.accNum} {0.patientNum} Gleason({1.left}+{1.right}={1.total})".format(
                        lastPatientData.patientData, match.gleason
                        )

Печать:

01/01/11: S11-55555 20/444-55-6666 Gleason(5+4=9)
01/02/11: S11-4444 20/111-22-3333 Gleason(3+3=6)
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Niels
1
gleason = re.compile("gleason\d+\d=\d")
scores = set()
for record in records:
    for line in record.lower().split("\n"):
        if "gleason" in line:
            scores.add(gleason.match(line.replace(" ", "")).group(0)[7:])

Error: User Rate Limit Exceededgleason i+j=kError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded Niels

Похожие вопросы