Сравнение концентраций ИЛ-6 в плазме больных COVID-19 в зависимости от исхода заболевания

А.С. Смирнов1, З.Р. Коробова2

1 - Кафедра биоинформатики МБФ РНИМУ им. Н.И. Пирогова

2 - Лаборатория молекулярной иммунологии НИИЭМ им. Пастера

Работа выполнена на базе Лаборатория молекулярной иммунологии НИИЭМ им. Пастера в рамках Летней школы "Наука - врачам будущего" 4-19 июля 2022 года

Научный руководитель - З.Р. Коробова

Дата составления отчета - 18 сентября 2022 года

Введение

COVID-19 — опасное инфекционное заболевание, вызываемое вирусом SARS-CoV-2 из рода Betacoronavirus1. Для этого заболевания характерны тяжелые поражения лёгких вследствие атипичной пневмонии и сравнительно высокая летальность, особенно у лиц старшей возрастной группы (старше 50 лет)2. В качестве основной причины смерти выделяют неадекватный иммунный ответ на возбудителя — цитокиновый шторм3. Цитокины — это большая группа гликопротеиновых молекул, отвечающая за гуморальную регуляцию жизнедеятельности клеток. С их помощью осуществляется межклеточное и межсистемное взаимодействие. Цитокины делят на две большие группы: провоспалительные и противовоспалительные. В литературе4 существуют данные, что концентрация провоспалительных цитокинов в плазме крови может быть важным прогностическим фактором. Цель нашего исследования: проверить, может ли являться концентрация интерлейкина-6, одного из главных провоспалительных цитокинов, в плазме крови прогностическим фактором исхода у больных старшей возрастной группы.

Уровень значимости при проверке гипотез = 0.05

Априорные гипотезы

  1. Первая

    • Н0: Средний уровень IL-6 у умерших равен таковому у выписывшихся пациентов
    • Н1: Средний уровень IL-6 у умерших выше таковому у выписывшихся пациентов
  2. Вторая

    • Н0: Средний уровень IL-6 у выписывшихся равен таковому у здоровых
    • Н1: Средний уровень IL-6 у выписывшихся выше таковому у здоровых
  3. Третья

    • Н0: Средний уровень IL-6 у больных равен таковому у здоровых пациентов
    • Н1: Средний уровень IL-6 у больных выше таковому у здоровых пациентов

Апостериорные гипотезы

  1. Результаты, полученные с помощью метода иммуноферментного анализа (набор Вектор Бест, Новосибирск, Россия), сопоставимы с результатами, полученными с помощью метода мультиплесного анализа Luminex Magpix (набор для определения 47 цитокинов и ростовых факторов, Millipore, Burlington, Massachusetts).
  2. Присутствует корреляция уровня ИЛ-6 с возрастом
  3. Различия в концентрациях ИЛ-6 в группах, с разной степенью тяжести
    • Н0: Средний уровень IL-6 у больных с разной степенью тяжести не отличается
    • Н1: Средний уровень IL-6 у больных с разной степенью тяжести отличается

Дизайн эксперимента

Материалы

Образцы крови были получены от пациентов, проходивших лечение в СЗОНКЦ им. Л.Г. Соколова ФМБА России на базе инфекционного COVID-специализированного отделения. Всем пациентам впервые был установлен диагноз "COVID-19" (U07.1), подтвержденный c помощью качественного ПЦР. Сбор периферической крови для образцов осуществлялся в пробирки с ЭДТА в приёмном покое. Для отделения плазмы осуществлялось центрифугирование образцов при 350g в течение 10 мин. В дальнейшем образцы плазмы замораживались при температуре -80C.

От всех пациентов было получено информированное добровольное согласие, исследование проводилось в соответствии с Хельсинской декларацией; проведение исследования было одобрено этическим комитетом СЗОНКЦ.

Методы

Определение уровня ИЛ-6 проводилось двумя методами:

  1. Методом ИФА на наборе Интерлейкин-6-ИФА-БЕСТ (Вектор Бест, Новосибирск, Россия) в соответствии с инструкцией производителя. Для регистрации результатов был использован ИФА анализатор Multiskan FC (Thermo Scientific, MA, USA).
  2. Методом мультиплексного анализа по технологии xMAP (Luminex, Austin, Texas), для определения цитокинов использовался набор для определения 47 цитокинов и ростовых факторов (Millipore, Burlington, Massachusetts).

Cтатистическая обработка данных была выполнена с помощью языка программирования Python.

Версия Python

In [2]:
import sys
print(sys.version)
3.8.10 (default, Jun 22 2022, 20:18:18) 
[GCC 9.4.0]

Версии используемых библиотек

In [10]:
import pandas as pd
import datetime
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
import scipy as sp
import statsmodels.api as sm

print("pandas = " + pd.__version__)
print("seaborn = " + sns.__version__)
print("matplotlib = " + matplotlib.__version__)
print("scipy = " + sp.__version__)
print("statsmodels = " + sm.__version__)
pandas = 1.3.4
seaborn = 0.11.2
matplotlib = 3.5.1
scipy = 1.7.3
statsmodels = 0.13.2

Критерии включения

  1. Возраст пациента от 50 до 70 лет включительно
  2. Госпитализация на 5-10 сутки заболевания

Критерии исключения

  1. Наличие перенесенного COVID-19 или соответствующей вакцинации в анамнезе
  2. Выявленный геновариант “Омикрон”
  3. Наличие иммуннокомпрометирующих состояний: беременность, онкологические заболевания, хронические заболевания в стадии обострения, аутоиммунные и аутовоспалительные заболевания, гепатиты В, С, ВИЧ-инфекция

Отбор пациентов

In [11]:
pd.set_option('display.max_columns', 500)
In [20]:
data = pd.read_excel("~/Documents/Piter/Immunology/tables/raw_Patients_data.xlsx")
print(data.shape)
data.head()
(1741, 24)
Out[20]:
ID Manifistation_Date Hospitalization_Date Disease_Day Outcome Age Sex Full_Vaccination Second_Component_Date Vaccine_Code Days_Beetween_Vaccination_Disease CT Saturation Lung_Damage Saturation_at_discharge Heaviness Etiotropy Steroids Target_therapy Anticoagulants Non_steroid_antiinfflam Comments Omicron Comments2
0 С-1 2021-06-12 00:00:00 2021-06-15 3.0 выписана 60.0 2.0 0.0 NaN NaN NaN 1 95 2.0 97 1.0 0 1 1 1 1 NaN NaN NaN
1 С-2 2021-06-06 00:00:00 2021-06-15 9.0 выписана 24.0 2.0 0.0 NaN NaN NaN 2 92 1.0 96 1.0 1 1 1 1 1 NaN NaN NaN
2 С-3 2021-06-11 00:00:00 2021-06-15 4.0 выписана 75.0 2.0 1.0 2021-03-14 00:00:00 1 NaN 0.12 0.96 0.0 0.96 1.0 NaN 1 0 1 NaN NaN NaN NaN
3 С-4 2021-06-08 00:00:00 2021-06-15 7.0 выписана 64.0 2.0 0.0 2021-05-27 00:00:00 1 однокр NaN КТ2 от 14.06, 24.06-40%, 05.07-40% 91 NaN 94 1.0 NaN 1 1 1 NaN NaN NaN NaN
4 С-5 2021-06-04 00:00:00 2021-06-15 11.0 выписан 29.0 1.0 0.0 NaN NaN NaN 3 92 2.0 96 1.0 1 1 0 1 1 NaN NaN NaN

