又到周末了,东哥赠送5本机器学习的书《机器学习线性代数基础 Python语言描述》,内容非常赞,推荐入手。老样子,免费包邮送出去5本,参与方式见文末~
本文主要介绍的是pandas中的一个移动函数:shift。最后结合一个具体的电商领域中用户的复购案例来说明如何使用shift函数。
这个案例综合性很强,除了需要掌握shift函数,你还会复习到以下pandas中的多个函数使用技巧,建议认真阅读、理解并收藏,欢迎点赞呀~
- 分组统计:groupby
- 过滤筛选数据:query
- 排序函数:sort_values
- 合并函数:concat
- 字段重命名:rename
- 缺失值删除:dropna
- 宝藏函数:apply
参数说明
代码语言:javascript复制DataFrame.shift(periods=1, freq=None, axis=0, fill_value=<no_default>)
- periods:表示移动的幅度,可正可负;默认值是1,1就表示移动一次。注意这里移动的都是数据,而索引是不移动的,移动之后没有对应值的,就赋值为NaN。
- freq:DateOffset, timedelta, or time rule string,可选参数,默认值为None,只适用于时间序列。如果这个参数存在,那么会按照参数值移动时间索引,而数据值没有发生变化。
- axis:表示按照哪个轴移动。axis=0表示index,横轴;axis=1表示columns,纵轴
- fill_value:表示当我们数据发生了移动之后,产生的缺失值用什么数据填充。如果是数值型的缺失值,用np.nan;如果是时间类型的缺失值,用NaT(not a time)
模拟数据
模拟了两份数据,其中一份和时间相关。
代码语言:javascript复制import pandas as pd
import numpy as np
另一份是和时间相关的:
参数periods
表示每次移动的幅度
可以看到默认情况下,shift函数是在行方向上移动一个单位
参数axis
用来表示在哪个方向上进行移动,上面的例子默认是在axis=0,或者表示成:axis="index"
如果我们想在列方向上移动,可以使用axis=1或者axis="columns"
同时移动的幅度是可正可负的:
参数fill_value
移动之后缺失值的填充数据
参数freq
表示移动的频率,专门用于时间序列的移动中
频率
时间序列变化频率有间隔相同的,也有不同的。许多字符串别名被赋予有用的普通时间序列频率。我们将这些别名称为偏移别名。上面的shift函数中使用的就是这些别名,具体如下表所示:
B | 工作日频率 |
---|---|
C | 自定义工作日频率 |
D | 日历日频率 |
W | 每周频率 |
M | 每月最后一个日历日 |
SM | 每半个月最后一个日历日(15日和月末) |
BM | 每月最后一个工作日 |
CBM | 自定义每月最后一个工作日 |
MS | 每月第一个日历日 |
SMS | 每半月第一个日历日(第1和第15) |
BMS | 每月第一个工作日 |
CBMS | 自定义每月第一个工作日 |
Q | 每季度最后一个月的最后一个日历日 |
BQ | 每季度最后一个月的最后一个工作日 |
QS | 每季度最后一个月的第一个日历日 |
BQS | 每季度最后一个月的第一个工作日 |
A, Y | 每年的最后一个日历日 |
BA, BY | 每年的最后一个工作日 |
AS, YS | 每年的第一个日历日 |
BAS, BYS | 每年的第一个工作日 |
BH | 工作日按“时”计算频率 |
H | 每小时频率 |
T, min | 每分钟频率 |
S | 每秒频率 |
L, ms | 毫秒频率 |
U, us | 微秒频率 |
N | 纳秒频率 |
电商用户复购数据实战
1、什么是用户的复购周期?
在这里我们结合一个电商销售数据来感受下shift函数的使用。我们有一份客户和购买时间的数据,现在想统计每位用户在今年的平均复购周期和全部用户的平均复购周期。
- 每位用户的平均复购周期:每两个复购时间之间的天数之和 / 用户总复购次数
- 全部用户的平均复购周期:全部用户的平均复购周期之和 / 总复购用户数
通过一个例子来解释用户的平均复购周期,假设某位用户购买情况如下:
张三用户的复购间隔分别为:6(1号和7号的间隔),3(7号和10号),10,8;也就是相邻两次购买时间之间的间隔。
那么张三的平均复购周期:(6 3 8 10)/ 4 = 6.75
2、模拟数据
模拟了一份电商数据,多位用户购买了一次或者多次:
下面通过Pandas来求解每位用户的平均复购周期和全部的平均复购周期
3、确定哪些用户存在复购行为
复购的用户指的是:在统计时间范围内,存在多次购买的用户。所以我们首先找到那些至少购买两次的用户
统计发现:小王同学只购买了一次,没有复购行为
筛选出复购用户:
4、原始数据中提取出复购用户的数据
5、根据每位用户的购买时间来升序排列
代码语言:javascript复制# 姓名的升序或者降序不重要,重要的是第二个字段-时间,一定要是升序
df3 = df2.sort_values(["姓名","时间"],ascending=[True,True]).reset_index(drop=True)
df3
6、根据每位复购用户的数据移动一个单位
在行方向上移动一个单位:
代码语言:javascript复制df4 = df3.groupby("姓名").shift(1).rename(columns={"时间":"时间1"}) # 改下名字,避免重复
df4
7、拼接数据
将排序后的df3和我们根据df3平移后的数据在列方向上拼接起来:
字段时间1相当于每个购买时间的前一个购买时间点
代码语言:javascript复制df5 = pd.concat([df3,df4],axis=1)
df5.head(10) # 查看前10行
上面的数据框中:
- 时间: 可以看做是我们的本次购买时间
- 时间1:上次购买时间。每个用户的第一次购买时间是不存在上次购买时间,所以显示为NaT
8、将NaT数据删除
使用dropna函数来删除缺失值的数据
代码语言:javascript复制df6 = df5.dropna().reset_index(drop=True)
df6
9、求出复购时间间隔
两个字段:时间和时间1的差值,就是每位用户的复购时间间隔,可能存在多个
查看数据的字段类型,我们发现间隔这个字段是一个timedelta64[ns]的类型
我们直接通过apply函数来获取timedelta64[ns]的days属性,也就是对一个的天数
10、统计每个复购用户的复购总天数和总次数
代码语言:javascript复制df7 = df6.groupby("姓名").agg({"天":"sum","间隔":"count"}).reset_index().rename(columns={"间隔":"复购次数"})
df7