O2O优惠券线下使用情况数据分析

一、数据的载入与描述

python 复制代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
%matplotlib inline


#parse_dates:将指定的列加载成日期的格式
offline = pd.read_csv('ccf_offline_stage1_train.csv',parse_dates=['Date_received','Date'])
offline.info()  #175+万条数据



<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1754884 entries, 0 to 1754883
Data columns (total 7 columns):
 #   Column         Dtype         
---  ------         -----         
 0   User_id        int64         
 1   Merchant_id    int64         
 2   Coupon_id      float64       
 3   Discount_rate  object        
 4   Distance       float64       
 5   Date_received  datetime64[ns]
 6   Date           datetime64[ns]
dtypes: datetime64[ns](2), float64(2), int64(2), object(1)
memory usage: 93.7+ MB




offline.head(10)   #NaT:时间日期格式的空值



	User_id	Merchant_id	Coupon_id	Discount_rate	Distance	Date_received	Date
0	1439408	2632	NaN	NaN	0.0	NaT	2016-02-17
1	1439408	4663	11002.0	150:20	1.0	2016-05-28	NaT
2	1439408	2632	8591.0	20:1	0.0	2016-02-17	NaT
3	1439408	2632	1078.0	20:1	0.0	2016-03-19	NaT
4	1439408	2632	8591.0	20:1	0.0	2016-06-13	NaT
5	1439408	2632	NaN	NaN	0.0	NaT	2016-05-16
6	1439408	2632	8591.0	20:1	0.0	2016-05-16	2016-06-13
7	1832624	3381	7610.0	200:20	0.0	2016-04-29	NaT
8	2029232	3381	11951.0	200:20	1.0	2016-01-29	NaT
9	2029232	450	1532.0	30:5	0.0	2016-05-30	NaT

二、数据的规整

python 复制代码
#判断每一列当中有多少个空值
offline.isnull().sum()
#优惠券id,折扣率,领券日期,三者可能存在同时==null的情况





User_id               0
Merchant_id           0
Coupon_id        701602
Discount_rate    701602
Distance         106003
Date_received    701602
Date             977900
dtype: int64



#2.1把"Discount_rate"列中的满减政策转换成折扣率
offline['Discount_rate'] = offline['Discount_rate'].fillna('null')
offline.head()




User_id	Merchant_id	Coupon_id	Discount_rate	Distance	Date_received	Date
0	1439408	2632	NaN	null	0.0	NaT	2016-02-17
1	1439408	4663	11002.0	150:20	1.0	2016-05-28	NaT
2	1439408	2632	8591.0	20:1	0.0	2016-02-17	NaT
3	1439408	2632	1078.0	20:1	0.0	2016-03-19	NaT
4	1439408	2632	8591.0	20:1	0.0	2016-06-13	NaT



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()



User_id	Merchant_id	Coupon_id	Discount_rate	Distance	Date_received	Date
0	1439408	2632	NaN	NaN	0.0	NaT	2016-02-17
1	1439408	4663	11002.0	0.87	1.0	2016-05-28	NaT
2	1439408	2632	8591.0	0.95	0.0	2016-02-17	NaT
3	1439408	2632	1078.0	0.95	0.0	2016-03-19	NaT
4	1439408	2632	8591.0	0.95	0.0	2016-06-13	NaT


#2.2 Coupon_id字段:null代表无优惠券,此时Discount_rate与Date_received字段无意义。
#检查Coupon_id和Discount_rate与Date_received判断空值和非空值是否一一对应。
#np.all():判断一个课迭代数据中是否都为True,如果是返回True,否则返回False
np.all([True,False,True])

False


nan1 = offline['Coupon_id'].isnull()  #判断优惠券是否为空
nan2 = offline['Date_received'].isnull()   #判断领券日期是否为空
np.all(nan1 == nan2)  #如果结果=true,说明之前的猜测:Coupon_id与Date_received空值与非空值是一一对应的关系


True


nan3 = offline['Discount_rate'].isnull()
np.all(nan1 == nan3)#如果结果=true,说明之前的猜测:Coupon_id与Discount_rate空值与非空值是一一对应的关系


True

#2.3
#如果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万,相比其他用户来说,占比较少



有券未消费:977900
无券未消费:0
无券消费:701602
有券消费:75382