Проверка дат

In [21]:
for i in data.index:
    if not (isinstance(data.loc[i, "Manifistation_Date"],datetime.date) and isinstance(data.loc[i, "Hospitalization_Date"],datetime.date)):
        data.drop(index = i, inplace = True)
data.shape
Out[21]:
(1684, 24)
In [22]:
data = data[data["Manifistation_Date"] < data["Hospitalization_Date"]]
data.shape
Out[22]:
(1664, 24)

Проверка по критериям включения

In [23]:
data = data[(data["Disease_Day"] >= 5) & (data["Disease_Day"] <= 10)]
data.shape
Out[23]:
(901, 24)

Примечание: изначально планировалось брать пациентов с 40 лет, но не набралось достаточное количество образцов с достаточным количеством плазмы. Поэтому отбор образцов ниже проводился с 40 лет, но потом вручную были исключены пациенты моложе 50 лет

In [25]:
# сначала планировалась брать пациентов с 40 лет, но не нашлось достаточное количество образцов
data = data[(data["Age"] >= 40) & (data["Age"] <= 70)]
data.shape
Out[25]:
(580, 24)

Проверка по критериям исключения

In [26]:
data = data[(data["Full_Vaccination"] == 0) & (data["Second_Component_Date"].isna()) & (data["Vaccine_Code"].isna()) & (data["Days_Beetween_Vaccination_Disease"].isna())]
data.shape
Out[26]:
(412, 24)
In [27]:
data = data[(data["Omicron"] == 0) | (data["Omicron"].isna())]
data.shape
Out[27]:
(412, 24)

Факторизация исходов

In [29]:
data["Outcome"].value_counts()
Out[29]:
выписана                   201
выписан                    182
амбул                        4
умер                         1
умерла 17.10                 1
умерла 01.01                 1
умерла 12.12                 1
умерла 17.11                 1
переведена на реабил         1
умер 08.10.2021              1
умер 07.10.2021              1
умерла 06.10                 1
переведена реаб.25.10        1
умерла 16.09                 1
переведен на реаб.15.10      1
умер 14.08.2021              1
умерла 25.08                 1
умерла 08.09                 1
умер 20.07.2021              1
умерла 17.08                 1
умер 21.07.2021              1
умерла 06.08                 1
умерла 31.08                 1
умер 17.07.2021              1
умер 13.06.2021              1
умер 16.07.2021              1
умер 13.07.2021              1
умер 07.02                   1
Name: Outcome, dtype: int64
In [30]:
data.loc[data['Outcome'].str.contains("выписан"),"Outcome"] = "выписка"
data.loc[data['Outcome'].str.contains("амбул"),"Outcome"] = "амбулаторно"
data.loc[data['Outcome'].str.contains("умер"),"Outcome"] = "смерть"
data.loc[data['Outcome'].str.contains("реаб"),"Outcome"] = "реабилитация"
In [31]:
data["Outcome"].value_counts()
Out[31]:
выписка         383
смерть           22
амбулаторно       4
реабилитация      3
Name: Outcome, dtype: int64

Формирование групп

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

In [33]:
sns.histplot(data.loc[data["Outcome"] == "смерть","Age"],binwidth = 5)
Out[33]:
<AxesSubplot:xlabel='Age', ylabel='Count'>
In [34]:
data.loc[data["Outcome"] == "смерть","Age"].value_counts().sort_index()
Out[34]:
42.0    1
50.0    1
53.0    1
55.0    1
57.0    2
59.0    3
60.0    1
61.0    1
62.0    1
65.0    3
66.0    1
68.0    3
69.0    2
70.0    1
Name: Age, dtype: int64
In [35]:
favorable = data[(data["Outcome"] == "выписка") | (data["Outcome"] == "реабилитация")]
favorable["Age"].value_counts().sort_index()
Out[35]:
40.0    13
41.0    10
42.0     3
43.0    13
44.0    13
45.0    10
46.0    12
47.0    14
48.0     5
49.0    11
50.0     8
51.0    14
52.0     8
53.0    10
54.0    12
55.0    10
56.0    10
57.0    17
58.0    13
59.0    12
60.0    18
61.0    16
62.0    22
63.0    22
64.0    15
65.0    14
66.0    14
67.0    11
68.0    16
69.0    10
70.0    10
Name: Age, dtype: int64
In [36]:
# необходимо было отобрать в 3 раза больше образцов, чем нужно, так как не было известно, 
# какие образцы есть в достаточном количестве
k = 3 
In [37]:
# Случайный выбор образцов из возрастных групп
age40_45 = favorable[(favorable["Age"] > 40) & (favorable["Age"] <= 45)].sample(n = 1 * k,random_state = 555)
age45_50 = favorable[(favorable["Age"] > 45) & (favorable["Age"] <= 50)].sample(n = 1 * k,random_state = 555)
age50_55 = favorable[(favorable["Age"] > 50) & (favorable["Age"] <= 55)].sample(n = 2 * k,random_state = 555)
age55_60 = favorable[(favorable["Age"] > 55) & (favorable["Age"] <= 60)].sample(n = 6 * k,random_state = 555)
age60_65 = favorable[(favorable["Age"] > 60) & (favorable["Age"] <= 65)].sample(n = 5 * k,random_state = 555)
age65_70 = favorable[(favorable["Age"] > 65) & (favorable["Age"] <= 70)].sample(n = 7 * k,random_state = 555)
In [38]:
sample_favor = pd.concat([age40_45,age45_50,age50_55,age55_60,age60_65,age65_70],axis = 0)
sample_favor["Age"].value_counts().sort_index()
Out[38]:
41.0    1
44.0    1
45.0    1
47.0    1
49.0    2
51.0    1
52.0    1
53.0    1
54.0    1
55.0    2
56.0    3
57.0    4
58.0    3
59.0    4
60.0    4
61.0    1
62.0    6
63.0    5
64.0    2
65.0    1
66.0    4
67.0    3
68.0    5
69.0    3
70.0    6
Name: Age, dtype: int64

Распределение по возрасту в выборке совпало с исходной генеральной совокупностью.

