• Идея

  • Реализация

  • Результат

Идея: в медицинском учреждении выписные эпикризы (информация из истории болезни) пациентов хранятся в общегоспитальной локальной сети.

Необходимо сформировать базу данных пациентов с перенесенным заболеванием COVID-19 (один выписной эпикриз ДО заболевания COVID-19, один выписной эпикриз во время заболевания и один ПОСЛЕ заболевания).

Вот как это выглядит

Реализация:

  1. Сформированы папки с файлами

  1. Формирование базы:

    с помощью модуля docx на Python можно перевести *.docx файл в обычный текст, называем этот скрипт readDocx.py (решение найдено на просторах интернета):

import docx


def getText(filename):
    doc = docx.Document(filename)
    fullText = []
    for para in doc.paragraphs:
        fullText.append((' ' + para.text))
    return '\n'.join(fullText)

Проблемы с которыми я столкнулся: некоторые файлы были в старом формате *.doc и *.rtf. для решения пришлось установить Libre Office и применить следующие команды в Терминале:


/Applications/LibreOffice.app/Contents/MacOS/soffice --headless --convert-to docx --outdir ~/Desktop/output ~/Desktop/COVID-2019/**/*.doc

/Applications/LibreOffice.app/Contents/MacOS/soffice --headless --convert-to docx --outdir ~/Desktop/output ~/Desktop/COVID-2019/**/*.rtf

Для получения базы каждый файл в директории переводится в текстовый формат, затем с помощью регулярных выражений требуемые показатели находятся и формируется общая таблица в формате *.xlsx (Exel):

import os, readDocx, re, pandas as pd
ROOT_DIR = r'/Users/insomnia/Documents/disser/COVID-2019'
docx_files = []
for root, dirs, files in os.walk(ROOT_DIR):
    for file in files:
        if file.endswith(".docx"):
            docx_files.append(os.path.join(root, file))
