DataWhale暑期夏令营第三期AI4S生命科学赛道baseline逐句注解

AI for Science 生命科学赛道Baseline 逐行注释

# 导入numpy库,用于进行数值计算
import numpy as np
# 导入pandas库,用于数据处理和分析
import pandas as pd
# 导入polars库,用于处理大规模数据集
import polars as pl
# 导入collections库中的defaultdict和Counter,用于统计
from collections import defaultdict, Counter
# 导入xgboost库,用于梯度提升树模型
import xgb
# 导入lightgbm库,用于梯度提升树模型
import lgb
# 导入CatBoostRegressor库,用于梯度提升树模型
from catboost import CatBoostRegressor
# 导入StratifiedKFold、KFold和GroupKFold,用于交叉验证
from sklearn.model_selection import StratifiedKFold, KFold, GroupKFold
# 导入mean_squared_log_error,mean_absolute_error用于评估模型性能
from sklearn.metrics import mean_squared_log_error, mean_absolute_error
# 导入sys、os、gc、argparse和warnings库,用于处理命令行参数和警告信息
import sys, os, gc, argparse, warnings
# 忽略警告信息
warnings.filterwarnings('ignore')

# 定义一个函数,用于减少DataFrame的内存使用
def reduce_mem_usage(df, verbose=True):
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64'] # 定义数值类型列表
    start_mem = df.memory_usage().sum() / 1024**2    # 计算初始内存使用量
    for col in df.columns: # 遍历DataFrame的每一列
        col_type = df[col].dtypes # 获取该列的数据类型
        if col_type in numerics: # 如果该列是数值类型
            c_min = df[col].min() # 获取该列的最小值
            c_max = df[col].max() # 获取该列的最大值
            if str(col_type)[:3] == 'int': # 如果该列是整数类型
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max: # 如果该列的数值范围在int8的范围内
                    df[col] = df[col].astype(np.int8) # 将该列的数据类型转换为int8
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max: # 如果该列的数值范围在int16的范围内
                    df[col] = df[col].astype(np.int16) # 将该列的数据类型转换为int16
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max: # 如果该列的数值范围在int32的范围内
                    df[col] = df[col].astype(np.int32) # 将该列的数据类型转换为int32
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max: # 如果该列的数值范围在int64的范围内
                    df[col] = df[col].astype(np.int64) # 将该列的数据类型转换为int64  
            else: # 如果该列不是整数类型
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max: # 如果该列的数值范围在float16的范围内
                    df[col] = df[col].astype(np.float16) # 将该列的数据类型转换为float16
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max: # 如果该列的数值范围在float32的范围内
                    df[col] = df[col].astype(np.float32) # 将该列的数据类型转换为float32
                else: # 如果该列的数值范围不在上述三种类型的范围内
                    df[col] = df[col].astype(np.float64) # 将该列的数据类型转换为float64
    end_mem = df.memory_usage().sum() / 1024**2 # 计算结束时的内存使用量
    if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem)) # 输出内存使用量的减少情况
    return df # 返回处理后的DataFrame

# 读取数据
path = 'ai4bio' # 定义数据集路径
# 可能因为内存问题所导致数据读取困难,可以选择放弃部分特征,仅读取部分行,baseline仅读取前10000行
#根据自己的算力情况,适当读取数据
traindata = pd.read_csv(f'{path}/traindata.csv', nrows=10000) # 读取训练数据
trainmap = pd.read_csv(f'{path}/trainmap.csv') # 读取训练数据的映射信息

testdata = pd.read_csv(f'{path}/ai4bio_testset_final/testdata.csv', nrows=10000) # 读取测试数据
testmap = pd.read_csv(f'{path}/ai4bio_testset_final/testmap.csv') # 读取测试数据的映射信息

# 压缩内存,因时间较长,所以暂时注释掉,按自身情况选择使用
# traindata = reduce_mem_usage(traindata)
# testdata = reduce_mem_usage(testdata)

