随着移动设备的完善和普及,移动互联网+各行各业进入了高速发展阶段,这其中以O2O(Online to Offline)消费最为吸引眼球。据不完全统计,O2O行业估值上亿的创业公司至少有10家,也不乏百亿巨头的身影。O2O行业关联数亿消费者,各类APP每天记录了超过百亿条用户行为和位置记录,因而成为大数据科研和商业化运营的最佳结合点之一。 以优惠券盘活老用户或吸引新客户进店消费是O2O的一种重要营销方式。然而随机投放的优惠券对多数用户造成无意义的干扰。对商家而言,滥发的优惠券可能降低品牌声誉,同时难以估算营销成本。个性化投放是提高优惠券核销率的重要技术,它可以让具有一定偏好的消费者得到真正的实惠,同时赋予商家更强的营销能力。
本数据提供用户在2016年1月1日至2016年6月30日之间真实线上线下消费行为。
Field | Description |
---|---|
User_id | 用户ID |
Merchant_id | 商户ID |
Coupon_id | 优惠券ID:null表示无优惠券消费,此时Discount_rate和Date_received字段无意义 |
Discount_rate | 优惠率:x\in[0,1]代表折扣率;x:y表示满x减y。单位元 |
Distance | User经常活动的地点离该merchant的最近距离是x*500米,x\in[0,10];null表示无此信息,0表示低于500米,10表示大于5公里; |
Date_received | 领取优惠券日期 |
Date | 消费日期;如果Date=null&Coupon_id != null,该记录表示领取优惠券但没有使用,即负样本;如果Date != null&Coupon_id=null,则表示普通消费日期;如果Date != null&Coupon_id != null,则表示用优惠券消费日期,即正样本; |
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns #绘图模块,基于matplotlib的可视化python包,不能完全替代matplotlib,只是对matplotlib进行升级
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
%matplotlib inline
#pip install seaborn
#parse_dates:将指定的列加载成日期的格式
offline = pd.read_csv('ccf_offline_stage1_train.csv',parse_dates=['Date_received','Date'])
offline.info() #175+万条数据
offline.head(10)
NaT:时间日期格式的空值
判断每一列当中有多少个空值
offline.isnull().sum()
优惠券id,折扣率,领券日期,三者可能存在同时==null的情况
offline['Discount_rate'] = offline['Discount_rate'].fillna('null')
offline.head()
def discount_rate_opt(s): #s代表每一个元素
if ':' in s:
split = s.split(':')
discount_rate = (int(split[0]) - int(split[1]))/int(split[0])
return round(discount_rate,2) #折扣率保留两位小数
elif s == 'null':
return np.NaN
else:
return float(s)
offline['Discount_rate'] = offline['Discount_rate'].map(discount_rate_opt)
offline.head()
检查Coupon_id和Discount_rate与Date_received判断空值和非空值是否一一对应。
np.all():判断一个课迭代数据中是否都为True,如果是返回True,否则返回False
np.all([True,False,True])
如果Date=null & Coupon_id!=null,有券未消费(cpon_no_consume) 如果Date=null & Coupon_id =null,无券未消费(no_cpon_no_consume) 如果Date!=null & Coupon_id=null,无券消费(no_cpon_consume) 如果Date!=null & Coupon_id!=null,有券消费(cpon_consume)
cpon_no_consume = offline[(offline['Date'].isnull() & offline['Coupon_id'].notnull())]
no_cpon_no_consume = offline[(offline['Date'].isnull() & offline['Coupon_id'].isnull())]
no_cpon_consume = offline[(offline['Date'].notnull() & offline['Coupon_id'].isnull())]
cpon_consume = offline[(offline['Date'].notnull() & offline['Coupon_id'].notnull())]
print('有券未消费:{}'.format(len(cpon_no_consume)))
print('无券未消费:{}'.format(len(no_cpon_no_consume))) #无意义,不需分析
print('无券消费:{}'.format(len(no_cpon_consume)))
print('有券消费:{}'.format(len(cpon_consume)))
用优惠券消费的用7万,相比其他用户来说,占比较少
#绘制饼图占比
consume_status_dict = {'cpon_no_consume':len(cpon_no_consume),'no_cpon_consume':len(no_cpon_consume),'cpon_consume':len(cpon_consume)}
consume_status = pd.Series(consume_status_dict)
consume_status
#消费方式构成的饼图(figure:看作是一张画布,axes:代表画布内的多个坐标系)
fig,ax=plt.subplots(1,1,figsize=(8,10))
consume_status.plot.pie(ax = ax,
autopct='%1.1f%%',
shadow=True,
explode=[0.02,0.05,0.2],
textprops={'fontsize':15,'color':'blue'},
wedgeprops={'linewidth':1,'edgecolor':'black'},
labels=['有券未消费 \n ({})'.format(len(cpon_no_consume)),
'无券消费 \n ({})'.format(len(no_cpon_consume)),
'用券消费 \n ({})'.format(len(cpon_consume))
]
)
ax.set_ylabel('') #去除ylable
ax.set_title('消费占比情况')
plt.legend(labels=['有券未消费','无券消费','用券消费'])
各商家对应的顾客到店平均距离
Merchant_distance = cpon_consume.groupby('Merchant_id')['Distance'].mean()
Merchant_distance[Merchant_distance==0]
有4076个商家,有1431个商家的用券消费用户平均范围在500米以内
各商家对应的顾客到店消费平均折扣力度
Merchant_discount_rate = cpon_consume.groupby('Merchant_id')['Discount_rate'].mean()
Merchant_discount_rate.sort_values()
Merchant_discount_rate.hist()
Merchant_discount_rate.mean() #所有商家平均折扣的平均值:0.88
Merchant_discount_rate
对商家进行分组,取出用户id,对用户id进行去重统计数量
popular_merchant = cpon_consume.groupby('Merchant_id')['User_id'].apply(lambda x:len(x.unique())).sort_values(ascending=False)
#找出持券消费人数>500的商家id
popular_merchant500 = popular_merchant[popular_merchant>500]
popular_merchant500.name = 'customer_count' #指定列名为消费者数量(持券消费者)
print(len(popular_merchant500))
print(popular_merchant500)
merchant_pop_dis = pd.merge(left=popular_merchant500,right=Merchant_distance,on='Merchant_id',how='inner')
merchant_pop_dis_rate = pd.merge(left=merchant_pop_dis,right=Merchant_discount_rate,on='Merchant_id',how='inner')
merchant_pop_dis_rate
merchant_pop_dis_rate.corr()
持券消费人数,与距离和折扣率都呈现出负相关,属于生活中的正常现象
用热力图展示相关系数(data:相关系数,annot:显示相关系数值,cmap:颜色范围,vmax:最大值,vmin:最小值)
sns.heatmap(data=merchant_pop_dis_rate.corr(),annot=True,cmap='Accent',vmax=1,vmin=-1)
由图可知:
每天优惠券的使用量(即持券消费人群)
consume_num_everday = cpon_consume[['User_id','Date_received']]
consume_num_everday = consume_num_everday.groupby('Date_received').count()
consume_num_everday = consume_num_everday.rename(columns={'User_id':'count'})
consume_num_everday
每天发放的优惠券数量(取出所有领券日期!=null的数据,在进行按天分组,计数就可以)
coupon_sendout_everyday = offline[offline['Date_received'].notnull()][['Date_received','User_id']]
coupon_sendout_everyday = coupon_sendout_everyday.groupby('Date_received').count()
coupon_sendout_everyday = coupon_sendout_everyday.rename(columns={'User_id':'count'})
coupon_sendout_everyday
绘制每天发券量和每天用券量
plt.figure(figsize=(18,6))
# plt.bar(x=date_receive_sort,height=coupon_sendout_everyday['count'],label='每天发券量')
plt.bar(x=date_sort,height=consume_num_everday['count'],label='每天用券量')
plt.yscale('log') #对y轴进行对数缩放
plt.legend()
16年2月为例,用券量级别再1000,发券量再10万左右,在100倍左右,优惠券的使用率还是非常低的
计算每天的优惠券与发券量占比
plt.figure(figsize=(18,6))
plt.bar(x=date_receive_sort,height=consume_num_everday['count']/coupon_sendout_everyday['count'],
label='百分比')
plt.legend()