print(docx_files)
setoftuples = []
for i in docx_files:
    x = readDocx.getText(i)
    name = [i]
    if re.search(r'\d\d[.]\d\d[.]\d{4}', x):
        birthdate = re.search(r'\d\d[.]\d\d[.]\d{4}', x).group()
    else:
        birthdate = "NA"
    if len(re.findall(r'\d\d[.]\d\d[.]\d{4}', x)) > 2:
        admission = re.findall(r'\d\d[.]\d\d[.]\d{4}', x)[1]
        discharge = re.findall(r'\d\d[.]\d\d[.]\d{4}', x)[2]
    else:
        admission = "NA"
        discharge = "NA"
    if re.findall(r'[Кк]орон[а]?[о]?вирусная инфекция', x):
        COVID = re.findall(r'[Кк]орон[а]?[о]?вирусная инфекция', x)
    else:
        COVID = "NA"
    if re.findall(r'КТ.?[0-4]', x):
        CT = re.findall(r'КТ.?[0-4]', x)
    else:
        CT = "NA"
    if re.findall(r'ПОСМЕРТНЫЙ|'
                  r'\bумер\b|'
                  r'\bсмерть\b', x):
        Death = re.findall(r'ПОСМЕРТНЫЙ|'
                  r'\bумер\b|'
                  r'\bсмерть\b', x)
    else:
        Death = "NA"
    if re.findall(r'фибрил\w+', x):
        AF = re.findall(r'фибрил\w+', x)
    else:
        AF = "NA"
    if re.findall(r'гемоглобин\s\S?\s?\d\d\d?|'
                  r'Hb\D?\D?\D?\d\d\d?', x):
        hb = re.findall(r'гемоглобин\s\S?\s?\d\d\d?|'
                  r'Hb\D?\D?\D?\d\d\d?', x)
    else:
        hb = "NA"
    if re.findall(r'Эр\w*\s?\S?\s?[0-9][.,][0-9]?[0-9]?|'
                  r'эр\w*\s?\S?\s?[0-9][.,][0-9]?[0-9]?', x):
        RBC = re.findall(r'Эр\w*\s?\S?\s?[0-9][.,][0-9]?[0-9]?|'
                         r'эр\w*\s?\S?\s?[0-9][.,][0-9]?[0-9]?', x)
    else:
        RBC = "NA"
    if re.findall(r'лейк\w+\s\S?\s?\d\d?[,.]\d?\d?|'
                  r'Л – \d\d?,?\.?\d?|'
                  r'Л-\d\d?,?\.?\d?|'
                  r'Le \d\d?,?\.?\d?', x):
        leu = re.findall(r'лейк\w+\s\S?\s?\d\d?[,.]\d?\d?|'
                  r'Л – \d\d?,?\.?\d?|'
                  r'Л-\d\d?,?\.?\d?|'
                  r'Le \d\d?,?\.?\d?', x)
    else:
        leu = "NA"
    if re.findall(r'лимф\w+\s?\S?\s?[0-9][.,][0-9]?[0-9?]?[0-9?]?', x):
        limf = re.findall(r'лимф\w+\s?\S?\s?[0-9][.,][0-9]?[0-9?]?[0-9?]?', x)
    else:
        limf = "NA"
    if re.findall(r'С.?реактивный белок\D*\d?\d?[.]?\d?\d?[.]?\d?\d?\d?\d?\D*\d\d?\d?[.,]\d?\d?|'
                  r'СРБ \D?\D? ?\d?\d.\d\d.\d\d\d?\d?\D*[0-9][0-9]?[0-9]?[.,][0-9]?[0-9]?|'
                  r'СРБ\D?\D?\D?\d\d?\d?\S?\d?\d?|'
                  r'С-реактивный белок – \d\d.\d\d.\d\d\d?\d? г. – \d\d?\d?\W?\d?\d?', x):
        CRP = re.findall(r'С.?реактивный белок\D*\d?\d?[.]?\d?\d?[.]?\d?\d?\d?\d?\D*\d\d?\d?[.,]\d?\d?|'
                         r'СРБ \D?\D? ?\d?\d.\d\d.\d\d\d?\d?\D*[0-9][0-9]?[0-9]?[.,][0-9]?[0-9]?|'
                         r'СРБ\D?\D?\D?\d\d?\d?\S?\d?\d?|'
                         r'С-реактивный белок – \d\d.\d\d.\d\d\d?\d? г. – \d\d?\d?\W?\d?\d?', x)
    else:
        CRP = "NA"
    if re.findall(r'[Хх]олестерин\D?\D?\D?\d\S?\d?\d?|'
                  r'[Хх]олестерин общий\D?\D?\D?\d\S?\d?\d?|'
                  r'[Cc]hol|CHOL\D?\D?\D?\d\S?\d?\d?', x):
        chol = re.findall(r'[Хх]олестерин\D?\D?\D?\d\S?\d?\d?|'
                          r'[Хх]олестерин общий\D?\D?\D?\d\S?\d?\d?|'
                          r'[Cc]hol|CHOL\D?\D?\D?\d\S?\d?\d?', x)
    else:
        chol = "NA"
    if re.findall(r'[Тт]риглицериды\D?\D?\D?\d\S?\d?\d?|'
                  r'TRIG[L]?\D?\D?\D?\d\S?\d?\d?|'
                  r'Триглицериды\D*\d\S?\d?\d?', x):
        TG = re.findall(r'триглицериды\D?\D?\D?\d\S?\d?\d?|'
                        r'TRIG[L]?\D?\D?\D?\d\S?\d?\d?|'
                        r'Триглицериды\D*\d\S?\d?\d?', x)
    else:
        TG = "NA"
    if re.findall(r'UHDL\D?\D?\D?\d\S?\d?\d?|'
                  r'ЛПВП\D?\D?\D?\d\S?\d?\d?', x):
        UHDL = re.findall(r'UHDL\D?\D?\D?\d\S?\d?\d?|'
                          r'ЛПВП\D?\D?\D?\d\S?\d?\d?', x)
    else:
        UHDL = "NA"
    if re.findall(r'DLDL\D?\D?\D?\d\S?\d?\d?|'
                  r'ЛПНП\D?\D?\D?\d\S?\d?\d?', x):
        DLDL = re.findall(r'DLDL\D?\D?\D?\d\S?\d?\d?|'
                          r'ЛПНП\D?\D?\D?\d\S?\d?\d?', x)
    else:
        DLDL = "NA"
    if re.findall(r'креатинин\D?\D?\D?\d\d?\d?\S?\d?', x):
        crea = re.findall(r'креатинин\D?\D?\D?\d\d?\d?\S?\d?', x)
    else:
        crea = "NA"
    if re.findall(r'КДРЛЖ\w?\s?\S?\s?\d\d\d?', x):
        LVEDD = re.findall(r'КДРЛЖ\w?\s?\S?\s?\d\d\d?', x)
    else:
        LVEDD = "NA"
    if re.findall(r'КС[Р]?ЛЖ\w?\s?\S?\s?\d\d\d?', x):
        LVESD = re.findall(r'КС[Р]?ЛЖ\w?\s?\S?\s?\d\d\d?', x)
    else:
        LVESD = "NA"
    if re.findall(r'КДОЛЖ\w?\s?\S?\s?\d\d\d?\d?', x):
        LVEDV = re.findall(r'КДОЛЖ\w?\s?\S?\s?\d\d\d?\d?', x)
    else:
        LVEDV = "NA"
    if re.findall(r'ИММЛЖ\w?\s?\S?\s?\d\d\d?|'
                  r'индекс массы\s?\S?\s?\d\d\d?', x):
        LVMI = re.findall(r'ИММЛЖ\w?\s?\S?\s?\d\d\d?|'
                          r'индекс массы\s?\S?\s?\d\d\d?', x)
    else:
        LVMI = "NA"
    if re.findall(r'ФВ[в]?[м?]?\s?\W?\s?[0-9]{2}', x):
        EF = re.findall(r'ФВ[в]?[м?]?\s?\W?\s?[0-9]{2}', x)
    else:
        EF = "NA"
    if re.findall(r'Систол\D?\s?ДЛА\s?\S?\s?[0-9]{2}|'
                  r'СДЛА\s?\S?\s?[0-9]{2}|'
                  r'Сист.ДЛА\s?\S?\s?[0-9]{2}', x):
        PASP = re.findall(r'Систол\D?\s?ДЛА\s?\S?\s?[0-9]{2}|'
                          r'СДЛА\s?\S?\s?[0-9]{2}|'
                          r'Сист.ДЛА\s?\S?\s?[0-9]{2}', x)
    else:
        PASP = "NA"
    if re.findall(r'ИОЛП.\s?\S?\s?[0-9]{2}', x):
        LAVI = re.findall(r'ИОЛП.\s?\S?\s?[0-9]{2}', x)
    else:
        LAVI = "NA"
    if re.findall(r'ИБС|'
                  r'[Ии]шемическая болезнь сердца', x):
        IHD = re.findall(r'ИБС|'
                         r'[Ии]шемическая болезнь сердца', x)
    else:
        IHD = "NA"
    if re.findall(r'[Ии]нфаркт миокарда|'
                  r'[Пп]остинфарктный', x):
        MI = re.findall(r'[Ии]нфаркт миокарда|'
                  r'[Пп]остинфарктный', x)
    else:
        MI = "NA"
    if re.findall(r'[Cc]ахарный диабет', x):
        Diabetus = re.findall(r'[Cc]ахарный диабет', x)
    else:
        Diabetus = "NA"
    if re.findall(r'[Оо]жирение', x):
        Obesity = re.findall(r'[Оо]жирение', x)
    else:
        Obesity = "NA"
    if re.findall(r'ХОБЛ', x):
        COPD = re.findall(r'ХОБЛ', x)
    else:
        COPD = "NA"
    if re.findall(r'[Бб]ронхиальная астма', x):
        asthma = re.findall(r'[Бб]ронхиальная астма', x)
    else:
        asthma = "NA"
    my_list = (name, birthdate, admission, discharge, COVID, CT, Death, hb, RBC, leu,
               limf, CRP, chol, TG, UHDL, DLDL, crea, LVEDD,
               LVESD, LVEDV, LVMI, EF, PASP, LAVI, AF, IHD, MI, Diabetus, Obesity,
               COPD, asthma)
    setoftuples.append(my_list)