三、数据分析

python 复制代码
#绘制饼图占比
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


cpon_no_consume    977900
no_cpon_consume    701602
cpon_consume        75382
dtype: int64



#消费方式构成的饼图(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=['有券未消费','无券消费','用券消费'])
#有券未消费占比55.7%最大,说明大多数人拿完券之后,尚未使用
#无圈消费用户占比40%,说明很多人没有使用优惠券,可能优惠券的吸引力不大,客户没在意;可能,新用户比较多。
#用券消费用户占比较小4.3%,说明我们的优惠券使用率不高。可以考虑是不是加大优惠券力度.....

python 复制代码
#3.1在有券消费人群中,分析距离和优惠折扣
#各商家对应的顾客到店平均距离
Merchant_distance = cpon_consume.groupby('Merchant_id')['Distance'].mean()
Merchant_distance[Merchant_distance==0]
#有4076个商家,有1431个商家的用券消费用户平均范围在500米以内


Merchant_id
3       0.0
4       0.0
13      0.0
14      0.0
18      0.0
       ... 
8806    0.0
8824    0.0
8828    0.0
8849    0.0
8856    0.0
Name: Distance, Length: 1431, dtype: float64


#各商家对应的顾客到店消费平均折扣力度
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

Merchant_id
3       0.670
4       0.830
5       0.750
13      0.900
14      0.830
        ...  
8844    0.895
8849    0.950
8850    0.950
8852    0.950
8856    0.925
Name: Discount_rate, Length: 4076, dtype: float64
python 复制代码
#3.2持券到店消费人数最多的商家

#对商家进行分组,取出用户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)
#共有16家店铺,持券消费人数在500人以上
#持券消费人数最多商家是5341,持券消费人数在2800
#排名最后的商家,持券消费人数未559人
#这批商家对优惠券的使用方法得当,消费者喜欢用优惠券进行消费,可以适当借鉴这批商家的推广力度





16
Merchant_id
5341    2800
760     2627
3381    2248
6485    2029
2099    1401
2934    1310
450     1094
3532     968
7555     925
1520     870
6901     855
3621     851
4142     832
1379     587
1469     584
1433     559
Name: customer_count, dtype: int64


#3.3持券消费人数在500人以上的商家,连接顾客到店平均距离和平均折扣力度
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


	customer_count	Distance	Discount_rate
Merchant_id			
5341	2800	0.168598	0.826036
760	2627	0.349866	0.799873
3381	2248	1.652429	0.744150
6485	2029	0.368567	0.770439
2099	1401	0.968072	0.900000
2934	1310	1.114833	0.830000
450	1094	0.892164	0.819274
3532	968	0.272498	0.852289
7555	925	1.329977	0.828381
1520	870	NaN	0.925632
6901	855	0.557895	0.834165
3621	851	0.472799	0.747565
4142	832	0.555882	0.900000
1379	587	0.706250	0.830096
1469	584	2.092800	0.721870
1433	559	1.054962	0.825959


#3.4计算到店消费人数与平均距离和折扣力度的相关系数
#corr(correlation:相关系数),用来计算df数据中列与列的相关性(皮尔逊相关系数),取值范围[-1,1]之间
#1:完全正相关,-1:完全负相关
#绝对值越大:相关性越大,反之成立
#正相关:随着变量的增大,而增大,反之同理
#负相关:随着变量的增大,而减小,反之同理
merchant_pop_dis_rate.corr()
#持券消费人数,与距离和折扣率都呈现出负相关,属于生活中的正常现象


	customer_count	Distance	Discount_rate
customer_count	1.000000	-0.306180	-0.204102
Distance	-0.306180	1.000000	-0.397376
Discount_rate	-0.204102	-0.397376	1.000000



#用热力图展示相关系数(data:相关系数,annot:显示相关系数值,cmap:颜色范围,vmax:最大值,#vmin:最小值)
sns.heatmap(data=merchant_pop_dis_rate.corr(),annot=True,cmap='Accent',vmax=1,vmin=-1)

由图可知:

1.到店消费人数的多少与顾客到店铺的距离之间呈现负相关,相关系数0.31,在0.3~0.5之间,为低度相关

2.到店消费人数的多少与优惠打折力度呈现负相关,相关系数0.2,在0~0.3之间,为相关程度极弱

