kaggle新赛:ICR EDA & Baseline
1. 项目简介
ICR - Identifying Age-Related Conditions是Kaggle中的一个Featured竞赛,2023.8.10截止。
- 赛题链接:https://www.kaggle.com/competitions/icr-identify-age-related-conditions/overview
- 背景:通过对个人病史的综合评估、体检和特定测试,评估其整体健康状况并确定任何与年龄相关的疾病,从而尽早发现潜在问题,以便及时干预和管理与年龄相关的健康问题。以往已经使用XGBoost和随机森林,想要探索更多可能。
- 目的:预测一个人是否患有三种疾病中的任何一种或多种(1),或不患有三种疾病中的任何一种(0)。——二分类问题
- 提交:0/1类的概率
- 评价指标:balanced logarithmic loss
- Score:0.14
- 本文内容:EDA & Baseline
2. 读取数据
- train.csv:训练集
- 617×58,有少量缺失值
- AB~GL:56个匿名健康特征;除了EJ为类别外,其余均为浮点数
- Class:0/1
- greeks.csv:补充元数据,仅适用于训练集。
- Alpha:A-0,B、D、G-1
- Beta, Gamma, Delta:三个实验特征
- Epsilon:数据收集日期,有Unknown
- test.csv:测试集。目标是预测该集合中的受试者属于两个类别中的每个类别的概率。5条
train = pd.read_csv('/kaggle/input/icr-identify-age-related-conditions/train.csv')
greeks = pd.read_csv('/kaggle/input/icr-identify-age-related-conditions/greeks.csv')
test = pd.read_csv('/kaggle/input/icr-identify-age-related-conditions/test.csv')
train的缺失值:
missing_count_by_column = (train.isnull().sum())
print(missing_count_by_column[missing_count_by_column > 0])
将EJ转换为onehot:
# EJ onehot
train['EJ'] = train['EJ'].replace({'A': 0, 'B': 1})
test['EJ'] = test['EJ'].replace({'A': 0, 'B': 1})
合并train和greeks:
# 合并train和greeks
train_all = pd.merge(train, greeks, on='Id', how='left')
train_all.set_index('Id', inplace=True)
3. EDA
- 各种特征与Class的分布密度图(KDE):
fig, axes = plt.subplots(nrows=14, ncols=4, figsize=(16, 50))
for i, col_name in enumerate(train.columns.tolist()[1:-1]):
ax = axes[i//4, i%4]
sns.kdeplot(data=train, x=col_name, hue='Class', shade=True, ax=ax, alpha=0.5, linewidth=2)
ax.set_xlabel(col_name, fontsize=14)
ax.set_ylabel('Class', fontsize=14)
ax.legend([1, 0], title='Class', fontsize=12)
plt.tight_layout()
分布差异较大的特征有:
BQ、BZ、CC、CF、CR、DA、FI、GI等
- 特征之间相关性的热力图:
corr_mat = train.iloc[:, 1:-1].corr()
# 为上三角矩阵生成掩码
mask = np.zeros_like(corr_mat, dtype=np.bool)
mask[np.triu_indices_from(mask)] = True
# 热力图
fig, ax = plt.subplots(figsize=(20, 20))
sns.set_style("ticks")
ax=sns.heatmap(corr_mat, square=True, mask=mask, annot=True,
annot_kws={'size':5}, cmap=plt.cm.RdBu_r)
ax.set_xticklabels(labels=train.columns.tolist()[1:-1], fontsize=14)
ax.set_yticklabels(labels=train.columns.tolist()[1:-1], fontsize=14);
相关性高的:GL-EJ(-0.98),EH-FD(0.97),CL-DV(0.95),BC-BZ(0.91),DU-EH(0.85),AR-DV(0.82),DU-FD(0.81),CS-EP(0.79)
- 特征的层次聚类树:
fig, ax = plt.subplots(figsize=(15, 10))
corr_all = train.iloc[:, 1:-1].corr()
# squareform:用于距离方阵与距离向量相互转换的函数,要求方阵对角线为0
# 所以需要做1 - np.abs的转换
corr_all_convert = 1 - np.abs(corr_all)
Z = hierarchy.linkage(squareform(corr_all_convert), 'complete')
dn = hierarchy.dendrogram(Z, labels=train.columns.tolist()[1:-1], ax=ax,
above_threshold_color='#ff0000', orientation='right')
热力图与层次聚类树相互印证。
- greeks数据集中的各类数量的条形图:
fig, ax = plt.subplots(2,2,figsize=(12, 10), constrained_layout=True)
sns.countplot(x='Alpha', hue='Alpha', data=data, ax=ax[0,0])
sns.countplot(x='Beta', hue='Beta', data=data, ax=ax[0,1])
sns.countplot(x='Gamma', hue='Gamma', data=data, ax=ax[1,0])
sns.countplot(x='Delta', hue='Delta', data=data, ax=ax[1,1])
可见,各类别数量差距较大
- greeks各特征与train中特征的关系:
① Alpha:
fig, axes = plt.subplots(nrows=8, ncols=7, figsize=(20, 20))
for i, col_name in enumerate(train.columns.tolist()[1:-1]):
ax = axes[i//7, i%7]
sns.boxplot(data=train_all, x='Alpha', y=col_name, ax=ax)
ax.set_xlabel(col_name, fontsize=14)
fig.suptitle('Alpha',fontsize=16, y=1.00)
plt.tight_layout()
对于Alpha:
- 一些特征的离群点较多,如AB、AM、BP等
- 大多数特征的数据分布比较集中且区分度不高,如AY、BR等
- 一些特征的数据分布较为离散且区分度高,如BQ、CW等
② Beta:
fig, axes = plt.subplots(nrows=8, ncols=7, figsize=(20, 20))
for i, col_name in enumerate(train.columns.tolist()[1:-1]):
ax = axes[i//7, i%7]
sns.boxplot(data=train_all, x='Beta', y=col_name, ax=ax)
ax.set_xlabel(col_name, fontsize=14)
fig.suptitle('Beta',fontsize=16, y=1.00)
plt.tight_layout()
对于Beta:
- 一些特征的离群点较多,如FE、FL、GF等
- 大多数特征的数据分布比较集中且区分度不高,如AY、BR、FR、FS、EJ、GL等
- 一些特征的数据分布较为离散且区分度高,如AF、BQ等
③ Delta:
对于Delta:
- 一些特征的离群点较多,如AB、DE等
- 大多数特征的数据分布比较集中且区分度不高,如AY、BR、FR等
- 一些特征的数据分布较为离散且区分度高,如CW等
④ Gamma:
对于Gamma:
- 一些特征的离群点较多,如AB、AM、BP等
- 大多数特征的数据分布比较集中且区分度不高,如AY、BR、FD、FR等
- 一些特征的数据分布较为离散且区分度高,如EL等
3. Baseline¶
3.1 数据集划分
从EDA可以发现各类别分布不均衡,按照原信息划分验证集:
kf = StratifiedKFold(n_splits=5, random_state=42, shuffle=True)
train['fold'] = -1
for fold, (train_idx, valid_idx) in enumerate(kf.split(train, greeks['Alpha'])):
train.loc[valid_idx, 'fold'] = fold
train.groupby('fold')["Class"].value_counts()
3.2 自定义评价函数
def balanced_log_loss(y_true, y_pred):
N_0 = np.sum(1 - y_true)
N_1 = np.sum(y_true)
# np.clip(a, a_min, a_max, out=None):
# 将一个array的值限定在给定的上下界,在界限外的值修改为边界值
p_1 = np.clip(y_pred, 1e-15, 1 - 1e-15)
p_0 = 1 - p_1
# print(p_1.shape, y_true.shape): (124, 2) c
# (124,)表示只有1维,是个向量,需要从列的维度扩维成矩阵
y_true = y_true[:, np.newaxis]
log_loss_0 = -np.sum((1 - y_true) * np.log(p_0))
log_loss_1 = -np.sum(y_true * np.log(p_1))
w_0 = 1 / N_0
w_1 = 1 / N_1
logloss = (w_0 * log_loss_0 + w_1 * log_loss_1) / 2
return logloss
3.3 Baseline
# 记录每个fold的权重
weights = []
test_preds = []
for fold in range(5):
train_df = train[train['fold'] != fold]
valid_df = train[train['fold'] == fold]
valid_ids = valid_df.Id.values.tolist()
train_X, train_y = train_df.drop(['Id', 'Class', 'fold'], axis=1), train_df['Class']
val_X, val_y = valid_df.drop(['Id', 'Class', 'fold'], axis=1), valid_df['Class']
# lightgbm
model = LGBMClassifier(boosting_type='gbdt',
learning_rate=0.05,
n_estimators = 50000,
early_stopping_round = 100,
random_state=42,
subsample=0.7,
colsample_bytree=0.6,
class_weight='balanced',
is_unbalance=True,
max_depth=9,
num_leaves=31)
model.fit(train_X,train_y,eval_set=[(val_X,val_y)], verbose=1000)
pred = model.predict_proba(val_X)
weights.append(1/balanced_log_loss(val_y,pred))
test_pred = model.predict_proba(test.drop('Id', axis=1))
test_preds.append(test_pred)
test_preds_weight = np.average(test_preds, weights=weights, axis=0)
提交:
test_dict = {}
test_dict.update(dict(zip(test.Id.values.tolist(), test_preds_weight)))
submission = pd.DataFrame.from_dict(test_dict, orient="index").reset_index()
submission.columns = ['Id', 'class_0', 'class_1']
submission.to_csv("submission.csv", index=False)