print(setoftuples)
df = pd.DataFrame(list(setoftuples),
                  columns=['name','birthdate','admission', 'discharge', 'COVID',
                           'CT', 'Death', 'hb', 'RBC',
                           'leu', 'limf', 'CRP', 'chol', 'TG', 'UHDL', 'DLDL',
                           'crea', 'LVEDD', 'LVESD', 'LVEDV',
                           'LVMI', 'EF', 'PASP', 'LAVI', 'AF', 'IHD',
                           'MI', 'Diabetus', 'Obesity', 'COPD', 'asthma'])
print(df)
df.to_excel(r'/Users/insomnia/Documents/disser/dataframe.xlsx', index=False)

Промежуточный результат:

количество строк - 934, столбцов - 31
количество строк - 934, столбцов - 31

Полученные данные необходимо привести в формат с которым можно работать - очистить от мусора, задать каждой колонке тип данных, и проч. Этот процесс называется очисткой данных (Data cleaning).

В общих словах определение таково: Data cleaning is the process of fixing or removing incorrect, corrupted, incorrectly formatted, duplicate, or incomplete data within a dataset.

Для этого процесса я воспользовался программой RStudio IDE (язык R), можно было и на Python сделать, но R для меня удобнее. Постарался оставить комментарии на каждом из этапов:

