游戏玩家付费金额预测大赛-解决策略


image.png

盯着上面的这个图,看着看着就想到: 使用前7天付费的数据,分两步,先分类出谁会继续付款,然后回归分析继续给钱的玩家会付款多少?还有第三部分,就是忽略掉前7天没付款,但是后面45天会付款的,然后就变成以下的图了。

image.png

一、分类

1、准备数据

先对数据进行一下预处理,主要是对object类的feature处理掉,没有什么归一化之类的处理的。

data = pd.read_csv("tap_fun_train.csv", parse_dates=True)

#提取object及其对应的数据
object_columns_df = data.select_dtypes(include=["object"])
#object罗列出来了。
print(object_columns_df.iloc[1])

# 有一个object特征:register_time,处理掉

data['register_time_month'] = data.register_time.str[5:7]
data['register_time_day'] = data.register_time.str[8:10]
data = data.drop(['register_time'],axis=1) 

# object转换float
data[['register_time_month','register_time_day']] = data[['register_time_month','register_time_day']].apply(pd.to_numeric)
# data=pd.DataFrame(data,dtype=np.float) 

# 对于注册时间,拆分开月和日之后,再合并一个数值,更好反馈时间的前后
data['register_time_count'] = data['register_time_month'] * 31 + data['register_time_day'] 

data.shape
# (2288007, 111)

#保存前7天会给钱的客户
data_7_pay = copy.copy(data[data['pay_price']>0])
data_7_pay.shape
# (41439, 111)
data_7_pay.to_csv ("tap_fun_train_7_pay.csv")


打标签label,删掉不该有的特征等。

# -------------------------读取train set中前7天有付款的玩家明细
data = pd.read_csv("tap_fun_train_7_pay.csv", index_col=0, parse_dates=True)

print(data.shape)  #(41439, 111)

# -------------------------打标签
data['7_45_same_pay_label'] = (data['pay_price'] == data['prediction_pay_price'])
data['7_45_same_pay_label']=data['7_45_same_pay_label'].map({True:1,False:0})

data['7_45_same_pay_label'].value_counts()

# 1    30130   前7天给,后面都不付款了
# 0    11309   前7天给了,后续还给的。

# 删掉不需要的字段,比如45天的付费金额(对于test set是没有的字段),user_id
data = data.drop(['prediction_pay_price', 'user_id'],axis=1)
data.shape

2、训练分类模型

分拆数据,训练和验证部分。


from sklearn.model_selection  import train_test_split

label = '7_45_same_pay_label'

# 将X和Y拆分开
X = data.loc[:, data.columns != label]
y = data.loc[:, data.columns == label]

X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.22, random_state = 0)
print("100% data")
print("Number transactions train dataset: ", len(X_train))
print("Number transactions test dataset: ", len(X_test))
print("Total number of transactions: ", len(X_train)+len(X_test))

# print(y_train.info())

# train和test拆分后,把train部分重新组合成data_train,也就是把test部分完整保留下来,除了test用,不参加任何处理了。
data_train = pd.concat( [X_train, y_train], axis=1 )
print(data.shape)
print('---------------------------------------')
print(data_train.shape)
# show_class(data_train,label)  自己写的函数,屏蔽了吧


#创建一个dataframe,然后对模型的效果进行记录。最后小结。
thresholds = [0.1,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.6,0.65,0.7,0.75,0.8,0.85,0.9]
thresholds_2 = thresholds[:]  #= thresholds,如果这样复杂是,浅复制,映射同一块内存
thresholds_2.append('time')

print(thresholds_2)
result_model_f1 = pd.DataFrame(index=thresholds_2)

print(result_model_f1)

训练,用GradientBoostingClassifier训练试试先,然后在留下的22%数据中,进行验证,f1有87.55%,挺不错的了。

import time
start = time.time()
print('start time:',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))

from sklearn.ensemble import GradientBoostingClassifier

gradient_boosting_classifier = GradientBoostingClassifier()
gradient_boosting_classifier.fit(X_train,y_train.values.ravel())

y_pred = gradient_boosting_classifier.predict(X_test.values)

# Plot non-normalized confusion matrix
plt.figure()
plot_confusion_matrix(y_test,y_pred,title='Confusion matrix')

end = time.time()
print(end-start,'s')
image.png

试试用不同的threshold value来划分,看看有没更优的threshold value,0.45会好一点点,87.69%,比0.5,高0.14%,小幅度提升。

#2、----------------------------------预测结果----------------------------------
y_pred_proba = gradient_boosting_classifier.predict_proba(X_test.values)  #array of shape = [n_samples, n_classes], or a list of n_outputs

#3、----------------------------------记录各种threshold下的结果----------------------------------
result_model_f1['GradientBoostingClassifier'] = 0  #扩充列,全为0.
print(result_model_f1)

