├── README.md ├── code.ipynb ├── data ├── test_all.csv ├── train_x.csv └── train_xy.csv ├── result ├── lgb_0.820296719142.csv └── readme.txt └── 新网银行杯竞赛报告.pdf /README.md: -------------------------------------------------------------------------------- 1 | # 西南财经大学“新网银行杯”数据科学竞赛总结与分享 2 | 3 | 西南财经大学“新网银行杯”数据科学竞赛 2/721 决赛 二等奖 4 | 5 | 详细的分析思路可以查看竞赛报告,或者查看我的博客:[16huakai——西南财经大学“新网银行杯”数据科学竞赛总结与分享](https://blog.csdn.net/huakai16/article/details/84099033) 6 | 7 | **注:** 8 | 9 | 赛事官方地址:[DC竞赛平台](http://www.dcjingsai.com/common/cmpt/%E8%A5%BF%E5%8D%97%E8%B4%A2%E7%BB%8F%E5%A4%A7%E5%AD%A6%E2%80%9C%E6%96%B0%E7%BD%91%E9%93%B6%E8%A1%8C%E6%9D%AF%E2%80%9D%E6%95%B0%E6%8D%AE%E7%A7%91%E5%AD%A6%E7%AB%9E%E8%B5%9B_%E7%AB%9E%E8%B5%9B%E4%BF%A1%E6%81%AF.html) 10 | 11 | 提供的原始数据和代码资料,仅供学习交流,请勿另作他用! 12 | 13 | 最后,希望我的分享,对你能有所帮助。 14 | 15 | **分享:**[数据竞赛top解决方案开源整理](https://github.com/Smilexuhc/Data-Competition-TopSolution) 16 | -------------------------------------------------------------------------------- /code.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "(15000, 160)\n", 13 | "(10000, 159)\n", 14 | "(15000, 160)\n", 15 | "(10000, 160)\n", 16 | "(25000, 160)\n", 17 | "数值型特征: 95\n", 18 | "类别型特征: 62\n", 19 | "(25000, 7)\n", 20 | "(25000, 167)\n", 21 | "重要的特征个数: 24\n", 22 | "(15000, 167)\n", 23 | "(10000, 167)\n", 24 | "所有特征的维度: 164\n", 25 | "X shape: (15000, 164)\n", 26 | "y shape: (15000,)\n", 27 | "test shape (10000, 164)\n", 28 | "start:********************************\n", 29 | "................Start training..........................\n", 30 | "Training until validation scores don't improve for 100 rounds.\n", 31 | "[100]\tvalid_0's auc: 0.807985\n", 32 | "[200]\tvalid_0's auc: 0.817738\n", 33 | "[300]\tvalid_0's auc: 0.820048\n", 34 | "[400]\tvalid_0's auc: 0.820099\n", 35 | "Early stopping, best iteration is:\n", 36 | "[319]\tvalid_0's auc: 0.821531\n", 37 | "................Start predict .........................\n", 38 | "valid auc: 0.821531454082\n", 39 | "................Start training..........................\n", 40 | "Training until validation scores don't improve for 100 rounds.\n", 41 | "[100]\tvalid_0's auc: 0.787632\n", 42 | "[200]\tvalid_0's auc: 0.789599\n", 43 | "[300]\tvalid_0's auc: 0.789777\n", 44 | "Early stopping, best iteration is:\n", 45 | "[245]\tvalid_0's auc: 0.790868\n", 46 | "................Start predict .........................\n", 47 | "valid auc: 0.790867843507\n", 48 | "................Start training..........................\n", 49 | "Training until validation scores don't improve for 100 rounds.\n", 50 | "[100]\tvalid_0's auc: 0.845806\n", 51 | "[200]\tvalid_0's auc: 0.853503\n", 52 | "[300]\tvalid_0's auc: 0.85152\n", 53 | "Early stopping, best iteration is:\n", 54 | "[240]\tvalid_0's auc: 0.854959\n", 55 | "................Start predict .........................\n", 56 | "valid auc: 0.854958527026\n", 57 | "................Start training..........................\n", 58 | "Training until validation scores don't improve for 100 rounds.\n", 59 | "[100]\tvalid_0's auc: 0.827864\n", 60 | "[200]\tvalid_0's auc: 0.834207\n", 61 | "[300]\tvalid_0's auc: 0.837352\n", 62 | "[400]\tvalid_0's auc: 0.838233\n", 63 | "[500]\tvalid_0's auc: 0.836863\n", 64 | "Early stopping, best iteration is:\n", 65 | "[432]\tvalid_0's auc: 0.838633\n", 66 | "................Start predict .........................\n", 67 | "valid auc: 0.838632657815\n", 68 | "................Start training..........................\n", 69 | "Training until validation scores don't improve for 100 rounds.\n", 70 | "[100]\tvalid_0's auc: 0.783255\n", 71 | "[200]\tvalid_0's auc: 0.789703\n", 72 | "[300]\tvalid_0's auc: 0.793277\n", 73 | "[400]\tvalid_0's auc: 0.794619\n", 74 | "[500]\tvalid_0's auc: 0.794141\n", 75 | "Early stopping, best iteration is:\n", 76 | "[455]\tvalid_0's auc: 0.795491\n", 77 | "................Start predict .........................\n", 78 | "valid auc: 0.79549058047\n", 79 | "the cv information:\n", 80 | "[0.82153145408201744, 0.79086784350661854, 0.85495852702579533, 0.8386326578150477, 0.79549058046998866]\n", 81 | "cv mean score 0.82029621258\n", 82 | "......................run with time: 7.369692754745484\n", 83 | "over:*********************************\n", 84 | "mean auc: 0.82029621258\n", 85 | "总的结果: (5, 10000)\n", 86 | "result shape: (10000,)\n" 87 | ] 88 | } 89 | ], 90 | "source": [ 91 | "# lgb模型\n", 92 | "import numpy as np\n", 93 | "import pandas as pd\n", 94 | "from pandas import DataFrame\n", 95 | "from pandas import Series\n", 96 | "import matplotlib\n", 97 | "import matplotlib.pyplot as plt\n", 98 | "import lightgbm as lgb\n", 99 | "import operator\n", 100 | "import time\n", 101 | "\n", 102 | "# 1.读取文件\n", 103 | "train_xy = pd.read_csv(\"data/train_xy.csv\",header=0,sep=\",\")\n", 104 | "test_all = pd.read_csv(\"data/test_all.csv\",header=0,sep=\",\")\n", 105 | "\n", 106 | "print(train_xy.shape)\n", 107 | "print(test_all.shape)\n", 108 | "# 2.合并数据\n", 109 | "train = train_xy.copy()\n", 110 | "test = test_all.copy()\n", 111 | "test['y'] = -1\n", 112 | "# 合并一下train 和 test\n", 113 | "data = pd.concat([train,test],axis = 0) # train_xy,test_all索引上连接\n", 114 | "print(train.shape)\n", 115 | "print(test.shape)\n", 116 | "print(data.shape)\n", 117 | "\n", 118 | "# 3.对特征进行分析,分为数值型 、 类别型\n", 119 | "numerical_features = []\n", 120 | "categorical_features = []\n", 121 | "for i in range(157):\n", 122 | " feat = \"x_\" + str(i+1)\n", 123 | " if i <= 94: # 1-95\n", 124 | " numerical_features.append(feat)\n", 125 | " else:\n", 126 | " categorical_features.append(feat)\n", 127 | "print(\"数值型特征:\",len(numerical_features))\n", 128 | "print(\"类别型特征:\",len(categorical_features))\n", 129 | "\n", 130 | "# 4.统计每个样本缺失值的个数\n", 131 | "def get_nan_count(data,feats,bins = 7):\n", 132 | " df = data[feats].copy()\n", 133 | " df = df.replace(-99,np.nan)\n", 134 | " df['nan_count'] = df.shape[1] - df.count(axis = 1).values # 列数 - 非nan数\n", 135 | " dummy = pd.get_dummies(pd.cut(df['nan_count'],bins),prefix = 'nan') # 对缺失个数进行离散化,划分为7个区间\n", 136 | " print(dummy.shape)\n", 137 | " res = pd.concat([data,dummy],axis = 1) # 合并到原来的数据\n", 138 | " print(res.shape)\n", 139 | " return res\n", 140 | "# 在特征行方向上面统计缺失值\n", 141 | "data = get_nan_count(data,data.columns.values,7)\n", 142 | "\n", 143 | "# 5.重要性top24\n", 144 | "imp_feat = [ 'x_80', 'x_2', 'x_81', 'x_95', 'x_1',\n", 145 | " 'x_52', 'x_63', 'x_54', 'x_43', 'x_40',\n", 146 | " 'x_93', 'x_42', 'x_157', 'x_62', 'x_29',\n", 147 | " 'x_61', 'x_55', 'x_79', 'x_59', 'x_69',\n", 148 | " 'x_48', 'x_56', 'x_7', 'x_64']\n", 149 | "\n", 150 | "print(\"重要的特征个数:\",len(imp_feat))\n", 151 | "# 对一些重要的特征进行填充,\n", 152 | "for feat in imp_feat[:10]: # 填充top K ,而不是所有\n", 153 | " if feat in numerical_features: # 数值型用均值\n", 154 | " data[feat] = data[feat].replace(-99,np.nan)\n", 155 | " data[feat] = data[feat].fillna(data[feat].mean()) # 均值比中位数好\n", 156 | " if feat in categorical_features: # 类别型:不处理\n", 157 | " print(\"这是类别特征:\",feat)\n", 158 | " \n", 159 | "# 6.对数值型的特征,处理为rank特征(鲁棒性好一点)\n", 160 | "for feat in numerical_features:\n", 161 | " data[feat] = data[feat].rank() / float(data.shape[0]) # 排序,并且进行归一化\n", 162 | "\n", 163 | "# 7.划分train 、test set\n", 164 | "train = data.loc[data['y']!=-1,:] # train set\n", 165 | "test = data.loc[data['y']==-1,:] # test set\n", 166 | "print(train.shape)\n", 167 | "print(test.shape) \n", 168 | "\n", 169 | "# 获取特征列,去除id,group,y\n", 170 | "no_features = ['cust_id','cust_group','y'] \n", 171 | "features = [feat for feat in train.columns.values if feat not in no_features]\n", 172 | "print(\"所有特征的维度:\",len(features))\n", 173 | "\n", 174 | "# 8.得到输入X ,输出y\n", 175 | "train_id = train['cust_id'].values\n", 176 | "y = train['y'].values.astype(int)\n", 177 | "X = train[features].values\n", 178 | "print(\"X shape:\",X.shape)\n", 179 | "print(\"y shape:\",y.shape)\n", 180 | "\n", 181 | "test_id = test['cust_id'].values\n", 182 | "test_data = test[features].values\n", 183 | "print(\"test shape\",test_data.shape)\n", 184 | "\n", 185 | "# 9.开始训练\n", 186 | "# 采取分层采样\n", 187 | "from sklearn.model_selection import StratifiedKFold\n", 188 | "from sklearn.metrics import roc_auc_score\n", 189 | "\n", 190 | "print(\"start:********************************\")\n", 191 | "start = time.time()\n", 192 | "\n", 193 | "N = 5 \n", 194 | "skf = StratifiedKFold(n_splits=N,shuffle=True,random_state=2018)\n", 195 | "\n", 196 | "auc_cv = []\n", 197 | "pred_cv = []\n", 198 | "\n", 199 | "for k,(train_in,test_in) in enumerate(skf.split(X,y)):\n", 200 | " X_train,X_test,y_train,y_test = X[train_in],X[test_in],\\\n", 201 | " y[train_in],y[test_in]\n", 202 | " \n", 203 | " # 数据结构\n", 204 | " lgb_train = lgb.Dataset(X_train, y_train)\n", 205 | " lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)\n", 206 | "\n", 207 | " # 设置参数\n", 208 | " params = {\n", 209 | " 'boosting_type': 'gbdt',\n", 210 | " 'objective': 'binary',\n", 211 | " 'metric': {'auc'},\n", 212 | " 'max_depth': 4,\n", 213 | " 'min_child_weight': 6,\n", 214 | " 'num_leaves': 16,\n", 215 | " 'learning_rate': 0.02,# 0.05\n", 216 | " 'feature_fraction': 0.7,\n", 217 | " 'bagging_fraction': 0.7,\n", 218 | " 'bagging_freq': 5,\n", 219 | " #'lambda_l1':0.25,\n", 220 | " #'lambda_l2':0.5,\n", 221 | " #'scale_pos_weight':10.0/1.0, #14309.0 / 691.0, #不设置\n", 222 | " #'num_threads':4,\n", 223 | " }\n", 224 | "\n", 225 | " print('................Start training..........................')\n", 226 | " # train\n", 227 | " gbm = lgb.train(params,\n", 228 | " lgb_train,\n", 229 | " num_boost_round=2000,\n", 230 | " valid_sets=lgb_eval,\n", 231 | " early_stopping_rounds=100,\n", 232 | " verbose_eval=100)\n", 233 | "\n", 234 | " print('................Start predict .........................')\n", 235 | " # 预测\n", 236 | " y_pred = gbm.predict(X_test,num_iteration=gbm.best_iteration)\n", 237 | " # 评估\n", 238 | " tmp_auc = roc_auc_score(y_test,y_pred)\n", 239 | " auc_cv.append(tmp_auc)\n", 240 | " print(\"valid auc:\",tmp_auc)\n", 241 | " # test\n", 242 | " pred = gbm.predict(test_data, num_iteration = gbm.best_iteration)\n", 243 | " pred_cv.append(pred) \n", 244 | " \n", 245 | "# K交叉验证的平均分数 \n", 246 | "print('the cv information:')\n", 247 | "print(auc_cv)\n", 248 | "print('cv mean score',np.mean(auc_cv))\n", 249 | "\n", 250 | "end = time.time()\n", 251 | "print(\"......................run with time: \",(end - start) / 60.0 )\n", 252 | "print(\"over:*********************************\")\n", 253 | "\n", 254 | "# 10.5折交叉验证结果均值融合,保存文件\n", 255 | "mean_auc = np.mean(auc_cv)\n", 256 | "print(\"mean auc:\",mean_auc)\n", 257 | "filepath = 'result/lgb_'+ str(mean_auc)+'.csv' # 线下平均分数\n", 258 | "\n", 259 | "# 转为array\n", 260 | "res = np.array(pred_cv)\n", 261 | "print(\"总的结果:\",res.shape)\n", 262 | "# 最后结果平均,mean\n", 263 | "r = res.mean(axis = 0)\n", 264 | "print('result shape:',r.shape)\n", 265 | "result = DataFrame()\n", 266 | "result['cust_id'] = test_id\n", 267 | "result['pred_prob'] = r\n", 268 | "result.to_csv(filepath,index=False,sep=\",\")" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": 1, 274 | "metadata": {}, 275 | "outputs": [ 276 | { 277 | "name": "stdout", 278 | "output_type": "stream", 279 | "text": [ 280 | "(15000, 160)\n", 281 | "(10000, 159)\n", 282 | "(10000, 159)\n", 283 | "(15000, 160)\n", 284 | "(10000, 160)\n", 285 | "(25000, 160)\n", 286 | "有用的数值型特征: 95\n", 287 | "有用的类别型特征: 62\n", 288 | "(25000, 7)\n", 289 | "(25000, 167)\n", 290 | "重要的特征个数: 24\n", 291 | "(15000, 167)\n", 292 | "(10000, 167)\n", 293 | "所有特征的维度: 164\n", 294 | "X shape: (15000, 164)\n", 295 | "y shape: (15000,)\n", 296 | "test shape (10000, 164)\n", 297 | "start:********************************\n", 298 | "[0]\ttrain-auc:0.766192\tvalid-auc:0.717799\n", 299 | "Multiple eval metrics have been passed: 'valid-auc' will be used for early stopping.\n", 300 | "\n", 301 | "Will train until valid-auc hasn't improved in 100 rounds.\n", 302 | "[100]\ttrain-auc:0.855048\tvalid-auc:0.803775\n", 303 | "[200]\ttrain-auc:0.873149\tvalid-auc:0.810846\n", 304 | "[300]\ttrain-auc:0.889259\tvalid-auc:0.816155\n", 305 | "[400]\ttrain-auc:0.904415\tvalid-auc:0.816466\n", 306 | "Stopping. Best iteration:\n", 307 | "[384]\ttrain-auc:0.901288\tvalid-auc:0.817537\n", 308 | "\n", 309 | "...........................auc value: 0.817537165236\n", 310 | "[0]\ttrain-auc:0.780705\tvalid-auc:0.733678\n", 311 | "Multiple eval metrics have been passed: 'valid-auc' will be used for early stopping.\n", 312 | "\n", 313 | "Will train until valid-auc hasn't improved in 100 rounds.\n", 314 | "[100]\ttrain-auc:0.8588\tvalid-auc:0.788037\n", 315 | "[200]\ttrain-auc:0.8779\tvalid-auc:0.793088\n", 316 | "[300]\ttrain-auc:0.893068\tvalid-auc:0.792412\n", 317 | "Stopping. Best iteration:\n", 318 | "[214]\ttrain-auc:0.880693\tvalid-auc:0.794448\n", 319 | "\n", 320 | "...........................auc value: 0.794447989143\n", 321 | "[0]\ttrain-auc:0.762538\tvalid-auc:0.798002\n", 322 | "Multiple eval metrics have been passed: 'valid-auc' will be used for early stopping.\n", 323 | "\n", 324 | "Will train until valid-auc hasn't improved in 100 rounds.\n", 325 | "[100]\ttrain-auc:0.844857\tvalid-auc:0.850306\n", 326 | "[200]\ttrain-auc:0.864348\tvalid-auc:0.854759\n", 327 | "[300]\ttrain-auc:0.88143\tvalid-auc:0.857201\n", 328 | "Stopping. Best iteration:\n", 329 | "[264]\ttrain-auc:0.876118\tvalid-auc:0.858698\n", 330 | "\n", 331 | "...........................auc value: 0.858698184102\n", 332 | "[0]\ttrain-auc:0.754456\tvalid-auc:0.738081\n", 333 | "Multiple eval metrics have been passed: 'valid-auc' will be used for early stopping.\n", 334 | "\n", 335 | "Will train until valid-auc hasn't improved in 100 rounds.\n", 336 | "[100]\ttrain-auc:0.852022\tvalid-auc:0.820218\n", 337 | "[200]\ttrain-auc:0.86894\tvalid-auc:0.830637\n", 338 | "[300]\ttrain-auc:0.886479\tvalid-auc:0.835807\n", 339 | "[400]\ttrain-auc:0.900755\tvalid-auc:0.837997\n", 340 | "[500]\ttrain-auc:0.914652\tvalid-auc:0.839329\n", 341 | "[600]\ttrain-auc:0.925195\tvalid-auc:0.839137\n", 342 | "Stopping. Best iteration:\n", 343 | "[545]\ttrain-auc:0.919469\tvalid-auc:0.840425\n", 344 | "\n", 345 | "...........................auc value: 0.840425262561\n", 346 | "[0]\ttrain-auc:0.743565\tvalid-auc:0.70423\n", 347 | "Multiple eval metrics have been passed: 'valid-auc' will be used for early stopping.\n", 348 | "\n", 349 | "Will train until valid-auc hasn't improved in 100 rounds.\n", 350 | "[100]\ttrain-auc:0.859187\tvalid-auc:0.772038\n", 351 | "[200]\ttrain-auc:0.877294\tvalid-auc:0.781173\n", 352 | "[300]\ttrain-auc:0.892113\tvalid-auc:0.785602\n", 353 | "[400]\ttrain-auc:0.906169\tvalid-auc:0.790248\n", 354 | "[500]\ttrain-auc:0.918636\tvalid-auc:0.791588\n", 355 | "[600]\ttrain-auc:0.930144\tvalid-auc:0.79194\n", 356 | "[700]\ttrain-auc:0.939063\tvalid-auc:0.792618\n", 357 | "[800]\ttrain-auc:0.94739\tvalid-auc:0.792254\n", 358 | "Stopping. Best iteration:\n", 359 | "[701]\ttrain-auc:0.939154\tvalid-auc:0.792646\n", 360 | "\n", 361 | "...........................auc value: 0.792646231935\n", 362 | "......................validate result mean : 0.820750966595\n", 363 | "......................run with time: 6.236948664983114\n", 364 | "over:*********************************\n", 365 | "mean auc: 0.820750966595\n", 366 | "5折结果: (5, 10000)\n", 367 | "result shape: (10000,)\n" 368 | ] 369 | } 370 | ], 371 | "source": [ 372 | "# xgb模型\n", 373 | "import numpy as np\n", 374 | "import pandas as pd\n", 375 | "from pandas import DataFrame\n", 376 | "from pandas import Series\n", 377 | "import time\n", 378 | "\n", 379 | "# 1.读取文件\n", 380 | "train_xy = pd.read_csv(\"data/train_xy.csv\",header=0,sep=\",\")\n", 381 | "train_x = pd.read_csv(\"data/train_x.csv\",header=0,sep=\",\")\n", 382 | "test_all = pd.read_csv(\"data/test_all.csv\",header=0,sep=\",\")\n", 383 | "\n", 384 | "print(train_xy.shape)\n", 385 | "print(train_x.shape)\n", 386 | "print(test_all.shape)\n", 387 | "\n", 388 | "# 2.合并数据\n", 389 | "train = train_xy.copy()\n", 390 | "test = test_all.copy()\n", 391 | "test['y'] = -1\n", 392 | "# 合并一下train 和 test\n", 393 | "data = pd.concat([train,test],axis = 0) # train_xy,test_all索引上连接\n", 394 | "print(train.shape)\n", 395 | "print(test.shape)\n", 396 | "print(data.shape)\n", 397 | "\n", 398 | "# 3.对特征进行分析,分为数值型 、 类别型\n", 399 | "numerical_features = []\n", 400 | "categorical_features = []\n", 401 | "for i in range(157):\n", 402 | " feat = \"x_\" + str(i+1)\n", 403 | " if i <= 94: # 1-95\n", 404 | " numerical_features.append(feat)\n", 405 | " else:\n", 406 | " categorical_features.append(feat)\n", 407 | "print(\"有用的数值型特征:\",len(numerical_features))\n", 408 | "print(\"有用的类别型特征:\",len(categorical_features))\n", 409 | "\n", 410 | "# 4.统计每个用户缺失值的个数\n", 411 | "def get_nan_count(data):\n", 412 | " df = data.copy()\n", 413 | " df = df.replace(-99,np.nan)\n", 414 | " df['nan_count'] = df.shape[1] - df.count(axis = 1).values # 列数 - 非nan数\n", 415 | " dummy = pd.get_dummies(pd.cut(df['nan_count'],7),prefix = 'nan') # 对缺失个数进行离散化,划分为7个区间\n", 416 | " print(dummy.shape)\n", 417 | " res = pd.concat([data,dummy],axis = 1) # 合并到原来的数据\n", 418 | " print(res.shape)\n", 419 | " return res\n", 420 | "data = get_nan_count(data)\n", 421 | "\n", 422 | "# 5.重要性top24\n", 423 | "imp_feat = [ 'x_80', 'x_2', 'x_81', 'x_95', 'x_1',\n", 424 | " 'x_52', 'x_63', 'x_54', 'x_43', 'x_40',\n", 425 | " 'x_93', 'x_42', 'x_157', 'x_62', 'x_29',\n", 426 | " 'x_61', 'x_55', 'x_79', 'x_59', 'x_69',\n", 427 | " 'x_48', 'x_56', 'x_7', 'x_64']\n", 428 | "print(\"重要的特征个数:\",len(imp_feat))\n", 429 | "# 对一些重要的特征进行填充,\n", 430 | "for feat in imp_feat[:10]: # 填充top 10 ,而不是所有\n", 431 | " if feat in numerical_features: # 数值型用均值\n", 432 | " data[feat] = data[feat].replace(-99,np.nan)\n", 433 | " data[feat] = data[feat].fillna(data[feat].mean()) # 非nan均值\n", 434 | " if feat in categorical_features: # 类别型:不处理\n", 435 | " print(\"这是类别特征:\",feat)\n", 436 | "'''\n", 437 | "# 6.对类别型的特征,进行one-hot \n", 438 | "def set_one_hot(data,categorical_feature):\n", 439 | " rest_feat = list(set(data.columns.values.tolist()) - set(categorical_feature))# 删除自己\n", 440 | " df = data[rest_feat].copy()\n", 441 | " dummies = [df]\n", 442 | " for feat in categorical_feature:\n", 443 | " dummy = pd.get_dummies(data[feat], prefix = feat) # 哑变量变换\n", 444 | " dummies.append(dummy)\n", 445 | " res = pd.concat(dummies,axis = 1) # 横向合并\n", 446 | " print(\"data shape:\",res.shape)\n", 447 | " return res\n", 448 | "\n", 449 | "\n", 450 | "# 最后的结果只对x_157进行onehot,有稍微的提升,其他的反而降分,所以感觉统一不加onehot\n", 451 | "data = set_one_hot(data,['x_157']) \n", 452 | "'''\n", 453 | "\n", 454 | "# 7.对数值型的特征,处理为rank特征(鲁棒性好一点)----其实不处理不影响,因为排序不影响大小关系,是单调的\n", 455 | "for feat in numerical_features:\n", 456 | " data[feat] = data[feat].rank() / float(data.shape[0]) # 排序,并且进行归一化\n", 457 | "\n", 458 | "# 8.划分train 、test set\n", 459 | "train = data.loc[data['y']!=-1,:] # train set\n", 460 | "test = data.loc[data['y']==-1,:] # test set\n", 461 | "print(train.shape)\n", 462 | "print(test.shape)\n", 463 | "\n", 464 | "# 获取特征列,去除id,group, y\n", 465 | "no_features = ['cust_id','cust_group','y'] \n", 466 | "features = [feat for feat in train.columns.values if feat not in no_features]\n", 467 | "print(\"所有特征的维度:\",len(features))\n", 468 | "\n", 469 | "# 9.得到输入X ,输出y\n", 470 | "train_id = train['cust_id'].values\n", 471 | "y = train['y'].values\n", 472 | "X = train[features].values\n", 473 | "print(\"X shape:\",X.shape)\n", 474 | "print(\"y shape:\",y.shape)\n", 475 | "\n", 476 | "test_id = test['cust_id'].values\n", 477 | "test_data = test[features].values\n", 478 | "print(\"test shape\",test_data.shape)\n", 479 | "\n", 480 | "# 10.xgb模型,5折交叉训练与验证\n", 481 | "import xgboost as xgb\n", 482 | "from sklearn.model_selection import StratifiedKFold\n", 483 | "from sklearn.metrics import roc_auc_score\n", 484 | "import time\n", 485 | "\n", 486 | "print(\"start:********************************\")\n", 487 | "start = time.time()\n", 488 | "\n", 489 | "auc_list = []\n", 490 | "pred_list = []\n", 491 | "\n", 492 | "skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=2018)\n", 493 | "for train_index, test_index in skf.split(X, y):\n", 494 | " X_train, X_test = X[train_index], X[test_index]\n", 495 | " y_train, y_test = y[train_index], y[test_index]\n", 496 | " #参数设置\n", 497 | " params = {'booster': 'gbtree',\n", 498 | " 'objective':'binary:logistic',\n", 499 | " 'eta': 0.02,\n", 500 | " 'max_depth':4,\n", 501 | " 'min_child_weight': 6, \n", 502 | " 'colsample_bytree': 0.7,\n", 503 | " 'subsample': 0.7,\n", 504 | " #'gamma':0,\n", 505 | " #'lambda':1,\n", 506 | " #'alpha ':0,\n", 507 | " 'silent':1\n", 508 | " }\n", 509 | " params['eval_metric'] = ['auc'] \n", 510 | " # 数据结构\n", 511 | " dtrain = xgb.DMatrix(X_train, label = y_train)\n", 512 | " dvali = xgb.DMatrix(X_test,label = y_test)\n", 513 | " evallist = [(dtrain,'train'),(dvali,'valid')] # 'valid-auc' will be used for early stopping\n", 514 | " # 模型train\n", 515 | " model = xgb.train(params, dtrain,\n", 516 | " num_boost_round=2000, \n", 517 | " evals = evallist,\n", 518 | " early_stopping_rounds = 100,\n", 519 | " verbose_eval=100)\n", 520 | " # 预测验证\n", 521 | " pred = model.predict(dvali, ntree_limit = model.best_ntree_limit)\n", 522 | " # 评估\n", 523 | " auc = roc_auc_score(y_test,pred)\n", 524 | " print('...........................auc value:',auc)\n", 525 | " auc_list.append(auc)\n", 526 | " # 预测\n", 527 | " dtest = xgb.DMatrix(test_data)\n", 528 | " pre = model.predict(dtest,ntree_limit = model.best_ntree_limit)\n", 529 | " pred_list.append(pre)\n", 530 | "\n", 531 | "print('......................validate result mean :',np.mean(auc_list))\n", 532 | "\n", 533 | "end = time.time()\n", 534 | "print(\"......................run with time: \",(end - start) / 60.0)\n", 535 | "\n", 536 | "print(\"over:*********************************\")\n", 537 | "\n", 538 | "# 11.5折结果均值融合,并保存文件\n", 539 | "mean_auc = np.mean(auc_list)\n", 540 | "print(\"mean auc:\",mean_auc)\n", 541 | "filepath = 'result/xgb_'+ str(mean_auc)+'.csv' # 线下平均分数\n", 542 | "# 转为array\n", 543 | "res = np.array(pred_list)\n", 544 | "print(\"5折结果:\",res.shape)\n", 545 | "\n", 546 | "# 最后结果,mean\n", 547 | "r = res.mean(axis = 0)\n", 548 | "print('result shape:',r.shape)\n", 549 | "result = DataFrame()\n", 550 | "result['cust_id'] = test_id\n", 551 | "result['pred_prob'] = r\n", 552 | "result.to_csv(filepath,index=False,sep=\",\")" 553 | ] 554 | }, 555 | { 556 | "cell_type": "code", 557 | "execution_count": 4, 558 | "metadata": {}, 559 | "outputs": [ 560 | { 561 | "name": "stdout", 562 | "output_type": "stream", 563 | "text": [ 564 | "(15000, 160)\n", 565 | "(10000, 159)\n", 566 | "(10000, 159)\n", 567 | "(15000, 160)\n", 568 | "(10000, 160)\n", 569 | "(25000, 160)\n", 570 | "所有特征的维度: 157\n", 571 | "X shape: (15000, 157)\n", 572 | "y shape: (15000,)\n", 573 | "test shape (10000, 157)\n", 574 | "start:********************************\n", 575 | "......................Start train all data .......................\n", 576 | "Training until validation scores don't improve for 100 rounds.\n", 577 | "[100]\ttraining's auc: 0.86073\n", 578 | "[200]\ttraining's auc: 0.877099\n", 579 | "[300]\ttraining's auc: 0.890507\n", 580 | "[400]\ttraining's auc: 0.904906\n", 581 | "Did not meet early stopping. Best iteration is:\n", 582 | "[450]\ttraining's auc: 0.909721\n", 583 | "......................run with time: 2.5277346928914386\n", 584 | "over:*********************************\n" 585 | ] 586 | }, 587 | { 588 | "data": { 589 | "image/png": "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\nvLw86zTUzFlHKhRrSYViLakQrCMVirWkQrCOGkdE/M/OxLkFT5IkSZIkSUVlA0qSJEmSJElFZQNK\nkiRJkiRJRWUDSpIkSZIkSUVlA0qSJEmSJElFZQNKkiRJkiRJRWUDSpIkSZIkSUVlA0qSJEmSJElF\nZQNKkiRJkiRJRWUDSpIkSZIkSUVlA0qSJEmSJElFZQNKkiRJkiRJRWUDSpIkSZIkNTsbNmzgmGOO\n4fDDD6dv375cc801W8zfeuutlJSUbDH2i1/8gsMOO4y+ffty3nnnNWa6rV67rBOQJEmSJEnaVXvu\nuSfz5s2jpKSEjRs38vnPf55TTz2VY489lhdeeIGqqqot4pctW8bkyZP53e9+x/7778+aNWsyyrx1\natINqIi4ARhG7UqtJ4FLU0opIo4C7gE6Ao9vGt/etdZvrOHACY8VOWO1BpeVVTPaWtJuso5UKNaS\nCsVaUiFYRyoUa0nbUjllWN3riKhb4bRx40Y2btxIRFBTU8Pll1/O+PHjeeaZZ+ri77zzTv7+7/+e\n/fffH4CuXbs2bvKtXJPdghcR/w84DugHlAJHAyfkp38MjAU+m//1hSxylCRJkiRJ2ampqaF///50\n7dqVoUOHMnDgQG6//XbOOOMMPvnJT24Ru3TpUpYuXcpxxx3Hsccey9y5czPKunVq9BVQEXE0MB04\nBmgLPA+MSCktrheagA7AHkAA7YE3IqIbsG9K6dn89WYCZwG/buBeY6ltVNG5cxeuLqsuyjOpdTmg\nY+3fyEi7wzpSoVhLKhRrSYVgHalQrCVtSy6X22rs5ptvpqqqiquuuoru3bvz05/+tG6spqam7jNv\nvPEGa9eu5brrruPNN9/kggsu4O67797qnCgVR6M3oFJK8yPiEWAStVvo7mug+URK6dmI+HdgFbUN\nqNtTSi9HxABgxWahK4Ae27jXNGAaQJ8+fdLXzz+zsA+jVimXy3FueXnWaaiZs45UKNaSCsVaUiFY\nRyoUa0kfx4svvsg777zDm2++ycUXX8yGDRv44IMPuOSSS3jllVc4/PDDOfbYY/nrv/5rAH76059y\nwAEHcPTRR2eceeuQ1Ra8icBQYABwQ0MBEdEbOBToSW2D6cSIGExtM6q+7Z7/JEmSJEmSWpY333yT\nd955B4D169fzb//2bxx11FGsXr2ayspKHnjgAfbaay9eeeUVAM466yz+/d//HYC33nqLpUuXcvDB\n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\n", 590 | "text/plain": [ 591 | "" 592 | ] 593 | }, 594 | "metadata": {}, 595 | "output_type": "display_data" 596 | }, 597 | { 598 | "name": "stdout", 599 | "output_type": "stream", 600 | "text": [ 601 | "有用的特征个数: 93\n", 602 | "有用的特征: ['x_80', 'x_2', 'x_81', 'x_95', 'x_1', 'x_52', 'x_63', 'x_54', 'x_43', 'x_40', 'x_93', 'x_42', 'x_157', 'x_62', 'x_29', 'x_140', 'x_61', 'x_55', 'x_79', 'x_59', 'x_69', 'x_50', 'x_48', 'x_35', 'x_45', 'x_44', 'x_56', 'x_78', 'x_7', 'x_64', 'x_97', 'x_36', 'x_58', 'x_24', 'x_30', 'x_19', 'x_83', 'x_53', 'x_60', 'x_47', 'x_57', 'x_156', 'x_51', 'x_46', 'x_41', 'x_84', 'x_68', 'x_20', 'x_14', 'x_154', 'x_88', 'x_39', 'x_8', 'x_72', 'x_82', 'x_141', 'x_67', 'x_66', 'x_91', 'x_13', 'x_142', 'x_49', 'x_38', 'x_99', 'x_32', 'x_65', 'x_96', 'x_98', 'x_143', 'x_3', 'x_90', 'x_4', 'x_139', 'x_155', 'x_153', 'x_87', 'x_101', 'x_73', 'x_33', 'x_22', 'x_26', 'x_9', 'x_76', 'x_100', 'x_77', 'x_5', 'x_16', 'x_150', 'x_144', 'x_86', 'x_27', 'x_75', 'x_85']\n" 603 | ] 604 | } 605 | ], 606 | "source": [ 607 | "# feature importance\n", 608 | "import numpy as np\n", 609 | "import pandas as pd\n", 610 | "from pandas import DataFrame\n", 611 | "from pandas import Series\n", 612 | "import time\n", 613 | "import matplotlib\n", 614 | "import matplotlib.pyplot as plt\n", 615 | "\n", 616 | "import operator\n", 617 | "\n", 618 | "# 读取文件\n", 619 | "train_xy = pd.read_csv(\"data/train_xy.csv\",header=0,sep=\",\")\n", 620 | "train_x = pd.read_csv(\"data/train_x.csv\",header=0,sep=\",\")\n", 621 | "test_all = pd.read_csv(\"data/test_all.csv\",header=0,sep=\",\")\n", 622 | "\n", 623 | "print(train_xy.shape)\n", 624 | "print(train_x.shape)\n", 625 | "print(test_all.shape)\n", 626 | "\n", 627 | "train = train_xy.copy()\n", 628 | "test = test_all.copy()\n", 629 | "test['y'] = -1\n", 630 | "# 合并一下train 和 test\n", 631 | "data = pd.concat([train,test],axis = 0) # train_xy,test_all索引上连接\n", 632 | "print(train.shape)\n", 633 | "print(test.shape)\n", 634 | "print(data.shape)\n", 635 | "\n", 636 | "# 获取特征列,去除id,group,y\n", 637 | "features = [feat for feat in train.columns.values if feat not in ['cust_id','cust_group','y']]\n", 638 | "print(\"所有特征的维度:\",len(features))\n", 639 | "\n", 640 | "# 得到输入X ,输出y\n", 641 | "train_id = train['cust_id']\n", 642 | "y = train[\"y\"].values\n", 643 | "X = train[features].values\n", 644 | "print(\"X shape:\",X.shape)\n", 645 | "print(\"y shape:\",y.shape)\n", 646 | "\n", 647 | "test_id = test['cust_id']\n", 648 | "test = test[features].values\n", 649 | "print(\"test shape\",test.shape)\n", 650 | "\n", 651 | "# 开始训练\n", 652 | "from sklearn.model_selection import StratifiedKFold\n", 653 | "import lightgbm as lgb\n", 654 | "from sklearn.metrics import roc_auc_score\n", 655 | "import operator\n", 656 | "import time\n", 657 | "\n", 658 | "print(\"start:********************************\")\n", 659 | "start = time.time()\n", 660 | "\n", 661 | "print('......................Start train all data .......................')\n", 662 | "\n", 663 | "# 最后用全部数据train\n", 664 | "train_all = lgb.Dataset(train[features], train['y'])\n", 665 | "# 设置参数\n", 666 | "params = {\n", 667 | " 'boosting_type': 'gbdt',\n", 668 | " 'objective': 'binary',\n", 669 | " 'metric': {'auc'},\n", 670 | " 'max_depth': 4,\n", 671 | " 'min_child_weight': 6,\n", 672 | " 'num_leaves': 16,\n", 673 | " 'learning_rate': 0.02,\n", 674 | " 'feature_fraction': 0.7,\n", 675 | " 'bagging_fraction': 0.7,\n", 676 | " 'bagging_freq': 5,\n", 677 | " #'lambda_l1':0.25,\n", 678 | " #'lambda_l2':0.5,\n", 679 | " #'scale_pos_weight':1,\n", 680 | "}\n", 681 | "model = lgb.train(params,\n", 682 | " train_all,\n", 683 | " num_boost_round=450,\n", 684 | " valid_sets=train_all,\n", 685 | " early_stopping_rounds=100,\n", 686 | " verbose_eval=100)\n", 687 | "\n", 688 | "end = time.time()\n", 689 | "print(\"......................run with time: \",(end - start) / 60.0 )\n", 690 | "print(\"over:*********************************\")\n", 691 | "# 显示top30的特征\n", 692 | "lgb.plot_importance(model,max_num_features = 30,figsize=(20,10))\n", 693 | "plt.show()\n", 694 | "# 对特征重要性排序\n", 695 | "df = pd.DataFrame({'feature': features,'importance': model.feature_importance()}).sort_values(by='importance',ascending = False) # 降序\n", 696 | "use = df.loc[df['importance']!=0,'feature'].tolist()\n", 697 | "print('有用的特征个数:',len(use))\n", 698 | "print('有用的特征:',use)" 699 | ] 700 | }, 701 | { 702 | "cell_type": "code", 703 | "execution_count": null, 704 | "metadata": { 705 | "collapsed": true 706 | }, 707 | "outputs": [], 708 | "source": [] 709 | } 710 | ], 711 | "metadata": { 712 | "kernelspec": { 713 | "display_name": "Python 3", 714 | "language": "python", 715 | "name": "python3" 716 | }, 717 | "language_info": { 718 | "codemirror_mode": { 719 | "name": "ipython", 720 | "version": 3 721 | }, 722 | "file_extension": ".py", 723 | "mimetype": "text/x-python", 724 | "name": "python", 725 | "nbconvert_exporter": "python", 726 | "pygments_lexer": "ipython3", 727 | "version": "3.6.3" 728 | } 729 | }, 730 | "nbformat": 4, 731 | "nbformat_minor": 2 732 | } 733 | -------------------------------------------------------------------------------- /result/readme.txt: -------------------------------------------------------------------------------- 1 | 结果文件会保存在该目录下 2 | -------------------------------------------------------------------------------- /新网银行杯竞赛报告.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingNie/CreditForecast/461712443508e41b848baf1c3caa83c3ae4d53c1/新网银行杯竞赛报告.pdf --------------------------------------------------------------------------------