library(openxlsx)
data <- read.xlsx('/Users/insomnia/Documents/disser/dataframe.xlsx')
library(tidyverse)
# initial data
glimpse(data)

### data cleaning process:

# name column

name <- data$name |> str_split("/")
name_unlisted <- unlist(lapply(name, '[[', 7)) # This returns a vector with the seven element
name_unlisted <- as.factor(name_unlisted) ###
lvls <- levels(name_unlisted) ###
levels(name_unlisted) <- seq_along(lvls) ###
data$name <- name_unlisted
data$name <- data$name |> as.factor()
data <- rename(data, patient_ID = name)

# 2,3,4 (time) columns

data <- data |> mutate(birthdate = dmy(birthdate),
               admission = dmy(admission),
               discharge = dmy(discharge))

data$admission[1]-data$birthdate[1]

#COVID column

covid <- data$COVID
covid <- lapply(covid, function(x) replace(x,!is.na(x),1))
covid <- lapply(covid, function(x) replace(x,is.na(x),0))
covid <- as.numeric(covid)
covid <- as.factor(covid)
data$COVID <- covid

# CT column

matches <- regmatches(data$CT, gregexpr("[[:digit:]]+", data$CT))
data$CT <- matches
data <- data |> rowwise() |> mutate(CT = max(CT)) |> ungroup()
data$CT <- as.numeric(data$CT)

#Death column
                
death <- data$Death
death <- lapply(death, function(x) replace(x,!is.na(x),1))
death <- lapply(death, function(x) replace(x,is.na(x),0))
death <- as.numeric(death)
data$Death <- death

# hb column
                
extracted_hb <- str_replace_all(data$hb,fixed(","), fixed("."))
extracted_hb <- str_extract_all(extracted_hb, pattern = "[0-9][0-9][0-9]?")
extracted_hb <- lapply(extracted_hb,as.numeric)
extracted_hb <- sapply(extracted_hb, mean, 0-20)
extracted_hb[906:913]
data$hb <- extracted_hb

