2022 Shuwei Cup 国際競技会の質問 C の脳の構造的特徴と認知行動の特徴を使用してアルツハイマー病を診断する方法
コンペティションのテーマ: 脳の構造的特徴と認知行動的特徴を使用してアルツハイマー病を診断する方法
1 はじめに
この文書には主に、コードと演算結果の説明を含む、2022 Shuwei Cup C 問題の解決プロセスが含まれています。下の図は、この記事の中国語要約です。
2 質問の再説明
- 質問 1: 添付データの特徴指標を前処理し、データの特徴とアルツハイマー病の診断との相関を調べます。
- 質問 2: 付属の脳構造の特徴と認知行動の特徴を使用して、アルツハイマー病のインテリジェントな診断を設計してください。
- 質問 3: まず、CN、MCI、および AD を 3 つの主要なカテゴリに分類します。次に、MCI に含まれる 3 つのサブクラス (SMC、EMCI、および LMCI) について、クラスターを 3 つのサブクラスに絞り込み続けました。
- 質問 4: 添付ファイル内の同じサンプルには、異なる時点で収集された特徴が含まれています。時間の経過に伴うさまざまな種類の疾患の進展を明らかにするために、それらと時点の間の関係を分析してください。
- 質問 5: CN、SMC、EMCI、LMCI、AD の早期介入と診断基準について説明する関連文献を参照してください。
注: このペーパーでは、最初に 2 番目の質問を解決し、次に 2 番目の質問の結果を使用して最初の質問を分析します。
3 問題 1 の解決
3.1 データの前処理
df = pd.read_csv('./data/ADNIMERGE_New.csv')
df
指定されたデータ セットには 116 列のデータが含まれており、合計 16222 のサンプルがあります。この問題では、DX_bl 列をラベル列として使用し、残りの 155 列を特徴列として使用します。ラベル列には、CN、SMC、LMCI、EMCI、AD の合計 5 つのカテゴリが含まれています。以下の図に示すように、CN サンプルが 4850 個、SMC サンプルが 1416 個、LMCI サンプルが 5236 個、EMCI サンプルが 2968 個、AD サンプルが 1738 個あります。115 個の特徴のうち、93 個の数値特徴と 22 個のカテゴリ特徴があります。
missingno は欠損値視覚化 Python ツール ライブラリであり、以下の図に示すように、トレーニング セットとテスト セットの欠損値を表示するために使用します。
# 查看数据缺失值分布概况
import missingno as msn
msn.matrix(df)
上の図では、白い部分が欠損値を表しており、空白が多いほど欠損が深刻になります。上の図からわかるように、一部の特徴には重大な欠損値があり、欠損値がサンプル サイズの 30% を超える特徴は削除されました。削除後のデータの欠損値分布は次のようになります。
# 删除缺失值大于百分之30的数据
df_dropna = df.dropna(axis=1, thresh=11355)
df_dropna
次に、DX_bl 列と PTMARRY 列が空のサンプルを削除すると、残ったデータの形式は 16207 行 × 52 列になります。さらに、特徴内容のさらなる分析により、分類タスクにとって重要ではない次の特徴が人為的に削除されます:「RID」、「COLPROT」、「ORIGPROT」、「SITE」、「VISCODE」、「EXAMDATE」、「DX」 '、'EXAMDATE_bl'、'FLDSTRENG_bl'、'FSVERSION_bl'、'IMAGEUID_bl'、'update_stamp'。この時点で、残ったデータの形状は 16207 行 × 40 列になります。
# 删除DX_bl列为空的样本
df_dropna = df_dropna.dropna(subset=['DX_bl'])
# 删除PTMARRY列为空的样本
df_dropna = df_dropna.dropna(subset=['PTMARRY'])
# 手动删除部分特征
df_new = df_dropna.drop(columns=['RID','COLPROT','ORIGPROT','SITE','VISCODE',
'EXAMDATE','DX','EXAMDATE_bl','FLDSTRENG_bl','FSVERSION_bl','IMAGEUID_bl','update_stamp'], axis=1)
df_new
まず、特徴「PTID」がカウントされ、現在のデータセット内の合計 2410 人の患者のデータが計算されます。次に、フィーチャ「PTID」に対して LabelEncoder エンコードを実行し、残りのカテゴリフィーチャに対して次のマッピングを実行します。
マッピング方法 |
---|
'CN':'0'、'SMC':'1'、'EMCI':'2'、'LMCI':'3'、'AD':'4' |
「女性」:「0」、「男性」:「1」 |
'ヒスプ/ラテン系ではない':'0'、'ヒスプ/ラテン系':'1'、'不明':'2 |
「インド人/アラスカ人です」:「0」、「アジア人」:「1」、「黒人」:「2」、「ハワイ人/その他のPI」:「3」、「複数人」:「4」、「不明」 ':'5'、'白':'6'、 |
「離婚」:「0」、「既婚」:「1」、「未婚」:「2」、「不明」:「3」、「未亡人」:「4」 |
# 统计PTID的个数
df_new['PTID'].value_counts()
名前: PTID、長さ: 2410、dtype: int64
# 对PTID进行labelEncode操作
from sklearn import preprocessing
lb = preprocessing.LabelEncoder()
lb =lb.fit(df_new['PTID']) #训练LabelEncoder
df_new['PTID']=lb.transform(df_new['PTID'])
df_new
# 对类别特征进行编码映射
dict = {
'CN':'0', 'SMC':'1', 'EMCI':'2', 'LMCI':'3', 'AD':'4',
'Female':'0', 'Male':'1',
'Not Hisp/Latino':'0', 'Hisp/Latino':'1', 'Unknown':'2',
'Am Indian/Alaskan':'0', 'Asian':'1', 'Black':'2', 'Hawaiian/Other PI':'3', 'More than one':'4', 'Unknown':'5', 'White':'6',
'Divorced':'0', 'Married':'1', 'Never married':'2', 'Unknown':'3', 'Widowed':'4'
}
df_new['DX_bl'] = df_new.DX_bl.map(dict)
df_new['PTGENDER'] = df_new.PTGENDER.map(dict)
df_new['PTETHCAT'] = df_new.PTETHCAT.map(dict)
df_new['PTRACCAT'] = df_new.PTMARRY.map(dict)
df_new['PTMARRY'] = df_new.PTMARRY.map(dict)
データ内の欠損値の分布をもう一度見てください
msn.matrix(df_new)
各特徴の平均は、欠落しているデータを埋めるために使用されます。
# 缺失值填充
from sklearn.impute import SimpleImputer
df_new= SimpleImputer().fit_transform(df_new)
df_new = pd.DataFrame(df_new)
df_new
下図に示すように、この時点での各カテゴリの疾患数は、LMCI: 5236、CN: 4850、EMCI: 2967、AD: 1738、SMC: 1416 です。、データを保存し、df_new_PTID.csv として記録します。
df_new.to_csv('./data/df_new.csv', index=False)
3.2 モデルのトレーニング
df = pd.read_csv('./data/df_new_PTID.csv')
df
データセットには同じ患者の複数のサンプルが含まれていることに注意してください。データの重複を避けるために、下図に示すように、データは次のルールに従って分割されます。
# PTID小于1687的70%的病人划分到训练集
train = df[df['PTID'] < 1687]
test = df[df['PTID'] >= 1687]
トレーニング セット: 1687 未満の患者 ID。11425サンプル。
テスト セット: 患者 ID が 1687 以上の 4782 サンプル。
以下のコードは、トレーニング セットとテスト セットのデータとラベルを分割したものです。また、この記事の実験では、使用されるモデルはデフォルトのパラメーターを使用します。
train_data = train.drop(['DX_bl','PTID'],axis=1)
train_target = train['DX_bl']
test_data = test.drop(['DX_bl','PTID'],axis=1)
test_target = test['DX_bl']
3.2.1 ロジスティック回帰
# 逻辑回归
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression()
clf.fit(train_data, train_target)
test_pred = clf.predict(test_data)
measure_result = classification_report(test_target, test_pred)
print('measure_result = \n', measure_result)
3.2.2 SVM
# 线性可分支持向量机
from sklearn.svm import LinearSVC
clf = LinearSVC(random_state=0, tol=1e-5)
clf.fit(train_data, train_target)
test_pred = clf.predict(test_data)
measure_result = classification_report(test_target, test_pred)
print('measure_result = \n', measure_result)
3.2.3 KNN
# KNN分类
from sklearn.neighbors import KNeighborsClassifier
# clf = KNeighborsClassifier(n_neighbors=10)
clf = KNeighborsClassifier()
clf.fit(train_data, train_target)
test_pred = clf.predict(test_data)
measure_result = classification_report(test_target, test_pred)
print('measure_result = \n', measure_result)
3.2.4 デシジョンツリー
# 决策树分类
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
clf.fit(train_data, train_target)
test_pred = clf.predict(test_data)
measure_result = classification_report(test_target, test_pred)
print('measure_result = \n', measure_result)
3.2.5 XGB
# xgb
params = {
'objective': 'multi:softmax', 'num_class' : 5
}
dtrain = xgb.DMatrix(train_data, train_target)
num_rounds = 500
model_xgb = xgb.train(params, dtrain, num_rounds)
dtest = xgb.DMatrix(test_data)
test_pred = model_xgb.predict(dtest)
measure_result = classification_report(test_target, test_pred)
print('measure_result = \n', measure_result)
モデルの特徴の重要性を確認する
xgb_feature_importance = pd.DataFrame(list(model_xgb.get_fscore().items()))
xgb_feature_importance.columns = ['Feature', 'Feature importrance']
#设置绘图风格
plt.style.use('ggplot')
#处理中文乱码
#对读入的数据做降序排序
xgb_feature_importance.sort_values(by='Feature importrance', inplace=True, ascending=False)
#取前15行的数据
xgb_feature_importance = xgb_feature_importance.iloc[:15, :] # 取前15行数据
#对读入的数据做升序排序
xgb_feature_importance.sort_values(by='Feature importrance', inplace=True, ascending=True)
#绘制条形图
plt.barh(y = range(xgb_feature_importance.shape[0]), #指定条形图y轴的刻度值
width = xgb_feature_importance['Feature importrance'], #指定条形图x轴的数值
tick_label = xgb_feature_importance['Feature'], #指定条形图y轴的刻度标签
color = 'lightblue', #指定条形图的填充色
)
#添加x轴的标签
plt.xlabel('Feature importrance')
#添加条形图的标题
plt.title('XGB Feature importrance')
#为每个条形图添加数值标签
for y,x in enumerate(xgb_feature_importance['Feature importrance']):
plt.text(x+0.1,y,"%s"%round(x,1),va='center') #round(y,1)是将y值四舍五入到一个小数位
#显示图形
plt.show()
3.2.6 LGBT
# lgb
from numpy import true_divide
params = {
'objective': 'multiclass', 'num_class' : 5
}
dtrain = lgb.Dataset(train_data, label=train_target)
cate_features_name = ['PTGENDER','PTETHCAT','PTRACCAT','PTMARRY']
model_lgb = lgb.train(params, dtrain, categorical_feature= cate_features_name)
test_pred = model_lgb.predict(test_data)
preds = test_pred
test_pred = []
for x in preds:
test_pred.append(np.argmax(x))
measure_result = classification_report(test_target, test_pred)
print('measure_result = \n', measure_result)
上と同じように、LGB モデルの特徴量の重要度を見てください。
feature_name = pd.DataFrame(model_lgb.feature_name())
feature_importance = pd.DataFrame(model_lgb.feature_importance())
lgb_model_importance = pd.concat([feature_name, feature_importance], axis=1)
lgb_model_importance.columns = ['Feature', 'Feature importrance']
3.2.7 CB
# 因为catboost的类别变量得是整形或者字符型
# 报错信息:cat_features must be integer or string, real number values and NaN values should be converted to str
train_data = train.drop(['DX_bl','PTID'],axis=1)
train_target = train['DX_bl']
test_data = test.drop(['DX_bl','PTID'],axis=1)
test_target = test['DX_bl']
train_data[['PTGENDER','PTETHCAT','PTRACCAT','PTMARRY']] = train_data[['PTGENDER','PTETHCAT','PTRACCAT','PTMARRY']].astype(str)
test_data[['PTGENDER','PTETHCAT','PTRACCAT','PTMARRY']] = test_data[['PTGENDER','PTETHCAT','PTRACCAT','PTMARRY']].astype(str)
# cb
cat_features_index = [1,3,4,5]
model_cb = cb.CatBoostClassifier()
model_cb.fit(train_data, train_target, cat_features=cat_features_index)
test_pred = model_cb.predict(test_data)
measure_result = classification_report(test_target, test_pred)
print('measure_result = \n', measure_result)
同様に、CB モデルの特徴重要度を確認します。
fea_ = model_cb.feature_importances_
fea_name = model_cb.feature_names_
cb_value = pd.DataFrame(fea_)
cb_name = pd.DataFrame(fea_name)
cb_feature_importrance = pd.concat([cb_name,cb_value], axis=1)
cb_feature_importrance.columns = ['Feature', 'Feature importrance']
3.3 各種モデルの比較
モデルの評価指標については、以下の記事を参照してください。
【数学モデリング】分類問題によく使われるいくつかの指標 (1) - 正解率、再現率、F1 値
7 つのモデルの精度、マクロ平均、加重平均を使用します。 -スコアは、次の図に示すように視覚化され、分析されます。
統合学習に基づくモデルが満足のいく結果を達成し、精度が 83% 以上に達していることがわかります。しかし、カテゴリ 1 の分類効果はまだ理想的ではありません。このモデルは、このカテゴリの SMC の特性を無視している可能性があります。実際には、このような症状を発見することは困難であり、アルツハイマー病の早期予防には依然として大きな課題があることがわかります。
4 2番目の問題を解決する
この質問ではいくつかの中間ステップが省略されていることに注意してください
4.1 データ処理
上記の XGB、LGB、および CB の 15 の最も重要な機能と「PTID」の結合を新しいデータ セットとして選択し、
新しいデータ セットを df_cluster_18F.csv として保存します。
# 选中其中的18个特征+1个label
df_cluster = df[['AGE','mPACCtrailsB_bl','ICV_bl','LDELTOTAL_BL','WholeBrain_bl',
'CDRSB_bl','MidTemp_bl','TRABSCOR_bl','Entorhinal_bl','mPACCdigit_bl','Fusiform_bl','Ventricles_bl','Hippocampus_bl',
'PTEDUCAT','RAVLT_immediate_bl','FAQ_bl','MMSE_bl','ADAS11_bl','DX_bl']]
df_cluster.to_csv('./data/df_cluster_18F.csv', index=False)
データセット内の 5 カテゴリのラベルを 3 カテゴリとして保存します。
つまり、CN-CN、SMC、EMCI、LMCI-MCI、AD-AD
# 将5类标签转换为3类标签
dict = {
0:0, 1:1, 2:1, 3:1, 4:2
}
df_cluster['DX_bl'] = df_cluster.DX_bl.map(dict)
df_cluster['DX_bl'].value_counts()
このデータセットを次の名前で保存します: df_3cluster_18F.csv
df_cluster.to_csv('./data/df_3cluster_18F.csv', index=False)
4.2 データ探索
上記の方法でdf_3cluster_18F.csvデータを読み込み、以下のようにデータを分割します。
# PTID小于1687的70%的病人划分到训练集
train = df[df['PTID'] < 1687]
test = df[df['PTID'] >= 1687]
train_data = train.drop(['DX_bl','PTID'],axis=1)
train_target = train['DX_bl']
test_data = test.drop(['DX_bl','PTID'],axis=1)
test_target = test['DX_bl']
これらの変数の箱ひげ図を描いて外れ値を表示します。
ヒストグラムと QQ プロットを描いて、これらの特徴が正規分布に準拠しているかどうかをテストします。これは、後続の相関分析でどのパラメータが選択されるかに関係します。
トレーニング セットとテスト セットの分布を確認し、分布が矛盾している場合は、特徴量を削除する必要があります。
4.3 相関分析
相関ヒートマップを描く
data_train1 = train.drop(['PTID'],axis=1)
train_corr = data_train1.corr()
train_corr
# 画出相关性热力图
ax = plt.subplots(figsize=(20, 16))#调整画布大小
ax = sns.heatmap(train_corr, vmax=.8, square=True, annot=True)#画热力图 annot=True 显示系数
最も関連性の高い 10 個の機能情報をプロットする
#寻找K个最相关的特征信息
k = 10 # number of variables for heatmap
cols = train_corr.nlargest(k, 'DX_bl')['DX_bl'].index
cm = np.corrcoef(data_train1[cols].values.T)
hm = plt.subplots(figsize=(10, 10))#调整画布大小
#hm = sns.heatmap(cm, cbar=True, annot=True, square=True)
#g = sns.heatmap(train_data[cols].corr(),annot=True,square=True,cmap="RdYlGn")
hm = sns.heatmap(data_train1[cols].corr(),annot=True,square=True)
plt.show()
同様に、相関が 0.5 より大きい特徴情報を描画します
。 上記の情報に従って、アルツハイマー病に影響を与える関連要因を分析できます。
5 3番目の問題を解く
この問題はクラスタリングの問題です。最初に df_3cluster_18F.csv データセットを読み取ります。
5.1 K値の選択
エルボの図を描く
data = np.array(train_data)
Scores = [] # 存放轮廓系数
SSE = [] # 存放每次结果的误差平方和
for k in range(2, 9):
estimator = KMeans(n_clusters=k) # 构造聚类器
estimator.fit(data)
Scores.append(silhouette_score(
np.array(df), estimator.labels_, metric='euclidean'))
SSE.append(estimator.inertia_) # estimator.inertia_获取聚类准则的总和
X = range(2, 9)
plt.figure(figsize=(15,5))
plt.subplot(121)
plt.xlabel('k', fontsize=15)
plt.ylabel('SSE', fontsize=15)
plt.plot(X, SSE, 'o-')
plt.subplot(122)
plt.xlabel('k值', fontsize=15)
plt.ylabel('轮廓系数', fontsize=15)
plt.plot(X, Scores, 'o-')
plt.savefig('./img/手肘法.png',dpi=300)
plt.show()
5.2 ランダムなシードの選択
また、このトピックを最初に 3 つのカテゴリにグループ化する理由がわかりませんでした。ここでは、k 値を 3 に選択し、ランダム シード値の範囲を 2000 ~ 2025 に設定しています。次の図を描きます。
Scores = [] # 存放轮廓系数
for i in range(2000,2025):
estimator = KMeans(n_clusters=3, random_state=i) # 构造聚类器
estimator.fit(data)
Scores.append(silhouette_score(np.array(df), estimator.labels_, metric='euclidean'))
X = range(2000, 2025)
plt.figure(figsize=(7,5))
plt.xlabel('random_state', fontsize=15)
plt.ylabel('silhouette coefficient', fontsize=15)
plt.plot(X, Scores, 'o-')
plt.xlim(2000, 2025)
plt.savefig('./img/随机种子的确定.png',dpi=300)
plt.show()
最終的なランダム シードの選択は 2009 です (最高点を選択します)
5.3 Kmeans クラスタリング
次に、k=3、random_state=2009 を使用し、kmeans クラスタリングを実行します (他のクラスタリング方法も試すことができます)。
# Kmens 聚类
from sklearn.cluster import KMeans
# 实例化K-Means算法模型,先使用3个簇尝试聚类
cluster = KMeans(n_clusters=3, random_state=2009)
# 使用数据集train_data进行训练
cluster = cluster.fit(train_data)
# 调用属性labels_,查看聚类结果
cluster.labels_
labels_pred = cluster.labels_
import collections
data_count2=collections.Counter(labels_pred)
data_count2
このときのARIインデックスは0.012940512409904181と計算され、
PCAを用いてデータの次元を削減し、同様にクラスタリングを行い、可視化した結果は以下の通りです
。 2 番目の質問は上記と同じで、結果が直接表示されます。
6 質問4と質問5の解答
6.1 質問 4
この問題を分析するために、まず時間の経過に伴う各特徴の変化を描画し、次にその傾向を自分で画像上に記述することができる視覚的な手法を使用しました。
分析のために選択された 4 種類の患者のうち、SMC の PTID は 135_S_5113、EMCI の PTID は 007_S_2394、LMCI の PTID は 021_S_0178、AD の PTID は 027_S_4938 でした。ここでは、次の図に示すように、CDRSB、ADAS11、ADAS13、ADASQ4、MMSE、RAVLT_immediate、RAVLT_learning、RAVLT_forgetting、および RAVLT_perc_forgetting の 9 つの特徴を選択して分析し、4 つの調査サンプルの特徴の変化を視覚化します。
質問5については、情報を確認しましょう。
まとめ
問題を解決するためのアイデアを提供しているだけで、無理な箇所もあり、具体的な解決策は自分でやるしかありません。
付録
整理する〜