AMP®-Parkinson's Disease Progression Prediction

Danh sách thành viên:

  • 19120454 - Bùi Quang Bảo (Nhóm trưởng)
  • 19120462 - Lục Minh Bửu
  • 19120151 - Nguyễn Trí Tuệ
  • 19120186 - Đỗ Lê Khánh Đăng

Table of Contents: Phía bên phải của trang Kaggle

1. GIỚI THIỆU ĐỀ TÀI 🧠

AMP®-Parkinson's Disease Progression Prediction.

Bệnh Parkinson là một loại bệnh thoái hóa chậm tiến triển của hệ thần kinh trung ương, chủ yếu ảnh hưởng đến hệ thống vận động. Hiện tại không có thuốc chữa cho bệnh này và bệnh nặng lên theo thời gian 💀.
Thang điểm phân độ bệnh Parkinson của Hiệp hội Rối loạn Vận động (MDS-UPDRS) là một đánh giá toàn diện về cả triệu chứng chuyển động và phi chuyển động. Đây là thang đo giúp đánh giá tiến triển của bệnh Parkinson.

Mục tiêu chính:

Phát triển một mô hình được huấn luyện trên tập dữ liệu về mức độ protein và peptide theo thời giannhững người mắc bệnh Parkinson so với những người cùng tuổi bình thường để dự đoán điểm MDS-UPDR (điểm này dùng cho đo lường sự tiến triển của bệnh nhân mắc bệnh Parkinson).

2. TIỀN XỬ LÝ - KHÁM PHÁ DỮ LIỆU 🔭

2.1 Giới thiệu bộ dữ liệu 🗂️

In [1]:
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np                # linear algebra
import pandas as pd               # data processing, CSV file I/O (e.g. pd.read_csv)
import plotly.express as px       #visualization
import plotly.graph_objects as go #visualization
import amp_pd_peptide             #TEST-API
import matplotlib.pyplot as plt
import seaborn as sns
from collections import Counter
from plotly.subplots import make_subplots
# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
/kaggle/input/amp-parkinsons-disease-progression-prediction/train_proteins.csv
/kaggle/input/amp-parkinsons-disease-progression-prediction/train_clinical_data.csv
/kaggle/input/amp-parkinsons-disease-progression-prediction/public_timeseries_testing_util.py
/kaggle/input/amp-parkinsons-disease-progression-prediction/supplemental_clinical_data.csv
/kaggle/input/amp-parkinsons-disease-progression-prediction/train_peptides.csv
/kaggle/input/amp-parkinsons-disease-progression-prediction/amp_pd_peptide/competition.cpython-37m-x86_64-linux-gnu.so
/kaggle/input/amp-parkinsons-disease-progression-prediction/amp_pd_peptide/__init__.py
/kaggle/input/amp-parkinsons-disease-progression-prediction/amp_pd_peptide_310/competition.cpython-310-x86_64-linux-gnu.so
/kaggle/input/amp-parkinsons-disease-progression-prediction/amp_pd_peptide_310/__init__.py
/kaggle/input/amp-parkinsons-disease-progression-prediction/example_test_files/sample_submission.csv
/kaggle/input/amp-parkinsons-disease-progression-prediction/example_test_files/test_proteins.csv
/kaggle/input/amp-parkinsons-disease-progression-prediction/example_test_files/test_peptides.csv
/kaggle/input/amp-parkinsons-disease-progression-prediction/example_test_files/test.csv
In [2]:
data_proteins     = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/train_proteins.csv')
data_clinical     = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/train_clinical_data.csv')
data_peptides     = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/train_peptides.csv')
data_supplemental = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/supplemental_clinical_data.csv')
In [3]:
data_proteins
Out[3]:
visit_id visit_month patient_id UniProt NPX
0 55_0 0 55 O00391 11254.3
1 55_0 0 55 O00533 732430.0
2 55_0 0 55 O00584 39585.8
3 55_0 0 55 O14498 41526.9
4 55_0 0 55 O14773 31238.0
... ... ... ... ... ...
232736 58648_108 108 58648 Q9UBX5 27387.8
232737 58648_108 108 58648 Q9UHG2 369437.0
232738 58648_108 108 58648 Q9UKV8 105830.0
232739 58648_108 108 58648 Q9Y646 21257.6
232740 58648_108 108 58648 Q9Y6R7 17953.1

232741 rows × 5 columns

In [4]:
data_clinical
Out[4]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication
0 55_0 55 0 10.0 6.0 15.0 NaN NaN
1 55_3 55 3 10.0 7.0 25.0 NaN NaN
2 55_6 55 6 8.0 10.0 34.0 NaN NaN
3 55_9 55 9 8.0 9.0 30.0 0.0 On
4 55_12 55 12 10.0 10.0 41.0 0.0 On
... ... ... ... ... ... ... ... ...
2610 65043_48 65043 48 7.0 6.0 13.0 0.0 Off
2611 65043_54 65043 54 4.0 8.0 11.0 1.0 Off
2612 65043_60 65043 60 6.0 6.0 16.0 1.0 Off
2613 65043_72 65043 72 3.0 9.0 14.0 1.0 Off
2614 65043_84 65043 84 7.0 9.0 20.0 3.0 Off

2615 rows × 8 columns

In [5]:
data_peptides
Out[5]:
visit_id visit_month patient_id UniProt Peptide PeptideAbundance
0 55_0 0 55 O00391 NEQEQPLGQWHLS 11254.30
1 55_0 0 55 O00533 GNPEPTFSWTK 102060.00
2 55_0 0 55 O00533 IEIPSSVQQVPTIIK 174185.00
3 55_0 0 55 O00533 KPQSAVYSTGSNGILLC(UniMod_4)EAEGEPQPTIK 27278.90
4 55_0 0 55 O00533 SMEQNGPGLEYR 30838.70
... ... ... ... ... ... ...
981829 58648_108 108 58648 Q9UHG2 ILAGSADSEGVAAPR 202820.00
981830 58648_108 108 58648 Q9UKV8 SGNIPAGTTVDTK 105830.00
981831 58648_108 108 58648 Q9Y646 LALLVDTVGPR 21257.60
981832 58648_108 108 58648 Q9Y6R7 AGC(UniMod_4)VAESTAVC(UniMod_4)R 5127.26
981833 58648_108 108 58648 Q9Y6R7 GATTSPGVYELSSR 12825.90

981834 rows × 6 columns

In [6]:
data_supplemental
Out[6]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication
0 35_0 35 0 5.0 3.0 16.0 0.0 NaN
1 35_36 35 36 6.0 4.0 20.0 0.0 NaN
2 75_0 75 0 4.0 6.0 26.0 0.0 NaN
3 75_36 75 36 1.0 8.0 38.0 0.0 On
4 155_0 155 0 NaN NaN 0.0 NaN NaN
... ... ... ... ... ... ... ... ...
2218 65382_0 65382 0 NaN NaN 0.0 NaN NaN
2219 65405_0 65405 0 5.0 16.0 31.0 0.0 NaN
2220 65405_5 65405 5 NaN NaN 57.0 NaN NaN
2221 65530_0 65530 0 10.0 6.0 24.0 0.0 NaN
2222 65530_36 65530 36 8.0 4.0 15.0 4.0 On

2223 rows × 8 columns

Kích thước các tập dữ liệu

In [7]:
data_peptides.shape
Out[7]:
(981834, 6)

Dữ liệu ở cấp độ peptide - peptide là các tiểu đơn vị thành phần của protein.(train_peptides.csv)

Dữ liệu gồm 6 thuộc tính và 981834 mẫu :