# RBC column

extracted_RBC <- str_replace_all(data$RBC,fixed(","), fixed("."))
extracted_RBC <- str_extract_all(extracted_RBC, pattern = "[0-9][.]?[0-9]?[0-9]?")
extracted_RBC <- lapply(extracted_RBC,as.numeric)
extracted_RBC <- sapply(extracted_RBC, mean, 0-20)
extracted_RBC[906:913]
data$RBC <- extracted_RBC

# leu column

str(data)
extracted_leu <- str_replace_all(data$leu,fixed(","), fixed("."))
extracted_leu <- str_extract_all(extracted_leu, pattern = "[2-9][.]?[0-9]?|[1-3][0-9]?[.]?[0-9]?") # spread 2-22
extracted_leu <- lapply(extracted_leu,as.numeric)
extracted_leu <- sapply(extracted_leu, mean, 0-20)
extracted_leu[906:913]
data$leu <- extracted_leu

# limf column
                
extracted_limf <- str_replace_all(data$limf,fixed(","), fixed("."))
extracted_limf <- str_extract_all(extracted_limf, pattern = "[0-9][.]?[0-9]?") # spread 0-9
extracted_limf<- lapply(extracted_limf,as.numeric)
extracted_limf <- sapply(extracted_limf, min)
extracted_limf[906:913]
data$limf <- extracted_limf
data <- rename(data, limf_min = limf)

# CRP column
                
data$CRP[7]
extracted_CRP<- str_replace_all(data$CRP,fixed(","), fixed("."))
# Removing date from CRP data:
extracted_CRP <- str_remove_all(extracted_CRP, 
                                pattern = "[0-9][0-9]?[.][0-9][0-9][.][0-9][0-9][0-9]?[0-9]?") 
extracted_CRP <- str_extract_all(extracted_CRP, pattern = "[0-9][0-9]?[0-9]?[.][0-9]?[0-9]?[0-9]?") # spread 0-999
extracted_CRP<- lapply(extracted_CRP,as.numeric)
# getting CRP max value which is more valuable in this case then mean()
extracted_CRP <- sapply(extracted_CRP, max)
extracted_CRP
data$CRP <- extracted_CRP
data <- rename(data, CRP_max = CRP)

# Chol column
                
extracted_chol <- str_replace_all(data$chol,fixed(","), fixed("."))
extracted_chol <- str_extract_all(extracted_chol, pattern = "[0-9][.][0-9]?[0-9]?") # spread 0-19
extracted_chol<- lapply(extracted_chol,as.numeric)
extracted_chol <- sapply(extracted_chol, mean)
extracted_chol[2]
data$chol <- extracted_chol

# TG column
                
extracted_TG <- str_replace_all(data$TG,fixed(","), fixed("."))
extracted_TG <- str_extract_all(extracted_TG, pattern = "[0-9][.]?[0-9]?[0-9]?") # spread 0-9
extracted_TG<- lapply(extracted_TG,as.numeric)
extracted_TG <- sapply(extracted_TG, mean)
extracted_TG[15]
data$TG <- extracted_TG

# UHDL colunm
                
extracted_UHDL <- str_replace_all(data$UHDL,fixed(","), fixed("."))
extracted_UHDL <- str_extract_all(extracted_UHDL, pattern = "[0-9][.]?[0-9]?[0-9]?") # spread 0-9
extracted_UHDL<- lapply(extracted_UHDL,as.numeric)
extracted_UHDL <- sapply(extracted_UHDL, mean, 0-20)
extracted_UHDL[906:913]
data$UHDL <- extracted_UHDL

# DLDL colunm
                
extracted_DLDL <- str_replace_all(data$DLDL,fixed(","), fixed("."))
extracted_DLDL <- str_extract_all(extracted_DLDL, pattern = "[0-9][.]?[0-9]?[0-9]?") # spread 0-9
extracted_DLDL<- lapply(extracted_DLDL,as.numeric)
extracted_DLDL <- sapply(extracted_DLDL, mean, 0-20)
extracted_DLDL[906:913]
data$DLDL <- extracted_DLDL

