又到周末了,东哥赠送5本机器学习的书《机器学习线性代数基础 Python语言描述》,内容非常赞,推荐入手。老样子,免费包邮送出去5本,参与方式见文末~
本文主要介绍的是pandas中的一个移动函数:shift。最后结合一个具体的电商领域中用户的复购案例来说明如何使用shift函数。
这个案例综合性很强,除了需要掌握shift函数,你还会复习到以下pandas中的多个函数使用技巧,建议认真阅读、理解并收藏,欢迎点赞呀~
DataFrame.shift(periods=1, freq=None, axis=0, fill_value=<no_default>)
模拟了两份数据,其中一份和时间相关。
import pandas as pd
import numpy as np
另一份是和时间相关的:
表示每次移动的幅度
可以看到默认情况下,shift函数是在行方向上移动一个单位
用来表示在哪个方向上进行移动,上面的例子默认是在axis=0,或者表示成:axis="index"
如果我们想在列方向上移动,可以使用axis=1或者axis="columns"
同时移动的幅度是可正可负的:
移动之后缺失值的填充数据
表示移动的频率,专门用于时间序列的移动中
时间序列变化频率有间隔相同的,也有不同的。许多字符串别名被赋予有用的普通时间序列频率。我们将这些别名称为偏移别名。上面的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 | 纳秒频率 |
在这里我们结合一个电商销售数据来感受下shift函数的使用。我们有一份客户和购买时间的数据,现在想统计每位用户在今年的平均复购周期和全部用户的平均复购周期。
通过一个例子来解释用户的平均复购周期,假设某位用户购买情况如下:
张三用户的复购间隔分别为:6(1号和7号的间隔),3(7号和10号),10,8;也就是相邻两次购买时间之间的间隔。
那么张三的平均复购周期:(6+3+8+10)/ 4 = 6.75
模拟了一份电商数据,多位用户购买了一次或者多次:
下面通过Pandas来求解每位用户的平均复购周期和全部的平均复购周期
复购的用户指的是:在统计时间范围内,存在多次购买的用户。所以我们首先找到那些至少购买两次的用户
统计发现:小王同学只购买了一次,没有复购行为
筛选出复购用户:
# 姓名的升序或者降序不重要,重要的是第二个字段-时间,一定要是升序
df3 = df2.sort_values(["姓名","时间"],ascending=[True,True]).reset_index(drop=True)
df3
在行方向上移动一个单位:
df4 = df3.groupby("姓名").shift(1).rename(columns={"时间":"时间1"}) # 改下名字,避免重复
df4
将排序后的df3和我们根据df3平移后的数据在列方向上拼接起来:
字段时间1相当于每个购买时间的前一个购买时间点
df5 = pd.concat([df3,df4],axis=1)
df5.head(10) # 查看前10行
上面的数据框中:
使用dropna函数来删除缺失值的数据
df6 = df5.dropna().reset_index(drop=True)
df6
两个字段:时间和时间1的差值,就是每位用户的复购时间间隔,可能存在多个
查看数据的字段类型,我们发现间隔这个字段是一个timedelta64[ns]的类型
我们直接通过apply函数来获取timedelta64[ns]的days属性,也就是对一个的天数
df7 = df6.groupby("姓名").agg({"天":"sum","间隔":"count"}).reset_index().rename(columns={"间隔":"复购次数"})
df7