# 数据预处理
traindata = traindata.set_index('cpgsite') # 将训练数据的索引设置为'cpgsite'列
traindata = traindata.T # 转置训练数据
traindata = traindata.reset_index() # 重置训练数据的索引
traindata = traindata.rename(columns={'index':'sample_id'}) # 重命名训练数据的列名
traindata.columns = ['sample_id'] + [i for i in range(10000)] # 设置训练数据的列名为'sample_id'加上一列自增的数字
traindata.to_pickle(f'{path}/traindata.pkl') # 将处理后的训练数据保存为pickle文件

testdata = testdata.set_index('cpgsite') # 将测试数据的索引设置为'cpgsite'列
testdata = testdata.T # 转置测试数据
testdata = testdata.reset_index() # 重置测试数据的索引
testdata = testdata.rename(columns={'index':'sample_id'}) # 重命名测试数据的列名
testdata.columns = ['sample_id'] + [i for i in range(10000)] # 设置测试数据的列名为'sample_id'加上一列自增的数字
testdata.to_pickle(f'{path}/testdata.pkl') # 将处理后的测试数据保存为pickle文件

# 使用 head() 函数查看前几行数据
trainmap.head() 
traindata.head()
# info()的方法,用于显示有关数据集的信息,例如列名、数据类型、非空值数量和内存使用情况等。
traindata.info()

# 使用循环来遍历数据集traindata的前10个特征,并计算每个特征的缺失率。
for i in range(10):
	# 使用traindata[i]获取第i个特征的数据,调用isnull()方法来判断数据是否为空值,返回一个布尔类型的数组,使用sum()方法对布尔数组求和,统计每行中空值的数量,将空值数量除以数据集的总行数,得到该特征的缺失率。
    null_cnt = traindata[i].isnull().sum() / traindata.shape[0] 
    # 使用print()函数输出特征名称和对应的缺失率。
    print(f'特征{i},对应的缺失率为{null_cnt}')

# 代码使用列表推导式生成一个包含前1000个特征的列表,即[i for i in range(1000)],这个列表作为索引传递给traindata,以获取这些特征对应的列数据,调用corr()方法计算这些列之间的相关系数矩阵。
traindata[[i for i in range(1000)]].corr()

# 数据拼接
# 将trainmap数据集中的'sample_id', 'age', 'gender', 'sample_type', 'disease'这几列数据合并到traindata数据集中,合并的依据是两个数据集中的'sample_id'列相同,合并后的结果存储在traindata中。
traindata = traindata.merge(trainmap[['sample_id', 'age', 'gender', 'sample_type', 'disease']],on='sample_id',how='left')
# 将testmap数据集中的'sample_id', 'gender'这两列数据合并到testdata数据集中,合并的依据是两个数据集中的'sample_id'列相同,合并后的结果存储在testdata中,由于使用了左连接(how='left'),如果testmap中没有与testdata匹配的'sample_id',那么对应的'gender'值将会被填充为NaN。
testdata = testdata.merge(testmap[['sample_id', 'gender']],on='sample_id',how='left')

# 定义了一个名为disease_mapping的字典,它将疾病名称映射为对应的数值。例如,'Alzheimer's disease'被映射为1,'Parkinson's disease'被映射为4,以此类推。这样的映射通常用于机器学习模型中的特征编码,以便将文本形式的类别标签转换为可以输入到模型的数字形式。
disease_mapping = {
    'control': 0,
    "Alzheimer's disease": 1,
    "Graves' disease": 2,
    "Huntington's disease": 3,
    "Parkinson's disease": 4,
    'rheumatoid arthritis': 5,
    'schizophrenia': 6,
    "Sjogren's syndrome": 7,
    'stroke': 8,
    'type 2 diabetes': 9
}
sample_type_mapping = {'control': 0, 'disease tissue': 1}
gender_mapping = {'F': 0, 'M': 1}