In [39]:
sns.histplot(data.loc[data["Outcome"] == "смерть","Age"], binwidth = 5)
sns.histplot(sample_favor["Age"],binwidth = 5,color = "red",alpha = 0.3)
Out[39]:
<AxesSubplot:xlabel='Age', ylabel='Count'>
In [41]:
print("Количество образцов = " + str(len(sample_favor.index)))
Количество образцов = 66

К сожалению, распределение по полу не удалось сделать равномерным, но мы постарались минимизировать различия.

In [42]:
sample_favor["Sex"].value_counts()
Out[42]:
2.0    39
1.0    27
Name: Sex, dtype: int64

Сохраняем списки

In [ ]:
final = pd.concat([data[data["Outcome"] == "смерть"],sample_favor])
final = final[["ID","Outcome","Age", "Sex"]].sort_values(by = ["Outcome","Age","Sex"])
final.head()
In [ ]:
final[(final["Sex"] == 1) & (final["Outcome"] == "выписка")].sort_values(by = "Age").to_excel("~/Documents/Piter/Immunology/surv_men.xlsx",index = False)
In [ ]:
final[(final["Sex"] == 2) & (final["Outcome"] == "выписка")].sort_values(by = "Age").to_excel("~/Documents/Piter/Immunology/surv_women.xlsx",index = False)
In [ ]:
final[(final["Sex"] == 1) & (final["Outcome"] == "смерть")].sort_values(by = "Age").to_excel("~/Documents/Piter/Immunology/dead_men.xlsx",index = False)
In [ ]:
final[(final["Sex"] == 2) & (final["Outcome"] == "смерть")].sort_values(by = "Age").to_excel("~/Documents/Piter/Immunology/dead_women.xlsx",index = False)

Также вручную были отобраны образцы 8 здоровых людей так, чтобы распределение по полу и возрасту соответствовали опытным группам.

Эксперимент

Поиск образцов

In [47]:
plan = pd.read_excel("~/Documents/Piter/Immunology/tables/planning_elisa.xlsx",  sheet_name="Список образцов", header = [0,1])
plan
Out[47]:
Мужчины Женщины Мужчины Женщины
ID Outcome Age Exist Exist.1 ID Outcome Age Exist Exist.1 ID.1 Outcome.1 Age.1 Exist.2 Exist.3 ID.1 Outcome.1 Age.1 Exist.2
0 С-311 выписка 41.0 0.0 NaN С-405 выписка 45.0 False NaN C-144 смерть 42.0 0.0 NaN С-1331 смерть 57.0 0.0
1 C-244 выписка 44.0 0.0 NaN C-1094 выписка 52.0 True NaN С-70 смерть 50.0 1.0 NaN С-313 смерть 59.0 1.0
2 С-270 выписка 47.0 0.0 NaN С-289 выписка 53.0 False NaN С-910 смерть 53.0 1.0 NaN С-540 смерть 59.0 1.0
3 С-267 выписка 49.0 0.0 NaN C-69 выписка 56.0 False NaN С-464 смерть 55.0 1.0 NaN C-1056 смерть 61.0 0.0
4 С-986 выписка 49.0 0.0 NaN С-542 выписка 57.0 True NaN C-110 смерть 57.0 1.0 NaN С-822 смерть 65.0 1.0
5 C-157 выписка 51.0 0.0 NaN С-1374 выписка 57.0 False NaN С-7 смерть 59.0 1.0 NaN С-546 смерть 66.0 1.0
6 С-770 выписка 54.0 1.0 NaN C-1126 выписка 57.0 True NaN C-191 смерть 60.0 1.0 NaN C-174 смерть 68.0 1.0
7 C-140 выписка 55.0 0.0 NaN С-903 выписка 58.0 True NaN C-129 смерть 62.0 1.0 NaN С-432 смерть 68.0 1.0
8 С-1325 выписка 55.0 0.0 NaN C-87 выписка 58.0 True NaN С-865 смерть 65.0 1.0 NaN C-1163 смерть 68.0 0.0
9 C-226 выписка 56.0 1.0 NaN С-1350 выписка 58.0 False NaN С-1347 смерть 65.0 0.0 NaN C-165 смерть 69.0 1.0
10 C-1153 выписка 56.0 1.0 NaN C-64 выписка 59.0 True NaN С-356 смерть 70.0 1.0 NaN С-790 смерть 69.0 1.0
11 С-1261 выписка 57.0 1.0 NaN С-670 выписка 59.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
12 C-76 выписка 59.0 0.0 NaN С-1185 выписка 59.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
13 С-774 выписка 60.0 1.0 NaN С-1362 выписка 60.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
14 С-722 выписка 60.0 1.0 NaN С-898a выписка 60.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
15 C-122 выписка 61.0 0.0 NaN C-176 выписка 62.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
16 С-462 выписка 62.0 0.0 NaN C-117 выписка 62.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
17 C-55 выписка 62.0 1.0 NaN С-941 выписка 62.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
18 C-118 выписка 64.0 0.0 NaN C-1129 выписка 62.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
19 C-238 выписка 66.0 1.0 NaN С-844 выписка 63.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
20 C-225 выписка 67.0 0.0 NaN С-406 выписка 63.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
21 С-260 выписка 68.0 0.0 NaN С-705 выписка 63.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
22 С-447 выписка 68.0 0.0 NaN C-241 выписка 63.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
23 С-689 выписка 68.0 1.0 NaN С-632 выписка 63.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
24 С-280 выписка 70.0 1.0 NaN C-39 выписка 64.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
25 С-809 выписка 70.0 1.0 NaN С-635 выписка 65.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
26 С-877 выписка 70.0 1.0 NaN С-811 выписка 66.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
27 NaN NaN NaN NaN NaN С-681 выписка 66.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
28 NaN NaN NaN NaN NaN С-607 выписка 66.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
29 NaN NaN NaN NaN NaN C-1087 выписка 67.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
30 NaN NaN NaN NaN NaN C-1077 выписка 67.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
31 NaN NaN NaN NaN NaN С-1392 выписка 68.0 False NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
32 NaN NaN NaN NaN NaN С-374 выписка 68.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
33 NaN NaN NaN NaN NaN С-351 выписка 69.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
34 NaN NaN NaN NaN NaN С-1377 выписка 69.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
35 NaN NaN NaN NaN NaN С-906 выписка 69.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
36 NaN NaN NaN NaN NaN C-184 выписка 70.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
37 NaN NaN NaN NaN NaN С-998 выписка 70.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
38 NaN NaN NaN NaN NaN С-605 выписка 70.0 True NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

Разметка планшета и результат

