Python:需求预估

作者: slade_sal | 来源:发表于2017-07-12 09:42 被阅读54次

    之前写了一篇以基于elastic的需求预估的文章,只不过用的是R语言开发的,最近在学python,就仿照逻辑写了一篇python的,主要修改点如下:

    • 用决策树替换了elastic算法
    • 用分层抽样替换了组合抽样

    需要看详细理论及思考过程参考链接:商品需求预估

    python code如下:

    # -*- coding:utf-8 -*-
    import pandas as pd
    import numpy as np
    import random as rd
    from sklearn import tree
    
    # 读取数据
    data_orgin = pd.read_table("C:/Users/17031877/Desktop/supermarket_second_hair_washing_train.txt")
    data_deal_1 = data_orgin.drop(['aimed_date', 'member_id', 'age', 'gender', 'diff_rgst'], axis=1)
    

    这边是常规的数据读取,删除了不必要的列


    #因变量单列
    label = data_deal_1['label']
    
    # 用户分量级
    value00 = ['max_date_diff', 'aimed_max_date_diff']
    data00 = data_deal_1[value00]
    
    value01 = ['max_pay', 'per_pay', 'six_month_max_pay', 'six_month_per_pay', 'three_month_max_pay', 'three_month_per_pay',
               'one_month_max_pay', 'one_month_per_pay', 'fifteen_day_max_pay', 'fifteen_day_per_pay', 'aimed_max_pay',
               'aimed_per_pay', 'aimed_six_month_max_pay', 'aimed_six_month_per_pay', 'aimed_three_month_max_pay',
               'aimed_three_month_per_pay', 'aimed_one_month_max_pay', 'aimed_one_month_per_pay',
               'aimed_fifteen_day_max_pay', 'aimed_fifteen_day_per_pay', 'qty_drtn_seven', 'qty_drtn_fourteen']
    data01 = data_deal_1[value01]
    
    value02 = ['cnt_time', 'six_month_cnt_time', 'three_month_cnt_time', 'one_month_cnt_time', 'fifteen_day_cnt_time',
               'aimed_cnt_time', 'aimed_six_month_cnt_time', 'aimed_three_month_cnt_time', 'aimed_one_month_cnt_time',
               'aimed_fifteen_day_cnt_time', 'pv_times_seven', 'pv_times_fourteen', 'search_times_seven',
               'search_times_fourteen', 'clc_times_seven', 'clc_times_fourteen', 'cart2_times_seven',
               'cart2_times_fourteen', 'cart1_times_seven', 'cart1_times_fourteen', 'unpay_times_seven',
               'unpay_times_fourteen']
    data02 = data_deal_1[value02]
    
    value03 = ['pv_visit_last_period', 'search_last_period', 'clc_last_period', 'cart2_last_period', 'cart1_last_period',
               'unpay_last_period']
    data03 = data_deal_1[value03]
    

    因为不同量级的数据之后做异常点处理的时候截断位置不同,所有需要分割数据处理


    def test_function_one(x, l):
        k = x.dropna(how='any')
        y = k.quantile(l)
        z = k.max()
        x[x > y] = y
        x = x.fillna(value=z)
        return x
    for i in range(len(data00.columns)):
        data00.iloc[:, i] = test_function_one(data00.iloc[:, i], 0.98)
    
    def test_function_two(x, l):
        k = x.dropna(how='any')
        y = k.quantile(l)
        z = 0
        x[x > y] = y
        x = x.fillna(value=z)
        return x
    for i in range(len(data01.columns)):
        data01.iloc[:, i] = test_function_two(data01.iloc[:, i], 0.95)
    for i in range(len(data02.columns)):
        data02.iloc[:, i] = test_function_two(data02.iloc[:, i], 0.99)
    
    def test_function_three(x):
        z = 14
        x[x > z] = z
        x = x.fillna(value=z)
        return x
    for i in range(len(data03.columns)):
        data03.iloc[:, i] = test_function_three(data03.iloc[:, i])
    # 数据合并
    data_train = pd.concat([label, data00, data01, data02, data03], axis=1)
    

    根据数据量的不同做数据分割,跑上面写完的code函数就可以


    #数量级对比
    zero_case = data_train[data_train['label'] == 0]['label'].count()
    print '负样本数:%d' % zero_case
    one_case = data_train[data_train['label'] == 1]['label'].count()
    print '正样本数: %d' % (one_case)
    
    负样本数:292936
    正样本数: 3973
    Backend TkAgg is interactive backend. Turning interactive mode on.
    

    实际看下来,正负样本的差异的确还是很大,这个其实做多了就有经验,常规的来看,潜在的浏览、搜索到最后的成单,普遍自然转化不到1%,也正是这么低的转化,才需要一些算法来做信息抓去。


    def case_sample(x, y, z):
        diff_case = pd.DataFrame(x[y]).drop_duplicates([y])
        result = []
        result = pd.DataFrame(result)
        for i in range(len(diff_case)):
            k = np.array(diff_case)[i]
            data_set = x[x[y] == k[0]]
            nrow_nb = data_set.iloc[:, 0].count()
            data_set.index = range(nrow_nb)
            index_id = rd.sample(range(nrow_nb), int(nrow_nb * z))
            result = pd.concat([result, data_set.iloc[index_id, :]], axis=0)
        return result
    
    
    zero_case = data_train[data_train['label'] == 0]
    one_case = data_train[data_train['label'] == 1]
    # 开始分层抽样
    new_zero_case = case_sample(zero_case, 'unpay_last_period', 0.1)
    # 新数量级对比
    new_zero_case_count = new_zero_case[new_zero_case['label'] == 0]['label'].count()
    # 数据集合并
    new_data_train = pd.concat([new_zero_case, one_case], axis=0)
    

    case_sample是一个简单的分层抽样的小函数,x是数据集,y是分层变量,z是抽样占比;新的样本new_data_train中正负样本比例在1:10左右,这边的样本比是我自己设置的,不一定是最合理的;且此处也不一定要求一定用分层抽样,只是我用来练练手的;推荐还是遵从奥卡姆原理,在未知的情况下,尽可能简单的解决问题,比如组合抽样就是很不错的方法。


    #函数设置
    clf = tree.DecisionTreeRegressor(criterion='mse', max_features='log2', random_state=1)
    
    #函数拟合
    y = new_data_train['label']
    x = new_data_train.drop('label', 1)
    clf.fit(x, y)
    
    #数据预测
    y_predict = clf.predict(x)
    
    # 结果对比
    y.index = range(len(y))
    combined_date = pd.concat([y, pd.DataFrame(y_predict)], axis=1)
    combined_date.columns = ['actual', 'predict']
    

    这边稍微讲解一下,我认为的sklearnDecisionTreeRegressor中比较终于的参数设置,criterion这边为模型优化的标准,常规的有msemae,建议在数据量差异不大的时候多考虑msemax_features是每次训练用的特征个数,综合特征量级考虑,一般有log2sqrt,尽可能是抽取比例在70%;max_depth刚开始可以默认,第一类模型出来后,可在结果附近迭代,寻找out of bag最小的error下的值;另外,我没有发现有weight设置,可能是我不熟悉,但是如果sklearn这边不提供weight的化,我们在做数据预处理的时候一定要平衡数据,不然当数据集过偏的时候最后的结果会以“牺牲”少类的判断正确率去完善整体正确率。


    
    # case 1
    x = []
    y = []
    for i in range(1, 10):
        test_data = combined_date
        i = i / float(10)
        for j in range(combined_date['actual'].count()):
            if test_data.iloc[j, 1] > i:
                test_data.iloc[j, 1] = 1
            else:
                continue
        z = test_data[test_data['actual'] == test_data['predict']]['actual'].count() / float(test_data['actual'].count())
        x.append(i)
        y.append(z)
    

    这边写了检查函数,检查了分别0.1~1,以0.1为间隔的情况下的分割点,每个分割点下预测正确的数量/所有统计的样本数,也就是下面的accuracy.


    # case 2
    test_data = combined_date
    aimed_data = test_data[test_data['predict']>0]
    k1=aimed_data[aimed_data['actual']==1]['predict'].count()
    k2=float(aimed_data['predict'].count())
    
    print '所有预测可能下单用户中真实下单用户数:%d' %(k1)
    print '所有预测可能下单用户数:%d' %(k2)
    

    因为这边需要对用户营销,所以更关系topN的转化率,需要看一下实际正样本被覆盖了多数,以上即为code,这边的效果值为98.7%,还是比较高的,但是应该是过拟合了,所有一般不建议单纯使用决策树模型


    所有的python code到这里就结束了,后续我做项目的同时会同时更新R及python两种code的思考,和大家讨论分享学习,谢谢。

    参考文献:

    相关文章

      网友评论

        本文标题:Python:需求预估

        本文链接:https://www.haomeiwen.com/subject/tdivhxtx.html