Использование данных для лучшего понимания тем в медицинской науке не ново. Одним из первых применений анализа данных в медицинской науке были попытки в начале девятнадцатого века понять холеру. В то время причины вспышек холеры не были поняты, поэтому болезнь была распространена в городах Европы и Северной Америки. Между 1832 и 1866 годами было три крупных волны холеры, которые распространились по миру через торговые связи. Во Франции были созданы тепловые карты Парижа, чтобы показать, насколько сильно пострадали различные районы города, а в 1854 году доктор Джон Сноу собрал и нанес на карту данные, которые показали сильную корреляцию между случаями холеры и некоторыми водяными насосами в лондонском Сити. в то время большинство людей использовали общественные насосы или колодцы для получения питьевой воды). Вероятно, это один из самых ранних примеров того, что мы сейчас называем анализом данных. Доктор Сноу не обнаружил причину холеры, но с помощью некоторой визуализации данных он показал связь между распространением болезни и водяными насосами, используемыми для получения воды.

На заре науки о данных были люди, которые видели потенциал анализа данных в медицине. Статья [1] «Интеллектуальный анализ медицинских данных: обнаружение знаний в хранилище клинических данных» была написана в 1997 году и описывает ранний пример использования анализа больших данных для развития медицинской науки. К сожалению, потребовалось некоторое время, чтобы наука о данных стала серьезно восприниматься в области медицины, но есть некоторые области, где она уже имеет существенное значение, например, с медицинскими изображениями и диагностикой.

В последнее десятилетие или около того машинное обучение, а затем нейронные сети использовались для помощи клиницистам в диагностике заболеваний. В статье [2]: «Глубокое 3D-обучение на медицинских изображениях: обзор» кратко рассказывается о внедрении CNN (сверточных нейронных сетей) и, в частности, об использовании CNN и 3D-медицинской визуализации для диагностики заболеваний.

Прежде чем мы начнем, давайте настроим вашу среду:

Чтобы следовать этой статье, вам необходимо установить следующие библиотеки Python:

Наука о данных и медицина

Пример 1. Изучение данных о раке по штатам

Данные, используемые в этом примере, находятся в трех CSV-файлах:

  • Рак.csv
  • Cancer_Occurrence.csv
  • Состояние.csv

Все 3 файла доступны на Kaggle

Давайте начнем с импорта необходимых библиотек и чтения файлов

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
df_c = pd.read_csv('medical/Cancer.csv')
df_co = pd.read_csv('medical/Cancer_Occurrence.csv')
df_s = pd.read_csv('medical/State.csv')

Мы можем использовать встроенные функции pandas для визуализации информации и ее формата, как показано ниже.

df_c.head(1)

df_c.shape
(64, 3)
df_s.head(1)

df_co.head(8)

Первый набор данных содержит различные типы рака, охватываемые набором данных, такие как рак легких, рак молочной железы и т. д. Форма df_c указывает на то, что существует 64 уникальных типа рака. Второй набор данных содержит информацию о заболеваемости раком по штатам, а последний предоставляет информацию о заболеваемости раком по расам.

Использование groupby для изучения набора данных о раке

Подсчет количества случаев возникновения рака по штатам можно выполнить с помощью функции pandas groupby. Сначала мы сгруппируем по State_name, а затем просуммируем столбец Count.

s = df_co.groupby(['State_name'])['Count'].sum()
s.sort_values(ascending=False).head()
State_name
California      165979
New York        111276
Florida         111273
Texas           106614
Pennsylvania     78663
Name: Count, dtype: int64

Количество случаев рака по штатам неудивительно, если учесть, что более крупные штаты (по населению) чаще имеют случаи рака.

Если мы посмотрим на первую пятерку штатов по численности населения:

df_s.sort_values(by=['Population'],ascending=False).head()

Вы можете видеть, что первые пять штатов по населению — это те же штаты, что и выше, но порядок отличается. По населению мы ожидаем, что в Техасе будет больше случаев рака, чем в Нью-Йорке, но имеющиеся у нас данные показывают, что рак чаще встречается в Нью-Йорке, чем в Техасе.