1 2 3 4 5 6 7 8 9 10 11 12
A калибратор1 910_1 191_1 313_1 174_1 770_1 542_1 898а_1 607_1 280_1 зд1_1 зд5_1
B калибратор2 910_2 70_2 313_2 174_2 770_2 542_2 898а_2 607_2 280_2 зд1_2 зд5_2
C калибратор3 464_1 129_1 540_1 432_1 226_1 1126_1 1129_1 1087_1 809_1 зд2_1 зд6_1
D калибратор4 464_2 129_2 540_2 432_2 226_2 1126_2 1129_2 1087_2 809_2 зд2_2 зд6_2
E калибратор5 110_1 865_1 822_1 165_1 1153_1 774_1 238_1 1077_1 998_1 зд3_1 зд7_1
F калибратор6 110_2 865_2 822_2 165_2 1153_2 774_2 238_2 1077_2 998_2 зд3_2 зд7_2
G контроль 7_1 356_1 546_1 790_1 1094_1 55_1 689_1 351_1 605_1 зд4_1 зд8_1
H 70_1 7_2 356_2 546_2 790_2 1094_2 55_2 689_2 351_2 605_2 зд4_2 зд8_2
1 2 3 4 5 6 7 8 9 10 11 12
A 94,23 65,35 52,7 46,57 73,58 34,15 150,9 34,73 40,8 6,815 11,78
B 43,04 54,35 52,85 48,7 73,07 30,57 142 35,22 42,31 7,806 12,84
C 39,49 133,3 39,96 38,61 7,302 8,317 23,55 47,04 210,4 8,691 7,111
D 37,82 155,1 42,34 44,5 7,424 7,446 28 45,32 141,2 10,03 5,004
E 17,86 10,91 11,18 60,07 72,61 34,25 45,68 44,8 60,05 8,37 4,788
F 21,03 16,38 16,02 48,2 44,25 31,1 36,73 38,17 54,74 9,677 4,937
G 65,07 30,68 37,25 56,83 65,81 24,26 26,29 49,21 28,9 26,17 10,4 5,281
H 51,69 31,13 32,89 57,87 41,14 29,4 24,84 65,47 44,88 32,18 21,62 25,78

Единицы измерения - пг/мл. Все образцы сделаны в дублях, за исключением одного (№191) из-за недостаточности плазмы. Ретроспективно мы добавили результаты оценки концентраций IL-6 с помощью технологии xMAP.

Обработка результатов

Описательная статистика

Сопоставление данных пациентов с результатами

In [49]:
results = pd.read_excel("~/Documents/Piter/Immunology/tables/results.ods",engine = "odf",index_col = 0)
results.head()
Out[49]:
Holes Outcome Age Sex Try_1, pg/ml Try_2, pg/ml Mean, pg/ml
ID
70 H1,B3 NaN NaN NaN 51.69 54.35 53.02
910 A2,B2 NaN NaN NaN 94.23 43.04 68.64
464 C2,D2 NaN NaN NaN 39.49 37.82 38.66
110 E2,F2 NaN NaN NaN 17.86 21.03 19.45
7 G2,H2 NaN NaN NaN 30.68 31.13 30.91
In [50]:
samples = pd.read_excel("~/Documents/Piter/Immunology/tables/samples.ods",engine = "odf",index_col = 0)
samples.head()
Out[50]:
Outcome Age Sex Exist
ID
770 выписка 54 1 ИСТИНА
226 выписка 56 1 ИСТИНА
1153 выписка 56 1 ИСТИНА
1261 выписка 57 1 ИСТИНА
774 выписка 60 1 ИСТИНА
In [52]:
for i in results.index:
    results.loc[i,"Outcome"] = samples.loc[i,"Outcome"]
    results.loc[i,"Age"] = samples.loc[i,"Age"]
    results.loc[i,"Sex"] = samples.loc[i,"Sex"]
In [53]:
results.head()
Out[53]:
Holes Outcome Age Sex Try_1, pg/ml Try_2, pg/ml Mean, pg/ml
ID
70 H1,B3 смерть 50.0 1.0 51.69 54.35 53.02
910 A2,B2 смерть 53.0 1.0 94.23 43.04 68.64
464 C2,D2 смерть 55.0 1.0 39.49 37.82 38.66
110 E2,F2 смерть 57.0 1.0 17.86 21.03 19.45
7 G2,H2 смерть 59.0 1.0 30.68 31.13 30.91

Распределение по полу и возрасту

In [57]:
fig,ax = plt.subplots(figsize = (8,8))
sns.boxplot(data=results, x="Outcome", y="Age", boxprops={'alpha': 0.4},ax = ax,showfliers = False)
sns.stripplot(data=results, x="Outcome", y="Age", dodge=True, ax=ax)
ax.set_xlabel("Исход")
ax.set_ylabel("Возраст, года")
Out[57]:
Text(0, 0.5, 'Возраст, года')
Группа Мужчины (n = 21) Женщины (n = 24)
Выписка (n = 20) 9 11
Смерть (n = 17) 9 8
Здоровые (n = 8) 3 5

Результаты

In [73]:
fig,ax = plt.subplots(figsize = (8,8))
sns.boxplot(data=results, x="Outcome", y="Mean, pg/ml", boxprops={'alpha': 0.4},ax = ax,showfliers = False)
sns.stripplot(data=results, x="Outcome", y="Mean, pg/ml", dodge=True, ax=ax)
ax.set_xlabel("Исход")
ax.set_ylabel("ИЛ-6, пг/мл")
plt.savefig("res.png",dpi = 300)
In [75]:
r1 = results.copy()
for i in r1.index:
    if samples.loc[i,"Outcome"] == "здоровый":    
        r1.loc[i,"Outcome"] = samples.loc[i,"Outcome"]
    else:
        r1.loc[i,"Outcome"] = "больной"
    r1.loc[i,"Age"] = samples.loc[i,"Age"]
    r1.loc[i,"Sex"] = samples.loc[i,"Sex"]
In [76]:
fig,ax = plt.subplots(figsize = (8,8))
sns.boxplot(data=r1, x="Outcome", y="Mean, pg/ml", boxprops={'alpha': 0.4},ax = ax,showfliers = False)
sns.stripplot(data=r1, x="Outcome", y="Mean, pg/ml", dodge=True, ax=ax)
ax.set_xlabel("Исход")
ax.set_ylabel("ИЛ-6, пг/мл")
plt.savefig("il6_1.png",dpi = 300)

Проверка априорных гипотез

Проверка на нормальность распределения

Результаты проверки зависят от того считаем ли мы уровень ИЛ-6 > 125 пг/мл выбросами.

Если не считаем

