会员部门在做会员营销是,希望通过数据预测下一次营销活动时,相应活动会员的具体名单和响应概率,以此来制定针对性的营销策略。
# 导入库
import numpy as np # numpy库
import pandas as pd # pandas库
from sklearn.preprocessing import OneHotEncoder # 导入OneHotEncoder库
from sklearn.model_selection import StratifiedKFold, cross_val_score # 导入交叉检验算法
from sklearn.feature_selection import SelectPercentile, f_classif # 导入特征选择方法库
from sklearn.ensemble import AdaBoostClassifier # 导入集成算法
from sklearn.pipeline import Pipeline # 导入Pipe-line库
from sklearn.metrics import accuracy_score # 准确率指标
from sklearn.model_selection import GridSearchCV
# 基本状态查看
def set_summary(df):
print ('Data Overview')
print ('Records: {0}tDimension{1}'.format(df.shape[0], (df.shape[1] - 1))) # 打印数据集X形状
print ('-' * 30)
print (df.head(2)) # 打印前2条数据
print ('-' * 30)
print ('Data DESC')
print (df.describe()) # 打印数据基本描述性信息
print ('Data Dtypes')
print (df.dtypes) # 打印数据类型
print ('-' * 60)
# 缺失值审查
def na_summary(df):
na_cols = df.isnull().any(axis=0) # 每一列是否具有缺失值
print ('NA Cols:')
print (na_cols) # 查看具有缺失值的列
print ('-' * 30)
print ('valid records for each Cols:')
print (df.count()) # 查看每一列有效值(非NA)的记录数
print ('-' * 30)
na_lines = df.isnull().any(axis=1) # 查看每一行是否具有缺失值
print ('Total number of NA lines is: {0}'.format(na_lines.sum())) # 查看具有缺失值的行总记录数
print ('-' * 30)
#查看样本是否均衡
def label_summary(df):
'''
查看每个类的样本量分布
:param df: 数据框
:return: 无
'''
print ('Labesl samples count:')
print (df['value_level'].groupby(df['response']).count()) # 以response为分类汇总维度对value_level列计数统计
print ('-' * 60)
# 数据类型转换
def type_con(df):
'''
转换目标列的数据为特定数据类型
:param df: 数据框
:return: 类型转换后的数据框
'''
var_list = {'edu': 'int32',
'user_level': 'int32',
'industry': 'int32',
'value_level': 'int32',
'act_level': 'int32',
'sex': 'int32',
'region': 'int32'
} # 字典:定义要转换的列及其数据类型
for var, type in var_list.items(): # 循环读出列名和对应的数据类型
df[var] = df[var].astype(type) # 数据类型转换
print ('Data Dtypes')
print (df.dtypes) # 打印数据类型
print ('-' * 30)
return df
# NA值替换
def na_replace(df):
'''
将数据集中的NA值使用自定义方法替换
:param df: 数据框
:return: NA值替换后的数据框
'''
na_rules = {'age': df['age'].mean(),
'total_pageviews': df['total_pageviews'].mean(),
'edu': df['edu'].median(),
'edu_ages': df['edu_ages'].median(),
'user_level': df['user_level'].median(),
'industry': df['user_level'].median(),
'act_level': df['act_level'].median(),
'sex': df['sex'].median(),
'red_money': df['red_money'].mean(),
'region': df['region'].median()
} # 字典:定义各个列数据转换方法
df = df.fillna(na_rules) # 使用指定方法填充缺失值
print ('Check NA exists:')
print (df.isnull().any().sum()) # 查找是否还有缺失值
print ('-' * 30)
return df
# 标志转换
def symbol_con(df, enc_object=None, train=True):
'''
将分类和顺序变量转换为二值化的标志变量
:param df: 数据框
:param enc_object: sklearn的标志转换对象,训练阶段设置默认值为None;预测阶段使用从训练阶段获得的转换对象
:param train: 是否为训练阶段的判断状态,训练阶段为True,预测阶段为False
:return: 标志转换后的数据框、标志转换对象(如果是训练阶段)
'''
convert_cols = ['edu', 'user_level', 'industry', 'value_level', 'act_level', 'sex', 'region'] # 选择要做标志转换的列名
df_con = df[convert_cols] # 选择要做标志转换的数据
df_org = df[['age', 'total_pageviews', 'edu_ages', 'blue_money', 'red_money', 'work_hours']].values # 设置不作标志转换的列
if train == True: # 如果处于训练阶段
enc = OneHotEncoder() # 建立标志转换模型对象
enc.fit(df_con) # 训练模型
df_con_new = enc.transform(df_con).toarray() # 转换数据并输出为数组格式
new_matrix = np.hstack((df_con_new, df_org)) # 将未转换的数据与转换后的数据合并
return new_matrix, enc
else:
df_con_new = enc_object.transform(df_con).toarray() # 使用训练阶段获得的转换对象转换数据并输出为数组格式
new_matrix = np.hstack((df_con_new, df_org)) # 将未转换的数据与转换后的数据合并
return new_matrix
raw_data=pd.read_excel('order.xlsx',sheetname=0)
X=raw_data.drop('response',axis=1)
y=raw_data['response']
set_summary(raw_data)
na_summary(raw_data)
label_summary(raw_data)
X_t1=na_replace(X)
X_t2=type_con(X_t1)
X_new, enc = symbol_con(X_t2, enc_object=None, train=True) # 将分类和顺序数据转换为标志
#开始建模
transform=SelectPercentile(f_classif,percentile=50)#
model_adaboost=AdaBoostClassifier()
model_pipe=Pipeline(steps=[('anova',transform),('model_adaboost',model_adaboost)])
cv=StratifiedKFold(5)
n_estimators=[20,50,80,100,150]
score_list=list()
for n in n_estimators:
model_pipe.set_params(model_adaboost__n_estimators=n)
score_tmp=cross_val_score(model_pipe,X_new,y,cv=cv)
score_list.append(score_temp)
score_mean=list()
score_std=list()
for i in range(len(score_list)):
score_mean.append(score_list[i].mean())
score_std.append(score_list[i].std())
a=pd.DataFrame([score_mean,score_std],columns=n_estimators)
print(a)
参数为80以后,调参的效果已经不明显了,所以选取80
transform.fit(X_new,y)
X_final=transform.transform(X_new)
final_model=AdaBoostClassifier(n_estimators=100)
final_model.fit(X_final,y)
new_data=pd.read_excel('order.xlsx',sheetname=1)
final_reponse=new_data['final_response']
new_data=new_data.drop(['final_response'],axis=1)
set_summary(new_data)
na_summary(new_data)
new_X_t1 = na_replace(new_data)
new_X_t2 = type_con(new_X_t1)
new_X_t3 = symbol_con(new_X_t2, enc_object=enc, train=False)
new_X_final = transform.transform(new_X_t3)
# 输出预测值以及预测概率
predict_labels = pd.DataFrame(final_model.predict(new_X_final), columns=['labels']) # 获得预测标签
predict_labels_pro = pd.DataFrame(final_model.predict_proba(new_X_final), columns=['pro1', 'pro2']) # 获得预测概率
predict_pd = pd.concat((new_data, predict_labels, predict_labels_pro), axis=1) # 将预测标签、预测数据和原始数据X合并
print ('Predict info')
print (predict_pd.head(2)) # 打印前2条结果
print ('-' * 60)
# 将预测结果写入Excel
writer = pd.ExcelWriter('order_predict_result.xlsx') # 创建写入文件对象
predict_pd.to_excel(writer, 'Sheet1') # 将数据写入sheet1
writer.save() # 保存文件
# 后续--与实际效果的比较
print ('final accuracy: {0}'.format(accuracy_score(final_reponse, predict_labels)))
均方达到0.86,准确率和做交叉验证时非常一致,说明该模型鲁棒性(即稳定性)非常好。