CDA Level2 模拟题2 Python代码实现

前言: 模拟题2是一道2分类预测类建模,需要预测利润而不是传统的准确率或召回率等,这就代表用一个模型是没有办法获得最高分的,必须根据不同的情况进行调参。

虽然没有答案,但是好在有一个类似的练习赛
地址是: http://jingsai.cda.cn/info/id/6.html
需要注意的是:
1、练习赛的数据集小于模拟题的数据集,不要搞混了;
2、练习赛的评判标准是accuracy
在这里插入图片描述
接下去的代码实现都是基于练习赛的数据集,以线上的accuracy作为评判标准。同时会写利润预测的metrics以供参考(目前还没有写出来)。如果要做到最优化,还是要使用代价敏感算法,但这已经超纲了。

重要提示:
1、从练习赛分数就可以看出,大家的得分都是比较接近的,而且不管是训练集还是测试集的数据质量都很差,很难做特征工程;
2、虽然练习赛分数是按accuracy,但是已经结束的B榜还是按照混淆矩阵模拟利润的,所以还是有必要自己自定义一个metrics以便模型调参。

01/ 任务

A公司希望发掘用户购买产品的行为习惯,建立产品精准营销模型,对有意向的客户进行精准营销,增加收入,减少开支。

02/ 数据

参赛选手将取得训练数df_training.csv及测试数据df_test.csv。

2.1 训练数据