In [113]:
dead_with_high = results.loc[(results["Outcome"] == "смерть"),"Mean, pg/ml"]
survive_with_high = results.loc[(results["Outcome"] == "выписка"),"Mean, pg/ml"]
sick_with_high = results.loc[((results["Outcome"] == "смерть") | (results["Outcome"] == "выписка")),"Mean, pg/ml"]
healthy_with_high = results.loc[(results["Outcome"] == "здоровый"),"Mean, pg/ml"]
In [114]:
print(sp.stats.shapiro(dead_with_high))
sp.stats.probplot(dead_with_high,plot = plt)
ShapiroResult(statistic=0.7973724603652954, pvalue=0.00186758185736835)
Out[114]:
((array([-1.7512281 , -1.29947609, -1.01741764, -0.79927841, -0.61386703,
         -0.44755083, -0.29283065, -0.14485444,  0.        ,  0.14485444,
          0.29283065,  0.44755083,  0.61386703,  0.79927841,  1.01741764,
          1.29947609,  1.7512281 ]),
  array([ 13.6 ,  13.65,  19.45,  30.91,  35.07,  38.66,  41.15,  41.56,
          47.64,  52.78,  53.02,  53.48,  54.14,  57.35,  65.35,  68.64,
         144.2 ])),
 (27.51478588756497, 48.86176470588236, 0.8775627782505899))
In [115]:
print(sp.stats.shapiro(survive_with_high))
sp.stats.probplot(survive_with_high,plot = plt)
ShapiroResult(statistic=0.72792649269104, pvalue=8.714490104466677e-05)
Out[115]:
((array([-1.8241636 , -1.38768012, -1.11829229, -0.91222575, -0.73908135,
         -0.5857176 , -0.44506467, -0.31273668, -0.18568928, -0.06158146,
          0.06158146,  0.18568928,  0.31273668,  0.44506467,  0.5857176 ,
          0.73908135,  0.91222575,  1.11829229,  1.38768012,  1.8241636 ]),
  array([  7.36,   7.88,  25.57,  25.78,  26.83,  29.18,  32.36,  32.68,
          34.98,  36.89,  41.21,  41.49,  41.56,  46.18,  57.34,  57.4 ,
          58.43,  73.33, 146.45, 175.8 ])),
 (36.932200631836686, 49.934999999999995, 0.8431906428725423))
In [116]:
print(sp.stats.shapiro(sick_with_high))
sp.stats.probplot(sick_with_high,plot = plt)
ShapiroResult(statistic=0.7563283443450928, pvalue=1.9274734768259805e-06)
Out[116]:
((array([-2.0844567 , -1.69509426, -1.46257551, -1.28983027, -1.14880688,
         -1.0275498 , -0.91978012, -0.82175965, -0.73106454, -0.646021  ,
         -0.56541462, -0.48832761, -0.41404133, -0.34197479, -0.27164383,
         -0.20263297, -0.13457495, -0.06713532,  0.        ,  0.06713532,
          0.13457495,  0.20263297,  0.27164383,  0.34197479,  0.41404133,
          0.48832761,  0.56541462,  0.646021  ,  0.73106454,  0.82175965,
          0.91978012,  1.0275498 ,  1.14880688,  1.28983027,  1.46257551,
          1.69509426,  2.0844567 ]),
  array([  7.36,   7.88,  13.6 ,  13.65,  19.45,  25.57,  25.78,  26.83,
          29.18,  30.91,  32.36,  32.68,  34.98,  35.07,  36.89,  38.66,
          41.15,  41.21,  41.49,  41.56,  41.56,  46.18,  47.64,  52.78,
          53.02,  53.48,  54.14,  57.34,  57.35,  57.4 ,  58.43,  65.35,
          68.64,  73.33, 144.2 , 146.45, 175.8 ])),
 (32.18002193424201, 49.4418918918919, 0.8632756095253724))
In [117]:
print(sp.stats.shapiro(healthy_with_high))
sp.stats.probplot(healthy_with_high,plot = plt)
ShapiroResult(statistic=0.9231124520301819, pvalue=0.45562413334846497)
Out[117]:
((array([-1.38519806, -0.83757156, -0.46579419, -0.15039337,  0.15039337,
          0.46579419,  0.83757156,  1.38519806]),
  array([ 4.86,  6.06,  7.31,  9.02,  9.36, 12.31, 15.53, 16.01])),
 (4.503134064185856, 10.057500000000001, 0.9728136559128856))

Если считаем

In [118]:
dead_without_high = results.loc[(results["Outcome"] == "смерть") & (results["Mean, pg/ml"] < 125),"Mean, pg/ml"]
survive_without_high = results.loc[(results["Outcome"] == "выписка") & (results["Mean, pg/ml"] < 125),"Mean, pg/ml"]
sick_without_high = results.loc[((results["Outcome"] == "смерть") | (results["Outcome"] == "выписка")) & (results["Mean, pg/ml"] < 125),"Mean, pg/ml"]
healthy_without_high = results.loc[(results["Outcome"] == "здоровый") & (results["Mean, pg/ml"] < 125),"Mean, pg/ml"]
In [119]:
print(sp.stats.shapiro(dead_without_high))
sp.stats.probplot(dead_without_high,plot = plt)
ShapiroResult(statistic=0.940981924533844, pvalue=0.3612158000469208)
Out[119]:
((array([-1.72352605, -1.26569652, -0.97848645, -0.75533862, -0.56472935,
         -0.39279634, -0.23181469, -0.07666006,  0.07666006,  0.23181469,
          0.39279634,  0.56472935,  0.75533862,  0.97848645,  1.26569652,
          1.72352605]),
  array([13.6 , 13.65, 19.45, 30.91, 35.07, 38.66, 41.15, 41.56, 47.64,
         52.78, 53.02, 53.48, 54.14, 57.35, 65.35, 68.64])),
 (17.648001593640767, 42.903125, 0.977087409833935))
In [120]:
print(sp.stats.shapiro(survive_without_high))
sp.stats.probplot(survive_without_high,plot = plt)
ShapiroResult(statistic=0.9634000062942505, pvalue=0.6683553457260132)
Out[120]:
((array([-1.77709673, -1.33087857, -1.05345661, -0.83977496, -0.6589352 ,
         -0.49749478, -0.34812942, -0.20618578, -0.06829777,  0.06829777,
          0.20618578,  0.34812942,  0.49749478,  0.6589352 ,  0.83977496,
          1.05345661,  1.33087857,  1.77709673]),
  array([ 7.36,  7.88, 25.57, 25.78, 26.83, 29.18, 32.36, 32.68, 34.98,
         36.89, 41.21, 41.49, 41.56, 46.18, 57.34, 57.4 , 58.43, 73.33])),
 (17.679068580211705, 37.580555555555556, 0.9805158159874794))