Отсюда мы могли бы копнуть немного глубже:

  1. Действительно ли рак более распространен в Нью-Йорке?
  2. Может ли это быть как-то связано с медицинской страховкой, более высокий процент людей в Нью-Йорке имеет медицинскую страховку, означает ли это, что случаи рака не выявляются в Техасе?
  3. Связан ли рак как-то с географическим положением?

Есть много возможных объяснений этому наблюдению, я оставлю это вам, чтобы выяснить, почему это правда.

Главное здесь в том, что наука о данных и компьютеры позволяют нам исследовать болезни и другие медицинские проблемы, как никогда раньше. Такой анализ в девятнадцатом веке занял бы НАМНОГО больше времени.

В качестве технического вывода мы использовали концепцию группировки для группировки строк по состоянию, а затем суммирования столбца «счетчик».

В следующем примере мы рассмотрим, как наука о данных позволяет нам делать вещи, которые ученому девятнадцатого века показались бы магией. Чтобы мотивировать следующий раздел, как вы думаете, можем ли мы использовать что-то вроде сердцебиения в покое и артериального давления, чтобы предсказать, кто подвергается риску сердечного приступа?

Пример 2 — Использование данных для прогнозирования сердечно-сосудистых заболеваний

Данные, использованные в этом примере бинарной классификации, доступны на Kaggle. Во-первых, давайте импортируем набор данных.

df_1 = pd.read_csv('medical/heart.csv')
df_1.head(1)

Дополнительная информация о каждом столбце представлена ​​ниже.

  • возраст - измеряется в годах
  • пол — мужской = 1; женщина = 0
  • cp — Тип боли в груди: Типичная стенокардия = 0, Атипичная стенокардия = 1, Неангинозная боль = 2, Бессимптомная = 3
  • trtbps — артериальное давление в покое (в мм рт.ст. при поступлении в стационар)
  • chol — холестерин в сыворотке в мг/дл
  • fbs — уровень сахара в крови натощак > 120 мг/дл (1 = верно; 0 = неверно)
  • restecg — результаты электрокардиографии в покое (0 = норма; 1 = наличие ST-T; 2 = гипертрофия)
  • талах — максимальная достигнутая частота сердечных сокращений
  • exng — стенокардия напряжения, вызванная физической нагрузкой (1 = да; 0 = нет)
  • oldpeak — депрессия ST, вызванная физической нагрузкой, по сравнению с состоянием покоя
  • slp — наклон пикового сегмента ST при нагрузке (1 = восходящий; 2 = плоский; 3 = нисходящий)
  • caa — количество крупных сосудов (0–3), окрашенных при флюороскопии
  • высокий — 2 = нормальный; 1 = фиксированный дефект; 3 = обратимый дефект
  • вывод — прогнозируемый признак — диагноз болезни сердца (ангиографический статус заболевания) (значение 0 = ‹ сужение диаметра; значение 1 = › 50% сужение диаметра)

Целью этого анализа является определение того, подвержен ли человек риску сердечного приступа с учетом имеющихся данных. Это проблема бинарной классификации, в которой мы определим класс 0 = отсутствие риска, класс 1 = риск.

Во-первых, давайте проверим наличие нулей:

df_1.isnull().sum()
age         0
sex         0
cp          0
trtbps      0
chol        0
fbs         0
restecg     0
thalachh    0
exng        0
oldpeak     0
slp         0
caa         0
thall       0
output      0
dtype: int64

Результаты показывают, что пустых значений нет, а данные во всех столбцах являются числовыми, поэтому нет необходимости в каких-либо преобразованиях типов. Однако мы можем захотеть нормализовать данные. В его текущем состоянии существует значительная разница в диапазоне значений для разных столбцов.

Мы можем визуализировать корреляции между различными переменными, используя библиотеку визуализации Python под названием Seaborn. Это одна из многих библиотек Python, которые можно использовать для создания корреляционной матрицы.