# 将traindata数据集中'disease'列中的疾病名称使用disease_mapping字典进行映射,将疾病名称替换为对应的数值。这样处理后,'disease'列中的值将变为0到9之间的整数。
traindata['disease'] = traindata['disease'].map(disease_mapping)
# 将traindata数据集中'sample_type'列中的样本类型使用sample_type_mapping字典进行映射,将样本类型替换为对应的数值,处理后,'sample_type'列中的值将变为0或1,表示不同的样本类型。
traindata['sample_type'] = traindata['sample_type'].map(sample_type_mapping)
# 将traindata和testdata数据集中'gender'列中的性别使用gender_mapping字典进行映射,将性别替换为对应的数值。这样处理后,'gender'列中的值将变为0或1,表示不同的性别。
traindata['gender'] = traindata['gender'].map(gender_mapping)
testdata['gender'] = testdata['gender'].map(gender_mapping)

# 特征工程
# 计算traindata和testdata数据集中前10000行每一列的最大值、最小值、标准差、方差、偏度、均值和中位数,并将结果分别存储在traindata和testdata的'max'、'min'、'std'、'var'、'skew'、'mean'和'median'列中,这些统计量可以用于描述数据集的特征和分布情况。
traindata['max'] = traindata[[i for i in range(10000)]].max(axis=1)
traindata['min'] = traindata[[i for i in range(10000)]].min(axis=1)
traindata['std'] = traindata[[i for i in range(10000)]].std(axis=1)
traindata['var'] = traindata[[i for i in range(10000)]].var(axis=1)
traindata['skew'] = traindata[[i for i in range(10000)]].skew(axis=1)
traindata['mean'] = traindata[[i for i in range(10000)]].mean(axis=1)
traindata['median'] = traindata[[i for i in range(10000)]].median(axis=1)

testdata['max'] = testdata[[i for i in range(10000)]].max(axis=1)
testdata['min'] = testdata[[i for i in range(10000)]].min(axis=1)
testdata['std'] = testdata[[i for i in range(10000)]].std(axis=1)
testdata['var'] = testdata[[i for i in range(10000)]].var(axis=1)
testdata['skew'] = testdata[[i for i in range(10000)]].skew(axis=1)
testdata['mean'] = testdata[[i for i in range(10000)]].mean(axis=1)
testdata['median'] = testdata[[i for i in range(10000)]].median(axis=1)

# 入模特征选择
cols = [i for i in range(10000)] + ['gender','max','min','std','var','skew','mean','median']