In [121]:
print(sp.stats.shapiro(sick_without_high))
sp.stats.probplot(sick_without_high,plot = plt)
ShapiroResult(statistic=0.9791789650917053, pvalue=0.7467572093009949)
Out[121]:
((array([-2.0500397 , -1.65502516, -1.41824878, -1.24178231, -1.09728771,
         -0.97267593, -0.86159007, -0.76023875, -0.66615719, -0.57763676,
         -0.49343093, -0.4125902 , -0.33436303, -0.25813285, -0.18337584,
         -0.10963119, -0.03647875,  0.03647875,  0.10963119,  0.18337584,
          0.25813285,  0.33436303,  0.4125902 ,  0.49343093,  0.57763676,
          0.66615719,  0.76023875,  0.86159007,  0.97267593,  1.09728771,
          1.24178231,  1.41824878,  1.65502516,  2.0500397 ]),
  array([ 7.36,  7.88, 13.6 , 13.65, 19.45, 25.57, 25.78, 26.83, 29.18,
         30.91, 32.36, 32.68, 34.98, 35.07, 36.89, 38.66, 41.15, 41.21,
         41.49, 41.56, 41.56, 46.18, 47.64, 52.78, 53.02, 53.48, 54.14,
         57.34, 57.35, 57.4 , 58.43, 65.35, 68.64, 73.33])),
 (17.45867310496335, 40.08529411764706, 0.9932140123518024))
In [122]:
print(sp.stats.shapiro(healthy_without_high))
sp.stats.probplot(healthy_without_high,plot = plt)
ShapiroResult(statistic=0.9231124520301819, pvalue=0.45562413334846497)
Out[122]:
((array([-1.38519806, -0.83757156, -0.46579419, -0.15039337,  0.15039337,
          0.46579419,  0.83757156,  1.38519806]),
  array([ 4.86,  6.06,  7.31,  9.02,  9.36, 12.31, 15.53, 16.01])),
 (4.503134064185856, 10.057500000000001, 0.9728136559128856))

Если мы считаем высокие значения ИЛ-6 выбросами, то все группы имеют нормальное распределение. Если нет, только группа здоровых имеет нормальное распределение, хотя малая величина группы позволяет сомневаться в этом. Гипотезы будут проверены как параметрическими критериями, так и непараметрическими. Мы рекомендуем использовать в качестве референтных непараметрические критерии.

Проверка статистическим критерием

In [123]:
sp.stats.mannwhitneyu(dead_with_high, survive_with_high, use_continuity = True, alternative = "greater")
Out[123]:
MannwhitneyuResult(statistic=190.5, pvalue=0.27107728855503743)
In [124]:
sp.stats.ttest_ind(dead_without_high, survive_without_high, alternative = "greater", random_state = 555)
Out[124]:
Ttest_indResult(statistic=0.9103588616246996, pvalue=0.1847199613537608)
In [125]:
sp.stats.mannwhitneyu(survive_with_high, healthy_with_high, use_continuity = True, alternative = "greater")
Out[125]:
MannwhitneyuResult(statistic=150.0, pvalue=4.375656549569593e-05)
In [126]:
sp.stats.ttest_ind(survive_without_high, healthy_without_high, alternative = "greater", random_state = 555)
Out[126]:
Ttest_indResult(statistic=4.460742655987436, pvalue=8.183262055374946e-05)
In [127]:
sp.stats.mannwhitneyu(sick_with_high, healthy_with_high, use_continuity = True, alternative = "greater")
Out[127]:
MannwhitneyuResult(statistic=282.0, pvalue=3.695407164693908e-05)
In [128]:
sp.stats.ttest_ind(sick_without_high, healthy_without_high, alternative = "greater", random_state = 555)
Out[128]:
Ttest_indResult(statistic=4.925368129574753, pvalue=7.507397385592211e-06)

Несмотря на споры по поводу вида распределения, выбор критерия не влияет на конечный результат. Визуально видно, что поправки на множественное сравнения также не повлияют на результат.

Выводы

  1. ИЛ-6 участвует в воспалительном процессе при COVID-19.
  2. Эксперимент проведен корректно, так как уровень ИЛ-6 в плазме крови у здоровых больных меньше, чем у больных: как умерших, так и выздоровевших.
  3. ИЛ-6 изолированно не может использоваться для прогноза исхода заболевания.

Проверка апостериорных гипотез

Результаты ИФА и MAGPIX

In [138]:
result = pd.read_excel("~/Documents/Piter/Immunology/tables/results_xmap_and_vector.xlsx")
result.head()
Out[138]:
ID Holes Outcome Age Sex Try_1, pg/ml Try_2, pg/ml ELISA, pg/ml xMAP, pg/ml
0 7 G2,H2 смерть 59 1 30.68 31.13 30.91 60.042296
1 55 G7,H7 тяжелый 62 1 26.29 24.84 25.57 20.481909
2 70 H1,B3 смерть 50 1 51.69 54.35 53.02 48.805099
3 110 E2,F2 смерть 57 1 17.86 21.03 19.45 7.412301
4 129 C3,D3 смерть 62 1 133.30 155.10 144.20 144.136971

Сопоставимость методов

Сопоставимость методов будем смотреть следующими способами:

  1. Коэффициент корреляции Спирмена. Применение коэффициента корреляции для сравнения двух методов не вполне корректно, но ввиду распространенности этого метода в медицинской и технической литературе мы выбрали включить его в анализ.
  2. Тест Вилкоксона
  3. График Бланда-Альтмана5
  4. Регрессия Пассинга-Баблока6,7

MAGPIX условно считается референсным, так как эксперимент с этой тест-системой выполнялся на более свежих образцах плазмы более опытным экспериментатором

Коэффициент корреляции Спирмена

In [142]:
fig, ax = plt.subplots(figsize = (8,8))
elisa_spearman, elisa_p = sp.stats.spearmanr(a = result["ELISA, pg/ml"], b = result["xMAP, pg/ml"])
res = sp.stats.linregress(x = result["ELISA, pg/ml"],y = result["xMAP, pg/ml"])
text = "Searman r = " + str(round(elisa_spearman,2)) + "\n" + "p-value = " + str(round(elisa_p,5)) + "\nRegression p-value = " + str(round(res.pvalue,5))
sns.scatterplot(data = result, x = "ELISA, pg/ml", y = "xMAP, pg/ml", hue = "Outcome", ax =ax)
x = result["ELISA, pg/ml"]
y = res.intercept + res.slope*result["ELISA, pg/ml"]
plt.plot(x, y, 'r', label='fitted line')
plt.text(50,200,text)
Out[142]:
Text(50, 200, 'Searman r = 0.87\np-value = 0.0\nRegression p-value = 0.0')

Тест Вилкоксона

In [144]:
sp.stats.wilcoxon(result["ELISA, pg/ml"], result["xMAP, pg/ml"], correction = True)
Out[144]:
WilcoxonResult(statistic=195.0, pvalue=0.0002784212790652485)

График Бланда-Альтмана

Для графика Бланда-Альтмана необходимо, чтобы разница между двумя методами была распределена нормально.

In [147]:
result["Diff"] = result["ELISA, pg/ml"] - result["xMAP, pg/ml"]
plt.hist(result["Diff"], bins=range(-30,40,3))
Out[147]:
(array([1., 0., 0., 0., 0., 2., 0., 1., 1., 3., 8., 8., 5., 2., 3., 2., 3.,
        0., 0., 2., 1., 1., 0.]),
 array([-30, -27, -24, -21, -18, -15, -12,  -9,  -6,  -3,   0,   3,   6,
          9,  12,  15,  18,  21,  24,  27,  30,  33,  36,  39]),
 <BarContainer object of 23 artists>)