# crea column
                
extracted_crea <- str_replace_all(data$crea,fixed(","), fixed("."))
extracted_crea <- str_extract_all(extracted_crea, pattern = "[0-9][0-9][0-9]?[.]?[0-9]?") # spread 0-9
extracted_crea<- lapply(extracted_crea,as.numeric)
extracted_crea <- sapply(extracted_crea, mean, 0-20)
extracted_crea[906:913]
data$crea <- extracted_crea

#creating age column
                
x = year(data$admission)
y = year(data$birthdate)
data <- data |> mutate(age = x-y)
data <- data |> relocate(age, .before = admission)

#creating LOS (length of stay(days of hospitalization)) column
                
x_LOS = (data$admission)
y_LOS = (data$discharge)
data <- data |> mutate(LOS = y_LOS-x_LOS)
data <- data |> relocate(LOS, .before = COVID)
data$LOS <- as.numeric(data$LOS)

# filtering data with 2 or more cases of hospitalization
                
matches <- data |> group_by(patient_ID) |> summarise(n=n()) |> filter(n>2)
matches <- matches$patient_ID
matched_data <- data |> filter(patient_ID %in% matches)
class(matched_data$COVID)
matched_data <- matched_data |> filter(patient_ID != "****")
matched_data |>group_by(patient_ID) |> summarise(n=n())

# admission/covid plot
                
matched_data |> ggplot(aes(x = admission, y = patient_ID))+
  geom_point(aes(color = COVID))+
  scale_color_manual(values = c("blue", "red"))
                
# запись обновленного файла:
write.xlsx(data, file = "structured_output.xlsx", colNames = T, borders = "columns")

Итоговый результат:

Итоговая таблица
Итоговая таблица

Общее время затраченное всё = 3-4 месяца. Из них 40% - поиск, сбор файлов(вручную). 30% - написание кода. 30% - проверка на ошибки, их исправление.

Буду рад услышать Ваши комментарии и замечания по выполненной работе!