for i in thresholds:
    y_test_predictions_high_recall = y_pred_proba[:,1] > i
    print('Threshold >= %s'%i)
    print_recall_precision_f1(y_test,y_test_predictions_high_recall)
    
print("------------------------------------")
    
for i in thresholds:
    y_test_predictions_high_recall = y_pred_proba[:,1] > i
    plt.figure(figsize=(4,4))
    plot_confusion_matrix(y_test,y_test_predictions_high_recall, title='Threshold >= %s'%i)
    result_model_f1.loc[i,'GradientBoostingClassifier'] = f1_score(y_test.values,y_test_predictions_high_recall) #记录f1
    
result_model_f1.loc['time','GradientBoostingClassifier'] = end-start #记录时间
    
print(result_model_f1)
image.png

备注下:
用xgboost也试过,默认参数下,差一点点。


image.png

导出模型

from sklearn.externals import joblib

print(gradient_boosting_classifier)
joblib.dump(gradient_boosting_classifier, 'gradient_boosting_classifier.model')


二、回归

1、准备数据

data = pd.read_csv("tap_fun_train_7_pay.csv", index_col=0, parse_dates=True)

data_pay_more = copy.copy(data[data['pay_price']<data['prediction_pay_price']])
data_pay_more.shape

#  (11309, 111)  前7天付款,且后来继续付款的有1.1万人。用来做回归。


#删掉user_id
data_pay_more = data_pay_more.drop([ 'user_id'],axis=1)
data = copy.copy(data_pay_more)
data.shape

2、训练模型

分拆数据,训练与验证部分

from sklearn.model_selection  import train_test_split

Quantity = 'prediction_pay_price'

# 将X和Y拆分开
X = data.loc[:, data.columns =='pay_price']
# X = data.loc[:, data.columns != Quantity]
y = data.loc[:, data.columns == Quantity]

X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.22, random_state = 0)
print("100% data")
print("Number transactions train dataset: ", len(X_train))
print("Number transactions test dataset: ", len(X_test))
print("Total number of transactions: ", len(X_train)+len(X_test))

来,训练吧,还是GradientBoosting,但是是Regressor版,GradientBoostingRegressor。

import time
start = time.time()
print('start time:',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))

from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score

gradient_boosting_regression = GradientBoostingRegressor()
gradient_boosting_regression.fit(X_train,y_train.values.ravel())

y_pred = gradient_boosting_regression.predict(X_test.values)

# The mean squared error
print("Root Mean squared error: %.2f"
      % mean_squared_error(y_test, y_pred) ** 0.5)
# Explained variance score: 1 is perfect prediction
print('Variance score: %.2f' % r2_score(y_test, y_pred))

end = time.time()
print(end-start,'s')

image.png

其实Root Mean Squared error,都去到851了,爆表啊,先不管,上系统看看先。

三、用模型预测,上系统看看排名

1、把test set处理得跟train set训练前的样子,要不模型不认啊

data = pd.read_csv("tap_fun_test.csv", parse_dates=True)
data.shape #(828934, 108)

#提取object及其对应的数据
object_columns_df = data.select_dtypes(include=["object"])
#object罗列出来了。
print(object_columns_df.iloc[1])
# 有一个object的feature,register_time ,处理掉

data['register_time_month'] = data.register_time.str[5:7]
data['register_time_day'] = data.register_time.str[8:10]
data = data.drop(['register_time'],axis=1) 
# object转换float
data[['register_time_month','register_time_day']] = data[['register_time_month','register_time_day']].apply(pd.to_numeric)

# 对于注册时间,拆分开月和日之后,再合并一个数值,更好反馈时间的前后
data['register_time_count'] = data['register_time_month'] * 31 + data['register_time_day'] 

data.shape
#(828934, 110)

# 保存前7天会给钱的客户
data_7_pay = copy.copy(data[data['pay_price']>0])
data_7_pay.shape  # (19549, 110)
data_7_pay.to_csv ("tap_fun_test_7_pay.csv")

# 保存前7天没有给钱的客户
data_7_NOT_pay = copy.copy(data[data['pay_price']==0])
data_7_NOT_pay.shape  #(809385, 110)
data_7_NOT_pay.to_csv ("tap_fun_test_7_NOT_pay.csv")

2、把test set里面前7天没付款的直接预测后续不会给钱。

data_test = pd.read_csv("tap_fun_test_7_NOT_pay.csv", index_col=0, parse_dates=True)
print(data_test.shape)# (809385, 110) , 80万的客户

# 把user_id和前7天的0元pay_price提炼出来。
data_test_part1 = data_test[['user_id','pay_price']]
data_test_part1.rename(columns={'pay_price':'prediction_pay_price'}, inplace = True)
data_test_part1.to_csv('tap_fun_test_part1_still0.csv')

