You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

267 lines
18 KiB
Markdown

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 05 | 数据探索怎样从数据中找到用户的RFM值
你好,我是黄佳。
今天我们正式进入“业务场景闯关篇”模块。我在开篇词中介绍过在这个模块中我会围绕电商场景下的运营环节带你挑战5个关卡获客关、变现关、激活关、留存关和裂变关帮你逐步掌握机器学习的相关知识和实操技巧。今天我们就从第一关“获客关”开始
![](https://static001.geekbang.org/resource/image/76/08/76466435764dc810ab26d93a8a2b5a08.jpg?wh=2284x1033)
人们常说移动互联网的运营已经进入了下半场,几乎所有的企业都希望能用更优质的产品和更精准的服务留住用户,这就需要制定出合适的获客策略。而要做到这一点,前提就是为用户精准画像,也就是根据用户的人口统计信息和消费行为数据,给用户分组,然后推测出用户的消费习惯和价值高低。
所以,为用户分组、画像,找到不同用户的特点,进而挖掘出哪些才是最有价值的用户,是目前互联网大厂中的数据分析师和机器学习工程师常做的工作。既然如此,那么我们就在获客关,结合一个具体的电商项目,来看看怎么根据用户的基本信息和消费行为数据,给用户分组画像。
# 定义问题
按照我们前两讲所说的机器学习“实战5步”我们首先要做的就是把项目的问题定义清楚。在我们这个项目中你可以想象自己就职于一家名为“易速鲜花”的创业公司担任这家公司的运营团队机器学习工程师。你现在要接手的第一个项目就是为公司的用户分组画像。
现在,你们的运营部门已经准备好了过往用户的基本信息和消费行为数据,你可以在[这里](https://github.com/huangjia2019/geektime/tree/main/%E8%8E%B7%E5%AE%A2%E5%85%B305)下载获取它然后用Excel打开就能看到这个数据集中所包含的详细信息。
![](https://static001.geekbang.org/resource/image/c1/c6/c1b4f484eb7884a2b44961d9abe842c6.png?wh=838x192)
既然我们要从这份销售订单数据中,为用户分组画像,那么有一个关键的问题就是,我们以什么为依据,给用户做分组?
我们知道,用户的消费行为本身是不能直接用于数据分析的,这就需要我们把用户的行为转化成具体的数值,也就是可量化的指标,让我们对用户有更直观的认识。而且我们还可以将这些指标用在数据分析、广告精准投放、产品推荐系统等多个运营场景,来提升我们产品和服务的精准度。
这个具体的数值呢就是RFMRecency、Frequency、Monetary ),它是用户画像的衍生品,也是目前很多互联网厂商普遍采用的分析方式。具体来看:
* **R是新进度代表自用户上次消费以来的天数。**这个指标其实也就是用户的热乎度比如说你上次使用App是一个月前你的系统里R值就是30。如果你今天又用了一次App那R值就会立刻被更新为1
* **F是消费频率代表用户是否频繁使用服务。**这也是用户黏性的风向标;
* **M是消费金额代表用户在一段时间内消费的总金额**这个不难理解。
通过用户消费行为的新进度、消费的总体频率还有消费总金额这三项指标我们可以将用户划分为不同的类别或集群来直观地描述用户的价值。什么意思呢简单来说就是只要我们从用户的基本信息和消费行为数据中得出RFM值就可以根据它对用户分组画像了。
所以我们这个项目整体上可以分为两个阶段第一个阶段是求出RFM值第二个阶段就是利用RFM值给用户分组画像进而绘制出高价值、中等价值和低价值用户的分布情况。我们今天这节课的目标就是解决第一阶段的问题。
搞清楚问题后,现在我们来看看这份数据的整体状况,根据需要对数据进行一个初步的预处理。
# 数据预处理
由于已经完成了数据收集工作我们现在直接用Pandas中的read\_csv工具把这个原始数据集读入到Pandas的DataFrame中用Dataframe形式呈现出来。关于Pandas包及其导入方式如果你感觉比较陌生可以再回顾一下[第三讲](https://time.geekbang.org/column/article/414504)的内容。
```typescript
import pandas as pd #导入Pandas
df_sales = pd.read_csv('易速鲜花订单记录.csv') #载入数据
df_sales.head() #显示头几行数据  
```
运行这段代码后,输出如下:
![](https://static001.geekbang.org/resource/image/73/ce/735b13c7baff56803b115a72fbdee8ce.png?wh=540x183)
从图中你可以看到这个数据集主要包含了订单号、产品码、消费日期产品说明、数量、单价、用户码和城市等字段。因为我们求的是用户消费行为的新进度R、消费的总体频率F还有消费总金额M所以在这些信息中我们重点关注的是以下几个数据
* 用户码
* 单价
* (订单中产品的)数量
* 消费日期
为了对公司运营团队交给我们的数据集有一个宏观上的了解,下面我们先做一个整体的数据可视化,看看这个数据集所覆盖的消费日期跨度是怎样的。
## 1.数据可视化
我们直接来看具体的实现代码:
```
import matplotlib.pyplot as plt #导入Matplotlib的pyplot模块
#构建月度的订单数的DataFrame
df_sales['消费日期'] = pd.to_datetime(df_sales['消费日期']) #转化日期格式
df_orders_monthly = df_sales.set_index('消费日期')['订单号'].resample('M').nunique() #每个月的订单数量
#设定绘图的画布
ax = pd.DataFrame(df_orders_monthly.values).plot(grid=True,figsize=(12,6),legend=False)
ax.set_xlabel('月份') # X轴label
ax.set_ylabel('订单数') # Y轴Label
ax.set_title('月度订单数') # 图题
#设定X轴月份显示格式
plt.xticks(
    range(len(df_orders_monthly.index)), 
    [x.strftime('%m.%Y') for x in df_orders_monthly.index], 
    rotation=45)
plt.show() # 绘图
```
在这段代码中你需要注意的是我们这里使用了Pandas的to\_datetime这个API把原始消费日期转换成了能处理的格式。而在df\_orders\_monthly中则是求出了每一个月的订单数量。再往后的代码是具体的绘图工具使用我就不过多说明了。
输出如下:
![](https://static001.geekbang.org/resource/image/74/9a/74193758e9c81523e1eb9826eda6949a.png?wh=719x406)
我们看到这个数据集收集了“易速鲜花”公司一整年的订单量。所以啊我们要求的消费额M实际上是每个用户一整年的总消费额。
你可能已经注意到在最后一个月也就是2021年6月订单量突然大幅下降。其实这是因为运营人员拉这个表的时候正是6月的第一个礼拜。所以6月的数据虽然不全但并不会影响我们对用户RFM值的分析。
## 2.数据清洗
完成了初步可视化之后,我们来清洗一下数据。请你注意,在刚才的可视化过程中,我们已经完成了对消费日期的观察,并没有发现什么异常。所以,现在我们重点要处理的是用户码、单价和(订单中产品的)数量。
首先我们用Pandas中的drop\_duplicates方法把完全相同的重复数据行删除掉。
```typescript
df_sales = df_sales.drop_duplicates() #删除重复的数据行
```
我之前介绍过你还可以用DataFrame的isna().sum()函数来统计NaN的个数。当然如果你尝试了这个方法会发现这个数据集中没有NaN值。
此外对于数量、金额等类型的数据我们还常常会使用describe方法来查看这些字段的统计信息是否有脏数据。
```
plain
df_sales.describe() #df_sales的统计信息
```
输出如下:
![](https://static001.geekbang.org/resource/image/82/b9/820ab32985e7c576aae570128d931ab9.png?wh=322x240)
在图中你可以看到这个数据集中共有9万多行的数据count统计数据条目的数量每条数据的平均采购数量是10mean统计均值商品平均单价是3.575元左右。
在概览中我们发现订单中产品的数量的最小值min是一个负数-9360这显然是不符合逻辑的所以我们要把这种脏数据清洗掉。具体的处理方式是用loc属性通过字段名也就是列名访问数据集同时只保留“数量”字段大于0的数据行
```plain
df_sales = df_sales.loc[df_sales['数量'] > 0] #清洗掉数量小于等于0的数据
```
在DataFrame对象中loc属性是通过行、列的名称来访问数据的我们做数据预处理时会经常用到还有一个常被用到的属性是iloc它是通过行列的位置也就是序号来访问数据的。
由于数据行的名称往往也是数值,这两者非常容易被弄混。所以,在这里我特地给你做了一张图,来说明什么是行列名、什么是行列序号,帮你弄清楚它们的区别。
![](https://static001.geekbang.org/resource/image/e4/de/e4165807e638ac602fb64fab28a0b8de.jpg?wh=2284x1165)
这个语句执行后如果你再用一次describe方法就会看到新的最小数量为1这就对了。
![](https://static001.geekbang.org/resource/image/04/b1/0425e6877dbf579eyy74f69a73c809b1.png?wh=323x240)
到这里我们就完成了数据清洗工作这份数据集中已经没有可以剔除的脏数据了。接下来我们就看看怎么从中求得RFM值。
# 求RFM值
不过在计算RFM之前我想请你再回顾一下我们整个项目的目标。我们的目标就是建立一个机器学习模型给用户做价值分组。而其中RFM实际上就是我们构建模型所需的关键特征数据。
讲到这里我想你应该很清楚了虽说我们这节课要求出RFM的值但实际上我们是在完成机器学习项目中的“特征工程”环节也就是对原始数据集中的信息进行选择、提取、合并、加工、转换甚至是基于原始信息构建出新的、对于模型的训练更具有意义的特征。
明白了这一点我们就可以继续往下走了。那怎么计算这个RFM值呢
其实这里的R值最近一次消费的天数和F值消费频率我们通过数据集中的消费日期就能得到但是对于M值消费金额你会发现数据集中有采购数量有单价但是还没有每一笔消费的总价。
因此我们通过一个语句对原有的数据集进行一个小小的扩展。在df\_sales 这个DataFrame对象中增加一个数据列计算总价总价等于由单价乘以数量
```plain
df_sales['总价'] = df_sales['数量'] * df_sales['单价'] #计算每单的总价
df_sales.head() #显示头几行数据       
```
输出如下:
![](https://static001.geekbang.org/resource/image/c9/ae/c91d515ac9e38b4a59aac6ba89717fae.png?wh=581x181)
现在,在这个数据集中,**用户码**、**总价**和**消费日期**这三个字段给我们带来了每一个用户的R、F、M信息。其中
* 一个用户上一次购物的日期也就是最新的消费日期就可以转化成这个用户的R值
* 一个用户下的所有订单次数之和就是消费频率值也就是该用户的F值
* 把一个用户所有订单的总价加起来就是消费金额值也就是该用户的M值。
不过我们目前的这个数据集是一个订单的历史记录并不是以用户码为主键的数据表。而R、F、M信息是和用户相关的每一个用户都拥有一个独立的R值、F值和M值所以在计算RFM值之前我们需要先构建一个用户层级表。
1. 构建用户层级表
构建用户层级表,简单来说就是生成一个以用户码为关键字段的 Dataframe对象df\_user然后在这个Dataframe对象中逐步加入每一个用户的新近度R、消费频率F、消费金额M以及最终总的分组信息。
在代码实现上我们用Dataframe的unique() 这个API就能创建出以用户码为关键字段的用户层级表df\_user然后我们再设定字段名并根据用户码进行排序最后显示出这个表就可以了
```plain
df_user = pd.DataFrame(df_sales['用户码'].unique()) #生成以用户码为主键的结构df_user
df_user.columns = ['用户码'] #设定字段名
df_user = df_user.sort_values(by='用户码',ascending=True).reset_index(drop=True) #按用户码排序
df_user #显示df_user
```
请你注意在上述语句中reset\_index(drop=True)的意思是重置索引,生成新的默认数值类型索引,并且不保留原来的索引。运行这段代码后,我们得到这样的结果:
![](https://static001.geekbang.org/resource/image/46/b9/46d77e14780785ef546daa64c3568ab9.png?wh=183x439)
可以看到我们一共有980个用户的数据。有了用户层级表现在我们依次求出RFM让这个用户层级表的结构越来越完整。
2. 求出R值
我们知道R值代表自用户上次消费以来的天数它与最近一次消费的日期相关。所以用表中最新订单的日期拉出来这张表的日期减去上一次消费的日期就可以确定对应用户的R值。
下面是具体的代码实现在每行代码中都给出了详细的注释而且Pandas的语句也都不难理解你可以看一看
```plain
df_sales['消费日期'] = pd.to_datetime(df_sales['消费日期']) #转化日期格式
df_recent_buy = df_sales.groupby('用户码').消费日期.max().reset_index() #构建消费日期信息
df_recent_buy.columns = ['用户码','最近日期'] #设定字段名
df_recent_buy['R值'] = (df_recent_buy['最近日期'].max() - df_recent_buy['最近日期']).dt.days #计算最新日期与上次消费日期的天数
df_user = pd.merge(df_user, df_recent_buy[['用户码','R值']], on='用户码') #把上次消费距最新日期的天数R值合并至df_user结构
df_user.head() #显示df_user头几行数据
```
输出如下:
![](https://static001.geekbang.org/resource/image/3e/30/3e46f92305d4d9bf3cd1aa172ed19230.png?wh=142x213)
R值越大说明该用户最近一次购物日距离当前日期越久那么这样的用户就越是处于休眠状态。从表中可以看出来编号为14682的用户已经有187天没有购物了。所以我们就可以判断这个用户呈现休眠态很可能已经被别的购物平台所吸引了也就是流失了。
3. 求出F值
类似地我们还可以求出F值并把F值添加至用户层级表
```plain
df_frequency = df_sales.groupby('用户码').消费日期.count().reset_index() #计算每个用户消费次数构建df_frequency对象
df_frequency.columns = ['用户码','F值'] #设定字段名称
df_user = pd.merge(df_user, df_frequency, on='用户码') #把消费频率整合至df_user结构
df_user.head() #显示头几行数据
```
这段代码的核心就是通过给消费日期做count()计数,来求出每一个用户的消费次数。最后,我们得到的输出结果如下:
![](https://static001.geekbang.org/resource/image/4f/62/4f02132dcfa59e87143ca3d513de9c62.png?wh=181x207)
4. **求出M值**
M值很容易求出它就是用户消费的总和
```plain
df_revenue = df_sales.groupby('用户码').总价.sum().reset_index() #根据消费总额构建df_revenue对象
df_revenue.columns = ['用户码','M值'] #设定字段名称
df_user = pd.merge(df_user, df_revenue, on='用户码') #把消费金额整合至df_user结构
df_user.head() #显示头几行数据
```
这段代码的核心是通过给用户每张订单的总价字段做sum()计数,来求出每一个用户的消费总和。最后,输出如下:
![](https://static001.geekbang.org/resource/image/13/c2/13bc1375b789e5173ce3016b84a01dc2.png?wh=260x208)
到这里我们就求出了每一个用户的R、F、M值。不过这只是完成了特征工程环节在下节课我们要根据这三个维度的值给用户分组这就需要聚类算法大显身手了。
# 总结一下
现在,我们来回顾一下这节课的重点。
我们这个项目的目标是为用户分组画像。要做分组画像RFM分析是一个不错的方法它也是目前很多互联网厂商普遍采用的分析方式。
我们今天的重点是根据用户码、总价和消费日期这三个字段从消费历史数据中求出每位用户的R、F、M的值这就好像给用户贴上了一堆数字化的标签。同时**用户行为的量化,也可以视作是进一步做机器学习项目之前的特征工程**。这也为我们后续给用户做聚类分组做好了准备。
理解RFM分析的精髓并利用好RFM三个指标是我们这个项目实战的核心价值所在。求出了RFM值就恭喜你闯过了我们获客阶段的数据探索这一关。
虽然我把实战的具体过程讲得很细,但我更希望你理解的是**“数据处理之道”** 而不是只学几个Python语句。Python语句都很简单但是怎么灵活应用数据是我们一开始就应该领悟的“道”因为只有对思路的深入理解才能让我们走得更高、更远。
# 思考题
好,这节课就到这里了,最后,我给你留个思考题:
RFM模型是衡量客户价值和客户创利能力的重要工具和手段而且也被广泛应用在企业的获客、促销、客户关系管理等诸多营销、推广环节。请你谈一谈你对RFM的理解以及这些指标可以应用于哪些业务场景。
欢迎你在留言区和我分享你的观点,如果你认为这节课的内容有收获,也欢迎把它分享给你的朋友,我们下一讲再见!
![](https://static001.geekbang.org/resource/image/80/45/800f834531ddbf7686054d0c4cce2445.jpg?wh=2284x736)