训练数据包含6000笔客户资料;每笔客户资料包含12个字段(1个客户ID字段、10个输入字段及1个目标字段-Purchase or not购买与否(0代表未购买,1代表购买)。

2.2 测试数据

测试数据包含2000笔客户资料;字段个数与训练数据相同,唯目标字段的值全部填“Withheld”。

2.3 数据字段说明

在这里插入图片描述

03/ 评分标准

我们通过混淆矩阵(Confusion matrix)来评价分类模型的准确率。准确率越高,说明正确预测出响应营销效果越好,混淆矩阵如下图所示:
在这里插入图片描述
准确率公式:
在这里插入图片描述
其中,acc是准确率,是正确预测购买产品和不买产品的数目与总数目(也就是2000)的比率;TP表示正确预测购买产品的客户数目,FN表示实际上购买但预测为不买的客户数目,FP是实际上不买但预测为购买的客户数目,TN是正确预测不购买产品的客户数目,TP+FP+FN+TN也就是测试数据里客户的总数2000人。

04/ 营销模型预演-B榜分数

实务应用中,老板是否采用你的营销方案往往不是以预测结果的正确率做为考虑‚而是以模型能带给公司多少的利润最为最终的考虑。

参与比赛的成绩以公司获得的利润结果排序,模型越赚钱者越好。为此‚我们做了以下两个假设,选手需分别将预测结果存于results_A.csv及results_B.csv。

两个假设如下:

(A) P产品价格$2,800元,成本$800元,营销文宣成本$500元/每位顾客,价格减成本后,每个产品若有卖出公司则获利$1,500元。根据上述数据‚以下是这个产品的成本矩阵:
在这里插入图片描述
因此‚若模型对测试数据预测的混淆矩阵如下:
在这里插入图片描述
则此模型的得分为1411500-76500=173,500分,分数越高越好。

(B) P产品价格$2,000元,成本$800元,营销文宣成本$500元/每位顾客,价格减成本后,每个产品若有卖出公司则获利$700元。根据上述数据‚以下是这个产品的成本矩阵:
在这里插入图片描述
因此‚若模型对测试数据预测的混淆矩阵如下:
在这里插入图片描述
则此模型的得分为83700-170500= 58,100-85,000=-26,900 分,分数越高越好。

以下是正文了,教你如何达到baseline(也仅仅只是baseline)

1、数据导入

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df_test = pd.read_csv('df_test.csv')
df_training = pd.read_csv('df_training.csv')

df_training.info()

在这里插入图片描述
分析:很多应该是数值型的都是object,后面需要转变形式。

2、数据分析(画图)

object_features = list(set(df_training.columns) - set(['ID', "'Purchase or not'"]))
for feature in object_features:
    print(feature)
    why_temp = df_training[df_training[feature]=='?']
    why_temp_per = why_temp["'Purchase or not'"].value_counts()[1]/why_temp.shape[0]
    print('空值的百分比:%.2f'% why_temp_per)
    
    df_train = df_training.copy()
    df_train[feature] = df_train[feature].replace('?', np.nan).dropna()
    if feature not in ["'User area'", 'gender']:
        df_train[feature] = pd.to_numeric(df_train[feature])
    if len(df_train[feature].unique())>=20:
        df_train['cut'] = pd.cut(df_train[feature],bins=20, include_lowest=True, right=False, precision=0)
        temp_pivot = df_train.pivot_table(index="cut", values='ID', columns="'Purchase or not'", aggfunc='count', fill_value=0)
    else:
        temp_pivot = df_train.pivot_table(index=feature, values='ID', columns="'Purchase or not'", aggfunc='count', fill_value=0)
    temp_pivot['percent'] = temp_pivot[1] / (temp_pivot[0]+temp_pivot[1])
    temp_pivot['percent'].plot.bar()
    plt.show()

说明:上面的代码主要是用来数据清洗和画图的,也就是传统的EDA

根据上面的数据分析,可以得出几个结论:
1、每个特征值的缺失值数量都差不多,且缺失值的’Purchase or not’比例基本接近,都是20%左右。所以针对这一点,不建议做缺失值填充;
2、对于gender来说,Female的比例(指’Purchase or not’中1的占比,下同)高于Male一大截;
3、对于Active User来说,0的用户要高于1;
4、对于age来说,总体呈近正态分布,48-62岁明显高于其它年龄段,72岁以后接近于0,但84岁有一个异常值,建议删除;
5、对于User area(用户地区)来说,Taichung的比例远高于Tainan和Taipei;
6、对于Product using score(产品使用分数)来说,410分以下的购买可能基本为0,其它年龄段则分布不均、时高时低;
7、对于Estimated salary(估计薪资)来说,除了少数的几个区间外,大部分都在20%左右;
8、对于Point balance(点数余额)来说,有2个区间特别高,其它则忽高忽低;
9、对于Cumulative using time(使用累计时间)来说,除0特别高以外,8特别低以外,其它都差不多;
10、对于Product service usage(产品服务使用量)来说,3和4特别高,1在30%,2则仅10%左右;
11、对于Pay a monthly fee by credit card(是否使用信用卡付月费)来说,两者差不多。

3、数据处理

df_total = pd.concat([df_training, df_test], axis=0)
area_map = {'Taichung': 0, 'Tainan': 1, 'Taipei': 2, '?': 3}
gender_map = {'Female': 0, 'Male': 1, '?': 3}
df_total["'User area'"] = df_total["'User area'"].map(area_map)
df_total["gender"] = df_total["gender"].map(gender_map)
df_total = df_total.replace('?', '-1')
for feature in object_features:
    df_total[feature] = pd.to_numeric(df_total[feature])
new_train_data = df_total[df_total["'Purchase or not'"]!='Withheld']
new_train_data["'Purchase or not'"] = pd.to_numeric(new_train_data["'Purchase or not'"])
new_test_data = df_total[df_total["'Purchase or not'"]=='Withheld']
new_train_data.info()

在这里插入图片描述

4、建模

#简单预测
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
import lightgbm as lgb

predictors = ["'Product using score'", "'User area'", "gender", "age", "'Cumulative using time'", "'Point balance'",
             "'Product service usage'", "'Pay a monthly fee by credit card'", "'Active user'", "' Estimated salary'"]

# cate_feature_en = ["'User area'", "gender", 'Pay a monthly fee by credit card', 'Active user']

params = {'num_leaves': 30, #结果对最终效果影响较大,越大值越好,太大会出现过拟合
          'min_data_in_leaf': 30,
          'objective': 'binary', #定义的目标函数
          'max_depth': -1,
          'learning_rate': 0.01,
          "min_sum_hessian_in_leaf": 6,
          "boosting": "gbdt",
          "feature_fraction": 0.8,  #提取的特征比率
          "bagging_freq": 1,
          "bagging_fraction": 0.8,
          "bagging_seed": 11,
          "lambda_l1": 0.1,             #l1正则
          # 'lambda_l2': 0.001,     #l2正则
          "verbosity": -1,
          "nthread": -1,                #线程数量,-1表示全部线程,线程越多,运行的速度越快
          'metric': {'binary_logloss'},  ##评价函数选择
          "random_state": 2019, #随机数种子,可以防止每次运行的结果不一致
          # 'device': 'gpu' ##如果安装的事gpu版本的lightgbm,可以加快运算
          }

X_train, X_val, y_train, y_val = train_test_split(new_train_data[predictors], new_train_data["'Purchase or not'"], 
                test_size=0.2, random_state=2019)
train_data = lgb.Dataset(X_train, label=y_train)
val_data = lgb.Dataset(X_val, label=y_val, reference=train_data)

evals_result = {}  #记录训练结果所用

model = lgb.train(params, 
                  train_data, 
                  num_boost_round=20000, 
                  valid_sets=val_data, 
                  early_stopping_rounds=100, 
#                   categorical_feature = cate_feature_en,
                  evals_result = evals_result,
                  verbose_eval=500)

pred1 = model.predict(new_test_data[predictors])
pred = np.where(pred1>=0.5, 1, 0)

#计算最终利润
con_matrix = confusion_matrix(y_val, pred)
print(con_matrix)
last_profit1 = con_matrix[0][0] * 1500 - con_matrix[0][1] * 500
print(last_profit1)
last_profit2 = con_matrix[0][0] * 700 - con_matrix[0][1] * 500
print(last_profit2)

df_test['Predicted_Results'] = pred
df_test[['ID', 'Predicted_Results']].to_csv('test_pred.csv', index=False)

有些人没有LightGBM,用RandomForest模型也是一样的

#使用RF进行简单预测
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import KFold

X_train, X_val, y_train, y_val = train_test_split(new_train_data[object_features], new_train_data["'Purchase or not'"], 
                test_size=0.2, random_state=2019, stratify=new_train_data["'Purchase or not'"])

rf = RandomForestClassifier(n_estimators=50, min_samples_split=5, min_samples_leaf=3)
rf.fit(X_train, y_train)
print(accuracy_score(rf.predict(X_val), y_val))

pred = rf.predict(new_test_data[object_features])
new_test_data['Predicted_Results'] = pred
new_test_data[['ID', 'Predicted_Results']].to_csv('up_answer.csv', index=False)

后续优化:
1、自定义损失函数;
2、自定义metrics

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值