stt Tên thuộc tính Kiểu dữ liệu Ý nghĩa
0 visit_id objec Mã định danh ghé thăm khám dịch vụ ( có thể trùng ), gồm các kí tự kiểu chuỗi được nối nhau bằng dấu “_”, là kết hợp giữa 2 thuộc tính patientid visit_month
1 visit_month int64 Tháng thăm khám, so với lần khám đầu tiên của bệnh nhân
2 patient_id int64 Mã ID của bệnh nhân ( Mỗi bệnh nhân có mã ID khác nhau
3 UniProt object Mã ID UniProt cho protein liên quan. Thường có một số peptide trên mỗi protein(Gồm 6 kí tự ).
4 Peptide object Thứ tự các Axit amin xuất hiện trong peptit
5 PeptideAbundance float64 Tần số xuất hiện của axit amin trong mẫu
In [8]:
data_proteins.shape
Out[8]:
(232741, 5)

Dữ liệu ở cấp độ protein được tổng hợp từ dữ liệu của mức peptide.(train_peptides.csv)

Dữ liệu gồm 232741 mẫu và 5 thuộc tính

stt Tên thuộc tính Kiểu dữ liệu Ý nghĩa
0 visit_id object Mã định danh ghé thăm khám dịch vụ ( có thể trùng ), gồm các kí tự kiểu chuỗi được nối nhau bằng dấu “_”, là kết hợp giữa 2 thuộc tính patientid visit_month
1 visit_month int64 Tháng thăm khám, so với lần khám đầu tiên của bệnh nhân
2 patient_id int64 Mã ID của bệnh nhân ( Mỗi bệnh nhân có mã ID khác nhau)
3 UniProt object Mã ID UniProt cho protein liên quan. Thường có một số peptide trên mỗi protein(Gồm 6 kí tự ).
4 NPX float64 Tần suất xuất hiện protein trong mẫu thuộc khoảng [8.460820e+01, 6.138510e+08]
In [9]:
data_clinical.shape
Out[9]:
(2615, 8)

Thông tin tập dữ liệu clinical khám bệnh lâm sàn về Parkinson

Dữ liệu gồm 2615 mẫu và 8 thuộc tính

stt Tên thuộc tính Kiểu dữ liệu Ý nghĩa
0 visit_id object Mã định danh ghé thăm khám dịch vụ ( có thể trùng ), gồm các kí tự kiểu chuỗi được nối nhau bằng dấu “_”, là kết hợp giữa 2 thuộc tính patientid visit_month
1 visit_month int64 Tháng thăm khám, so với lần khám đầu tiên của bệnh nhân
2 patient_id int64 Mã ID của bệnh nhân ( Mỗi bệnh nhân có mã ID khác nhau)
3 updrs_[1-4] Float64 Điểm đánh giá người bệnh cho các mục updrs ,điểm này được so sánh với Unified Parkinson's Disease Rating Scale. Con số cao hơn cho thấy các triệu chứng nghiêm trọng hơn. Mỗi updrs bao gồm một loại triệu chứng riêng biệt updrs 1 thể hiện tâm trạng và hành vi,updrs 2 thể hiện hoạt động thường ngày ,updrs 3 và các chức năng vận động , updrs 4 là điểm đánh giá cho biến chứng của điều trị
4 upd23b_clinical_state_on_medication object Ghi nhận Bệnh nhân có dùng thuốc Levodopa hay không trong quá trình đánh giá UPDRS.[On, Off] Ảnh hưởng chủ yếu đến điểm số của updrs 3 (chức năng vận động). Những loại thuốc này hết tác dụng khá nhanh (theo thứ tự trong một ngày), vì vậy thông thường bệnh nhân sẽ thực hiện kiểm tra chức năng vận động hai lần trong một tháng, cả khi có và không dùng thuốc.
In [10]:
data_supplemental.shape
Out[10]:
(2223, 8)

Thông tin tập dữ liệu supplemental khám bệnh lâm sàn về Parkinson

Dữ liệu gồm 2223 mẫu và 8 thuộc tính

stt Tên thuộc tính Kiểu dữ liệu Ý nghĩa
0 visit_id object Mã định danh ghé thăm khám dịch vụ ( có thể trùng ), gồm các kí tự kiểu chuỗi được nối nhau bằng dấu “_”, là kết hợp giữa 2 thuộc tính patientid visit_month
1 visit_month int64 Tháng thăm khám, so với lần khám đầu tiên của bệnh nhân
2 patient_id int64 Mã ID của bệnh nhân ( Mỗi bệnh nhân có mã ID khác nhau)
3 updrs_[1-4] Float64 Điểm đánh giá người bệnh cho các mục updrs ,điểm này được so sánh với Unified Parkinson's Disease Rating Scale. Con số cao hơn cho thấy các triệu chứng nghiêm trọng hơn. Mỗi updrs bao gồm một loại triệu chứng riêng biệt updrs 1 thể hiện tâm trạng và hành vi,updrs 2 thể hiện hoạt động thường ngày ,updrs 3 và các chức năng vận động , updrs 4 là điểm đánh giá cho biến chứng của điều trị
4 upd23b_clinical_state_on_medication object Ghi nhận Bệnh nhân có dùng thuốc Levodopa hay không trong quá trình đánh giá UPDRS.[On, Off] Ảnh hưởng chủ yếu đến điểm số của updrs 3 (chức năng vận động). Những loại thuốc này hết tác dụng khá nhanh (theo thứ tự trong một ngày), vì vậy thông thường bệnh nhân sẽ thực hiện kiểm tra chức năng vận động hai lần trong một tháng, cả khi có và không dùng thuốc.

2.2 Tiền xử lý dữ liệu 🗂️

2.2.1 Phân tích dữ liệu bị thiếu

In [11]:
data_list = [data_proteins, data_peptides, data_clinical, data_supplemental]
data_name = ['data_proteins', 'data_peptides', 'data_clinical', 'data_supplemental']

for i, temp_df in enumerate(data_list):
    print(f'Dataframe "{data_name[i]}":\n{temp_df.isnull().sum(axis=0)}\n')
Dataframe "data_proteins":
visit_id       0
visit_month    0
patient_id     0
UniProt        0
NPX            0
dtype: int64

Dataframe "data_peptides":
visit_id            0
visit_month         0
patient_id          0
UniProt             0
Peptide             0
PeptideAbundance    0
dtype: int64

Dataframe "data_clinical":
visit_id                                  0
patient_id                                0
visit_month                               0
updrs_1                                   1
updrs_2                                   2
updrs_3                                  25
updrs_4                                1038
upd23b_clinical_state_on_medication    1327
dtype: int64

Dataframe "data_supplemental":
visit_id                                  0
patient_id                                0
visit_month                               0
updrs_1                                 213
updrs_2                                 214
updrs_3                                   5
updrs_4                                 928
upd23b_clinical_state_on_medication    1101
dtype: int64

Nhận xét:

  • Dữ liệu proteinspeptides không chứa giá trị null
  • Dữ liệu clinicalsupplemental chứa các giá trị null
In [12]:
import matplotlib.pyplot as plt
import missingno as msno # Thư viện missingno hỗ trợ trực quan hoá dữ liệu bị thiếu

2.2.1.1. Tập dữ liệu clinical

In [13]:
data_clinical_temp = data_clinical
data_clinical_temp['null_values_count'] = data_clinical.isnull().sum(axis=1)

data_clinical_null_per_row = data_clinical_temp['null_values_count'].value_counts()[:-1].reindex([0, 1, 2, 3])
fig = plt.figure(figsize=(8, 8))
plt.pie(
    data_clinical_null_per_row, 
    labels = [str(e) + ' null(s)' for e in data_clinical_null_per_row.index],
    colors = ['PaleTurquoise', 'Moccasin', 'LightSalmon', 'Tomato'],
    explode = [0.05, 0, 0, 0],
    autopct='%1.1f%%'
)
plt.title('Clinical Data - Null values per row')
plt.legend()
plt.show()

Nhận xét:

  • Dữ liệu có 51.8% dòng có chứa giá trị null, trong đó:
    • 12.7% dòng chứa 1 giá trị null
    • 38.8% dòng chứa 2 giá trị null
    • 0.4% dòng chứa 3 giá trị null
In [14]:
null_count_labels = [data_clinical_temp[(data_clinical_temp['null_values_count'] == x)].isnull().sum().index[:-1] for x in range(1, 6)]
null_count_values = [data_clinical_temp[(data_clinical_temp['null_values_count'] == x)].isnull().sum().values[:-1] for x in range(1, 6)]

fig, axs = plt.subplots(nrows=1, ncols=3, figsize=(15, 4))
fig.suptitle("Clinical Data - Null values count")

colors = ['Moccasin', 'LightSalmon', 'Tomato']

for i in range(0, 3):
    ax = axs[i]
    labels = null_count_labels[i]
    sns.barplot(x=labels, y=null_count_values[i], ax=ax, color=colors[i])
    ax.set_title(f"Rows with {i + 1} null(s)")
    ax.set_ylabel("Null values count" if i == 0 else "")
    ax.set_xticks([u for u in range(len(labels))], labels, rotation=90)
    for p in ax.patches:
        height = p.get_height()
        ax.text(x=p.get_x()+(p.get_width()/2), y=height, s="{:d}".format(int(height)), ha="center")

Nhận xét:

  • Với những dòng chứa 1 giá trị null, dữ liệu thiếu chủ yếu tập trung vào cột upd23b_clinical_state_on_medication
  • Với những dòng chứa 2 giá trị null, dữ liệu thiếu chủ yếu tập trung vào cột updrs_4upd23b_clinical_state_on_medication
  • Với những dòng chứa 3 giá trị null, dữ liệu thiếu chủ yếu tập trung vào cột updrs_3, updrs_4upd23b_clinical_state_on_medication
In [15]:
msno.matrix(data_clinical, figsize=(10, 5), fontsize=10)
plt.title('Clinical Data - Missing Data Matrix')
plt.show()

Nhận xét:

  • Dữ liệu bị thiếu chủ yếu tập trung tại 2 thuộc tính là updrs_4upd23b_clinical_state_on_medication
  • Dữ liệu bị thiếu tại 2 thuộc tính updrs_4upd23b_clinical_state_on_medication có thể pattern xuất hiện cùng nhau
In [16]:
msno.heatmap(data_clinical, figsize=(10, 5), fontsize=10)
plt.title('Clinical Data - Missing Data Relationship')
plt.show()

Nhận xét:

  • 2 thuộc tính updrs_4upd23b_clinical_state_on_medication dữ liệu bị thiếu xuất hiện cùng nhau (Tương quan bị thiếu rất cao, 0.8)
  • Chúng ta sẽ không kết luận 2 thuộc tính updrs_1updrs_2 có dữ liệu bị thiếu xuất hiện cùng nhau, vì dữ liệu bị thiếu tại 2 thuộc tính này xuất hiện rất ít (chỉ 1 đến 2 mẫu)

2.2.1.2. Tập dữ liệu supplemental:

In [17]:
data_supplemental_temp = data_supplemental
data_supplemental_temp['null_values_count'] = data_supplemental.isnull().sum(axis=1)

data_supplemental_null_per_row = data_supplemental_temp['null_values_count'].value_counts()[:-1].reindex([0, 1, 2, 3, 4])
fig = plt.figure(figsize=(8, 8))
plt.pie(
    data_supplemental_null_per_row, 
    labels = [str(e) + ' null(s)' for e in data_supplemental_null_per_row.index],
    colors = ['PaleTurquoise', 'Moccasin', 'LightSalmon', 'Tomato', 'Crimson'],
    explode = [0.05, 0, 0, 0, 0],
    autopct='%1.1f%%'
)
plt.title('Supplemental Data - Null values per row')
plt.legend()
plt.show()

Nhận xét:

  • Dữ liệu có 63.2% dòng có chứa giá trị null, trong đó:
    • 34.4% dòng chứa 1 giá trị null
    • 19.2% dòng chứa 2 giá trị null
    • 0.8% dòng chứa 3 giá trị null
    • 8.9% dòng chứa 4 giá trị null
In [18]:
null_count_labels = [data_supplemental_temp[(data_supplemental_temp['null_values_count'] == x)].isnull().sum().index[:-1] for x in range(1, 6)]
null_count_values = [data_supplemental_temp[(data_supplemental_temp['null_values_count'] == x)].isnull().sum().values[:-1] for x in range(1, 6)]

fig, axs = plt.subplots(nrows=1, ncols=4, figsize=(20, 4))
fig.suptitle("Supplemental Data - Null values count")

colors = ['Moccasin', 'LightSalmon', 'Tomato', 'Crimson']

for i in range(0, 4):
    ax = axs[i]
    labels = null_count_labels[i]
    sns.barplot(x=labels, y=null_count_values[i], ax=ax, color=colors[i])
    ax.set_title(f"Rows with {i + 1} null(s)")
    ax.set_ylabel("Null values count" if i == 0 else "")
    ax.set_xticks([u for u in range(len(labels))], labels, rotation=90)
    for p in ax.patches:
        height = p.get_height()
        ax.text(x=p.get_x()+(p.get_width()/2), y=height, s="{:d}".format(int(height)), ha="center")

Nhận xét:

  • Với những dòng chứa 1 giá trị null, dữ liệu thiếu chủ yếu tập trung vào cột upd23b_clinical_state_on_medication, tiếp sau đó là updrs_4
  • Với những dòng chứa 2 giá trị null, dữ liệu thiếu chủ yếu tập trung vào cột updrs_4upd23b_clinical_state_on_medication
  • Với những dòng chứa 3 giá trị null, dữ liệu thiếu chủ yếu tập trung vào cột updrs_1, updrs_2upd23b_clinical_state_on_medication
  • Với những dòng chứa 4 giá trị null, dữ liệu thiếu chủ yếu tập trung vào cột updrs_1, updrs_2, updrs_4upd23b_clinical_state_on_medication
In [19]:
msno.matrix(data_supplemental, figsize=(10, 5), fontsize=10)
plt.title('Supplemental Data - Missing Data Matrix')
plt.show()

Nhận xét:

  • Dữ liệu bị thiếu chủ yếu tập trung tại 2 thuộc tính là updrs_4upd23b_clinical_state_on_medication, tiếp theo đó là 2 thuộc tính updrs_1updrs_2.
  • Khác với dữ liệu clinical, ở dữ liệu supplemental, dữ liệu bị thiếu tại 2 thuộc tính updrs_4upd23b_clinical_state_on_medication có vẻ như không có pattern xuất hiện cùng nhau
  • Dữ liệu bị thiếu tại 2 thuộc tính updrs_1updrs_2 có thể pattern xuất hiện cùng nhau
In [20]:
msno.heatmap(data_supplemental, figsize=(10, 5), fontsize=10)
plt.title('Supplemental Data - Missing Data Relationship')
plt.show()

Nhận xét:

  • 2 thuộc tính updrs_1updrs_2 dữ liệu bị thiếu xuất hiện cùng nhau (Tương quan bị thiếu tối đa, 1.0) -> Hễ updrs_1 thiếu thì updrs_2 sẽ thiếu
  • Các cặp thuộc tính có mối tương quan thấp khi bị thiếu dữ liệu:
    • updrs_1updrs_4: 0.3
    • updrs_1upd23b_clinical_state_on_medication: 0.3
    • updrs_2updrs_4: 0.3
    • updrs_2upd23b_clinical_state_on_medication: 0.3
    • updrs_4upd23b_clinical_state_on_medication: 0.3

2.2.1.3. Kết luận và hướng giải quyết

Kết luận chung:

  • Trong 4 tập dữ liệu:
    • 2 tập dữ liệu liên quan đến hàm lượng protein của bệnh nhân là proteinspeptides không chứa giá trị null
    • 2 tập dữ liệu liên quan đến điểm đánh giá UPDRS và tình trạng uống thuốc của bệnh nhân là clinicalsupplemental chứa giá trị null
  • Tập dữ liệu clinical:
    • Có 51.8% dòng dữ liệu có chứa giá trị null -> lượng dữ liệu bị thiếu rất lớn
    • Dữ liệu bị thiếu chủ yếu tập trung tại 2 thuộc tính là updrs_4upd23b_clinical_state_on_medication
      • Cặp 2 thuộc tính updrs_4upd23b_clinical_state_on_medication có tương quan bị thiếu rất cao (0.8).
  • Tập dữ liệu supplemental:
    • Có 63.2% dòng dữ liệu có chứa giá trị null -> lượng dữ liệu bị thiếu rất lớn
    • Dữ liệu bị thiếu chủ yếu tập trung tại 2 thuộc tính là updrs_4upd23b_clinical_state_on_medication, tiếp theo đó là 2 thuộc tính updrs_1updrs_2.
      • Cặp 2 thuộc tính updrs_4upd23b_clinical_state_on_medication có tương quan bị thiếu không cao như ở tập dữ liệu clinical (0.3).
      • Cặp 2 thuộc tính updrs_1updrs_2 có tương quan bị thiếu rất cao (1.0) -> Hễ updrs_1 thiếu thì updrs_2 sẽ thiếu

Phân tích và đề xuất hướng giải quyết:

  • Đối với dữ liệu UPDRS, cụ thể gồm updrs_1, updrs_2, updrs_3updrs_4:
    • Các cột này là điểm đánh giá người bệnh thông qua các triệu chứng trong các mục của UPDRS, bao gồm:
      • updrs_1: đánh giá tâm trạng và hành vi
      • updrs_2: đánh giá các hoạt động thường ngày
      • updrs_3: đánh giá các chức năng vận động
      • updrs_4: đánh giá các biến chứng của điều trị
    • Quan trọng: Điểm số đánh giá cao hơn cho thấy các triệu chứng tương ứng nghiêm trọng hơn. Và điểm số bằng 0 có nghĩa là người bệnh đã bình thường (giá trị 0 có ý nghĩa). Vì thế mà không thể xem giá trị null là giá trị 0 được.
    • Nguyên nhân dẫn đến thiếu dữ liệu (giả thuyết):
      • Quá trình thu thập dữ liệu đã không thu thập được / quên thu thập điểm kiểm tra đánh giá UPDRS ở một số người bệnh.
      • Bệnh nhân không tiến hành kiểm tra đánh giá UPDRS -> không có dữ liệu để thu thập.
      • Người bệnh từ chối cung cấp thông tin kiểm tra đánh giá UPDRS
    • Các hướng giải quyết:
      1. Loại bỏ các dòng chứa giá trị null ở các cột đánh giá UPDRS, hoặc
      2. Gán giá trị null ở các cột đánh giá UPDRS bằng một giá trị cụ thể, điều kiện là khi nhìn vào giá trị đó, chúng ta có thể biết được rằng giá trị này đã từng là null, ví dụ: giá trị -1.
  • Đối với dữ liệu của cột upd23b_clinical_state_on_medication:
    • Cột này ghi nhận bệnh nhân có dùng thuốc Levodopa hay không trong quá trình đánh giá và chỉ nhận 1 trong 2 giá trị On hoặc Off. Giá trị null xuất hiện ở cột này không chắc chắn là bệnh nhân có dùng thuốc hay không.
    • Nguyên nhân dẫn đến thiếu dữ liệu (giả thuyết):
      • Quá trình thu thập dữ liệu đã không thu thập được / quên thu thập trạng thái uống thuốc ở một số người bệnh.
      • Người bệnh từ chối cung cấp thông tin uống thuốc.
    • Các hướng giải quyết:
      1. Loại bỏ cột upd23b_clinical_state_on_medication, hoặc
      2. Loại bỏ các dòng chứa giá trị null ở cột upd23b_clinical_state_on_medication, hoặc
      3. Gán giá trị null ở cột upd23b_clinical_state_on_medication bằng giá trị Unknown, có ý nghĩa là không biết được tình trạng uống thuốc của người bệnh. Như vậy, cột upd23b_clinical_state_on_medication sẽ có 3 giá trị là On, OffUnknown.

2.2.2 Kiểm tra dữ liệu bị trùng

In [21]:
for i, temp_df in enumerate(data_list):
    print(f'Dataframe "{data_name[i]}" contains {temp_df.duplicated().sum()} duplicated samples.')
Dataframe "data_proteins" contains 0 duplicated samples.
Dataframe "data_peptides" contains 0 duplicated samples.
Dataframe "data_clinical" contains 0 duplicated samples.
Dataframe "data_supplemental" contains 0 duplicated samples.

Nhận xét:

  • 4 tập dữ liệu bao gồm proteins, peptides, clinicalsupplemental không chứa các mẫu bị lặp.

Kết luận chung:

  • Dữ liệu không bị trùng lặp.

2.2.3 Kiểm tra và xử lý outlier

2.2.3.1 Tập dữ liệu data_supplement

In [22]:
data_supplemental.head()
Out[22]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication null_values_count
0 35_0 35 0 5.0 3.0 16.0 0.0 NaN 1
1 35_36 35 36 6.0 4.0 20.0 0.0 NaN 1
2 75_0 75 0 4.0 6.0 26.0 0.0 NaN 1
3 75_36 75 36 1.0 8.0 38.0 0.0 On 0
4 155_0 155 0 NaN NaN 0.0 NaN NaN 4
In [23]:
data_supplemental.shape
Out[23]:
(2223, 9)

Ta không xét cột visit_id vì đây là cột ghép từ patient_id và visit_month. Nên ta sẽ xem xét 2 cột còn lại thay vì cột này.

Cột visit_month: tháng khám bệnh

In [24]:
print(data_supplemental.visit_month.nunique())
data_supplemental.visit_month.unique()
8
Out[24]:
array([ 0, 36,  5,  6, 12, 18, 24, 30])

Có 8 mốc thời gian khám bệnh

In [25]:
data_supplemental.visit_month.value_counts().sort_index().plot(kind='bar')
plt.xlabel('Tháng')
plt.ylabel('Số lượng')
plt.title('Số lượng mẫu theo visit_month')
plt.show()

Số lượng dữ liệu ở các mốc cũng không có nhiều bất thường, chỉ có mốc 0 là cao hơn hẵn các mốc còn lại. Có thể hiểu rằng vì đây là tháng đầu tiên các bệnh nhân đến khám bệnh. Nên việc nó cao nhất là điều có khả năng xảy ra.

Cột patient_id: mã bệnh nhân

In [26]:
data_supplemental.patient_id.nunique()
Out[26]:
771
In [27]:
data_supplemental.patient_id.value_counts().plot(kind='box')
plt.ylabel('Số lần khám của 1 bệnh nhân')
plt.title('Box plot số lần khám của 1 bệnh nhân')
plt.show()

Cột updrs_1: điểm UPDRS Part I - Non-Motor Aspects of Experiences of Daily Living (nM-EDL)

In [28]:
data_supplemental.updrs_1.plot(kind='hist')
plt.xlabel('Điểm UPDRS-1')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố điểm updrs_1')
plt.show()

Theo bài báo https://www.movementdisorders.org/MDS-Files1/PDFs/Rating-Scales/MDS-UPDRS_Vol23_Issue15_2008.pdf được đính kèm trong mô tả dữ liệu, Part I này sẽ có 13 chỉ tiêu, điểm số mỗi chỉ tiêu là 0 - 4. Nên khoảng giá trị hợp lệ của part 1 là 0 - 52.

Ta thấy toàn bộ giá trị trong dữ liệu đều nằm trong khoảng này.

Ta thấy dữ liệu đang bị lệch phải, và giá trị ở khoảng ~ 25 khá ít. Nên sẽ chuẩn hóa dữ liệu và dùng thống kê để kiểm tra xem đây có phải là outlier hay không.

In [29]:
norm = np.log(data_supplemental['updrs_1']+1)
norm.plot(kind='box')
plt.title('Box plot updrs_1 đã chuẩn hóa log')
plt.ylabel('Giá trị')
plt.show()

Ta thấy đây không phải là outlier

Cột updrs_2: điểm UPDRS Part II - Motor Aspects of Experiences of Daily Living (M-EDL)

In [30]:
data_supplemental.updrs_2.plot(kind='hist')
plt.xlabel('Điểm UPDRS-2')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố điểm updrs_2')
plt.show()

Tương tự như trên, Part II có khoảng hợp lệ là 0 - 52 và tất cả giá trị đều nằm trong khoảng này

Tuy nhiên, khoảng giá trị >25 có khá ít mẫu, nên ta sẽ detect outlier như updrs-1

In [31]:
norm = np.log(data_supplemental['updrs_2']+1)
norm.plot(kind='box')
plt.title('Box plot updrs_2 đã chuẩn hóa log')
plt.ylabel('Giá trị')
plt.show()

Ta thấy trong cột này cũng không có outlier

Cột updrs_3: điểm UPDRS Part III - Motor Examination

In [32]:
data_supplemental.updrs_3.plot(kind='hist')
plt.xlabel('Điểm UPDRS-3')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố updrs_3')
plt.show()

Part III có khoảng giá trị từ 0 - 132 (33 chỉ tiêu) và tất cả giá trị đều nằm trong khoảng này.

Cột updrs_4: điểm UPDRS Part IV - Motor Complications

In [33]:
data_supplemental.updrs_4.plot(kind='hist')
plt.xlabel('Điểm UPDRS-4')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố updrs_4')
plt.show()

Part IV gồm 6 chỉ tiêu. Khoảng điểm là 0 - 24 nên tất cả giá trị trong dữ liệu đều hợp lệ.

Dữ liệu hiện tại đang bị lệch nặng về điểm 0. Và khoảng từ 8 trở lên có khá ít dữ liệu.
Tuy nhiên thì nó vẫn phù hợp với xu hướng giảm dần của dữ liệu. Dù vậy ta vẫn có thể xem xét bỏ đi để cải thiện kết quả nếu cần thiết.

Cột upd23b_clinical_state_on_medication

In [34]:
print(data_clinical.upd23b_clinical_state_on_medication.nunique())
data_clinical.upd23b_clinical_state_on_medication.unique()
2
Out[34]:
array([nan, 'On', 'Off'], dtype=object)
In [35]:
value_count = data_supplemental.upd23b_clinical_state_on_medication\
.value_counts()
value_count.plot(kind='bar')
for i in range(len(value_count.index)):
    plt.text(i, value_count.values[i]+2,str(value_count.values[i]), ha='center')
plt.title('Số lượng mẫu theo tình trạng sử dụng thuốc')
plt.show()

Ta thấy giữa số lượng mẫu On và Off có sự chênh lệch rất lớn, số lượng Off chỉ có 29. Hơn nữa cột này đang bị thiếu gần 50% dữ liệu nên ta có thể xem xét bỏ cột này ở các bước sau.

2.2.3.2 Tập dữ liệu data_clinical

In [36]:
data_clinical.head()
Out[36]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication null_values_count
0 55_0 55 0 10.0 6.0 15.0 NaN NaN 2
1 55_3 55 3 10.0 7.0 25.0 NaN NaN 2
2 55_6 55 6 8.0 10.0 34.0 NaN NaN 2
3 55_9 55 9 8.0 9.0 30.0 0.0 On 0
4 55_12 55 12 10.0 10.0 41.0 0.0 On 0
In [37]:
data_clinical.shape
Out[37]:
(2615, 9)

Ta không xét cột visit_id vì đây là cột ghép từ patient_id và visit_month. Nên ta sẽ xem xét 2 cột còn lại thay vì cột này.

Cột visit_month: tháng khám bệnh

In [38]:
print(data_clinical.visit_month.nunique())
data_clinical.visit_month.unique()
17
Out[38]:
array([  0,   3,   6,   9,  12,  18,  24,  30,  36,  42,  48,  54,  60,
        72,  84,  96, 108])
In [39]:
month_count = data_clinical.visit_month.value_counts().sort_index()
for i in range(len(month_count.index)):
    plt.text(i, month_count.values[i]+2,str(month_count.values[i]), ha='center')
month_count.plot(kind='bar')
plt.title('Số lượng mẫu theo visit_month')
plt.xlabel('Tháng')
plt.ylabel('Số lượng mẫu')
plt.plot()
Out[39]:
[]

Ta thấy cột này không có gì bất thường, hầu hết các mốc thời gian đều có số lượng mẫu đủ lớn. Mốc cuối cùng tuy có số lượng chỉ 12 mẫu, nhưng đây là một mốc rất xa (108 tháng) so với lần khám đầu nên sẽ có ít mẫu hơn và phù hợp với xu hướng giảm dần theo các mốc.

Cột patient_id: mã bệnh nhân

In [40]:
data_clinical.patient_id.value_counts().plot(kind='box')
plt.ylabel('Số lần khám của 1 bệnh nhân')
plt.title('Box plot số lần khám của 1 bệnh nhân')
plt.show()

Ở đây ta sẽ chú ý đến 2 số min và max. Ta thấy rằng trong dữ liệu, 1 bệnh nhân ít nhất khám tại 3 mốc tháng khác nhau và nhiều nhất là 17 mốc khám. Điều này hoàn toàn phù hợp với mô tả dữ liệu (1 mốc tháng sẽ chỉ lấy duy nhất 1 kết quả, và dữ liệu của ta có 17 mốc tháng). Vậy cột này cũng không có outlier

Cột updrs_1: điểm UPDRS Part I - Non-Motor Aspects of Experiences of Daily Living (nM-EDL)

In [41]:
data_clinical.updrs_1.plot(kind='hist')
plt.xlabel('Điểm UPDRS-1')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố điểm updrs_1')
plt.show()

Khoảng giá trị hợp lệ của part 1 là 0 - 52. Ta thấy toàn bộ giá trị trong dữ liệu đều nằm trong khoảng này.

Tuy nhiên điểm ~30 có số lượng rất ít nên ta sẽ xem xét bằng thống kê

In [42]:
norm = np.log(data_clinical['updrs_1']+1)
norm.plot(kind='box')
plt.title('Box plot giá trị updrs_1 đã chuẩn hóa log')
plt.show()

Ta thấy dữ liệu không có bất thường

Cột updrs_2: điểm UPDRS Part II - Motor Aspects of Experiences of Daily Living (M-EDL)

In [43]:
data_clinical.updrs_2.plot(kind='hist')
plt.xlabel('Điểm UPDRS-2')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố điểm updrs_2')
plt.show()

Tương tự như trên, Part II có khoảng hợp lệ là 0 - 52 và tất cả giá trị đều nằm trong khoảng này. Và khoảng điểm > 30 số lượng rất ít, tuy nhiên thì nó phù hợp với xu hướng giảm dần của dữ liệu. Dù vậy, ta có thể xem xét bỏ những điểm này đi để cải thiện kết quả.

Cột updrs_3: điểm UPDRS Part III - Motor Examination

In [44]:
data_clinical.updrs_3.plot(kind='hist')
plt.xlabel('Điểm UPDRS-3')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố điểm updrs_3')
plt.show()

Part III có khoảng giá trị từ 0 - 132 (33 chỉ tiêu) và tất cả giá trị đều nằm trong khoảng này. Tuy nhiên ta thấy điểm > 75 nằm tách biệt với phần còn lại. Nên ta có thể xem xét bỏ các điểm này ở các bước sau.

Cột updrs_4: điểm UPDRS Part IV - Motor Complications

In [45]:
data_clinical.updrs_4.plot(kind='hist')
plt.xlabel('Điểm UPDRS-4')
plt.ylabel('Số mẫu')
plt.title('Histogram phân bố điểm updrs_4')
plt.show()

Part IV gồm 6 chỉ tiêu. Khoảng điểm là 0 - 24 nên tất cả giá trị trong dữ liệu đều hợp lệ. Và tương tự như ở file supplemental, ta cũng có thể xem xét bỏ đi các điểm > 17.5 để cải thiện kết quả.

Cột upd23b_clinical_state_on_medication

In [46]:
data_clinical.upd23b_clinical_state_on_medication\
.value_counts().sort_values().plot(kind='bar')
plt.title('Số lượng mẫu theo tình trạng sử dụng thuốc')
plt.show()

Ta thấy 2 lớp On và Off không chênh lệch nhau quá nhiều. Nên cột này không có outlier

2.2.3.2 Tập dữ liệu data_peptides

In [47]:
data_peptides.head()
Out[47]:
visit_id visit_month patient_id UniProt Peptide PeptideAbundance
0 55_0 0 55 O00391 NEQEQPLGQWHLS 11254.3
1 55_0 0 55 O00533 GNPEPTFSWTK 102060.0
2 55_0 0 55 O00533 IEIPSSVQQVPTIIK 174185.0
3 55_0 0 55 O00533 KPQSAVYSTGSNGILLC(UniMod_4)EAEGEPQPTIK 27278.9
4 55_0 0 55 O00533 SMEQNGPGLEYR 30838.7
In [48]:
data_peptides.shape
Out[48]:
(981834, 6)

Theo mô tả của dữ liệu, ta có sự ràng buộc ở data_clinical và data_peptides rằng tất cả bệnh nhân và tháng thăm khám của data_peptides phải tồn tại trong data_clinical. Nên nếu có điểm nào nằm ngoài clinical, đó sẽ là điểm cần loại bỏ.

Cột patient_id

In [49]:
not_in = data_peptides[~data_peptides.patient_id.isin(data_clinical.patient_id)]
print('Số mẫu không thỏa ràng buộc ở patient_id:', not_in.shape[0])
Số mẫu không thỏa ràng buộc ở patient_id: 0

Cột visit_month

In [50]:
not_in = data_peptides[~data_peptides.visit_month.isin(data_clinical.visit_month)]
print('Số mẫu không thỏa ràng buộc ở visit_month:', not_in.shape[0])
Số mẫu không thỏa ràng buộc ở visit_month: 0

Ta thấy cả 2 cột đều thỏa điều kiện trên.

Cột UniProt

In [51]:
data_peptides.UniProt.nunique()
Out[51]:
227
In [52]:
data_peptides.UniProt.value_counts().describe()
Out[52]:
count      227.000000
mean      4325.259912
std       6446.449146
min        489.000000
25%       1010.000000
50%       2008.000000
75%       5031.000000
max      51916.000000
Name: UniProt, dtype: float64

Dữ liệu có nhiều UniProt khác nhau, tuy nhiên UniProt có số lần xuất hiện thấp nhất cũng có đến 489 mẫu, nên cột này hầu như không có outlier

Cột peptide

In [53]:
data_peptides.Peptide.nunique()
Out[53]:
968
In [54]:
data_peptides.Peptide.value_counts().describe()
Out[54]:
count     968.000000
mean     1014.291322
std       117.042625
min       489.000000
25%       977.750000
50%      1062.000000
75%      1097.000000
max      1113.000000
Name: Peptide, dtype: float64

Tương tự, cột này cũng hầu như không có outlier

Cột PeptideAbundance

In [55]:
data_peptides.PeptideAbundance.plot(kind='hist')
plt.title('Histogram phân bố của PeptideAbundance')
plt.show()

Dữ liệu đang bị lệch nặng về khoảng 0-0.25xe8. Nên ta sẽ chuẩn hóa và quan sát thống kê của dữ liệu

In [56]:
norm = np.log(data_peptides.PeptideAbundance)
norm.plot(kind='box')
plt.title('Box plot PeptideAbundance sau khi chuẩn hóa log')
plt.show()

Quan sát dữ liệu sau khi chuẩn hóa, ta thấy có nhiều điểm được detect là outlier. Tuy nhiên hầu hết các điểm đều xuất hiện khá gần nhau và dày đặc. Chỉ có 2 điểm phía dưới cùng là cách xa phần còn lại. Nên ta có thể xem xét bỏ 2 điểm này đi ở các bước sau.

In [57]:
data_peptides.iloc[norm[norm<3].index]
Out[57]:
visit_id visit_month patient_id UniProt Peptide PeptideAbundance
126128 49683_0 0 49683 P02774 RTHLPEVFLSK 10.9985
456992 4923_24 24 4923 P02774 RTHLPEVFLSK 19.7057

Đây là 2 điểm cần được xem xét

In [58]:
plt.figure(figsize=(15,7))
plt.subplot(1,2,1)
plt.title('Phân bố PeptideAbundance theo visit_month')
plt.scatter('PeptideAbundance', 'visit_month', data=data_peptides)
plt.xlabel('PeptideAbundance')
plt.ylabel('visit_month')

plt.subplot(1,2,2)
plt.title('Phân bố PeptideAbundance theo patient_id')
plt.scatter('PeptideAbundance', 'patient_id', data=data_peptides)
plt.xlabel('PeptideAbundance')
plt.ylabel('patient_id')
plt.show()

Quan sát PeptideAbundance theo tháng và bệnh nhân, ta thấy có 1 điểm giá trị 1.75xe8 nằm tách biệt hoàn toàn so với phần còn lại, nên ta có thể xem xét điểm này ở các bước sau.

2.2.3.4 Tập dữ liệu data_proteins

In [59]:
data_proteins.head()
Out[59]:
visit_id visit_month patient_id UniProt NPX
0 55_0 0 55 O00391 11254.3
1 55_0 0 55 O00533 732430.0
2 55_0 0 55 O00584 39585.8
3 55_0 0 55 O14498 41526.9
4 55_0 0 55 O14773 31238.0

Patient_id và visit_month ở file này cũng có ràng buộc với data_clinical như trên

Cột patient_id

In [60]:
not_in = data_proteins[~data_proteins.patient_id.isin(data_clinical.patient_id)]
print('Số mẫu không thỏa ràng buộc ở patient_id:', not_in.shape[0])
Số mẫu không thỏa ràng buộc ở patient_id: 0

Cột visit_month

In [61]:
not_in = data_proteins[~data_proteins.visit_month.isin(data_clinical.visit_month)]
print('Số mẫu không thỏa ràng buộc ở visit_month:', not_in.shape[0])
Số mẫu không thỏa ràng buộc ở visit_month: 0

Ta thấy cả 2 đều thỏa ràng buộc

Cột UniProt

In [62]:
data_proteins.UniProt.nunique()
Out[62]:
227
In [63]:
data_proteins.UniProt.value_counts().describe()
Out[63]:
count     227.000000
mean     1025.290749
std       135.788240
min       489.000000
25%       974.500000
50%      1100.000000
75%      1112.000000
max      1113.000000
Name: UniProt, dtype: float64

Tương tự như UniProt ở data_peptides, tần suất xuất hiện của UniProt thấp nhất đã là 489. Nên cột này khả năng cao không có outlier

Cột NPX

In [64]:
data_proteins.NPX.plot(kind='hist')
plt.title('Histogram phân bố giá trị NPX')
plt.show()

Cột này bị lệch phải nặng, tập trung chủ yếu ở khoảng 0-0.5xe8

In [65]:
np.log(data_proteins.NPX).plot(kind='box')
plt.title('Box plot giá trị NPX sau khi chuẩn hóa log')
plt.show()

Sau khi chuẩn hóa, ta thấy box plot giúp detect rất nhiều điểm là outlier. Nhưng các điểm này lại nằm khá gần nhau, gần khoảng tập trung của dữ liệu và tần suất cũng rất dày đặc. Nên ta sẽ không xem đây là outlier

2.3 Khám phá dữ liệu 🧬

2.3.1 Phân tích các chỉ số thống kê và phân phối 📊

Phân tích tập dữ liệu ! Kiểm tra có bao nhiêu bệnh nhân thực trong tập dữ liệu

In [66]:
data_peptides['patient_id'].nunique()
Out[66]:
248

NHẬN XÉT:

  • Có 248 bệnh nhân khác nhau trong tập dữ liệu
TẬP TRAIN_PEPTIDES
In [67]:
data_peptides.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 981834 entries, 0 to 981833
Data columns (total 6 columns):
 #   Column            Non-Null Count   Dtype  
---  ------            --------------   -----  
 0   visit_id          981834 non-null  object 
 1   visit_month       981834 non-null  int64  
 2   patient_id        981834 non-null  int64  
 3   UniProt           981834 non-null  object 
 4   Peptide           981834 non-null  object 
 5   PeptideAbundance  981834 non-null  float64
dtypes: float64(1), int64(2), object(3)
memory usage: 44.9+ MB
In [68]:
data_peptides.describe()
Out[68]:
visit_month patient_id PeptideAbundance
count 981834.000000 981834.000000 9.818340e+05
mean 26.105061 32603.465361 6.428902e+05
std 22.913897 18605.934422 3.377989e+06
min 0.000000 55.000000 1.099850e+01
25% 6.000000 16566.000000 2.817425e+04
50% 24.000000 29313.000000 7.430830e+04
75% 48.000000 49995.000000 2.213388e+05
max 108.000000 65043.000000 1.787520e+08
TẬP_PROTEINS
In [69]:
data_proteins.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 232741 entries, 0 to 232740
Data columns (total 5 columns):
 #   Column       Non-Null Count   Dtype  
---  ------       --------------   -----  
 0   visit_id     232741 non-null  object 
 1   visit_month  232741 non-null  int64  
 2   patient_id   232741 non-null  int64  
 3   UniProt      232741 non-null  object 
 4   NPX          232741 non-null  float64
dtypes: float64(1), int64(2), object(2)
memory usage: 8.9+ MB
In [70]:
data_proteins.describe()
Out[70]:
visit_month patient_id NPX
count 232741.000000 232741.000000 2.327410e+05
mean 26.099205 32593.881873 2.712077e+06
std 22.874719 18608.479506 2.224155e+07
min 0.000000 55.000000 8.460820e+01
25% 6.000000 16566.000000 2.946440e+04
50% 24.000000 29313.000000 1.135560e+05
75% 48.000000 49995.000000 5.638940e+05
max 108.000000 65043.000000 6.138510e+08
TẬP CLINICAL
In [71]:
data_clinical.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2615 entries, 0 to 2614
Data columns (total 9 columns):
 #   Column                               Non-Null Count  Dtype  
---  ------                               --------------  -----  
 0   visit_id                             2615 non-null   object 
 1   patient_id                           2615 non-null   int64  
 2   visit_month                          2615 non-null   int64  
 3   updrs_1                              2614 non-null   float64
 4   updrs_2                              2613 non-null   float64
 5   updrs_3                              2590 non-null   float64
 6   updrs_4                              1577 non-null   float64
 7   upd23b_clinical_state_on_medication  1288 non-null   object 
 8   null_values_count                    2615 non-null   int64  
dtypes: float64(4), int64(3), object(2)
memory usage: 184.0+ KB
In [72]:
data_clinical.describe()
Out[72]:
patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 null_values_count
count 2615.000000 2615.000000 2614.000000 2613.00000 2590.000000 1577.000000 2615.000000
mean 32651.743786 31.190822 7.110559 6.74359 19.421236 1.861763 0.915105
std 18535.758700 25.199053 5.525955 6.32323 15.000289 3.022112 0.938661
min 55.000000 0.000000 0.000000 0.00000 0.000000 0.000000 0.000000
25% 16574.000000 10.500000 3.000000 1.00000 6.000000 0.000000 0.000000
50% 29417.000000 24.000000 6.000000 5.00000 19.000000 0.000000 1.000000
75% 50611.000000 48.000000 10.000000 10.00000 29.000000 3.000000 2.000000
max 65043.000000 108.000000 33.000000 40.00000 86.000000 20.000000 4.000000
TẬP SUPPLEMENTAL
In [73]:
data_supplemental.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2223 entries, 0 to 2222
Data columns (total 9 columns):
 #   Column                               Non-Null Count  Dtype  
---  ------                               --------------  -----  
 0   visit_id                             2223 non-null   object 
 1   patient_id                           2223 non-null   int64  
 2   visit_month                          2223 non-null   int64  
 3   updrs_1                              2010 non-null   float64
 4   updrs_2                              2009 non-null   float64
 5   updrs_3                              2218 non-null   float64
 6   updrs_4                              1295 non-null   float64
 7   upd23b_clinical_state_on_medication  1122 non-null   object 
 8   null_values_count                    2223 non-null   int64  
dtypes: float64(4), int64(3), object(2)
memory usage: 156.4+ KB
In [74]:
data_supplemental.describe()
Out[74]:
patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 null_values_count
count 2223.000000 2223.000000 2010.000000 2009.000000 2218.000000 1295.000000 2223.000000
mean 32478.016194 12.910481 5.684080 6.507715 22.917944 0.840154 1.107063
std 18637.562796 13.060532 4.366964 4.968132 12.342596 1.860247 1.176296
min 35.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 16703.000000 0.000000 2.000000 2.000000 14.000000 0.000000 0.000000
50% 32915.000000 6.000000 5.000000 5.000000 22.000000 0.000000 1.000000
75% 47243.000000 24.000000 8.000000 10.000000 31.000000 0.000000 2.000000
max 65530.000000 36.000000 27.000000 34.000000 72.000000 12.000000 5.000000

TRỰC QUAN HÓA MỘT SỐ THUỘC TÍNH

In [75]:
fig, axes = plt.subplots(4,2 , figsize=(20, 14))
 
fig.suptitle('Tỉ lệ điểm của 4 thuộc tính Updrs[1-4]')


sns.histplot(ax=axes[0, 0],data=data_clinical, x="updrs_1",color = "#FF99CC")
sns.boxplot(ax=axes[0,1],x=data_clinical["updrs_1"],color = "#FF99CC")

sns.histplot(ax=axes[1, 0],data=data_clinical, x="updrs_2",color = "#00CC33")
sns.boxplot(ax=axes[1, 1],x=data_clinical["updrs_2"],color = "#00CC33")

sns.histplot(ax=axes[2, 0],data=data_clinical, x="updrs_3",color = "#888888")
sns.boxplot(ax=axes[2,1],x=data_clinical["updrs_3"],color = "#888888")

sns.histplot(ax=axes[3, 0],data=data_clinical, x="updrs_4",color = "#FFCC33")
sns.boxplot(ax=axes[3,1],x=data_clinical["updrs_4"],color = "#FFCC33")
Out[75]:
<AxesSubplot:xlabel='updrs_4'>

NHẬN XÉT

  • Các chỉ số updrs[1-4] : quan trọng trong việc đánh giá bệnh , các biểu đồ trực quan giá trị điểm của các updrs[1-4] cho thấy mức độ phân bố của các điểm này - điều này là tiền đề để xây dựng mô hình cho ra tri thức
  • Đối với chỉ số updrs_1 : Điểm của dữ liệu tập trung chủ yếu ở khoảng từ [0-15]
  • Đối với chỉ số updrs_2 : Điểm của dữ liệu tập trung chủ yếu ở khoảng từ [0-10] và cao nhất ở 0
  • Đối với chỉ số updrs_3 : Điểm của dữ liệu tập trung chủ yếu ở khoảng từ [0-40] và cao nhất ở 0
  • Đối với chỉ số updrs_4 : Điểm của dữ liệu khá thưa thớt, tập trung nhiều ở khoảng từ [0-1] và cao nhất ở 0 và có khá nhiều dữ liệu ngoại lệ
Kiểm tra tỉ lệ người dùng thuốc
In [76]:
plt.figure(figsize=(10,6))
plt.xlabel('Trạng thái') 
plt.ylabel('Số lượng') 
  
# displaying the title
plt.title("Thuộc tính upd23b_clinical_state_on_medicationd")
data_clinical['upd23b_clinical_state_on_medication'].value_counts().sort_values().plot(kind = 'bar',color='#4169E1');

Nhận xét

Như vậy có thể thấy ! Đa số bệnh nhân dùng thuốc Levodopa Với tỉ lệ Có sử dụng ( ON ) xấp xỉ 800 Trạng thái Off là gần 500 Mẫu dữ liệu chứa giá trị NaN khá nhiều

=> Vẫn chưa kết luận được gì ! Dữ liệu thiếu là quá nhiều

In [77]:
df = data_clinical.loc[data_clinical['upd23b_clinical_state_on_medication']=='On']
df_off = data_clinical.loc[data_clinical['upd23b_clinical_state_on_medication']=='Off']

Tỉ lệ điểm của 4 thuộc tính Updrs[1-4] dựa trên visit_month với upd23b_clinical_state_on_medication là On

In [78]:
pallete = sns.color_palette("tab10", 5)
fig, axes = plt.subplots(2,2, figsize=(20, 10))
 
fig.suptitle('Tỉ lệ điểm của 4 thuộc tính Updrs[1-4] dựa trên visit_month với upd23b_clinical_state_on_medication là On')

sns.boxplot(ax=axes[0, 0],x=df["visit_month"],y =df["updrs_1"] ,color = pallete[2])
sns.boxplot(ax=axes[0, 1],x=df["visit_month"],y =df["updrs_2"] ,color = pallete[1])
sns.boxplot(ax=axes[1, 0],x=df["visit_month"],y =df["updrs_3"] ,color = pallete[0])
sns.boxplot(ax=axes[1, 1],x=df["visit_month"],y =df["updrs_4"] ,color = pallete[3])
Out[78]:
<AxesSubplot:xlabel='visit_month', ylabel='updrs_4'>
NHẬN XÉT :
  • Dữ liệu cho thấy ở các thuộc tính updrs_1,2,3 khá tương đồng nhau , dữ liệu phân bố khá đều ở các tháng , tuy nhiên vẫn có các điểm ngoại lai
  • Đối với thuộc tính updrs_4 thì giá trị NaN khá nhiều và xuất hiện nhiều các điểm dữ liệu ngoại lệ có thể là ngoại lai , nhiều hơn so với các updrs còn lại

Tỉ lệ điểm của 4 thuộc tính Updrs[1-4] dựa trên visit_month với upd23b_clinical_state_on_medication là Off

In [79]:
fig, axes = plt.subplots(2,2, figsize=(20, 10))
 
fig.suptitle('Tỉ lệ điểm của 4 thuộc tính Updrs[1-4] dựa trên visit_month với upd23b_clinical_state_on_medication là Off')

sns.boxplot(ax=axes[0, 0],x=df_off["visit_month"],y =df_off["updrs_1"] ,color = pallete[2])
sns.boxplot(ax=axes[0, 1],x=df_off["visit_month"],y =df_off["updrs_2"] ,color = pallete[1])
sns.boxplot(ax=axes[1, 0],x=df_off["visit_month"],y =df_off["updrs_3"] ,color = pallete[0])
sns.boxplot(ax=axes[1, 1],x=df_off["visit_month"],y =df_off["updrs_4"] ,color = pallete[3])
Out[79]:
<AxesSubplot:xlabel='visit_month', ylabel='updrs_4'>
NHẬN XÉT :
  • Dữ liệu cho thấy giữa updrs_1 và updrs_2 khá tương đồng về dữ liệu - vẫn xuất hiện các điểm ngoại lai ở các thuộc tính này
  • Dữ liệu của thuộc tính updrs_3 phân bố khá ít ở 2 tháng 6 và 108 - vẫn xuất hiện các điểm ngoại lai
  • Đối với updrs_4 Dữ liệu bị thiếu khá nhiều các điểm ngoại lai chiếm phần lớn trong thuộc tính

2.3.2 Phân tích về mối quan hệ giữa các thuộc tính 📊

In [80]:
df_corr = data_clinical.corr()
fig = go.Figure()
fig.add_trace(
    go.Heatmap(
        x = df_corr.columns,
        y = df_corr.index,
        z = np.array(df_corr),
        text=df_corr.values,
        texttemplate='%{text:.2f}',
         colorscale ='sunset'
    )
)
fig.show()
1.00 -0.02 0.05 0.04 0.05 -0.01 -0.07 -0.02 1.00 0.12 0.13 0.14 0.14 -0.30 0.05 0.12 1.00 0.66 0.43 0.43 -0.37 0.04 0.13 0.66 1.00 0.74 0.38 -0.44 0.05 0.14 0.43 0.74 1.00 0.22 -0.48 -0.01 0.14 0.43 0.38 0.22 1.00 -0.26 -0.07 -0.30 -0.37 -0.44 -0.48 -0.26 1.00 patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 null_values_count patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 null_values_count
−0.4 −0.2 0 0.2 0.4 0.6 0.8 1

Nhận xét:

  • Cả 4 cột điểm đều có độ tương quan thấp đối với cột visit_month
  • Nhưng chúng lại có độ tương quan với nhau khá cao. Từ điều này ta có thể biết được các triệu chứng của căn bệnh có liên quan mật thiết với nhau vì đều ảnh hưởng đến các chức năng vận động

Tiếp theo ta sẽ xem xét diễn biến 4 cột điểm theo thời gian như thế nào

Diễn biến của 4 cột điểm theo các tháng

In [81]:
for upd in ["updrs_1","updrs_2","updrs_3","updrs_4"]:
    fig = px.scatter(data_clinical, x="visit_month", y=upd, color="upd23b_clinical_state_on_medication", trendline="ols")
    fig.show()

Độ tương quan giữa 4 thang điểm với các loại proteins

In [82]:
data_clinical[data_clinical['upd23b_clinical_state_on_medication']=='Off'].corr()
Out[82]:
patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 null_values_count
patient_id 1.000000 -0.079003 0.032051 -0.039165 0.054467 -0.053515 0.033596
visit_month -0.079003 1.000000 0.157032 0.195861 0.265885 0.277408 -0.068994
updrs_1 0.032051 0.157032 1.000000 0.657617 0.304638 0.342962 -0.071623
updrs_2 -0.039165 0.195861 0.657617 1.000000 0.517941 0.373278 -0.015005
updrs_3 0.054467 0.265885 0.304638 0.517941 1.000000 0.282634 -0.001512
updrs_4 -0.053515 0.277408 0.342962 0.373278 0.282634 1.000000 NaN
null_values_count 0.033596 -0.068994 -0.071623 -0.015005 -0.001512 NaN 1.000000
In [83]:
data_clinical[['visit_month','updrs_1']]['visit_month'].unique()
Out[83]:
array([  0,   3,   6,   9,  12,  18,  24,  30,  36,  42,  48,  54,  60,
        72,  84,  96, 108])
In [84]:
merge_protein_clinical = pd.merge(data_clinical, data_proteins, on=['patient_id','visit_month'])
merge_protein_clinical.columns
Out[84]:
Index(['visit_id_x', 'patient_id', 'visit_month', 'updrs_1', 'updrs_2',
       'updrs_3', 'updrs_4', 'upd23b_clinical_state_on_medication',
       'null_values_count', 'visit_id_y', 'UniProt', 'NPX'],
      dtype='object')
In [85]:
columns   = ['updrs_1', 'updrs_2',
             'updrs_3', 'updrs_4','UniProt', 'NPX']
columns_2 = ['UniProt','updrs_1', 'updrs_2',
             'updrs_3', 'updrs_4']
corr_matrix = merge_protein_clinical[columns].groupby('UniProt').corr().reset_index()
corr_matrix = corr_matrix[corr_matrix['level_1']=='NPX'][columns_2].reset_index(drop=True)
corr_matrix = corr_matrix.set_index('UniProt')
for index in range(0,corr_matrix.T.shape[1],15):
    fig = px.imshow(corr_matrix.T.iloc[:,index:index+15],text_auto=True,color_continuous_scale ='sunset')
    fig.show()
−0.06570694 −0.1454305 −0.05328226 −0.1110249 −0.02972861 0.0503568 −0.157596 −0.1115196 −0.07951965 −0.1036344 −0.05843781 −0.08441123 −0.08590208 −0.08165535 0.01245643 −0.1301265 −0.1827766 −0.04757714 −0.1659717 −0.1071825 0.0258104 −0.2138361 −0.1602345 −0.1257232 −0.1147265 −0.04668863 −0.1706882 −0.099387 −0.07970441 −0.05063522 −0.1005703 −0.2224959 164.8102μ −0.1456763 −0.06078721 0.001936807 −0.229327 −0.1753268 −0.1343346 −0.1455721 −0.0723321 −0.1207432 −0.08055297 −0.08759129 −0.04728938 0.01141857 −0.005587241 −0.09208184 −0.08400809 −0.04763516 0.04827707 −0.01309827 −0.08925958 0.03052477 −0.01826169 0.05046442 −0.09571098 0.04439864 0.01944175 −0.08601553 O00391 O00533 O00584 O14498 O14773 O14791 O15240 O15394 O43505 O60888 O75144 O75326 O94919 P00441 P00450 updrs_4 updrs_3 updrs_2 updrs_1
−0.2 −0.15 −0.1 −0.05 0 0.05 UniProt
−0.0274778 −0.0982794 0.01477309 −0.03537089 −0.002651107 0.03243619 −0.020854 0.04340027 0.05794958 −0.05393076 −0.0912955 −0.0994735 −0.04840312 0.03747948 0.01544495 −0.02734466 −0.08004023 0.08457831 −0.04525603 −0.05620932 −0.03711624 −0.06536734 −0.01367344 0.0774196 −0.005190687 −0.08170992 −0.0990697 −0.05375847 0.01893499 −0.002109363 −0.007253232 −0.06033357 0.1224386 0.003650239 −0.02622554 −0.04135076 −0.05944548 0.02675063 0.0810268 0.0583147 −0.02545287 −0.05718811 −0.06113661 0.02626268 0.009377005 0.01308473 −0.08411079 −0.02698556 −0.1111985 −0.04019732 −0.033639 −0.03448916 −0.06678125 −0.01359964 −0.09603947 −0.00993307 −0.08073951 −0.04318947 −0.05986866 0.01829811 P00734 P00736 P00738 P00746 P00747 P00748 P00751 P01008 P01009 P01011 P01019 P01023 P01024 P01031 P01033 updrs_4 updrs_3 updrs_2 updrs_1
−0.1 −0.05 0 0.05 0.1 UniProt
−0.0978157 −0.05994867 −0.08105025 −0.05937923 0.08322452 −0.02065873 −0.0716247 0.01080375 −0.01459678 −0.06228953 −0.07032347 −0.03835687 −0.005986709 −0.01481399 0.04702395 −0.0989617 −0.1276118 −0.06497829 −0.02326855 0.1102296 0.05799903 −0.02292704 0.09090665 −0.03371896 −0.04839287 0.01177283 0.05877542 0.02990542 0.06737862 0.1366247 −0.1242671 −0.1106638 −0.04372888 0.01210916 0.08333454 0.05965649 0.03400066 0.08594332 −0.005208686 0.02060321 0.05188155 0.02294957 0.1030286 0.01697105 0.1298068 0.02153231 −0.06221219 −0.01071468 0.005602178 −0.03467127 −0.009479894 −0.0391487 −0.03684748 −0.08017134 −0.02776227 −0.06936192 −0.0981473 −0.07773602 −0.06952119 −0.02406566 P01034 P01042 P01344 P01591 P01594 P01608 P01621 P01717 P01780 P01833 P01834 P01857 P01859 P01860 P01861 updrs_4 updrs_3 updrs_2 updrs_1
−0.1 −0.05 0 0.05 0.1 UniProt
0.004335026 −0.008781096 −0.07617094 −0.005272405 −0.1569531 −0.05242422 −0.05863205 −0.02479607 0.005692043 0.004995263 0.0172466 −0.1250026 0.06286574 0.002525331 0.02593743 0.03142583 0.08545383 −0.1204436 0.02219364 −0.1764377 −0.03334888 −0.06242565 −0.08198913 0.0658094 0.05240394 0.04487869 −0.09651389 0.07017802 0.01436844 0.01781245 0.04587216 0.1297301 −0.0900073 0.0101194 −0.1601029 −0.05034013 −0.1181798 −0.06838228 0.08961456 0.06781533 0.06353475 −0.03616785 0.07838912 0.02536648 0.02624034 −0.04569629 −0.04229741 −0.04840338 −0.07679627 −0.08551274 −0.02735199 0.004417599 −0.08291951 −0.009139926 −0.05917996 0.005105862 −0.1256169 −0.04981314 −0.1055137 −0.07458493 P01876 P01877 P02452 P02647 P02649 P02652 P02655 P02656 P02671 P02675 P02679 P02747 P02748 P02749 P02750 updrs_4 updrs_3 updrs_2 updrs_1
−0.15 −0.1 −0.05 0 0.05 0.1 UniProt
−0.1376144 −0.08912875 −0.01077495 0.009188998 0.01562476 0.04625455 −0.05887906 −0.07318815 −0.1398046 −0.03837138 −0.03677818 0.005842251 −0.113276 −0.1367048 −0.1823307 −0.1186973 −0.1737186 −0.009413662 0.04067315 0.04949393 0.03888142 −0.03962832 −0.08756684 −0.1900064 −0.0688855 −0.07545335 −0.05228874 −0.1429998 −0.16526 −0.2181768 −0.109557 −0.1390015 0.02898425 0.008059338 0.01309638 −0.005529611 −0.007359494 −0.06838016 −0.1702901 −0.05802377 −0.1073702 −0.05763463 −0.1494527 −0.1491779 −0.1614945 −305.9903μ −0.08652977 −0.06181168 −0.06300458 0.02662469 0.02974619 −0.04850642 −0.1358247 −0.08108605 −0.1051794 −0.0438408 0.002807834 −0.05279326 −0.02285025 −0.06674212 P02751 P02753 P02760 P02763 P02765 P02766 P02768 P02774 P02787 P02790 P02792 P04004 P04075 P04156 P04180 updrs_4 updrs_3 updrs_2 updrs_1
−0.2 −0.15 −0.1 −0.05 0 UniProt
−0.03663945 −0.05691299 −0.05702209 −0.1058493 −0.02723002 −0.03062428 −0.01507316 0.0162536 −0.1823641 −0.1364078 −0.07616121 −0.1147511 −0.02913343 −0.07176236 −0.1344927 −0.05325897 −0.04412729 −0.02141314 −0.1423252 −0.04255042 0.01065178 −0.04067819 0.04759215 −0.2152657 −0.1698679 −0.05807376 −0.08413255 −0.03608512 −0.1248259 −0.1255448 −0.02928522 0.01402529 0.04695397 −0.1323199 0.008369272 −0.02152119 −0.01795545 0.05620362 −0.206055 −0.1931675 −0.03884845 −0.03211539 −0.03524981 −0.1193343 −0.1061265 −0.01671219 −0.02094973 −0.1453744 −0.007242907 −0.1819666 0.05097899 −0.01935809 −0.02469587 −0.07843035 0.00693979 −0.08952485 −0.1491132 0.01499367 −0.1005434 −0.07106587 P04196 P04207 P04211 P04216 P04217 P04275 P04406 P04433 P05060 P05067 P05090 P05155 P05156 P05408 P05452 updrs_4 updrs_3 updrs_2 updrs_1
−0.2 −0.15 −0.1 −0.05 0 0.05 UniProt
−0.05159873 0.01873928 −0.0991688 −0.001930894 −0.04666478 −0.03801559 −0.07638028 −0.0994386 −0.05287381 −0.05398312 −0.1203329 −0.02164017 −0.07567961 −0.1121778 0.002525911 −0.09100073 −0.07919275 −0.08508775 0.004644135 −0.05607278 −0.0629138 −0.1345806 −0.08999702 −0.1011132 −0.05872505 −0.1370205 −0.08228882 −0.105003 −0.08711673 −0.0208909 −0.08615563 −0.02054892 −0.08004844 0.01534051 −0.05083912 −0.05291125 −0.1416908 −0.04626814 −0.1264432 −0.02485784 −0.1246236 −0.09446106 −0.09582689 −0.07151128 −0.05509066 −0.04784126 0.02597811 −0.04927129 −0.03308483 −0.1027676 −0.00992734 −0.01875408 −0.1132679 0.01216321 −0.04085351 −0.03608854 −0.009191707 −0.04159938 −0.05571552 0.00978792 P05546 P06310 P06396 P06454 P06681 P06727 P07195 P07225 P07333 P07339 P07602 P07711 P07858 P07998 P08123 updrs_4 updrs_3 updrs_2 updrs_1
−0.14 −0.12 −0.1 −0.08 −0.06 −0.04 −0.02 0 0.02 UniProt
−0.1060084 −0.0422806 −0.02799921 −0.08247249 −0.1392058 −0.08627965 −0.0452605 −0.04533414 −0.09120303 −0.1221589 −0.1320586 −0.07547242 −0.07922836 −0.148268 −0.0767393 −0.1437463 −0.08961296 −0.02331018 −0.04128842 −0.1258439 −0.05247675 −0.04276762 −0.04619669 −0.1679951 −0.1216516 −0.1024094 −0.06280227 −0.04795517 −0.1772066 −0.1114211 −0.1471949 −0.09034507 −0.04455738 −0.006040984 −0.08576468 −0.02667399 −0.06331463 −0.0317409 −0.1779411 −0.1104733 −0.08535624 −0.07259499 0.006056904 −0.185136 −0.09336791 −0.02888484 −0.02468897 −0.02858567 −0.04776546 −0.04757035 −0.04077102 0.007657551 −0.004910883 −0.05979077 −0.1028295 −0.05710134 0.04013566 −0.09737169 −0.02871071 −0.02847228 P08133 P08253 P08294 P08493 P08571 P08603 P08637 P08697 P09104 P09486 P09871 P10451 P10643 P10645 P10909 updrs_4 updrs_3 updrs_2 updrs_1
−0.15 −0.1 −0.05 0 UniProt
−0.0676679 0.0133169 −0.1092428 −0.05337326 −0.1422663 −0.1464557 −0.1360772 −0.05882326 −0.1318989 −0.06779549 −0.1352128 −0.1653356 −0.04299455 −0.1055938 0.01527848 −0.1546318 0.0276201 −0.1292948 −0.06082504 −0.1943504 −0.1550362 −0.1077555 −0.04731416 −0.1170314 −0.06971615 −0.1156226 −0.1767796 −0.04758458 −0.07567294 0.0368807 −0.1695369 −0.004585094 −0.07295795 −0.04150585 −0.2228173 −0.1492392 −0.054544 0.01395558 −0.1178109 −0.06486311 −0.1014686 −0.1665185 −0.02275959 −0.02939901 0.06385255 −0.04620595 0.07685377 −0.01931881 −0.03695835 −0.03058882 −0.02399871 −0.01534952 −0.06305027 −0.04153179 −0.02489949 0.02124083 −0.04965148 −0.00299059 −0.008444679 0.1164595 P11142 P11277 P12109 P13473 P13521 P13591 P13611 P13671 P13987 P14174 P14314 P14618 P16035 P16070 P16152 updrs_4 updrs_3 updrs_2 updrs_1
−0.2 −0.15 −0.1 −0.05 0 0.05 0.1 UniProt
−0.09595003 −0.1755837 −0.04200261 −0.07187841 −0.1351406 −0.007240682 −0.0019947 0.09769068 −0.08330589 −0.08773267 −0.04118756 −0.1397704 −0.08579907 0.01367349 0.004188297 −0.1127544 −0.1946734 0.009725079 −0.03562687 −0.1762735 −0.01713052 −0.03199958 0.05737452 −0.08122551 −0.09167582 −0.04177353 −0.1058144 −0.0994654 0.01868261 −0.04669697 −0.09667959 −0.1876488 0.03558972 0.001939392 −0.157831 0.02868786 −0.01328425 0.0521396 −0.06223751 −0.05278968 −0.07690668 −0.08793067 −0.07430154 0.04040859 −0.008643847 −0.006464494 −0.1069557 −0.04507737 −0.007451354 −0.0959712 −0.0991861 −0.01994258 0.06972433 −0.1131197 −0.002118203 0.08279459 −0.1109402 −0.1018815 −0.05469034 −0.01712558 P16870 P17174 P17936 P18065 P19021 P19652 P19823 P19827 P20774 P20933 P23083 P23142 P24592 P25311 P27169 updrs_4 updrs_3 updrs_2 updrs_1
−0.15 −0.1 −0.05 0 0.05 UniProt
−0.09514022 −0.007691169 0.01976363 −0.01591761 −0.04654615 −0.08152282 0.03464502 −0.1201065 −0.08457584 −0.09337122 −0.1588758 −0.0907372 −0.08273763 −0.03472643 −0.1024177 −0.08433145 0.03957743 0.02016661 0.01410718 −0.03373448 −0.07975452 −0.04644296 −0.1531908 −0.1596155 −0.1121612 −0.2011304 −0.1073713 −0.09047428 −0.0518301 −0.1315875 −0.08904861 0.01533741 0.01955166 0.0773878 −0.01912899 −0.03767428 −0.004492837 −0.1014934 −0.1742816 −0.09394093 −0.1865001 −0.109273 −0.1010927 −0.06538877 −0.1256428 −0.02958875 0.1034421 0.02004181 −0.02781044 −0.07204307 −0.03434527 −0.02986189 −0.1213575 0.03159738 −0.04001992 −0.02297859 0.05013523 −0.06458869 −0.0576089 245.0172μ P30086 P31997 P32754 P35542 P36222 P36955 P36980 P39060 P40925 P41222 P43121 P43251 P43652 P49588 P49908 updrs_4 updrs_3 updrs_2 updrs_1
−0.2 −0.15 −0.1 −0.05 0 0.05 0.1 UniProt
−0.08958191 −0.08642859 −0.08335464 0.008172228 −0.1267634 −527.6402μ −0.1204119 −0.137494 0.04396571 −0.1150654 −0.1205015 −0.2029312 −0.1084042 −0.0977931 −0.0677401 −0.074059 −0.1234178 −0.1484517 −0.02479979 −0.1414631 −0.002787399 −0.1104204 −0.1166911 0.04693046 −0.07259275 −0.1310939 −0.2289249 −0.1018618 −0.08737467 −0.06420079 −0.04057307 −0.1195588 −0.1790074 −0.09500654 −0.1532654 0.06406178 −0.09222113 −0.078915 0.0322376 −0.0397408 −0.09063075 −0.2005983 −0.1120864 −0.06405042 −0.04749438 −0.06510856 −0.02003995 0.06540369 0.008794179 −0.02918875 −0.09162312 −0.04524206 −0.09648208 −0.0429378 −0.0996442 −0.07205735 −0.1198429 −0.04138125 −0.03470487 −0.0561942 P51884 P54289 P55290 P60174 P61278 P61626 P61769 P61916 P80748 P98160 Q02818 Q06481 Q08380 Q12805 Q12841 updrs_4 updrs_3 updrs_2 updrs_1
−0.2 −0.15 −0.1 −0.05 0 0.05 UniProt
−0.1214031 0.006694272 −0.1361144 −0.1232012 −0.03122534 −0.0276585 −0.1246609 −0.1060842 −0.07185767 0.01688768 −0.1539786 −0.01945228 −0.1225147 0.05385347 −0.02581092 −0.1315811 −0.02635182 −0.176563 −0.1261741 −0.03783756 −0.1119113 −0.1503802 −0.1305783 −0.1296065 0.01389849 −0.1787268 −0.005795514 −0.1630452 −0.04819886 −0.008736499 −0.1140335 −0.06636805 −0.1890898 −0.09761733 −0.07213115 −0.08701644 −0.1567178 −0.154323 −0.1446275 0.06574182 −0.1433307 −0.01852972 −0.1685532 −0.02529658 −707.1356μ −0.0480079 0.0464601 −0.001771477 −0.05079086 −0.002297491 −0.05187887 −0.05381732 −0.08185537 −0.07791042 −0.07550916 −0.03289862 −0.03348262 0.02491652 −0.03855872 −83.92602μ Q12907 Q13283 Q13332 Q13449 Q13451 Q13740 Q14118 Q14508 Q14515 Q14624 Q15904 Q16270 Q16610 Q562R1 Q6UX71 updrs_4 updrs_3 updrs_2 updrs_1
−0.15 −0.1 −0.05 0 0.05 UniProt
−0.1232625 −0.04786954 −0.1160736 −0.02346827 0.002384802 −0.06098777 −0.1164345 −0.06359176 −0.1319981 −0.1258042 −0.08141206 −0.0756325 −0.1061559 −0.03939992 −0.08366654 −0.1068238 −0.1174581 −0.1475063 0.002079225 −0.03463492 −0.07275091 −0.1582292 −0.1157766 −0.1613608 −0.1737292 −0.07412966 −0.1113368 −0.14208 −0.04474226 −0.05240957 −0.06533461 −0.1500734 −0.1697813 −0.03589718 −0.005126326 −0.05608846 −0.1454448 −0.09608539 −0.1783649 −0.183866 −0.07552432 −0.08702037 −0.1418193 −0.02627661 −0.005089052 −0.08002543 −0.004190428 0.01330114 0.05740511 0.02657971 −0.05628854 −0.03797585 −0.05627293 −0.01822459 −0.001391291 0.02120404 −0.08921012 −0.00774051 −0.06925281 −0.04034162 Q6UXB8 Q6UXD5 Q7Z3B1 Q7Z5P9 Q8IWV7 Q8N2S1 Q8NBJ4 Q8NE71 Q92520 Q92823 Q92876 Q96BZ4 Q96KN2 Q96PD5 Q96S96 updrs_4 updrs_3 updrs_2 updrs_1
−0.15 −0.1 −0.05 0 0.05 UniProt
−0.06381502 −0.1238305 0.0227962 −0.1290756 −0.05171899 −0.01629805 −0.1634706 0.007106891 −0.09575675 −0.1154606 −0.07366152 −0.04806019 −0.1296338 −0.08107951 −0.08889562 −0.125613 −0.1485536 −0.02105673 −0.1550415 −0.09533273 −0.02758637 −0.1945172 0.04699707 −0.1575598 −0.1941862 −0.0745686 −0.04553718 −0.1397293 −0.01026607 −0.06623259 −0.1488746 −0.1555693 0.0228456 −0.1278643 −0.09620697 −0.04260924 −0.1922722 0.06825993 −0.1662681 −0.1824492 −0.04203938 −0.01400744 −0.1369911 0.04550004 −0.02888282 −0.04475888 −0.03172373 −0.09734237 −0.04244456 −0.01985265 −0.04986294 −0.04636606 −0.04877202 −0.1031984 −0.03504106 −0.0150856 −0.02829258 −0.04682778 −0.09418401 −0.06840027 Q99435 Q99674 Q99683 Q99829 Q99832 Q99969 Q9BY67 Q9HDC9 Q9NQ79 Q9NYU2 Q9UBR2 Q9UBX5 Q9UHG2 Q9UKV8 Q9UNU6 updrs_4 updrs_3 updrs_2 updrs_1
−0.15 −0.1 −0.05 0 0.05 UniProt
−0.03331656 0.008722613 −0.09487425 0.03992014 −0.09549151 0.04429132 0.02572163 −0.001064318 Q9Y646 Q9Y6R7 updrs_4 updrs_3 updrs_2 updrs_1
−0.08 −0.06 −0.04 −0.02 0 0.02 0.04 UniProt
In [86]:
top_20_proteins = []
count_all       = []
for col in corr_matrix.columns.tolist():
    temp = corr_matrix.nlargest(20, col).index.tolist()
    top_20_proteins.append(temp)
    count_all.extend(temp)
    print(temp)
['P19827', 'P01594', 'P02748', 'P01009', 'Q562R1', 'O14791', 'P01861', 'P02766', 'P80748', 'P01008', 'P01031', 'P36980', 'P00748', 'P02750', 'Q99683', 'P32754', 'P06310', 'P02679', 'Q14624', 'P04433']
['P01861', 'P01594', 'P01717', 'P01877', 'P00738', 'P01009', 'P02748', 'P01860', 'P02671', 'P01857', 'P01608', 'P19827', 'P02675', 'P02765', 'P04433', 'Q9HDC9', 'P80748', 'P02679', 'P02763', 'Q9Y6R7']
['P01861', 'P01877', 'P00738', 'P01859', 'P02671', 'P01717', 'P01594', 'P01009', 'P02748', 'P35542', 'Q9HDC9', 'P02675', 'Q14624', 'P61626', 'P16152', 'P02679', 'P01608', 'P01011', 'P04433', 'P19827']
['P16152', 'P31997', 'P23083', 'P11277', 'P19827', 'P55290', 'Q7Z5P9', 'P04275', 'O75144', 'P43251', 'O14791', 'Q13283', 'O94919', 'P10451', 'P40925', 'O43505', 'P02766', 'P02765', 'Q8IWV7', 'P06310']
In [87]:
occu = pd.DataFrame.from_dict(Counter(count_all), orient='index')
occu = occu.sort_values(by=[0],ascending=False)
occu
Out[87]:
0
P19827 4
P01861 3
P01594 3
P04433 3
P02679 3
P02748 3
P01009 3
O14791 2
P16152 2
Q9HDC9 2
P02765 2
P02675 2
P01608 2
P02671 2
P00738 2
P01877 2
Q14624 2
P01717 2
P06310 2
P02766 2
P80748 2
P40925 1
P31997 1
P23083 1
P11277 1
P55290 1
P10451 1
P04275 1
O94919 1
Q13283 1
Q7Z5P9 1
P43251 1
O75144 1
O43505 1
P01011 1
P01857 1
P61626 1
P35542 1
P01859 1
Q9Y6R7 1
P02763 1
P01860 1
P32754 1
Q99683 1
P02750 1
P00748 1
P36980 1
P01031 1
P01008 1
Q562R1 1
Q8IWV7 1

Như ta có thể thấy, protein P19827 có sự tương quan lớn đến cả 4 thang điểm.
Các proteins P01594, P02748, P01009, P01861, P02679, P04433 chỉ có sự tương quan lớn đến 3 thang điểm.
Các protein còn lại thì tương quan với 1 hoặc 2 thang điểm.

Tuy nhiên, chúng ta cũng cần xét tới tỷ lệ của chúng trong tập dữ liệu, nếu độ tương quan cao mà tỷ lệ thấp thì cũng không có ý nghĩa gì.

Tỷ lệ các protein trong mẫu CFS của bệnh nhân

In [88]:
percent_protein = {}
for protein in occu.index.tolist():
    percent_protein[protein] = data_proteins[data_proteins.UniProt == protein].shape[0]*100 \
                                                        /data_clinical.shape[0]
percent_protein = pd.DataFrame.from_dict(percent_protein,orient='index').sort_values(by=[0],ascending=False)
fig = px.bar(percent_protein,orientation='h')
fig.show()
0 5 10 15 20 25 30 35 40 P02766 P01009 P02763 P02671 P80748 P04433 P10451 P01859 P01857 Q7Z5P9 O75144 Q14624 Q8IWV7 P35542 P43251 P61626 Q9Y6R7 P40925 Q9HDC9 Q99683 P11277 P01861 P32754 P23083 P19827 Q562R1
variable 0 value index

Các protein đều có tỷ lệ % xuất hiện trong các mẫu lớn hơn 23%, nên chúng ta không cần loại bỏ mẫu nào

3. KHAI THÁC DỮ LIỆU

3.1. ARIMA + Linear Interpolation

3.1.1. Trích chọn và Rút trích đặc trưng

Dựa trên cấu trúc, các bộ dữ liệu có thể kết hợp lại dựa trên biểu đồ sau:

Image

Sau nhiều lần thử nghiệm mô hình với các thay đổi khác nhau, nhóm có nhận xét như sau:

Khi sử dụng mô hình ARIMA:

  • Kết hợp bộ dữ liệu supplement với clinical cho ra kết quả tốt hơn (so với chỉ sử dụng bộ dữ liệu clinical)
  • Loại bỏ những bệnh nhân khám ít hơn 24 tháng ra khỏi bộ dữ liệu cho ra kết quả tốt hơn.
  • Nếu bỏ đi những dòng dữ liệu có visit_month > 24 (hay nói cách khác, chỉ giữ lại những tháng 0, 6, 12, 24) thì cho ra kết quả tương đương chứ không bị tệ đi.
  • Nếu 1 bệnh nhân bị thiếu dữ liệu các tháng ở giữa (ví dụ chỉ có dữ liệu tháng 0 và tháng 36, bị thiếu dữ liệu các tháng 6, 12 và 24) thì điền những giá trị thiếu bằng Linear Interpolation cho ra kết quả tốt hơn (so với việc loại bỏ bệnh nhân đó, hoặc là chỉ drop những tháng bị thiếu)
  • Nếu không sử dụng Linear Interpolation để điền dữ liệu bị thiếu, thì lúc train mô hình cho điểm UPDRS nào thì mới drop NaN trên cột điểm UPDRS đó thì ra kết quả tốt hơn (so với việc là trước khi train mô hình, drop tất cả những dòng nào có giá trị NaN)

Từ nhận xét trên, ở phiên bản cuối cùng xây dựng mô hình ARIMA, nhóm sẽ thực hiện tiền xử lý như sau:

  • Sử dụng kết hợp 2 bộ dữ liệu clinicalsupplement
  • Loại bỏ những bệnh nhân có thời gian khám ít hơn 24 tháng
  • Loại bỏ những dòng dữ liệu có visit_month > 24 (chỉ giữ lại những tháng 0, 6, 12, 24)
  • Sử dụng Linear Interpolation để điền giá trị điểm UPDRS bị thiếu

Nhóm sẽ sử dụng các đặc trưng đích cần dự đoán: updrs_1, updrs_2, updrs_3updrs_4 để xây dựng 4 mô hình ARIMA cho 4 chỉ số UPDRS.

In [89]:
import amp_pd_peptide
from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
import missingno as msno
from statsmodels.tsa.arima.model import ARIMA
import statsmodels.api as sm
pd.options.mode.chained_assignment = None  # default='warn'
plt.rc("figure",figsize=(12,5))
In [90]:
train_clinical_data = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/train_clinical_data.csv')
train_clinical_data['source'] = 'standard'
supplemental_clinical_data = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/supplemental_clinical_data.csv')
supplemental_clinical_data['source'] = 'supplemental'
train_clinical_all = pd.concat([train_clinical_data, supplemental_clinical_data])

# train_clinical_all = train_clinical_all[~train_clinical_all.visit_month.isin([3, 5, 9])]
# train_clinical_all = train_clinical_all[train_clinical_all.visit_month.isin([0, 6, 12, 24])]

cli_data = train_clinical_all
pep_data = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/train_peptides.csv')
pro_data = pd.read_csv('/kaggle/input/amp-parkinsons-disease-progression-prediction/train_proteins.csv')
In [91]:
def get_train_data(pep_data, pro_data, cli_data, join_type = 'inner'):
    # Average PeptideAbundance of all proteins
    avg_abundance_one_row = pep_data.groupby(['visit_id']).mean().reset_index().drop(columns=['visit_month', 'patient_id']).rename(columns = {'PeptideAbundance':'PeptideAbundance_AVG'})
    # Average NPX of all proteins
    avg_npx_one_row = pro_data.groupby(['visit_id']).mean().reset_index()[['visit_id', 'NPX']].rename(columns = {'NPX':'NPX_AVG'})
    # NPX of important proteins
    NPX_P19827 = pro_data[pro_data['UniProt']=='P19827'][['visit_id', 'NPX']].rename(columns = {'NPX':'NPX_P19827'})
    # Join on visit_id
    train = cli_data.merge(
        avg_abundance_one_row, on='visit_id', how=join_type
    ).merge(
        avg_npx_one_row, on='visit_id', how=join_type
    ).merge(
        NPX_P19827, on='visit_id', how=join_type
    )
    return train

train = get_train_data(pep_data, pro_data, cli_data, join_type = 'outer')
train = train.dropna(subset=['patient_id', 'visit_month'])
train = train.sort_values(by=['patient_id', 'visit_month'], ascending=[True, True])

train
Out[91]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication source PeptideAbundance_AVG NPX_AVG NPX_P19827
2615 35_0 35.0 0.0 5.0 3.0 16.0 0.0 NaN supplemental NaN NaN NaN
2616 35_36 35.0 36.0 6.0 4.0 20.0 0.0 NaN supplemental NaN NaN NaN
0 55_0 55.0 0.0 10.0 6.0 15.0 NaN NaN standard 748153.907014 3.180508e+06 36232.9
1 55_3 55.0 3.0 10.0 7.0 25.0 NaN NaN standard NaN NaN NaN
2 55_6 55.0 6.0 8.0 10.0 34.0 NaN NaN standard 685218.599872 2.942039e+06 14899.5
... ... ... ... ... ... ... ... ... ... ... ... ...
4833 65382_0 65382.0 0.0 NaN NaN 0.0 NaN NaN supplemental NaN NaN NaN
4834 65405_0 65405.0 0.0 5.0 16.0 31.0 0.0 NaN supplemental NaN NaN NaN
4835 65405_5 65405.0 5.0 NaN NaN 57.0 NaN NaN supplemental NaN NaN NaN
4836 65530_0 65530.0 0.0 10.0 6.0 24.0 0.0 NaN supplemental NaN NaN NaN
4837 65530_36 65530.0 36.0 8.0 4.0 15.0 4.0 On supplemental NaN NaN NaN

4838 rows × 12 columns

In [92]:
patient_have_visit_than_24 = train[train['visit_month']>=24]['patient_id'].unique()
train = train[train['patient_id'].isin(patient_have_visit_than_24)]
train
Out[92]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication source PeptideAbundance_AVG NPX_AVG NPX_P19827
2615 35_0 35.0 0.0 5.0 3.0 16.0 0.0 NaN supplemental NaN NaN NaN
2616 35_36 35.0 36.0 6.0 4.0 20.0 0.0 NaN supplemental NaN NaN NaN
0 55_0 55.0 0.0 10.0 6.0 15.0 NaN NaN standard 748153.907014 3.180508e+06 36232.9
1 55_3 55.0 3.0 10.0 7.0 25.0 NaN NaN standard NaN NaN NaN
2 55_6 55.0 6.0 8.0 10.0 34.0 NaN NaN standard 685218.599872 2.942039e+06 14899.5
... ... ... ... ... ... ... ... ... ... ... ... ...
4830 65290_30 65290.0 30.0 4.0 16.0 13.0 0.0 On supplemental NaN NaN NaN
4831 65303_0 65303.0 0.0 0.0 2.0 20.0 0.0 NaN supplemental NaN NaN NaN
4832 65303_36 65303.0 36.0 4.0 1.0 26.0 0.0 NaN supplemental NaN NaN NaN
4836 65530_0 65530.0 0.0 10.0 6.0 24.0 0.0 NaN supplemental NaN NaN NaN
4837 65530_36 65530.0 36.0 8.0 4.0 15.0 4.0 On supplemental NaN NaN NaN

4405 rows × 12 columns

In [93]:
visit_month_ls = [0,6,12,24]
patient_id_ls = []
for p in train['patient_id'].unique():
    for i in range(len(visit_month_ls)):
        patient_id_ls.append(p)
visit_month_ls = visit_month_ls * len(train['patient_id'].unique())

dummy_df = pd.DataFrame(
    {'patient_id': patient_id_ls, 'visit_month': visit_month_ls}
)

dummy_df
Out[93]:
patient_id visit_month
0 35.0 0
1 35.0 6
2 35.0 12
3 35.0 24
4 55.0 0
... ... ...
2951 65303.0 24
2952 65530.0 0
2953 65530.0 6
2954 65530.0 12
2955 65530.0 24

2956 rows × 2 columns

In [94]:
merged_df = pd.merge(train, dummy_df, on=['patient_id', 'visit_month'], how='outer')
merged_df = merged_df.sort_values(by=['patient_id', 'visit_month'], ascending=[True, True])
merged_df
Out[94]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication source PeptideAbundance_AVG NPX_AVG NPX_P19827
0 35_0 35.0 0.0 5.0 3.0 16.0 0.0 NaN supplemental NaN NaN NaN
4405 NaN 35.0 6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4406 NaN 35.0 12.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4407 NaN 35.0 24.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 35_36 35.0 36.0 6.0 4.0 20.0 0.0 NaN supplemental NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ...
4403 65530_0 65530.0 0.0 10.0 6.0 24.0 0.0 NaN supplemental NaN NaN NaN
5291 NaN 65530.0 6.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
5292 NaN 65530.0 12.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
5293 NaN 65530.0 24.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN
4404 65530_36 65530.0 36.0 8.0 4.0 15.0 4.0 On supplemental NaN NaN NaN

5294 rows × 12 columns

In [95]:
interpolated_df = merged_df.interpolate(subset=['updrs_1', 'updrs_2', 'updrs_3', 'updrs_4'])
interpolated_df
Out[95]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication source PeptideAbundance_AVG NPX_AVG NPX_P19827
0 35_0 35.0 0.0 5.00 3.00 16.00 0.0 NaN supplemental NaN NaN NaN
4405 NaN 35.0 6.0 5.25 3.25 17.00 0.0 NaN NaN NaN NaN NaN
4406 NaN 35.0 12.0 5.50 3.50 18.00 0.0 NaN NaN NaN NaN NaN
4407 NaN 35.0 24.0 5.75 3.75 19.00 0.0 NaN NaN NaN NaN NaN
1 35_36 35.0 36.0 6.00 4.00 20.00 0.0 NaN supplemental NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ...
4403 65530_0 65530.0 0.0 10.00 6.00 24.00 0.0 NaN supplemental 669411.457384 2.917391e+06 24047.4
5291 NaN 65530.0 6.0 9.50 5.50 21.75 1.0 NaN NaN 669411.457384 2.917391e+06 24047.4
5292 NaN 65530.0 12.0 9.00 5.00 19.50 2.0 NaN NaN 669411.457384 2.917391e+06 24047.4
5293 NaN 65530.0 24.0 8.50 4.50 17.25 3.0 NaN NaN 669411.457384 2.917391e+06 24047.4
4404 65530_36 65530.0 36.0 8.00 4.00 15.00 4.0 On supplemental 669411.457384 2.917391e+06 24047.4

5294 rows × 12 columns

In [96]:
train = interpolated_df[interpolated_df['visit_month'].isin([0, 6, 12, 24])]
train.index = pd.DatetimeIndex(train['visit_month']).to_period('M')
train
Out[96]:
visit_id patient_id visit_month updrs_1 updrs_2 updrs_3 updrs_4 upd23b_clinical_state_on_medication source PeptideAbundance_AVG NPX_AVG NPX_P19827
visit_month
1970-01 35_0 35.0 0.0 5.00 3.00 16.00 0.0 NaN supplemental NaN NaN NaN
1970-01 NaN 35.0 6.0 5.25 3.25 17.00 0.0 NaN NaN NaN NaN NaN
1970-01 NaN 35.0 12.0 5.50 3.50 18.00 0.0 NaN NaN NaN NaN NaN
1970-01 NaN 35.0 24.0 5.75 3.75 19.00 0.0 NaN NaN NaN NaN NaN
1970-01 55_0 55.0 0.0 10.00 6.00 15.00 0.0 NaN standard 748153.907014 3.180508e+06 36232.9
... ... ... ... ... ... ... ... ... ... ... ... ...
1970-01 NaN 65303.0 24.0 3.00 1.25 24.50 0.0 NaN NaN 669411.457384 2.917391e+06 24047.4
1970-01 65530_0 65530.0 0.0 10.00 6.00 24.00 0.0 NaN supplemental 669411.457384 2.917391e+06 24047.4
1970-01 NaN 65530.0 6.0 9.50 5.50 21.75 1.0 NaN NaN 669411.457384 2.917391e+06 24047.4
1970-01 NaN 65530.0 12.0 9.00 5.00 19.50 2.0 NaN NaN 669411.457384 2.917391e+06 24047.4
1970-01 NaN 65530.0 24.0 8.50 4.50 17.25 3.0 NaN NaN 669411.457384 2.917391e+06 24047.4

2956 rows × 12 columns

In [97]:
# Seasonal or Non-seasonal?        -> Seasonal (period=4)
# Trend or Non-trend?              -> Non-trend
# Non-stationary or stationary?    -> Stationary

for updrs in ["updrs_1", "updrs_2", "updrs_3", "updrs_4"]:
    updrs_values = train[[updrs]].reset_index()[updrs][:200]
    res = sm.tsa.seasonal_decompose(updrs_values, period = 4, model="additive")
    fig = res.plot()
    fig.tight_layout()
    plt.show()

Dựa trên cách tiền xử lý mà nhóm đã chọn (cộng với nhiều thử nghiệm với các trường hợp khác nhau), nhóm sẽ tiến hành xây dựng mô hình ARIMA cho các chuỗi dữ liệu có đặc tính:

  • Seasonal (period=4): Tính chu kỳ (chu kỳ = 4)

  • Non-trend: Dữ liệu không có tính xu hướng

  • Stationary: Dữ liệu có tính ổn định

3.1.2. Xây dựng mô hình

Mô hình $SARIMA$ (Seasonal AutoRegressive Integrated Moving Average) cấu hình từ 7 tham số:

S A R I M A ( p , d , q ) ( P , D , Q ) [ m ]

Với:

3 tham số $p,d,q$ tương tự như ở mô hình $ARIMA$:

  • $p$: Bậc của mô hình $AR$ - autoregressive (số lượng độ trễ thời gian - "lags" của dữ liệu dùng để dự đoán).

  • $d$: Mức độ chênh lệch - regular differencing (số lần mà dữ liệu trừ đi các giá trị trong quá khứ) cần thiết để khiến cho dữ liệu tĩnh (stationary, not trendy). Nếu dữ liệu đầu vào đã tĩnh (stationary) sẵn thì $d=0$.

  • $q$: Bậc của mô hình $MA$ - moving-average (số lượng lỗi - lagged forecast error được đưa vào mô hình).

Mô hình $SARIMA$ bổ sung thêm 3 tham số bắt buộc và 1 tham số tuỳ chọn, có liên quan đến tính chu kỳ (seasonal) như sau:

  • $P$: Tương tự $p$, nhưng mà là bậc của mô hình $SAR$ - seasonal autoregressive.

  • $D$: Tương tự $d$, nhưng là mức độ chênh lệch theo chu kỳ - seasonal differencing.

  • $Q$: Tương tự $q$, nhưng mà là bậc của mô hình $SMA$ - seasonal moving-average.

  • $m$: Số bước/giai đoạn của một chu kỳ thời gian (Ví dụ: Chu kỳ thời gian là 1 năm, chúng ta có dữ liệu của mỗi tháng, vậy $m=12$ vì 1 năm có 12 tháng)

In [98]:
%%time

target = ["updrs_1", "updrs_2", "updrs_3", "updrs_4"]

# Dictionary to store the models for each UPDRS score
model = {u: None for u in target}

for updrs in target:
    temp = train #.dropna(subset=[updrs])
    y = temp[updrs]

    # Fit the ARIMA model
    trained = ARIMA(y, order=(4, 0, 1), seasonal_order=(0, 0, 0, 4)).fit()
    model[updrs] = trained

model
CPU times: user 28.1 s, sys: 22.2 s, total: 50.3 s
Wall time: 13.3 s
Out[98]:
{'updrs_1': <statsmodels.tsa.arima.model.ARIMAResultsWrapper at 0x7cd488fed5d0>,
 'updrs_2': <statsmodels.tsa.arima.model.ARIMAResultsWrapper at 0x7cd4892afd90>,
 'updrs_3': <statsmodels.tsa.arima.model.ARIMAResultsWrapper at 0x7cd489269dd0>,
 'updrs_4': <statsmodels.tsa.arima.model.ARIMAResultsWrapper at 0x7cd48923f410>}
In [99]:
def get_predictions(my_test, model):
    my_test = my_test.fillna(0)
    for u in target:
        my_test['result_' + str(u)] = 0
        # Predict    
        X = my_test["visit_month"]
        if u == 'updrs_4':
            my_test['result_' + str(u)] = 0
        else:
            n = len(model[u].model.endog)
            start = n
            end = n + len(X) - 1
            my_test['result_' + str(u)] = np.ceil(model[u].predict(start=start, end=end).values)
    result = pd.DataFrame()
    for m in [0, 6, 12, 24]:
        for u in [1, 2, 3, 4]:
            temp = my_test[["visit_id", "result_updrs_" + str(u)]]
            temp["prediction_id"] = temp["visit_id"] + "_updrs_" + str(u) + "_plus_" + str(m) + "_months"
            temp["rating"] = temp["result_updrs_" + str(u)]
            temp = temp [['prediction_id', 'rating']]
            result = result.append(temp)
    result = result.drop_duplicates(subset=['prediction_id'])
    result['rating'] = result['rating'] + 0.0 # -0.0 -> 0.0
    result = result.reset_index().drop(columns=['index'])
    return result
In [100]:
%%time

env = amp_pd_peptide.make_env()
iter_test = env.iter_test()
for (test, test_peptides, test_proteins, sample_submission) in iter_test:
    my_test = get_train_data(test_peptides, test_proteins, test, join_type='left')
    result = get_predictions(my_test,model)
    env.predict(result)
This version of the API is not optimized and should not be used to estimate the runtime of your code on the hidden test set.
CPU times: user 230 ms, sys: 4.2 ms, total: 234 ms
Wall time: 263 ms
In [101]:
result
Out[101]:
prediction_id rating
0 3342_6_updrs_1_plus_0_months 7.0
1 50423_6_updrs_1_plus_0_months 6.0
2 3342_6_updrs_2_plus_0_months 5.0
3 50423_6_updrs_2_plus_0_months 6.0
4 3342_6_updrs_3_plus_0_months 18.0
5 50423_6_updrs_3_plus_0_months 21.0
6 3342_6_updrs_4_plus_0_months 0.0
7 50423_6_updrs_4_plus_0_months 0.0
8 3342_6_updrs_1_plus_6_months 7.0
9 50423_6_updrs_1_plus_6_months 6.0
10 3342_6_updrs_2_plus_6_months 5.0
11 50423_6_updrs_2_plus_6_months 6.0
12 3342_6_updrs_3_plus_6_months 18.0
13 50423_6_updrs_3_plus_6_months 21.0
14 3342_6_updrs_4_plus_6_months 0.0
15 50423_6_updrs_4_plus_6_months 0.0
16 3342_6_updrs_1_plus_12_months 7.0
17 50423_6_updrs_1_plus_12_months 6.0
18 3342_6_updrs_2_plus_12_months 5.0
19 50423_6_updrs_2_plus_12_months 6.0
20 3342_6_updrs_3_plus_12_months 18.0
21 50423_6_updrs_3_plus_12_months 21.0
22 3342_6_updrs_4_plus_12_months 0.0
23 50423_6_updrs_4_plus_12_months 0.0
24 3342_6_updrs_1_plus_24_months 7.0
25 50423_6_updrs_1_plus_24_months 6.0
26 3342_6_updrs_2_plus_24_months 5.0
27 50423_6_updrs_2_plus_24_months 6.0
28 3342_6_updrs_3_plus_24_months 18.0
29 50423_6_updrs_3_plus_24_months 21.0
30 3342_6_updrs_4_plus_24_months 0.0
31 50423_6_updrs_4_plus_24_months 0.0

3.1.3. Kết quả và nhận xét

Nhận xét 1

Giải thích cho việc:

  • Loại bỏ những bệnh nhân có thời gian khám ít hơn 24 tháng -> Kết quả tốt hơn
  • Loại bỏ những dòng dữ liệu có visit_month > 24 -> Kết quả tốt hơn

Vì: Khi tiền xử lý dữ liệu như trên, dữ liệu cuối cùng sẽ là 1 chuỗi time-series có cấu trúc visit_month (thời gian) của 1 bệnh nhân lần lượt là các giá trị 0, 6, 12, 24.

Khi có cấu trúc như thế này, vô hình chung chuỗi time-series sẽ có chu kỳ bằng 4.

Mà mô hình SARIMA lại hoạt động tốt nếu chuỗi dữ liệu có tính chu kỳ, từ đó mà kết quả cho ra tốt hơn.

Nhận xét 2

Giải thích cho việc:

  • Sử dụng kết hợp 2 bộ dữ liệu clinical và supplement -> Kết quả tốt hơn

Vì:

  • Với hướng tiếp cận không sử dụng dữ liệu proteins và peptides, 2 bộ dữ liệu clinicalsupplemental (vốn đã có cấu trúc giống nhau) sẽ mang lại giá trị tương đương nhau (so sánh với trường hợp sử dụng dữ liệu đo nồng độ proteins thì rõ ràng bộ dữ liệu supplemental mang giá trị thấp hơn vì bệnh nhân ở tập này không được đo nồng độ proteins).
  • Vì giá trị của 2 bộ dữ liệu tương đương nhau nên khi kết hợp, kết quả cho ra tốt hơn (vì đơn giản là mô hình có nhiều dữ liệu hơn để học)

Kết luận

  • Bệnh Parkinson có xu hướng tiến triển và nặng dần đều theo thời gian. Khi sử dụng Linear Interpolation để điền giá trị điểm UPDRS bị thiếu, mô hình cho ra kết quả tốt hơn giúp củng cố hơn kết luận này.
  • Bệnh nhân có sử dụng thuốc trong quá trình điều trị, mặc dù vẫn nặng lên theo thời gian, nhưng tiến triển sẽ chậm hơn so với bệnh nhân không sử dụng thuốc trong điều trị.
  • Những proteins có ảnh hưởng lớn đến tiến triển bệnh Parkinson:
    • P19827 có sự ảnh hưởng lớn đến cả 4 thang điểm UPDRS
    • Các proteins P01594, P02748, P01009, P01861, P02679, P04433 có sự ảnh hưởng lớn đến 3 trên 4 thang điểm.