In [148]:
sp.stats.shapiro(result["Diff"])
Out[148]:
ShapiroResult(statistic=0.9151248931884766, pvalue=0.0029017869383096695)
In [149]:
sp.stats.probplot(result["Diff"],plot = plt)
Out[149]:
((array([-2.16261902, -1.78552545, -1.56210618, -1.397212  , -1.26342445,
         -1.1490787 , -1.04806126, -0.95674251, -0.87277802, -0.79455752,
         -0.72092154, -0.65100286, -0.58413203, -0.51977796, -0.45750906,
         -0.39696667, -0.33784646, -0.27988499, -0.22284956, -0.16653055,
         -0.11073524, -0.05528282,  0.        ,  0.05528282,  0.11073524,
          0.16653055,  0.22284956,  0.27988499,  0.33784646,  0.39696667,
          0.45750906,  0.51977796,  0.58413203,  0.65100286,  0.72092154,
          0.79455752,  0.87277802,  0.95674251,  1.04806126,  1.1490787 ,
          1.26342445,  1.397212  ,  1.56210618,  1.78552545,  2.16261902]),
  array([-44.2808    , -29.13229574, -13.8038491 , -13.09559303,
          -6.721808  ,  -3.84382361,  -1.19848503,  -0.20797493,
          -0.1030144 ,   0.06302913,   0.23861845,   0.33807919,
           2.01117685,   2.1925812 ,   2.21902093,   2.487163  ,
           2.7506151 ,   3.0761598 ,   3.3581629 ,   4.1268331 ,
           4.21490081,   4.45508894,   4.75780982,   4.86      ,
           5.08809087,   6.28      ,   6.5996361 ,   6.88339407,
           7.74339043,   8.3096361 ,   9.53096043,  10.18000009,
          12.03769946,  12.53191038,  14.93076337,  15.20818342,
          17.59515972,  18.12609811,  18.1452818 ,  20.05524804,
          28.78048781,  29.63606573,  31.11434807,  34.27065731,
          46.20152   ])),
 (14.780667372384976, 6.400225037734283, 0.9469916562343702))

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

Обычные остатки
In [157]:
fig, ax = plt.subplots(figsize = (8,8))
#result["sqrt_Vector"] = np.sqrt(result["Mean, pg/ml"])
#result["sqrt_xMAP"] = np.sqrt(result["xMAP, pg/ml"])
#result["delta_sqrt"] = result["sqrt_Vector"] - result["sqrt_xMAP"]
#sp.stats.shapiro(result["delta_sqrt"])
t = sp.stats.t(df = len(result.index) - 1).ppf(0.975)
se_mean = np.std(result["Diff"]) / np.sqrt(len(result.index))
se_limit = np.std(result["Diff"]) * np.sqrt(3) / np.sqrt(len(result.index))
sm.graphics.mean_diff_plot(result["ELISA, pg/ml"],result["xMAP, pg/ml"], ax = ax)
x = list(range(0,200))
upper = result["Diff"].mean() + result["Diff"].std() * 1.96
lower = result["Diff"].mean() - result["Diff"].std() * 1.96
plt.plot(x,np.repeat(0,len(x)),color = "r", linestyle = "--")
ax.fill_between(x, np.repeat(result["Diff"].mean() - t*se_mean,len(x)), np.repeat(result["Diff"].mean() + t*se_mean,len(x)), color = "grey", alpha = 0.3)
ax.fill_between(x, np.repeat(upper - t*se_limit,len(x)), np.repeat(upper + t*se_limit,len(x)), color = "grey", alpha = 0.3)
ax.fill_between(x, np.repeat(lower - t*se_limit,len(x)), np.repeat(lower + t*se_limit,len(x)), color = "grey", alpha = 0.3)
plt.plot()
Out[157]:
[]
Корень из остатков
In [160]:
result["sqrt_Vector"] = np.sqrt(result["ELISA, pg/ml"])
result["sqrt_xMAP"] = np.sqrt(result["xMAP, pg/ml"])
result["delta_sqrt"] = result["sqrt_Vector"] - result["sqrt_xMAP"]
print(sp.stats.shapiro(result["delta_sqrt"]))
t = sp.stats.t(df = len(result.index) - 1).ppf(0.975)
se_mean = np.std(result["delta_sqrt"]) / np.sqrt(len(result.index))
se_limit = np.std(result["delta_sqrt"]) * np.sqrt(3) / np.sqrt(len(result.index))
ShapiroResult(statistic=0.9743266701698303, pvalue=0.4111796021461487)
In [163]:
fig, ax = plt.subplots(figsize = (8,8))
sm.graphics.mean_diff_plot(result["sqrt_Vector"],result["sqrt_xMAP"], ax = ax)
x = list(range(1,15))
upper = result["delta_sqrt"].mean() + result["delta_sqrt"].std() * 1.96
lower = result["delta_sqrt"].mean() - result["delta_sqrt"].std() * 1.96
plt.plot(x,np.repeat(0,len(x)),color = "r", linestyle = "--")
ax.fill_between(x, np.repeat(result["delta_sqrt"].mean() - t*se_mean,len(x)), np.repeat(result["delta_sqrt"].mean() + t*se_mean,len(x)), color = "grey", alpha = 0.3)
ax.fill_between(x, np.repeat(upper - t*se_limit,len(x)), np.repeat(upper + t*se_limit,len(x)), color = "grey", alpha = 0.3)
ax.fill_between(x, np.repeat(lower - t*se_limit,len(x)), np.repeat(lower + t*se_limit,len(x)), color = "grey", alpha = 0.3)
plt.show()

Регрессия Пассинга-Баблока

In [166]:
import rpy2
%load_ext rpy2.ipython
print(rpy2.__version__)
The rpy2.ipython extension is already loaded. To reload it, use:
  %reload_ext rpy2.ipython
3.5.4
In [ ]:
%%R
install.packages("mcr")
install.packages("cellranger")
install.packages("readxl")
In [187]:
%%R
print(R.version)
               _                           
platform       x86_64-pc-linux-gnu         
arch           x86_64                      
os             linux-gnu                   
system         x86_64, linux-gnu           
status                                     
major          4                           
minor          2.0                         
year           2022                        
month          04                          
day            22                          
svn rev        82229                       
language       R                           
version.string R version 4.2.0 (2022-04-22)
nickname       Vigorous Calisthenics       
In [173]:
%%R
print(packageVersion("mcr"))
[1] ‘1.2.2’
In [168]:
%%R
library(mcr)
data = as.data.frame(readxl::read_excel("~/Documents/Piter/Immunology/tables/results_xmap_and_vector.xlsx"))
head(data)
   ID Holes Outcome Age Sex Try_1, pg/ml Try_2, pg/ml ELISA, pg/ml xMAP, pg/ml
