您的位置:首页 > 移动开发

电商app用户行为分析(含全过程python代码)

2020-06-07 05:43 302 查看

一、分析背景

        对于很多互联网公司来说,流量和用户价值都是核心问题。根据产品生命周期理论,在产品发布初期,更多的关注点应该放在拉新上,只有足够的用户量级才能为公司提供发展的不竭动力,初期对流量和市场份额的竞争,很大程度上决定了后期的行业格局。所以回顾滴滴和优步、美团和饿了么的烧钱大战,正是对“流量为王”这一理论的践行。但是流量进来之后呢?一个较为成熟的产品(如淘宝、滴滴、美团等)应该思考的更多,产品经理人和数据分析师一定要好奇用户进来之后干了什么,产生了什么价值。基于此,才能对产品进行优化,使其更加符合用户的行为习惯。所以,用户行为分析就十分必要。在这里以来自天池的淘宝用户行为数据为基础进行数据分析工作,数据地址

二、明确需求

        进行数据分析的第一步就是明确需求,知道为什么要分析,或者说要解决什么问题/疑惑。在提出分析目标之前可以先了解一下数据,脱离数据提需求是空中楼阁,脱离需求分析数据是无头苍蝇。

        可以从数据介绍中看到,(1)数据有5个字段,分别是用户ID,商品ID,商品类目ID,行为类型,时间戳。(2)行为类型有4种,浏览、购买、加购物车、收藏。(3)时间跨度是9天。根据数据的介绍我们可以提出以下分析目标:
        1. 数据总体情况,pv,uv,付费用户数,复购率等
        2. 变化趋势:流量随天、时间的变化趋势,以及在一周之内的变化趋势
        3. 用户行为的转化漏斗
        4. 基于用户、商品细分的转化率
       需要注意的是这里只是根据已知的数据结构提出的目标和需求,能不能得出有价值、有启发意义的结论,还需要根据分析结果来确定。

三、数据处理

(一)遇到的坑

       拿到数据之后,笔者高高兴兴的用Anaconda的Spyder打开导入数据,想先简单看看数据,再进行清洗和分析。数据导入倒是没有遇到太大的问题:

import pandas as pd
import datetime
start_time = datetime.datetime.now()
user_data = pd.read_csv(r'F:\数据分析项目\淘宝用户行为分析\UserBehavior.csv',header = None)
end_time = datetime.datetime.now()
print(f'导入数据耗时:{end_time-start_time}s')

       笔者明显能感觉到导入数据耗时过长,心里着实有点奇怪,特地查看了以下导入数据花费的时间,输出结果显示---->导入数据耗时:0:01:20.114552s。花了1分20秒才读完数据,我当时就有点郁闷,区区百万级别的数据怎么会这么慢?手动挠头。但是我也没多想,继续往下搞吧!
浏览前几行

user_data.head(10)


       先给数据加列名,方便后续处理:

user_data.columns = ['user_id','item_id','category_id','behavior','time_stamp']
user_data.head(5)


       基本的处理之后,可以考虑开始进行数据清洗了,其实本数据都是来源于淘宝后台的数据,并非由人为填写的,所以遇到数据还是非常干净的,谨慎起见,可以对空值检查一下,笔者习惯使用:

user_data.info()


       或许是由于数据量较大,所以都没有显示每个字段的非空记录有多少行。(汗,笔者到现在都还以为只有百万行数据,眼角膜可以捐了……)
       由于没有得到非空数值分布情况,所以笔者只能选择下面的方法:

user_data['user_id'].notnull().value_counts()
user_data['tiem_id'].notnull().value_counts()
user_data['category_id'].notnull().value_counts()
user_data['behavior'].notnull().value_counts()
user_data['time_stamp'].notnull().value_counts()


       从结果发现,没有空值,数据非常干净。由于要进行日期、时间的分析,所以自然要把时间戳格式转化为正常的日期时间格式。简单科普一下,时间戳:格林威治时间自1970年1月1日(00:00:00 )至当前时间的总秒数,也就是北京时间1970年1月1日(08:00:00)至当前时间的总秒数。可以用**time.localtime()**方法转化为对应的东八区北京时间,但是由于time.localtime()括号里面必须加一个具体的时间戳,如1585385176(笔者写到此处的时间戳):