Размер графика по умолчанию немного мал, поэтому с помощью matplotlib мы можем увеличить размер рисунка, что упрощает чтение числовых значений на графике.

plt.rcParams['figure.figsize'] = [10, 10]

использование параметра annot=True отобразит значения корреляции на графике, значения корреляции предоставляются функцией pandas corr():

sns.heatmap(df_1.corr(),annot = True);

Эта корреляция показывает, например, что существует некоторая отрицательная корреляция между возрастом и максимальной достигнутой частотой сердечных сокращений (thalachh) и некоторая положительная корреляция между столбцами «Выход» и «Тип боли в груди» (cp). Столбец «Вывод» — это значение, которое мы пытаемся предсказать, поэтому, глядя на этот график, мы можем получить некоторое представление о том, какие функции (столбцы) имеют более сильную корреляцию с выводом и поэтому играют большую роль в прогнозе.

Есть и другие вещи, которые мы можем сделать, чтобы изменить внешний вид этого графика, например, мы можем определить диапазон, используя vmin=-1, vmax=1, center= 0

sns.heatmap(df_1.corr().round(2),annot = True, vmin=-1, vmax=1, center= 0);

Эстетический вид является субъективным, разные люди имеют разные мнения, но вышеуказанное изменение действительно немного более четко выявляет более высокие значения корреляции как для положительных, так и для отрицательных корреляций. Сразу же мы теперь видим, что переменные: restecg, fbs, chol и trtbps почти не имеют корреляции с выводом.

Мы попробуем машину опорных векторов, чтобы предсказать результат. Мы масштабируем значения столбцов и используем scikit Learns test-train-split, чтобы разделить данные на обучающие и тестовые данные. SVM — это контролируемый алгоритм, поэтому для него требуются обучающие данные.

from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report, roc_curve

Далее мы разделим данные на X и y, где y — переменная, которую мы пытаемся предсказать. Мы также масштабируем данные, чтобы устранить любую погрешность из-за того, что некоторые столбцы имеют более высокий числовой диапазон, чем другие столбцы.

X = df_1.drop(['output'],axis=1)
y = df_1['output']

scaler = RobustScaler()

# scaling the continuous featuree
X = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.2, random_state = 42)

Наконец, мы можем запустить модель, используя описанную ниже функцию обучения scikit.

clf = SVC(kernel='linear', C=1, random_state=42).fit(X_train,y_train)

# predicting the values
y_pred = clf.predict(X_test)

# printing the test accuracy
print(accuracy_score(y_test, y_pred))
0.8688524590163934

Производительность модели можно оценить с помощью classification_report.

print(classification_report(y_test, y_pred))
precision    recall  f1-score   support

           0       0.86      0.86      0.86        29
           1       0.88      0.88      0.88        32

    accuracy                           0.87        61
   macro avg       0.87      0.87      0.87        61
weighted avg       0.87      0.87      0.87        61

Чтобы повысить точность нашей модели, мы могли бы поэкспериментировать, пробуя разные алгоритмы, разные соотношения тестов и поездов и т. д. Я оставлю вам в качестве упражнения изучение того, как эти изменения влияют на результаты модели. В целом, этот пример продемонстрировал, как вы можете использовать машинное обучение, в частности алгоритм опорных векторов, для решения этой проблемы бинарной классификации.

В следующем примере мы попытаемся предсказать болезни сердца с помощью логистической регрессии.

Пример 3. Прогнозирование сердечно-сосудистых заболеваний с использованием логистической регрессии

Данные, использованные в этом примере, доступны на Kaggle.

Модель логистической регрессии предсказывает вероятность чего-то с учетом некоторых данных. В этом примере мы решим задачу бинарной классификации так же, как и в примере 2, но с использованием другого типа модели. Обратите внимание, что классификация — очень распространенная задача в науке о данных, поэтому вы можете применить то, что вы узнали здесь, к широкому кругу задач.