综上所述,这些店家之所以火爆,应该是物美价廉导致,与距离和优惠力度相关性不大

python 复制代码
#4.1分析每天中优惠券的总体发放量与使用量情况
#业务分析:日期(优惠券的发放日期Date_received,使用日期date)用作图表的x轴
#需要统计每天优惠券发放数量和使用数量


offline['Date'].notnull().sum()  #77.7万消费数据

776984

offline['Date_received'].notnull().sum()  #已经发送出105万优惠券

1053282

#取出存在消费日期的记录,进行升序,再去重
date_sort = offline[offline['Date'].notnull()]['Date'].sort_values().unique()
date_sort[:5]

array(['2016-01-01T00:00:00.000000000', '2016-01-02T00:00:00.000000000',
       '2016-01-03T00:00:00.000000000', '2016-01-04T00:00:00.000000000',
       '2016-01-05T00:00:00.000000000'], dtype='datetime64[ns]')

#取出存在领券日期的记录,进行升序,再去重
date_receive_sort = offline[offline['Date_received'].notnull()]['Date_received'].sort_values().unique()
date_receive_sort[:5]


array(['2016-01-01T00:00:00.000000000', '2016-01-02T00:00:00.000000000',
       '2016-01-03T00:00:00.000000000', '2016-01-04T00:00:00.000000000',
       '2016-01-05T00:00:00.000000000'], dtype='datetime64[ns]')


#每天优惠券的使用量(即持券消费人群)
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

	count
Date_received	
2016-01-01	74
2016-01-02	67
2016-01-03	74
2016-01-04	98
2016-01-05	107
...	...
2016-06-11	351
2016-06-12	330
2016-06-13	439
2016-06-14	394
2016-06-15	355
167 rows × 1 columns

#每天发放的优惠券数量(取出所有领券日期!=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


	count
Date_received	
2016-01-01	554
2016-01-02	542
2016-01-03	536
2016-01-04	577
2016-01-05	691
...	...
2016-06-11	5211
2016-06-12	4005
2016-06-13	7861
2016-06-14	4755
2016-06-15	3475
167 rows × 1 columns


#绘制每天发券量和每天用券量
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倍左右,优惠券的使用率还是非常低的
python 复制代码
#计算每天的优惠券与发券量占比
plt.figure(figsize=(18,6))
plt.bar(x=date_receive_sort,height=consume_num_everday['count']/coupon_sendout_everyday['count'],
       label='百分比')
plt.legend()

由图可知,优惠券使用率最高在16年3月底,达到了30%,使用率最低在16年1月底,最低为3%左右,整体来看,优惠券使用率波动较大。

相关推荐
AI生成网页工具16 小时前
2026年北京本地企业级AI提效解决方案提供商推荐名单与专业对比
数据挖掘
好家伙VCC21 小时前
# 发散创新:用Python+Pandas构建高效BI数据清洗流水线在现代数据分析领域,**BI(商业智能)工具的核心竞
java·python·数据分析·pandas
Lun3866buzha1 天前
机械零件识别与分类_基于YOLO11-seg的六角螺栓、方颈螺栓、六角螺母、弹性卡环、弹簧锁紧垫片和平垫片自动检测与识别_DRB_1
人工智能·分类·数据挖掘
海天一色y1 天前
使用BEiT模型进行CIFAR-100图像分类:迁移学习实战指南
分类·数据挖掘·迁移学习
Lun3866buzha1 天前
法兰盘表面缺陷识别与分类:基于YOLO13-C3k2-RFAConv的智能检测系统完整实现
人工智能·分类·数据挖掘
计算机编程-吉哥1 天前
大数据毕业设计 基于大数据的计算机岗位招聘数据可视化分析系统 计算机毕业设计【项目+论文+安装调试】
大数据·机器学习·信息可视化·数据分析·毕业设计·计算机毕业设计选题·大数据毕业设计选题推荐
Liue612312311 天前
基于YOLO11-CARAFE的手指区域识别与标注分类方法研究
人工智能·分类·数据挖掘
babe小鑫1 天前
高职商务数据分析与应用专业学习数据分析的重要性
学习·数据挖掘·数据分析
AI科技星1 天前
光速为何是宇宙的终极速度极限?
人工智能·线性代数·算法·矩阵·数据挖掘