Комментарии (11)


  1. LunarBirdMYT
    17.08.2024 17:42
    +2

    Вы провели большую и отличную работу, запарсили кучу документов, очистили их и сделали красивый свод. Можно большое количество if-else заменить на цикл и словарь, возможно. Так будет проще поддерживать код или менять его. Целью, видимо, было продемонстрировать очистку? А дальше этот свод просто для анализа или вы планируете сделать модельку с прогнозами?


    1. pogozhy Автор
      17.08.2024 17:42
      +7

      спасибо большое за похвалу, это моя первая работа подобного характера, я врач-кардиолог, код наверняка не без ошибок новичка, буду стараться и учиться) У меня есть вторая часть работы, где я провожу моделирование, визуализацию, статистические тесты с полученными данными, к примеру разбиваю пациентов на группы и сравниваю уровень холестерина до и после заболевания. Не стал выкладывать сразу все, чтобы не получилась слишком объемная статья. Если кому то будет интересно могу выложить


      1. jbourne
        17.08.2024 17:42

        Очень интересно. Выкладывайте.

        И такой вот вопрос: не думали как вычищать/исправлять нетехнический мусор - ошибки ввода данных, противоречивые показания, халатности, подтасовки, халатности записанные на ковид, не ковидные смерти записанные как ковидные и наоборот, и т.д.?

        Сам сталкивался с такой проблемой ранее, и тогда ничего лучше не придумали, чем проводить периодические аудиты случайных ситуаций с мед специалистом + закодили часть кросс-проверок простых.

        Но это было до ковида. Случаи были редкие, но довольно уникальные, поэтому тогда не получилось нормально автоматизировать их выявление.


        1. pogozhy Автор
          17.08.2024 17:42

          К сожалению пришлось проводить вручную много проверок и перепроверок. За время работы в каждый файл пришлось зайти как минимум 2 раза, плюс при добавлении файла изначально проверялись и исправлялись некоторые моменты. К примеру: 1. первый три даты dd.dd.dddd во всех файлах вручную проверялись и исправлялись. 2. Таблицы внутри word не переводятся в текст, каждый файл проверялся вручную, таблицы удалялись 3. Некоторые файлы содержат несколько выписных эпикризов разных пациентов - лишнее удалялось вручную. 4. Лимфоциты крови и лимфоциты мочи могут подтягиваться регуляркой как одно и то же

          И таких ошибок очень много, все и не перечислить. Я не представляю как это автоматизировать/закодить, мне кажется это невозможно. Единственный вариант который я посчитал наиболее правильным - вручную перепроверять каждый показатель по несколько раз, глядя на то что первично получается (до data tydying). И сомневаюсь что специалист без медицинского образования качественно разберётся в этих помоях)


  1. Andrey_Solomatin
    17.08.2024 17:42
    +1

        if re.search(r'\d\d[.]\d\d[.]\d{4}', x):
            birthdate = re.search(r'\d\d[.]\d\d[.]\d{4}', x).group()
        else:
            birthdate = "NA"

    Если документ не создан людьми, то простая регулярка может пропускать часть случаев, когда дата написанна по другому. Например 22, вместо 2022.

    Так можно потерять часть данных. Для особо важных полей можно собрать все пропущенные данные и попробовать глянуть на статистику.

    Такой подход уже отчищает данные, хотя отчистку вы делаете следующим шагом.




    1. Andrey_Solomatin
      17.08.2024 17:42
      +1

      re.findall(r'[Тт]...


      Гляньте на флаг для регуоярок, вроде он должен работать с русским. https://docs.python.org/3/library/re.html#re.IGNORECASE


  1. Andrey_Solomatin
    17.08.2024 17:42
    +1

    re.findall(r'\D?\D?\D?


    Можно упростить повторяющиеся символы
    re.findall(r'\D{0,3}


  1. Andrey_Solomatin
    17.08.2024 17:42
    +1

            if re.findall(r'ИБС|'
                      r'[Ии]шемическая болезнь сердца', x):
            IHD = re.findall(r'ИБС|'
                             r'[Ии]шемическая болезнь сердца', x)

    Повторение регулярок, путь к ошибкам.

    Можно вот так упростить, а еще лучше при сохраненни заменить на NA или оставить это на шаг отчистки.

    IHD = re.findall(r'ИБС|[Ии]шемическая болезнь сердца', x)
    IHD = IHD if IHD else "NA"


  1. Andrey_Solomatin
    17.08.2024 17:42
    +1

        if re.findall(r'ИОЛП.\s?\S?\s?[0-9]{2}', x):
            LAVI = re.findall(r'ИОЛП.\s?\S?\s?[0-9]{2}', x)
        else:
            LAVI = "NA"

    Я за декларативный стиль описания. Вместо всех этих IF напишите цикл.

    expressions = {   "LAVI": re.compile(r'ИОЛП.\s?\S?\s?[0-9]{2}')}
    data = {}
    
    for name, exr in expressions.items():
        data[name] = exr.findall(x)    


    И тогда columns для DataFrame возьмёте из data.

    Такой подход спасёт вас от copy-paste ошибок при добавлении новых проверок.


    1. pogozhy Автор
      17.08.2024 17:42

      Спасибо большое за все Ваши замечания, я подозревал, но не знал как это можно было сделать. Эти решения сильно сократят количество кода и повысят его качество. Буду учиться)


  1. Tom_Rench
    17.08.2024 17:42

    Почитайте про библиотеку yargy, хорошо подходит для поиска сущностей в тексте:

    https://github.com/natasha/yargy

    Я делал похожую задачу для диплома в магистратуре , может полезно будет:

    https://github.com/ezhkovskii/graduate_work_nlp_in_ehr