3、在前7天有付费的客户中分类出后续不再付费的客户,并保存

data_test = pd.read_csv("tap_fun_test_7_pay.csv", index_col=0, parse_dates=True)
print(data_test.shape)  # (19549, 110)

data_test_model = data_test.drop([ 'user_id'],axis=1),# 跑模型前要删掉user_id

#跑分类模型
y_test_pred = gradient_boosting_classifier.predict(data_test_model.values)

# 结果转ndarray换成dataframe
y_test_pred = pd.DataFrame(y_test_pred, columns= {'pred_label'})

# 为了把index清零,重头开始,先转成ndarray,再转回来dataframe
columns_test = data_test.columns
data_test = data_test.values
data_test = pd.DataFrame(data_test, columns = columns_test )

# 重新把预测结果放回去原始数据
y_test_pred = pd.concat([data_test, y_test_pred], axis=1)
y_test_pred.shape # (19549, 111)


y_test_pred['pred_label'].value_counts()
# 1    15587 不会再给钱的有这么多
# 0     3962  会继续给钱的。

# part2:把1的留出来,并预测为原来的值。
# part3:把0的放到回归里面去猜。
y_test_pred_part2 = copy.copy(y_test_pred[y_test_pred['pred_label']==1])
y_test_pred_part3 = copy.copy(y_test_pred[y_test_pred['pred_label']==0])

y_test_pred_part2_user_id = pd.DataFrame(y_test_pred_part2,columns ={'user_id'})
y_test_pred_part2_pay = pd.DataFrame(y_test_pred_part2,columns ={'pay_price'})
y_test_pred_part2 = pd.concat([y_test_pred_part2_user_id, y_test_pred_part2_pay], axis=1)
y_test_pred_part2.rename(columns={'pay_price':'prediction_pay_price'}, inplace = True)

y_test_pred_part2.to_csv('tap_fun_test_part2_nopaymore.csv')
y_test_pred_part3.to_csv('tap_fun_test_part3_paymore.csv')

4、前7天付费的,【分类】预判会继续付费的,【回归】出付费金额

y_test_pred_part3 = pd.read_csv("tap_fun_test_part3_paymore.csv", index_col=0, parse_dates=True)
y_test_pred_part3.shape #(3962, 111)

# 把user_id保留出来。
user_id_pay_more = y_test_pred_part3['user_id'].values
user_id_pay_more[:10]

# 提出需要的字段,也就是pay_price
y_test_pred_part3_test = pd.DataFrame(y_test_pred_part3,columns=['pay_price'])
y_test_pred_part3_test.shape

# 跑模型
y_test_pred_part3_howmuch = gradient_boosting_regression.predict(y_test_pred_part3_test.values)

# 把预测结果和user_id合并回去。
y_test_pred_part3_user_id = pd.DataFrame(user_id_pay_more,columns = {'user_id'})
y_test_pred_part3_howmuch = pd.DataFrame(y_test_pred_part3_howmuch,columns = {'prediction_pay_price'})

y_test_pred_part3 = pd.concat([y_test_pred_part3_user_id, y_test_pred_part3_howmuch], axis=1)
y_test_pred_part3.shape  # (3962, 2)

y_test_pred_part3.to_csv('tap_fun_test_part3_paymore_result.csv')

5、重新合并三块的结果

pred_part1 = pd.read_csv("tap_fun_test_part1_still0.csv", index_col=0, parse_dates=True)
print(pred_part1.shape)
pred_part2 = pd.read_csv("tap_fun_test_part2_nopaymore.csv", index_col=0, parse_dates=True)
print(pred_part2.shape)
pred_part3 = pd.read_csv("tap_fun_test_part3_paymore_result.csv", index_col=0, parse_dates=True)
print(pred_part3.shape)

# (809385, 2)
# (15587, 2)
# (3962, 2)

pred = pred_part1.append(pred_part2)
pred = pred.append(pred_part3)
pred.shape # (828934, 2)

pred.to_csv('result.csv') 

幸好最后的数量跟test set的客户规模对得上。

6、上系统看分数
有个小插曲,打开文件,把第一列index删掉,然后保存为逗号作为分割的csv文件。


image.png

改用RandomForestClassifier+GradientBoostingregression,基本没变化。


image.png

改用RandomForestClassifier和LinearRegression,效果突然降低了10。


image.png

四、结果分析

从上面三次结果看,回归部分才是提升的关键所在。
sklearn里面有哪些regressor,点这里

image.png

image.png

image.png

image.png

image.png

image.png

image.png

改用RandomForestClassifier和huber_regressor


image.png

迟点再试试其他的regressor吧。

五、优化方向

1、后续试试对三个部分的RMES看看。
2、归回部分、被忽略的后来付费用户部分。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容