df_2 = pd.read_csv('medical/framingham.csv')
df_2.tail(5)

df_2.shape
(4238, 16)

В этом наборе данных чуть более четырех тысяч строк и шестнадцати столбцов. Столбцы образования и глюкозы имеют значительное количество нулей.

df_2.isnull().sum()
male                 0
age                  0
education          105
currentSmoker        0
cigsPerDay          29
BPMeds              53
prevalentStroke      0
prevalentHyp         0
diabetes             0
totChol             50
sysBP                0
diaBP                0
BMI                 19
heartRate            1
glucose            388
TenYearCHD           0
dtype: int64

Самый быстрый способ справиться с нулевыми значениями — удалить строки, содержащие нулевые значения, но имейте в виду, что вы теряете информацию.

df_2 = df_2.dropna()
df_2.isnull().sum()
male               0
age                0
education          0
currentSmoker      0
cigsPerDay         0
BPMeds             0
prevalentStroke    0
prevalentHyp       0
diabetes           0
totChol            0
sysBP              0
diaBP              0
BMI                0
heartRate          0
glucose            0
TenYearCHD         0
dtype: int64

Затем нам нужно отделить переменную, которую мы пытаемся предсказать («y»), от всех других переменных («x»):

X=df_2.iloc[:,:-1].values
y=df_2.iloc[:,-1].values

Теперь мы должны разделить X и y на данные обучения и данные тестирования, чтобы мы могли оценить производительность нашей модели:

X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.30,random_state=42)

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

from sklearn.preprocessing import StandardScaler
sc=StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
from sklearn.linear_model import LogisticRegression
classifier = LogisticRegression(random_state=0)
classifier.fit(X_train,y_train)
y_pred = classifier.predict(X_test)
from sklearn.metrics import confusion_matrix, accuracy_score
cm=confusion_matrix(y_test,y_pred)
print(cm)
recall = cm[0][0]/(cm[0][0] + cm[1][0])
print(recall)
print(accuracy_score(y_test,y_pred))
[[912  11]
 [157  17]]
0.8531337698783911
0.8468550592525068

Существуют различные показатели, которые мы можем использовать для оценки успеха прогностической модели.

  • Точность = какая часть тестовых данных была правильно классифицирована
  • Матрица путаницы = сколько истинно положительных, истинно отрицательных, ложноположительных и ложноотрицательных результатов
  • Отзыв = TP/(TP+FN), где TP истинно положительный, а FN ложноотрицательный.

Другими словами, отзыв — это количество людей, правильно идентифицированных как подверженные риску сердечного приступа, деленное на сумму людей, правильно идентифицированных как подверженные риску сердечного приступа, и людей, ошибочно идентифицированных как не подверженные риску сердечного приступа. С медицинской точки зрения эти ложноотрицательные результаты важны, и мы хотим их максимально уменьшить. Вывод из этого примера: не полагайтесь только на точность, обратите внимание также на полноту и матрицу путаницы. Идеальная модель сведет ложные срабатывания и ложноотрицательные результаты к нулю.

Краткий обзор того, что вы узнали:

Чтобы подвести итоги, давайте подведем итоги того, что мы обсудили. На этом этапе вы должны хорошо понимать:

  • Как использовать данные groupby и sum с более сложной структурой
  • Как использовать SVM для решения задачи бинарной классификации
  • Как использовать метрики, отличные от точности, для измерения эффективности модели.

Спасибо, что нашли время прочитать эту статью. Если у вас есть какие-либо отзывы или предложения по улучшению этой статьи, мы будем рады услышать от вас.

Использованная литература:

  1. Пратер Дж., Интеллектуальный анализ медицинских данных: обнаружение знаний в хранилище клинических данных, дата извлечения = 05.03.2021, ссылка
  2. Сингх, С., Глубокое 3D-обучение на медицинских изображениях: обзор, дата получения = 05.03.2021, ссылка

Свяжитесь с г-ном Наука о данных:

MrDataScience.com, GitHub, Средний,