# 模型训练与验证
# 定义一个名为catboost_model的函数,接收四个参数:train_x, train_y, test_x和seed
def catboost_model(train_x, train_y, test_x, seed = 2023):
    folds = 5  # 设置K折交叉验证折数为5
    kf = KFold(n_splits=folds, shuffle=True, random_state=seed) # 使用KFold方法创建一个交叉验证对象kf,设置折数、是否打乱顺序和随机种子
    oof = np.zeros(train_x.shape[0]) # 初始化一个全零数组oof,长度为train_x的长度
    test_predict = np.zeros(test_x.shape[0]) # 初始化一个全零数组test_predict,长度为test_x的长度
    cv_scores = [] # 初始化一个空列表cv_scores,用于存储交叉验证得分
    # 使用for循环遍历kf的每个折叠
    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
   	    # 打印当前折数的序号
        print('************************************ {} ************************************'.format(str(i+1)))
        # 获取当前折叠的训练集索引和验证集索引,根据索引获取训练集和验证集的特征和标签
        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]
        # 定义CatBoostRegressor模型的参数
        params = {'learning_rate': 0.1, # 学习率,控制模型参数更新的速度。值越大,模型更新越快,但可能陷入局部最优解;值越小,模型更新越慢,但可能收敛到更好的解。
          'depth': 5,  # 树的深度,即决策树的最大层数。树的深度越深,模型的复杂度越高,可能导致过拟合;树的深度越浅,模型的复杂度越低,可能导致欠拟合。
          'bootstrap_type':'Bernoulli', # 自助法的类型,用于有放回地抽样。'Bernoulli'表示使用伯努利分布进行抽样,每次抽样后将结果反馈到训练集中。
          'random_seed':2023, # 随机种子,用于控制随机过程。设置相同的随机种子可以保证每次运行代码时得到相同的结果。
          'od_type': 'Iter',  # 迭代次数优化方法的类型。'Iter'表示使用迭代次数优化方法,通过多次迭代来寻找最优的迭代次数。
          'od_wait': 100,  # 迭代次数优化方法的等待时间,即两次迭代之间的最小间隔。设置较长的等待时间可以加快收敛速度,但可能导致过拟合;设置较短的等待时间可以加快收敛速度,但可能导致欠拟合。
          'allow_writing_files': False, # 是否允许写入文件。设置为False表示不保存模型参数,只返回模型对象。
          'task_type':"GPU",  # 任务类型,表示模型运行在GPU还是CPU上。设置为"GPU"表示模型运行在GPU上,如果计算机没有GPU,可以设置为"CPU"。
          'devices':'0:1'} # 设备列表,表示使用哪些GPU设备。"0:1"表示只使用第一个GPU设备。
        
        # 创建CatBoostRegressor模型实例
        # 根据自己的算力与精力,调整iterations,V100环境iterations=500需要跑10min
        model = CatBoostRegressor(iterations=500, **params)
        # 使用训练集和验证集拟合模型
        model.fit(trn_x, trn_y, # 训练集的特征和标签,用于模型的训练。
                  eval_set=(val_x, val_y), # 验证集的特征和标签,用于在训练过程中评估模型性能。
                  metric_period=500, # 定评估指标的计算周期,即每隔多少次迭代计算一次评估指标。
                  use_best_model=True, # 设置为True表示在训练过程中使用验证集上性能最好的模型参数。
                  cat_features=[], # 包含需要转换为类别特征的特征名称,没有需要转换的特征,所以为空列表。
                  verbose=1) # 设置日志输出的详细程度,1表示输出详细信息。
                  
        # 使用模型对测试集进行预测
        val_pred  = model.predict(val_x)
        test_pred = model.predict(test_x)
        # 将验证集预测结果存储到oof数组中
        oof[valid_index] = val_pred
        # 计算K折测试集预测结果的平均值并累加到test_predict数组中
        test_predict += test_pred / kf.n_splits
        
        # 暂时忽略健康样本和患病样本在计算MAE上的差异,仅使用常规的MAE指标
        # 计算验证集预测结果与真实值之间的平均绝对误差(MAE)
        score = mean_absolute_error(val_y, val_pred)
        # 将MAE添加到cv_scores列表中
        cv_scores.append(score)
        print(cv_scores) # 打印cv_scores列表
        
        # 获取特征重要性打分,便于评估特征
        if i == 0:
        	# 将特征名称和打分存储到DataFrame中
            fea_ = model.feature_importances_
            fea_name = model.feature_names_
            fea_score = pd.DataFrame({'fea_name':fea_name, 'score':fea_})
            # 按照打分降序排列DataFrame
            fea_score = fea_score.sort_values('score', ascending=False)
            # 将排序后的DataFrame保存为CSV文件(命名为feature_importances.csv)
            fea_score.to_csv('feature_importances.csv', index=False)
        
    return oof, test_predict # 返回oof和test_predict数组

# 调用catboost_model函数,进行模型训练与结果预测
cat_oof, cat_test = catboost_model(traindata[cols], traindata['age'], testdata[cols])

# 输出赛题提交格式的结果
testdata['age'] = cat_test # 将testdata数据框中的age列赋值为cat_test。
testdata['age'] = testdata['age'].astype(float) # 将age列的数据类型转换为浮点数。
testdata['age'] = testdata['age'].apply(lambda x: x if x>0 else 0.0) # 使用lambda函数对age列中的每个元素进行判断,如果大于0,则保持不变,否则将其替换为0.0。
testdata['age'] = testdata['age'].apply(lambda x: '%.2f' % x) # 使用lambda函数将age列中的每个元素格式化为保留两位小数的字符串。
testdata['age'] = testdata['age'].astype(str) # 将age列的数据类型转换为字符串。
testdata[['sample_id','age']].to_csv('submit.txt',index=False) # 将sample_id和age两列保存到名为submit.txt的文件中,不包含索引。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码不跑那我跑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值