1   7 G2,H2  смерть  59   1        30.68        31.13        30.91   60.042296
2  55 G7,H7 тяжелый  62   1        26.29        24.84        25.57   20.481909
3  70 H1,B3  смерть  50   1        51.69        54.35        53.02   48.805099
4 110 E2,F2  смерть  57   1        17.86        21.03        19.45    7.412301
5 129 C3,D3  смерть  62   1       133.30       155.10       144.20  144.136971
6 165 E5,F5  смерть  69   2        60.07        48.20        54.14   53.901382
In [170]:
%%R
PB.reg <- mcreg(as.numeric(data[,"ELISA, pg/ml"]),as.numeric(data[,"xMAP, pg/ml"]), method.reg = "PaBa")
PB.reg@para
                EST SE         LCI       UCI
Intercept -6.980365 NA -10.9658199 -2.985483
Slope      1.063538 NA   0.8368185  1.207993
In [171]:
%%R
plot(PB.reg)
In [172]:
%%R
printSummary(PB.reg)

------------------------------------------

Reference method: Method1
Test method:     Method2
Number of data points: 45

------------------------------------------

The confidence intervals are calculated with
 bootstrap  ( quantile ) method.
Confidence level: 95%


------------------------------------------

PASSING BABLOK REGRESSION FIT:

                EST SE         LCI       UCI
Intercept -6.980365 NA -10.9658199 -2.985483
Slope      1.063538 NA   0.8368185  1.207993


------------------------------------------

BOOTSTRAP SUMMARY

          global.est bootstrap.mean     bias bootstrap.se
Intercept   -6.98036       -7.09656 -0.11619      2.11293
Slope        1.06354        1.05759 -0.00595      0.08816

Bootstrap results generated with environment RNG settings.
NULL

Корреляция ИЛ-6 с возрастом

In [177]:
r, p = sp.stats.spearmanr(a = result["Age"], b = result["xMAP, pg/ml"])
ax = sns.jointplot(data = result, x = "Age", y = "xMAP, pg/ml", kind="reg", line_kws={'label':"r={:.2f};p={:.2f}".format(r,p)})
ax.ax_joint.legend()
Out[177]:
<matplotlib.legend.Legend at 0x7f0a9d8c6070>

Сравнение уровня ИЛ-6 у больных с разной степенью тяжести

In [178]:
disease = result[result["Outcome"] != "здоровый"]
print(disease.shape)
(37, 13)
In [179]:
disease["Outcome"].value_counts()
Out[179]:
смерть     17
средний    14
тяжелый     6
Name: Outcome, dtype: int64
In [180]:
dead = disease.loc[disease["Outcome"] == "смерть", "xMAP, pg/ml"]
moderate = disease.loc[disease["Outcome"] == "средний", "xMAP, pg/ml"]
severe = disease.loc[disease["Outcome"] == "тяжелый", "xMAP, pg/ml"]
In [181]:
sp.stats.kruskal(dead,moderate,severe)
Out[181]:
KruskalResult(statistic=0.36038825202913927, pvalue=0.8351080794715648)
In [182]:
sp.stats.mannwhitneyu(dead,moderate)
Out[182]:
MannwhitneyuResult(statistic=128.0, pvalue=0.735814797438752)
In [183]:
sp.stats.mannwhitneyu(severe,moderate)
Out[183]:
MannwhitneyuResult(statistic=47.0, pvalue=0.7180598555211559)
In [184]:
sp.stats.mannwhitneyu(severe,dead)
Out[184]:
MannwhitneyuResult(statistic=59.0, pvalue=0.608794714057872)

Выводы

  1. Методы ИФА и MAGPIX в целом сопоставимы, но имеется статистически значимое смещение результатов одного метода над другим: у ИФА завышенные результаты относительно MAGPIX
  2. Присутствует статистически значимая корреляция уровня ИЛ-6 в плазме крови с возрастом
  3. Уровень ИЛ-6 не отличается в группах с различной тяжестью заболевания

Выводы

ИЛ-6 не может изолированно использоваться ни для прогноза исхода заболевания, ни степени тяжести. Методы ИФА и MAGPIX сопоставимы для определения ИЛ-6 в плазме, пусть и с некоторой систематической ошибкой. Концентрация ИЛ-6 в плазме коррелирует с возрастом пациентов.

Обсуждения

В ходе обработки результатов, мы столкнулись с двумя лимитирующими факторами:

  1. Постановка иммуноферментного анализа и мультиплексного анализа выполнялась в технически различающихся условиях.
  2. В наше исследование включены образцы с генетически подтвержденным вариантом Дельта (B1.617.2). Возможно, полученные нами результаты не будут в полной мере применимы к другим геновариантам вируса.

Библиография

  1. P, N., R, N., B, V., S, R., & A, S. (2022). COVID-19: Invasion, pathogenesis and possible cure - A review. Journal of virological methods, 300, 114434. https://doi.org/10.1016/j.jviromet.2021.114434

  2. Siordia J. A., Jr (2020). Epidemiology and clinical features of COVID-19: A review of current literature. Journal of clinical virology : the official publication of the Pan American Society for Clinical Virology, 127, 104357. https://doi.org/10.1016/j.jcv.2020.104357

  3. Hu, B., Huang, S., & Yin, L. (2021). The cytokine storm and COVID‐19. Journal of medical virology, 93(1), 250-256.

  4. Santa Cruz, A., Mendes-Frias, A., Oliveira, A. I., Dias, L., Matos, A. R., Carvalho, A., Capela, C., Pedrosa, J., Castro, A. G., & Silvestre, R. (2021). Interleukin-6 Is a Biomarker for the Development of Fatal Severe Acute Respiratory Syndrome Coronavirus 2 Pneumonia. Frontiers in immunology, 12, 613422. https://doi.org/10.3389/fimmu.2021.613422

  5. Bland, J. M., & Altman, D. G. (1986). Statistical methods for assessing agreement between two methods of clinical measurement. Lancet (London, England), 1(8476), 307–310.

  6. Passing, H., & Bablok (1983). A new biometrical procedure for testing the equality of measurements from two different analytical methods. Application of linear regression procedures for method comparison studies in clinical chemistry, Part I. Journal of clinical chemistry and clinical biochemistry. Zeitschrift fur klinische Chemie und klinische Biochemie, 21(11), 709–720. https://doi.org/10.1515/cclm.1983.21.11.709

  7. Passing, H., & Bablok, W. (1984). Comparison of several regression procedures for method comparison studies and determination of sample sizes. Application of linear regression procedures for method comparison studies in Clinical Chemistry, Part II. Journal of clinical chemistry and clinical biochemistry. Zeitschrift fur klinische Chemie und klinische Biochemie, 22(6), 431–445. https://doi.org/10.1515/cclm.1984.22.6.431