{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":50160,"databundleVersionId":7921029,"sourceType":"competition"}],"dockerImageVersionId":30646,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Created by yunsuxiaozi 2024/3/12\n\n### This is the notebook after a two-week break from the competition,you can check the running time of my program.After the competition restarted, I have experienced \"threw exception\" three times, and here I am trying to implement a baseline with as few files as possible.Linear regression is used as the baseline here.","metadata":{}},{"cell_type":"markdown","source":"### Import necessary libraries","metadata":{}},{"cell_type":"code","source":"import polars as pl#和pandas类似,但是处理大型数据集有更好的性能.\n#necessary\nimport pandas as pd#导入csv文件的库\nimport numpy as np#进行矩阵运算的库\n#model\nfrom lightgbm import LGBMClassifier\n#metric\nfrom sklearn.metrics import roc_auc_score#导入roc_auc曲线\n#KFold是直接分成k折,StratifiedKFold还要考虑每种类别的占比\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.decomposition import TruncatedSVD#截断奇异值分解,是一种数据降维的方法\nimport dill#对对象进行序列化和反序列化(例如保存和加载树模型)\nimport gc#垃圾回收模块\nimport time#标准库的时间模块\n#为了方便后期调用训练的模型时不会调用错版本,提供模型训练的时间\n#time.strftime()函数用于将时间对象格式化为字符串，time.localtime()函数返回表示当前本地时间的time.struct_time对象\ncurrent_time = time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime())\nprint(\"this notebook training time is \", current_time)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Config and random seed.","metadata":{}},{"cell_type":"code","source":"#config\nclass Config():\n    seed=2024\n    num_folds=10\n    TARGET_NAME ='target'\n    batch_size=1000#由于不知道测试数据的大小,所以分批次放入模型.\nimport random#提供了一些用于生成随机数的函数\n#设置随机种子,保证模型可以复现\ndef seed_everything(seed):\n    np.random.seed(seed)#numpy的随机种子\n    random.seed(seed)#python内置的随机种子\nseed_everything(Config.seed)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Feature engineer","metadata":{}},{"cell_type":"code","source":"def preprocessor(mode='train'):#mode='train'|'test'\n    #base 文件\n    print(\"base file\")\n    feats=pl.read_csv(f\"/kaggle/input/home-credit-credit-risk-model-stability/csv_files/{mode}/{mode}_base.csv\")\n        \n    print(\"deposit file num 1\")\n    deposit=pl.read_csv(f\"/kaggle/input/home-credit-credit-risk-model-stability/csv_files/{mode}/{mode}_deposit_1.csv\")\n    #数值列的特征工程  从1开始是为了把'case_id'去掉    \n    for idx in range(1,len(deposit.columns)):\n        col=deposit.columns[idx]\n        column_type = deposit[col].dtype\n        is_numeric = (column_type == pl.datatypes.Int64) or (column_type == pl.datatypes.Float64) \n        if is_numeric:#数值列构造特征\n            feat=deposit.group_by('case_id').agg( pl.max(col).alias(f\"max_deposit_{col}\"),\n                                           pl.mean(col).alias(f\"mean_deposit_{col}\"),\n                                           pl.median(col).alias(f\"median_deposit_{col}\"),\n                                           pl.std(col).alias(f\"std_deposit_{col}\"),\n                                           pl.min(col).alias(f\"min_deposit_{col}\"),\n                                           pl.count(col).alias(f\"count_deposit_{col}\"),\n                                           pl.sum(col).alias(f\"sum_deposit_{col}\"),\n                                           pl.n_unique(col).alias(f\"n_unique_deposit_{col}\"),\n                                           pl.first(col).alias(f\"first_deposit_{col}\"),\n                                           pl.last(col).alias(f\"last_deposit_{col}\")\n                                         )\n            feats=feats.join(feat,on='case_id',how='left')\n\n    #static_cb文件\n    print(\"static_cb_file num 1\")\n    static_cb=pl.read_csv(f\"/kaggle/input/home-credit-credit-risk-model-stability/csv_files/{mode}/{mode}_static_cb_0.csv\")\n    feats=feats.join(static_cb,on='case_id',how='left')\n    del static_cb\n    gc.collect()#手动触发垃圾回收,强制回收由垃圾回收器标记为未使用的内存\n     \n    return feats\n\nprint(\"---------Feature Engineer-------------\\n\")\ntrain_feats=preprocessor(mode='train')\n#由于这是时间序列数据,加上数据量太大,构造太多的特征就会超内存了,所以这里考虑就用最后一年的数据来训练模型,以达到最好效果.\n#这里的时间差不多是从2019年10月初开始.\ntrain_feats=train_feats.filter(train_feats['WEEK_NUM']>=40)\nprint(f\"len(train_feats):{len(train_feats)}\")\n\ntest_feats=preprocessor(mode='test')\nprint(f\"len(test_feats):{len(test_feats)}\")\n\nprint(\"---------Feature Transformer-------------\\n\")\n\"\"\"\n下面是对特征进行处理,提取出来的特征最初是有字符串类型和数值类型.\n1.先对字符串 类别型变量 进行 one hot encoder\n2.剩下的字符串直接去掉,一列有唯一值的去掉,缺失值占比>0.95的列选择drop.\n这样数据中就只有数值列了.\n3.填充缺失值,这里考虑用-1填充.\n4.数值列先将相关性高达0.99的列只保留一列,其余去掉.\n5.对相关性高的那些列进行降维处理.\n\"\"\"\n\n#如果打开的两个文件的相同列一个是浮点数类型,一个是object或者str,就把两个都转成浮点数类型.\nfor col in test_feats.columns:\n    if (train_feats[col].dtype==pl.datatypes.Float64) or (test_feats[col].dtype==pl.datatypes.Float64):\n        train_feats.with_columns(train_feats[col].cast(pl.datatypes.Float64))\n        test_feats.with_columns(test_feats[col].cast(pl.datatypes.Float64))\ntrain_feats=train_feats.to_pandas()\ntest_feats=test_feats.to_pandas()\n\n#对字符串特征列进行独热编码的转换\nprint(\"----------string one hot encoder ****\")\nfor col in test_feats.columns:\n    n_unique=train_feats[col].nunique()\n    #如果是类别型变量的话,独热编码转换\n    #如果类别是2类,像性别一样,如果是(0,1)了,或者说数值类型的话,没必要转换.如果是字符串类型的话,转换成数值\n    if n_unique==2 and train_feats[col].dtype=='object':\n        print(f\"one_hot_2:{col}\")\n        unique=train_feats[col].unique()\n        #随便选择一个类别进行转换,比如gender='Female'\n        train_feats[col]=(train_feats[col]==unique[0]).astype(int)\n        test_feats[col]=(test_feats[col]==unique[0]).astype(int)\n    elif (n_unique<10) and train_feats[col].dtype=='object':#由于内存有限 类别型变量的n_unique设置为20\n        print(f\"one_hot_10:{col}\")\n        unique=train_feats[col].unique()\n        for idx in range(len(unique)):\n            if unique[idx]==unique[idx]:#这里是为了避免字符串中存在nan值的情况\n                train_feats[col+\"_\"+str(idx)]=(train_feats[col]==unique[idx]).astype(int)\n                test_feats[col+\"_\"+str(idx)]=(test_feats[col]==unique[idx]).astype(int)\n        train_feats.drop([col],axis=1,inplace=True)\n        test_feats.drop([col],axis=1,inplace=True)\n\n#如果是字符串的列或者一列只有唯一值,去掉\nprint(\"----------drop other string or unique value full null value ****\")\ndrop_cols=[]\nfor col in test_feats.columns:\n    if (train_feats[col].dtype=='object') or (test_feats[col].dtype=='object') \\\n        or (train_feats[col].nunique()==1) or train_feats[col].isna().mean()>0.95:\n        drop_cols+=[col]\n#case_id目前看来和id一样没什么用,WEEK_NUM在测试数据中比训练数据大.\ndrop_cols+=['case_id','WEEK_NUM','MONTH']\nprint(f\"len(drop_cols):{len(drop_cols)},drop_cols:{drop_cols}\")\ntrain_feats=train_feats.drop(drop_cols,axis=1)\ntest_feats=test_feats.drop(drop_cols,axis=1)\n\nprint(\"----------fillna value ****\")\ntrain_feats.fillna(-1,inplace=True)\ntest_feats.fillna(-1,inplace=True)\n\nprint(f\"len(drop_cols):{len(drop_cols)},total_features_count:{len(test_feats.columns)}\")\ntrain_feats.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Feature Selection","metadata":{}},{"cell_type":"code","source":"#我们这里就是找相关性特别高的特征对,可以考虑对它们进行降维操作.\n#计算两组变量的皮尔逊相关系数\ndef pearson_corr(x1,x2):\n    \"\"\"\n    x1,x2:np.array\n    \"\"\"\n    mean_x1=np.mean(x1)\n    mean_x2=np.mean(x2)\n    std_x1=np.std(x1)\n    std_x2=np.std(x2)\n    pearson=np.mean((x1-mean_x1)*(x2-mean_x2))/(std_x1*std_x2)\n    return pearson\n#有没有和target相关性特别高的特征,拿来做逻辑回归\nchoose_cols=[]\nfor col in train_feats.columns:\n    if col!='target':\n        pearson=pearson_corr(train_feats[col].values,train_feats['target'].values) \n        if abs(pearson)>0.002:\n            choose_cols.append(col)\nlen(choose_cols),choose_cols","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### K-fold and Model training","metadata":{}},{"cell_type":"code","source":"from sklearn.linear_model import LinearRegression\n# 创建逻辑回归模型\nmodel = LinearRegression()\n\n#保存训练好的树模型,obj是保存的模型,path是需要保存的路径\ndef pickle_dump(obj, path):\n    #打开指定的路径path,binary write(二进制写入)\n    with open(path, mode=\"wb\") as f:\n        #将obj对象保存到f,使用协议版本4进行序列化\n        dill.dump(obj, f, protocol=4)\n        \nX=train_feats[choose_cols].copy()\ny=train_feats[Config.TARGET_NAME].copy()\ntest_X=test_feats[choose_cols].copy()#.drop([Config.TARGET_NAME],axis=1)\noof_pred_pro=np.zeros((len(X)))\ntest_pred_pro=np.zeros((Config.num_folds,len(test_X)))\n#10折交叉验证\nskf = StratifiedKFold(n_splits=Config.num_folds,random_state=Config.seed, shuffle=True)\n\nfor fold, (train_index, valid_index) in (enumerate(skf.split(X, y.astype(str)))):\n    print(f\"fold:{fold}\")\n\n    X_train, X_valid = X.iloc[train_index], X.iloc[valid_index]\n    y_train, y_valid = y.iloc[train_index], y.iloc[valid_index]\n\n    model.fit(X_train,y_train)\n\n    oof_pred_pro[valid_index]=model.predict(X_valid)\n    #将数据分批次进行预测.\n    for idx in range(0,len(test_X),Config.batch_size):\n        test_pred_pro[fold][idx:idx+Config.batch_size]=model.predict(test_X[idx:idx+Config.batch_size]) \n    pickle_dump(model, f'/kaggle/working/linear_fold{fold}.model') #保存训练好的模型  \n\ngini=2*roc_auc_score(y.values,oof_pred_pro)-1\nprint(f\"mean_gini:{gini}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Submission","metadata":{}},{"cell_type":"code","source":"test_preds=test_pred_pro.mean(axis=0)\nsubmission=pd.read_csv(\"/kaggle/input/home-credit-credit-risk-model-stability/sample_submission.csv\")\nsubmission['score']=np.clip(np.nan_to_num(test_preds,nan=0.3),0,1)\nsubmission.to_csv(\"submission.csv\",index=None)\nsubmission.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}