mport time
time.localtime(1585385176)


       由于笔者是要把user_data[‘time_stamp’]这一整列转化,所以不能简单使用time.localtime()这个方法,需要小小变形:

user_data['date_time'] = user_data['time_stamp'].apply(lambda x: time.localtime(x))

       结果一运行,电脑在嗡嗡响了一阵后,程序直接报错:

       笔者不死心连续运行好几次,最后电脑直接当场退役,领盒饭了(黑屏了),我只能手动重启。为了解惑我只能询问度娘,“好心人”告诉我是因为数据量太大,电脑内存不足,所以产生这个问题。为了解决这个问题,我尝试了各种方法,“好心人”地址:解决python运行大数据电脑内存不足的问题
       我先修改虚拟内存,但是没有解决,然后想到了分块读取数据的方法,我灵机一动,分块读取既然专门用来解决大数据问题,那我可不可以先转化每一块数据的时间戳,最后完成合并呢?走投无路就撞出条路!
       正常的分块读取数据:

start_time = datetime.datetime.now()
user_data = pd.read_csv(r'F:\数据分析项目\淘宝用户行为分析\UserBehavior.csv',
header =None, iterator = True )
loop = True
chunksize = 1000000
chunks = []
while loop:
try:
chunk = user_data.get_chunk(chunksize)
chunks.append(chunk)
except StopIteration:
loop = False
print('iteration is stopped.')
print('开始合并')
user_data = pd.concat(chunks,ignore_index = True)
print('合并完成')
end_time = datetime.datetime.now()
print(f'分块导入数据耗时:{end_time-start_time}s')

       结果显示耗时:1分29秒,比一次性读入耗时略多,但是内存使用明显下降,之前内存使用超过了90%,分块读入数据只有不到70%。分块读完数据依旧用之前的方法转化时间戳,**结果依旧不成功,内存还是不足!**说实话我真的挺崩溃,信心满满要分析,结果被个时间戳绊死,我不要面子的吗?也是病急乱投医,我想要不要在分块读取数据的时候就把每一块的时间戳转换,然后合并到一起,这样会不会成功?马上动手尝试,修改了一下循环:

user_data = pd.read_csv(r'F:\数据分析项目\淘宝用户行为分析\UserBehavior.csv',
header =None, iterator = True )
# 分块写入数据
chunksize = 1000000
chunks = []
loop = True
import time,datetime
while loop:
try:
chunk = user_data.get_chunk(chunksize)
chunk.columns = ['user_id','item_id','category_id','behavior','time_stamp']
chunk['date_time'] = chunk['time_stamp'].apply(lambda x: time.localtime(x))
del chunk['time_stamp']  # 利用完就删除,尽可能节省空间
# 把localtime方法转换完的再转换成年-月-日 时:分:秒格式
chunk['date_time'] = chunk['date_time'].apply(lambda x: time.strftime('%Y-%m-
%d %H:%M:%S',x))
# 再转换成可以提取的datetime格式
chunk['date_time'] = pd.to_datetime(chunk['date_time'])
# 提取日期、时间以及对应的星期
chunk['date'] = chunk['date_time'].dt.date
chunk['time'] = chunk['date_time'].dt.hour
chunk['weekday'] = chunk['date_time'].dt.weekday_name
del chunk['date_time']
chunks.append(chunk)
except StopIteration:
loop = False
print('Iteration is stopped. ')
print('===========================================')
print('开始合并')
user_data = pd.concat(chunks,ignore_index = True)
print('合并完成')

       结果依旧不成功………………,到了这一步我明白了,当排除所有的可能之后,那最后只有一个可能了: 我的电脑不行!这是命,得认啊!这个时候的我百般郁闷,我的电脑好歹也是堂堂8G内存,竟然给区区百万数据跪了?好奇而无聊的我又去看数据,咦,这是多少行,这串数怎么这么长呢?像极了我的银行余额。靠!竟然是100150807行数据,1亿+行数据,回想一下,之前我看数据介绍是淘宝百万用户的脱敏数据,竟然把数据当成了百万行,每个用户可以有多个行为,这是1亿行的数据。好吧,我原谅你了!
       有感而发:在数据分析过程中真是会遇到各种各样的问题,有些是意料之中的,有些是意料之外的,不管怎么样,数据分析师要逢山开路,遇水叠桥,有仇报仇,有怨报怨!还要学会认怂,亿级的电脑处理不了,我就处理千万级的吧,从中随机抽取10万的用户行为数据,最后得到新的数据:part_data.csv。当然,电脑配置较高的朋友完全可以直接处理亿级数据,pandas完全没有压力。错估数据量这个坑让我耽误了将近一天的时间,但是也让我绞尽脑汁寻找各种解决方法,期间get了分块读取数据,也算是有所收获了!

(二)日期、时间、星期提取

       初步预览数据可知,数据有一列是时间戳数据,所以下面要先对时间戳数据进行转化,并提取出日期(年月日)、时间、星期作为新的3列。
       首先,读取数据,并添加列名。

user_data = pd.read_csv(r'F:\数据分析项目\淘宝用户行为分析\part_data.csv',header =None)
user_data.columns = ['user_id','item_id','category_id','behavior','time_stamp']

       第二步,转化时间戳。

# to_datetime方法比time.localtime感觉更方便,可以不用再转换一次格式。
user_data['time_stamp'] = pd.to_datetime(user_data['time_stamp'],unit = 's')
# 转化完之后是格林威治时间,而北京时间需要加8小时
user_data['time_stamp'] = user_data['time_stamp'] + datetime.timedelta(hours = 8)

       第三步,分别提取日期、时间和星期。

user_data['date'] = user_data['time_stamp'].dt.date
user_data['time'] = user_data['time_stamp'].dt.hour
user_data['time_stamp'] = user_data['time_stamp'].dt.weekday_name
# 没有添加新列,而是在原列上修改,是为了节省内存空间。这就需要把列名修改一下。
user_data.rename(columns = {'time_stamp':'weekday'},inplace = True)
user_data.head(5)


       至此,数据处理环节完成,可以根据我们的分析需求进行后续分析。

四、数据分析

(一)数据总体情况

       总浏览量pv:9079927。

user_data.groupby('behavior').count()


       用户数(独立访客uv):99399。

user_data.drop_duplicates('user_id').count()

       付费用户数:67469,付费比例为67.9%。

buy_data = user_data[user_data['behavior'] == 'buy']
buy_data.drop_duplicates('user_id').count()

       复购用户数:44309,复购率为65.7%。

double_user = buy_data.groupby(buy_data['user_id']).count()
double_user[double_user['behavior']>=2].count()

(二)流量的日期、时间、星期变化

       1. 流量的日期、星期变化

       使用pandas的分组统计功能对pv、fav、cart、buy的次数按照日期分组进行统计:

user_data['behavior'].groupby(by = user_data['date']).value_counts()

       得到结果如下的数据统计结果:

       俗话说“文不如表、表不如图”,接下来进行可视化展示。python的数据可视化功能也很强大,但我习惯使用Power BI的数据可视化,美观度更高,更漂亮。将数据导入Power BI,进行了一些处理,过程省略。


       从图中可以看出,在11-25到12-3的统计周期内,流量波动明显,尤其是进入12月,日活激增。购买、收藏、加购物车行为波动与日活波动趋势一致。
       从日活波动常见原因来看,(1)周末效应。空闲时间多,活跃用户多。(2)营销活动。在本案例的时间段内,营销活动是导致日活波动的主要原因。因为我们可以看到11-25,11-26也是周末,但流量增幅并不离谱,而12-1以后,流量激增。所以可能与淘宝的双十二活动有关系。同样,购买、收藏、加购物车行为的波动原因类似。

       2. 流量的时间变化

       对‘behavior’字段以‘time’字段为分组依据进行分组统计,然后依旧导入到power BI进行绘图:

user_data['behavior'].groupby(by = user_data['time']).value_counts()
pd.set_option('display.max_rows',100)   # 统计结果较长,这样显示全方面导出



       从图中可以看出,pv、buy等4中行为均随时间变化明显,1-6点为低谷;6点以后使用人次逐渐攀升,在10点钟达到小高峰;10-18点使用人次小范围波动;18点以后使用人次快速上升,21、22点左右达到一天中的峰值,22点以后则逐渐回落。变化趋势与人一天中的作息、工作规律密切相关。所以淘宝以及一些店铺进行营销活动可以根据图中的时间趋势合理安排。

(三)用户行为转化漏斗

       1. 总体转化率及各环节转化率

       漏斗分析就是预先假设用户行为的发生路径,然后计算每一个环节到下一个环节的发生概率,由于整个过程会不断有用户被刷掉,就如同一个漏斗,所以称为漏斗分析。可以根据不同环节的转化率发现问题并优化产品。用户从浏览商品到最终下单购买,中间还有收藏和加入购物车(不分先后),所以转化路径就是“pv ----> cart/fav ----> buy”。

user_data['behavior'].value_counts()

       将数据导入power BI,产出漏斗图。

       从浏览到最终购买,转化率为2.2%,而从收藏/加购物车到购买转化率为23.3%,从浏览到收藏或加购物车的转化率为9.5%。所以,关键环节是如何让用户在浏览后迅速喜欢上商品并产生购买意向,应该思考的是个性化推荐是否真的符合用户的购买需求。

       2. 销售量前十的商品转化率

       先对商品进行pv、cart/fav、buy的统计,并计算各环节的转化率:

# item_data包含各商品的pv次数
s1 = user_data['item_id'].value_counts()
item_data = pd.DataFrame({'item_id':s1.index,'pv':s1.values})
item_data.head(5)
# item_data2包含各商品的cart和fav次数
user_data2 = user_data[user_data['behavior'].isin(['cart','fav'])]
s2 = user_data2['item_id'].value_counts()
item_data2 = pd.DataFrame({'item_id':s2.index,'cart/fav':s2.values})
# 先合并 item_data 和 item_data2
item_data3 = pd.merge(item_data,item_data2,how = 'left',on = 'item_id')
item_data3
# item_data4 包含各商品的buy次数
user_data3 = user_data[user_data['behavior']=='buy']
s3 = user_data3['item_id'].value_counts()
item_data4 = pd.DataFrame({'item_id':s3.index, 'buy':s3.values})
# 合并item_data4和item_data3作为item_data
item_data = pd.merge(item_data3, item_data4, on = 'item_id', how = 'left')
item_data.fillna(0,inplace = True)
item_data['cart/fav'] = item_data['cart/fav'].astype('int')
item_data['buy'] = item_data['buy'].astype('int')
item_data.head(5)
# 计算每个商品的转化率
item_data['transform1'] = item_data['cart/fav']/item_data['pv']
item_data['transform2'] = item_data['buy']/item_data['cart/fav']
item_data['transform_total'] = item_data['buy']/item_data['pv']
item_data.head(5)
# 小数显示转化为百分数显示
item_data['transform1'] = item_data['transform1'].apply(lambda x: format(x, '.2%'))
item_data['transform2'] = item_data['transform2'].apply(lambda x: format(x, '.2%'))
item_data['transform_total'] = item_data['transform_total'].apply(lambda x: format(x, '.2%'))
# 排序,展示购买次数前十的商品信息
item_data.sort_values('buy',ascending = False).head(10)

item_data.sort_values('pv',ascending = False).head(10)

       从统计表中可以看出,除了id为‘3031354’的商品外,其他销量前十的商品浏览量均没有在top10,说明一些曝光量非常高的商品没有很好的转化为销量。直接观察浏览次数top10商品发现,转化率并不理想,其中9款商品的转化率低于2.2%(总转化率)。如果想找出其中的原因和优化点就需要结合具体商品情况进行分析了,笔者猜测可能原因如下:一是商家的营销活动、折扣活动等不能很好抓住消费者的心理;二是商品本身可能属于大件耐用品,这样成交量显然不会太大。

       3. 头部用户购买和转化率

       按照用户对pv,cart/fav,buy进行统计:

# 统计每个用户的pv
user_data.head(5)
user_data = user_data.rename(columns = {'time_stamp':'weekday'})
pv_data = user_data[user_data['behavior']=='pv'].groupby('user_id').count()
pv_data = pv_data.reset_index()
pv_data.head(4)
del pv_data['item_id'],pv_data['category_id'],pv_data['weekday'],pv_data['date'],pv_data['time']
pv_data.rename(columns = {'behavior':'pv'},inplace = True)

# 统计每个用户的cart+fav
cart_data = user_data[user_data['behavior'].isin(['cart','fav'])].groupby('user_id').count()
cart_data = cart_data.reset_index()
cart_data.head(5)
cart_data.rename(columns = {'behavior':'cart/fav'},inplace = True)
del cart_data['item_id'],cart_data['category_id'],cart_data['weekday'],cart_data['date'],cart_data['time']

# 统计每个用户的buy
pay_data = user_data[user_data['behavior']=='buy'].groupby('user_id').count()
pay_data.reset_index(inplace = True)
pay_data.rename(columns = {'behavior':'buy'},inplace = True)
del pay_data['item_id'],pay_data['category_id'],pay_data['weekday'],pay_data['date'],pay_data['time']
pay_data.head(5)

# 将三个DataFrame进行连接
user1 = pd.merge(pv_data,cart_data,how = 'left')
user1.head(5)
user_behavior = pd.merge(user1,pay_data,how = 'left')
user_behavior.fillna(0,inplace = True)
user_behavior.head(5)
user_behavior['cart/fav'] = user_behavior['cart/fav'].astype('int')
user_behavior['buy'] = user_behavior['buy'].astype('int')
user_behavior['transform1'] = user_behavior['cart/fav']/user_behavior['pv']
user_behavior['transform2'] = user_behavior['buy']/user_behavior['pv']
user_behavior['transform_total'] = user_behavior['buy']/user_behavior['pv']
# 小数转换为百分数
user_behavior['transform1'] = user_behavior['transform1'].apply(lambda x: format(x,'.2%'))
user_behavior['transform2'] = user_behavior['transform2'].apply(lambda x: format(x,'.2%'))
user_behavior['transform_total'] = user_behavior['transform_total'].apply(lambda x: format(x,'.2%'))
# 分别展示浏览次数和购买次数top10的用户
user_behavior.sort_values('pv',ascending = False).head(10)
user_behavior.sort_values('buy',ascending = False).head(10)



       从统计结果可以看出,这两类用户的差异非常大,土豪用户(购买次数多)的购买决策非常果断,转化率非常高(28.3%),超过了大盘转化率的10倍。而且从行为特征上看,购买行为不太需要经过收藏/加购物车环节的转化,也就是购买行为比较“任性”和直接。而闲逛用户(浏览次数多)的行为模式类似于大盘,是标准的漏斗形状,购买率非常低,只有0.3%,远远低于大盘,很可能针对该类用户的商品推荐和营销活动存在优化点,没有很好地将浏览转化为购买行为。

五、结论

       1. 用户在每天10:00会出现活跃的小高峰,在每天21:00—22:00会出现大高峰,产品和运营可以根据这一规律设置消息弹窗、渠道广告等;商家和店铺也可以据此合理安排营销活动。
       2. 对于识别出的高曝光率商品可以有针对性采取措施提高转化率,目前浏览量top10的商品转化率不理想。也可以考虑用这部分商品引流,关联高转化率商品,提高成交量。
       3. 对于闲逛用户,采取更加细致的功能模块分析、路径分析,找到优化点,努力提升闲逛用户的转化率。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: