Python电商订单数据初步分析

背景

Python初步分析订单数据,如果是学习需要虚拟数据,则可以参考我的这篇文章自定义一些虚拟数据。

定义

复购率:复购率是指在一个特定的时间窗口内,多次购买同一产品或服务的客户占该时间段内总客户数的比例。它主要关注的是客户在同一时间段内的重复购买行为,衡量的是客户的消费频率和忠诚度。比如,在一个月内,购买过两次或以上的客户占该月总客户数的比例就是该月的复购率。

回购率:回购率关注的是客户在不同时间段内的再次购买行为。它计算的是在一个时间段内购买过产品或服务的客户,在后续的时间段内再次购买的比例。例如,如果一个客户在1月份购买了产品,然后在2月份又进行了购买,那么他就属于回购客户,回购率则是这类客户占所有1月份客户的比例。

这中间就涉及到产品生命周期的一些问题了,具体的这个时间界定还是得看商家产品生命周期有多长。
—————————————————————————————————————————————————————

留存率:留存率是一个用于反映互联网应用、网站或网络游戏运营情况的统计指标。它具体表示在某一统计周期(如周或月)内,那些在某日活跃的用户在接下来的一段时间(如第2日、第7日、第30日等)内仍然保持活跃的比例。留存率常用于衡量用户粘性,即用户对于应用的持续使用意愿和忠诚度。

留存率的计算公式为:留存率 = (留存用户数 ÷ 初始用户数) × 100%。其中,留存用户数指的是在特定时间周期内仍然活跃的用户数量,而初始用户数则是该周期开始时的活跃用户数量。

留存率根据统计的时间周期不同,可以分为次日留存率、三日留存率、周留存率、半月留存率和月留存率等。通常,N取值越大,而留存率越高时,说明用户的粘性越高,应用或网站的运营效果越好。
————————————————————————————————————————————————-—————
RFM是客户关系管理中的一种分析模式,也是衡量客户价值和客户创利能力的重要工具和手段。它具体包含三个关键要素:

R(Recency):指客户最近一次消费的时间间隔。这个指标主要反映了客户的活跃程度,R值越小,表示客户的活跃程度越高;R值越大,则表示客户上一次交易的时间越久远,可能面临流失的风险。
F(Frequency):指客户在最近一段时间内消费的次数。F值越大,说明客户消费频次越高,越活跃。
M(Monetary):指客户在最近一段时间内消费的金额。M值越高,则客户消费金额越大,可能属于高价值或VIP客户。
————————————————————————————————————————————————-—————

分析

  • 初步分析

  • 配置
import pandas as pd
import pymysql
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()
  • 每个订单的初步分析
# 每个订单的初步分析
df = pd.read_sql(
    "select customer_name,left(order_create_time,10) as order_create_time,item_num,cast(total_money as float ) AS total_money from order_data WHERE order_status = '交易成功'",
    db)

print(df.describe())
"""
         item_num  total_money
count  507.000000   507.000000
mean     5.459566  2594.013807
std      2.863123  1350.196286
min      1.000000   303.000000
25%      3.000000  1439.000000
50%      5.000000  2614.000000
75%      8.000000  3786.500000
max     10.000000  4973.000000

平均购买5.5个商品,标准差在2.8个左右,中位数是购买5个商品,75分位数在购买8个商品,这么这个店铺绝大多数的订单量都不错。
基本购买数量和金额都成线性增长。
"""
  • 每个用户的初步分析
# 每个用户的初步分析
df_group = df.groupby('customer_name').sum()
print(df_group.describe())
"""
         item_num   total_money
count  243.000000    243.000000
mean    11.390947   5412.201646
std      8.378904   4069.483713
min      1.000000    319.000000
25%      6.000000   2557.500000
50%      9.000000   4471.000000
75%     15.000000   7222.500000
max     52.000000  28617.000000

平均每个用户购买11.4件商品,消费5400元左右,标准差在8.4件左右,标准差消费4000元左右,说明用户消费差异大,中位数在9件的样子,75分位数在15件的样子,最多一个用户购买了52件,花费了2.8W元。
"""
  • 按照月度初步分析
# 按照月度初步分析
df['month'] = df['order_create_time'].apply(lambda x: x[0:7])
df.groupby('month').item_num.sum().plot()
df.groupby('month').total_money.sum().plot()
plt.show()

在这里插入图片描述
表明销量成周期性变化,平均半年一个周期,

下面是销售额的数据

在这里插入图片描述
可以看出基本与销售量成线性匹配,说明该订单类型应该属于标品类型。

  • 绘制每笔订单的散点图
#绘制每笔订单的散点图
df.plot.scatter(x='total_money', y='item_num')

结果如下
在这里插入图片描述
这边因为订单生成原因,所以没什么,如果真实情况和图中类似,建议可以拓宽价格带,这个说明你的价格带区间很均匀,你的商品并没有涉及到商品价格边缘。

  • 按照用户分组的散点图
df.groupby('customer_name').sum().plot.scatter(x='total_money', y='item_num')

结果
在这里插入图片描述
用户主要消费水平在1W以下,数量和金额成正比。

  • 观察不同用户的消费能力
# 观察不同用户的消费能力
plt.figure(figsize=(12,4))
plt.subplot(121)
df.total_money.hist(bins=30)
plt.subplot(122)
df.groupby('customer_name').item_num.sum().hist(bins=30)

结果:
在这里插入图片描述
从直方图看,大部分用户的消费能力可以,高消费用户几乎和低消费用户持平。消费数量在10件之后成指数性降低,这也符合消费规律。

  • 消费时间分析
# 将用户分组,求月份的最小值,即用户消费行为中的第一次消费时间
df_month_min = pd.DataFrame(df.groupby('customer_name').month.min().value_counts())
df_month_min.reset_index(level=0, inplace=True)
# 按照时间排序
df_month_min = df_month_min.sort_values(by='month')
df_month_min.plot.scatter(x='month', y='count')
xticks, xticklabels = plt.xticks()
plt.xticks(xticks, xticklabels, rotation=90)
plt.show()

结果
在这里插入图片描述

df_month_max = pd.DataFrame(df.groupby('customer_name').month.max().value_counts())
df_month_max.reset_index(level=0, inplace=True)
# 按照时间排序
df_month_max = df_month_max.sort_values(by='month')
df_month_max.plot.scatter(x='month', y='count')
xticks, xticklabels = plt.xticks()
plt.xticks(xticks, xticklabels, rotation=90)
plt.show()

结果
在这里插入图片描述
由于我的数据是自制数据,并没有太多参考意义,如果有需要可以参考我当初学习的原文

  • 复购率

第一种写法

import pymysql
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# pandas展示所有列
pd.set_option('display.max_columns', None)

# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()

根据定义,统计月复购次数大于1的用户

# 复购率:自然月内,购买多次的用户占比(即,购买了两次以上)
df = pd.read_sql("select customer_name,left(order_create_time,10) as order_create_time,left(order_create_time,7) as month,cast(total_money as float ) AS total_money from order_data WHERE order_status = '交易成功'", db)
# 对于用户名和月份作为聚合条件对于日期进行统计次数
df = pd.DataFrame(df.groupby(['customer_name', 'month'])['order_create_time'].count())
df.reset_index(level=1, inplace=True)
df.reset_index(level=0, inplace=True)
df.rename(columns={'order_create_time': 'count'}, inplace=True)
print(df[df['count']>1])

代码结果为
在这里插入图片描述
统计每个月的月复购大于1的人数

# 每个月复购人数
df_count = pd.DataFrame(df_count.groupby('month')['count'].count())
print(df_count)

结果为
在这里插入图片描述
然后求取每个月的购买总人数

# 统计每个月的人数
df_person = pd.DataFrame(df.groupby('month').agg({'customer_name': 'nunique'}))
df_person.rename(columns={'customer_name': 'count'}, inplace=True)
df_person.reset_index(level=0, inplace=True)
print(df_person)

结果为:
在这里插入图片描述
用 每个月的复购人数除以每个月的总人数得出每个月的复购率

# 用 每个月的复购人数除以每个月的总人数得出每个月的复购率
merged_outer = pd.merge(df_count, df_person, on='month', how='outer')
merged_outer['rate'] = merged_outer.apply(lambda x: 0 if pd.isnull(x.count_x) else x.count_x / x.count_y, axis=1)
print(merged_outer)

结果为
在这里插入图片描述

另一种写法

# 表透视
pivoted_counts = df.pivot_table(index='customer_name',
                                columns='month',
                                values='order_create_time',
                                aggfunc='count').fillna(0)
print(pivoted_counts)

在这里插入图片描述``

import numpy as np
# 将数据转换一下,消费两次及以上记为1,消费一次记为0,没有消费记为NaN
purchase_r = pivoted_counts.applymap(lambda x: 1 if x > 1 else np.NAN if x == 0 else 0)
print(purchase_r)

在这里插入图片描述
结果为
在这里插入图片描述
画图
在这里插入图片描述
复购高点基本三个季度一个周期。

  • 回购率

获取数据库配置

import matplotlib.pyplot as plt
import pymysql
import pandas as pd
import numpy as np

# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()

获取数据并进行表透视

df = pd.read_sql(
    "select customer_name,left(order_create_time,10) as order_create_time,left(order_create_time,7) as month,cast(total_money as float ) AS total_money from order_data WHERE order_status = '交易成功'",
    db)
# 表透视
pivoted_amount = df.pivot_table(index='customer_name',
                                columns='month',
                                values='total_money',
                                aggfunc='mean').fillna(0)
                                
pivoted_amount = pivoted_amount.applymap(lambda x: 1 if x > 0 else 0)                               

新建一个判断函数,status用来保存用户是否回购的字段。
如果用户本月进行过消费,且下月消费过,记为1,没有消费过是0。如果本月没有进行过消费,为NaN。

def purchase_return(data):
    status = []
    for i in range(17):
        if data[i] == 1:
            if data[i + 1] == 1:
                status.append(1)
            if data[i + 1] == 0:
                status.append(0)
        else:
            status.append(np.NaN)
    status.append(np.NaN)
    dic = dict(map(lambda x, y: [x, y], data.index, status))
    series = pd.Series(dic)
    return series


pivoted_amount_return = pivoted_amount.apply(purchase_return, axis=1)
print(pivoted_amount_return)

计算回购率

#用户本月进行过消费,且下月消费过/(用户本月进行过消费,且下月消费过 + 用户本月进行过消费,且下月没有消费过)
huogou = pivoted_amount_return.sum() / pivoted_amount_return.count()
huogou_month = pd.DataFrame(huogou)
huogou_month.reset_index(level=0, inplace=True)
print(huogou_month)
# 画图
(pivoted_amount_return.sum()/pivoted_amount_return.count()).plot(figsize=(8, 5))
plt.show()

结果
在这里插入图片描述
说明基本一个季度一个周期,会有一个回购高峰期,回购率远高于复购率。

而当回购率远高于复购率时,这通常意味着:
客户忠诚度差异:回购率是指曾经购买过的客户再次购买的比例,而复购率则是指同一客户在一定时间内多次购买的比例。如果回购率高而复购率低,可能表明虽然有很多老客户选择再次购买,但这些客户中大部分并没有频繁地、多次地购买
一次性购买行为较多:高回购率低复购率可能表示很多客户只是偶尔购买,而不是频繁地或定期地购买。这可能是因为他们的需求不是持续性的,或者他们只是出于某种特定原因(如促销、新品尝鲜等)进行了单次购买。
缺乏持续吸引客户的策略:这种情况可能也反映出企业在维持客户长期购买兴趣方面做得不够。例如,可能缺乏有效的客户忠诚度计划、产品更新迭代不够快、服务体验不够优质等,导致客户虽然愿意回来购买,但不愿意频繁购买。
新客户与老客户的比例:如果回购率高,可能意味着企业吸引新客户的能力较强,但这些新客户可能没有转化为频繁购买的忠实客户

为了改善这种情况,企业可能需要:
深入了解客户的需求和购买习惯,以提供更加符合他们期望的产品和服务。
加强客户关系管理,通过定期沟通、优惠活动等方式提高客户粘性。
不断优化产品和服务,提升客户体验,从而增加客户的复购意愿。

  • 用户分层

按照用户的消费行为,分成几个维度:新用户、活跃用户、不活跃用户、回流用户。

将还没有消费的命名为未注册用户,

用户有过第一次消费命名为新用户,

新用户之后如果这个月消费,上个月也是消费的,命名为活跃用户,如果上个月不消费的命名为回流用户

新用户之后这个月和上个月都不消费的命名为不活跃用户

以上的时间窗口都是按月统计。

数据库配置获取数据

import pymysql
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# pandas展示所有列
pd.set_option('display.max_rows', 100)
# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()

df = pd.read_sql(
    "select customer_name,left(order_create_time,10) as order_create_time,left(order_create_time,7) as month,cast(total_money as float ) AS total_money from order_data WHERE order_status = '交易成功'",
    db)

表透视

# 表透视
pivoted_counts = df.pivot_table(index='customer_name',
                                columns='month',
                                values='order_create_time',
                                aggfunc='count').fillna(0)

df_purchase = pivoted_counts.applymap(lambda x: 1 if x > 0 else 0)
print(df_purchase)

结果
在这里插入图片描述
从上图中可以看到,表中数据有0,1,2···,要将有消费的变为1,没有消费变为0
这里由于进行数据透视,填充了一些 null 值为0,而实际可能用户在当月根本就没有注册,这样会误导第一次消费数据的统计。

用户量月统计

# 每个月的统计量
purchase_status_counts = purchase_data.replace(2, np.NaN).apply(lambda x: pd.value_counts(x))
print(purchase_status_counts)
purchase_status_counts.fillna(0).T.plot.area(figsize=(8, 5))
plt.show()
# 生成面积图。
# 因为只是某时间段消费过的用户的后续行为,蓝色和橙色区域不看,
# 只绿色回流和红色活跃这两个分层,用户数比较稳定。

结果
在这里插入图片描述
因为只是某时间段消费过的用户的后续行为,蓝色和橙色区域不看,
只绿色回流和红色活跃这两个分层,用户数比较稳定。

回流用户与活跃用户

purchase_status_counts_new = purchase_data.replace(2, np.NaN).replace(3, np.NaN).replace(4, np.NaN).apply(lambda x: pd.value_counts(x))
purchase_status_counts_new.fillna(0).T.plot.area(figsize=(8, 5))
plt.show()
# 用户数有逐年螺旋稳定上升的趋势

在这里插入图片描述

return_rate = purchase_status_counts.apply(lambda x: x/x.sum(), axis=0)
return_rate.loc[5].plot(figsize=(8, 5))
plt.show()
# 回流占比基本在0.1以下,说明召回客户做的不太好,并且有逐年下降的趋势。

结果
在这里插入图片描述
回流占比基本在0.1以下,说明召回客户做的不太好,并且有逐年下降的趋势。

用户分层标签并存储(作为标签体系数据)

def active_data(data):
    # 未注册用户 = 2
    # 不活跃用户 = 3
    # 新用户 = 4
    # 回流用户 = 5
    # 活跃用户 = 6
    for i in range(len(data.columns.values)):
        if data[data.columns.values[i]].values[0] == 0:
            if i > 0:
                if data[data.columns.values[i - 1]].values[0] == 2:
                    data[data.columns.values[i]].values[0] = 2
                else:
                    data[data.columns.values[i]].values[0] = 3
            else:
                data[data.columns.values[i]].values[0] = 2
        else:
            if i == 0:
                data[data.columns.values[i]].values[0] = 4
            else:
                if data[data.columns.values[i - 1]].values[0] == 3:
                    data[data.columns.values[i]].values[0] = 5
                elif data[data.columns.values[i - 1]].values[0] == 2:
                    data[data.columns.values[i]].values[0] = 4
                else:
                    data[data.columns.values[i]].values[0] = 6
    return data


# 第四步
purchase_data = df_purchase.groupby('customer_name').apply(lambda x: active_data(x))
purchase_data.replace(2, '未注册用户', inplace=True)
purchase_data.replace(3, '不活跃用户', inplace=True)
purchase_data.replace(4, '新用户', inplace=True)
purchase_data.replace(5, '回流用户', inplace=True)
purchase_data.replace(6, '活跃用户', inplace=True)
user_layering_history = pd.DataFrame(purchase_data.stack())
user_layering_history.reset_index(level=0, inplace=True)
user_layering_history.rename(columns={0: 'level'}, inplace=True)
user_layering_history_tosql = user_layering_history[user_layering_history['level'] != '未注册用户']
print(user_layering_history_tosql)
# 第五步
user_layering_history_tosql = user_layering_history_tosql.head(1000)
user_layering_history_tosql.to_sql('user_layering_history',engine,if_exists='replace')
for i in range(1000,len(user_layering_history_tosql),1000):
    user_layering_history_tosql.iloc[i:i+1000].to_sql('user_layering_history',engine,if_exists='append')

结果
在这里插入图片描述

  • 分析用户质量

数据库配置以及获取数据

import pymysql
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# pandas展示所有列
pd.set_option('display.max_rows', 100)
# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()

df = pd.read_sql(
    "select customer_name,left(order_create_time,10) as order_create_time,left(order_create_time,7) as month,cast(total_money as float ) AS total_money from order_data WHERE order_status = '交易成功'",
    db)

我们通过消费金额来查看高质量用户的分层情况

# 我们通过消费金额来查看高质量用户的分层情况
user_amount = df.groupby('customer_name').total_money.sum().sort_values().reset_index()
user_amount['total_money'] = user_amount.total_money.cumsum()
amount_total = user_amount.total_money.max()
user_amount['prop'] = user_amount.apply(lambda x: x.total_money / amount_total, axis=1)
user_amount.prop.plot()
plt.show()
print(user_amount)

结果
在这里插入图片描述
在这里插入图片描述
上图为总消费额趋势图,横坐标是按用户贡献金额大小排序而成,纵坐标则是用户累计贡献。可以清楚的看到,前150个用户贡献了40%的消费额,后面100位用户贡献了60%的消费额,呈现二八倾向,但差距不算很大。

  • 计算用户生命周期

定义第一次消费至最后一次消费为整个用户生命周期。

数据库配置以及获取数据

import pymysql
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# pandas展示所有列
pd.set_option('display.max_rows', 100)
# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()

df = pd.read_sql(
    "select customer_name,left(order_create_time,10) as order_create_time,left(order_create_time,7) as month,cast(total_money as float ) AS total_money,item_num from order_data WHERE order_status = '交易成功'",
    db)

计算用户生命周期

df['order_create_time'] = pd.to_datetime(df['order_create_time'])
order_date_min = df.groupby('customer_name').order_create_time.min()
order_date_max = df.groupby('customer_name').order_create_time.max()
print(order_date_max - order_date_min)
print((order_date_max - order_date_min).mean())
# 平均用户生命周期半年。

结果
在这里插入图片描述
平均用户生命周期半年

((order_date_max - order_date_min) / np.timedelta64(1, 'D')).hist(bins=15)
plt.show()
# 大部分用户只消费了一次,生命周期集中在了0天。将只消费了一次的新客排除,来计算所有消费过两次以上的老客的生命周期。

结果
在这里插入图片描述
大部分用户只消费了一次,生命周期集中在了0天。将只消费了一次的新客排除,来计算所有消费过两次以上的老客的生命周期。

life_time = (order_date_max - order_date_min).reset_index()
life_time['life_time'] = life_time.order_create_time / np.timedelta64(1, 'D')
life_time[life_time.life_time > 0].life_time.hist(bins=100, figsize=(8, 5))
plt.show()
# 排除仅消费了一次的用户,做直方图。在用户首次消费90天内应该尽量引导。这个类型普通和高质量的回购都差不多。

在这里插入图片描述

排除仅消费了一次的用户,做直方图。在用户首次消费90天内应该尽量引导。这个类型普通和高质量的回购都差不多。

print(life_time[life_time.life_time > 0].life_time.mean())
# 消费两次以上的用户生命周期是308天,远高于总体,用户首次消费后应该引导其进行多次消费,提高生命周期。
  • 留存率

数据库配置以及获取数据

import pymysql
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# pandas展示所有列
pd.set_option('display.max_rows', 100)
# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()

df = pd.read_sql(
    "select customer_name,left(order_create_time,10) as order_create_time,left(order_create_time,7) as month,cast(total_money as float ) AS total_money,item_num from order_data WHERE order_status = '交易成功'",
    db)

计算日期差

df['order_create_time'] = pd.to_datetime(df['order_create_time'])
order_date_min = df.groupby('customer_name').order_create_time.min()
user_purchase_retention = pd.merge(left=df, right=order_date_min.reset_index(),
                                   how='inner', on='customer_name', suffixes=('', '_min'))
# 计算日期差, 将order_create_time和order_create_time_min相减,获得一个新的列,为用户每一次消费距第一次消费的时间差值。
user_purchase_retention[
    'order_date_diff'] = user_purchase_retention.order_create_time - user_purchase_retention.order_create_time_min
print(user_purchase_retention)

将order_create_time和order_create_time_min相减,获得一个新的列,为用户每一次消费距第一次消费的时间差值。

结果
在这里插入图片描述

user_purchase_retention['date_diff'] = user_purchase_retention.order_date_diff.apply(
    lambda x: x / np.timedelta64(1, 'D'))
bin = [0, 3, 7, 15, 30, 60, 90, 180, 365]
user_purchase_retention['date_diff_bins'] = pd.cut(user_purchase_retention.date_diff, bins=bin)
print(user_purchase_retention)

在这里插入图片描述
将时间差值分桶,分成0~3天,3~7天,7~15天等,代表用户当前消费时间距第一次消费属于哪个时间段。
这里date_diff=0并没有被划分入0~3天,因为计算的是留存率,如果用户仅消费了一次,留存率应该是0。
另外一方面,如果用户第一天内消费了多次,但是往后没有消费,也算作留存率0。

用pivot_table数据透视,获得的结果是用户在第一次消费之后,在后续各时间段内的消费总额。

pivoted_retention = user_purchase_retention.pivot_table(index='customer_name', columns='date_diff_bins',
                                                        values='total_money', aggfunc=sum)
print(pivoted_retention.mean())

结果
在这里插入图片描述
计算一下用户在后续各时间段的平均消费额,只统计有消费的平均值。 虽然后面时间段的金额高,但是它的时间范围也较广。
从平均效果看,用户第一次消费后的0~3天内,更可能消费多,特别是3-7天的,一点都没有。

查看整体中有多少用户在0~3天消费。
1代表在该时间段内有消费,0代表没有。

pivoted_retention_trans = pivoted_retention.fillna(0).applymap(lambda x: 1 if x > 0 else 0)
print(pivoted_retention_trans)
(pivoted_retention_trans.sum()/pivoted_retention_trans.count()).plot.bar()
plt.show()

结果
在这里插入图片描述
只有1%的用户在第一次消费的次日至3天内有过消费,0%的用户在3~7天内有过消费,有17%的用户在第一次消费后的三个月到半年之间有过消费,29%的用户在半年后至1年内有过消费。可以延长时间跨度,在较长时间跨度上召回用户购买。

  • 平均购买周期

在上面的用户生命周期基础上,将数据按照用户名和订单时间排序

user_purchase_retention = user_purchase_retention.sort_values(['customer_name', 'order_create_time'])

结果
在这里插入图片描述

def diff(group):
    d = group.date_diff - group.date_diff.shift(-1)
    return d


last_diff = user_purchase_retention.groupby('customer_name').apply(diff)
print(last_diff.mean()

结果
在这里插入图片描述
用户的平均消费间隔时间是166天。想要召回用户,在166天左右的消费间隔是比较好的。

last_diff.hist(bins=20)
plt.show()

结果
在这里插入图片描述
上图告诉我们,我们召回客户最好在50天以内进行召回,可以考虑消费后立即赠送优惠券,消费后10天询问用户消费体验,消费后30天提醒优惠券到期,消费后50天短信推送。

城市等级分析

一几年网上找的城市等级,如果需要,建议去国家网站去找一下最新数据。

import numpy as np
import pandas as pd
import pymysql

# pandas展示所有列
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', 20)

# 网上获取的关于城市等级的数据
city_level1 = "北京、上海、广州、深圳"
city_level1_new = "成都、杭州、重庆、武汉、西安、苏州、天津、南京、长沙、郑州、东莞、青岛、沈阳、宁波、昆明"
city_level2 = "无锡、佛山、合肥、大连、福州、厦门、哈尔滨、济南、温州、南宁、长春、泉州、石家庄、贵阳、常州、南通、嘉兴、太原、徐州、南昌、金华、惠州、珠海、中山、台州、烟台、兰州、绍兴、海口、扬州"
city_level3 = "汕头、湖州、盐城、潍坊、保定、镇江、洛阳、泰州、乌鲁木齐、临沂、唐山、漳州、赣州、廊坊、呼和浩特、芜湖、桂林、银川、揭阳、三亚、遵义、江门、济宁、莆田、湛江、绵阳、淮安、连云港、淄博、宜昌、邯郸、上饶、柳州、舟山、咸阳、九江、衡阳、威海、宁德、阜阳、株洲、丽水、南阳、襄阳、大庆、沧州、信阳、岳阳、商丘、肇庆、清远、滁州、龙岩、荆州、蚌埠、新乡、鞍山、湘潭、马鞍山、三明、潮州、梅州、秦皇岛、南平、吉林、安庆、泰安、宿迁、包头、郴州"
city_level4 = "韶关、常德、六安、汕尾、西宁、茂名、驻马店、邢台、南充、宜春、大理、丽江、延边、衢州、黔东南、景德镇、开封、红河、北海、黄冈、东营、怀化、阳江、菏泽、黔南、宿州、日照、黄石、周口、晋中、许昌、拉萨、锦州、佳木斯、淮南、抚州、营口、曲靖、齐齐哈尔、牡丹江、河源、德阳、邵阳、孝感、焦作、益阳、张家口、运城、大同、德州、玉林、榆林、平顶山、盘锦、渭南、安阳、铜仁、宣城、永州、黄山、西双版纳、十堰、宜宾、丹东、乐山、吉安、宝鸡、鄂尔多斯、铜陵、娄底、盘水、承德、保山、毕节、泸州、恩施、安顺、枣庄、聊城、百色、临汾、梧州、亳州、德宏、鹰潭、滨州、绥化、眉山、赤峰、咸宁"
city_level5 = "防城港、玉溪、呼伦贝尔、普洱、葫芦岛、楚雄、衡水、抚顺、钦州、四平、汉中、黔西南、内江、湘西、漯河、新余、延安、长治、文山、云浮、贵港、昭通、河池、达州、淮北、濮阳、通化、松原、通辽、广元、鄂州、凉山、张家界、荆门、来宾、忻州、克拉玛依、送宁、朝阳、崇左、辽阳、广安、萍乡、阜新、吕梁、池州、贺州、本溪、铁岭、自贡、锡林郭勒、白城、自山、雅安、酒泉、天水、晋城、巴彦淖尔、随州、兴安、临沧、鸡西、迪庆、攀枝花、鹤壁、黑河、双鸭山、三门峡、安康、乌兰察布、庆阳、伊犁、儋州、哈密、海西、甘孜、伊春、陇南、乌海、林芝、怒江、朔州、阳泉、嘉峪、关鹤岗、张掖、辽源、吴忠、昌吉、大兴安岭、巴音郭楞、阿坝、日喀则、阿拉善、巴中、平凉、阿克苏、定西、商洛、金昌、七台河、石嘴山、白银、铜川武威、吐鲁番、固原、山南、临夏、海东、喀什甘南、昌都、中卫、资阳、阿勒泰、塔城、博尔塔拉、海南、克孜、阿里、和田、玉树、那曲、黄南、海北、果洛、三沙"
list1 = city_level1.split("、")
list1_new = city_level1_new.split("、")
list2 = city_level2.split("、")
list3 = city_level3.split("、")
list4 = city_level4.split("、")
list5 = city_level5.split("、")

# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)
cursor = db.cursor()

获取数据并通过城市名称进行合并

df = pd.DataFrame(columns=('level', 'city'))
for i in list1:
    df = df.append([{'level': '一线城市', 'city': i}], ignore_index=True)
for i in list1_new:
    df = df.append([{'level': '新一线城市', 'city': i}], ignore_index=True)
for i in list2:
    df = df.append([{'level': '二线城市', 'city': i}], ignore_index=True)
for i in list3:
    df = df.append([{'level': '三线城市', 'city': i}], ignore_index=True)
for i in list4:
    df = df.append([{'level': '四线城市', 'city': i}], ignore_index=True)
for i in list5:
    df = df.append([{'level': '五线城市', 'city': i}], ignore_index=True)


df_data = pd.read_sql("select * from order_data", db)

def fun(x):
    city = x.city.split("市")[0]
    province = x.province.split("市")[0]
    if city in df['city'].values.tolist():
        return df[df['city'] == city]['level'].values[0]
    elif province in df['city'].values.tolist():
        return df[df['city'] == province]['level'].values[0]
    else:
        return np.NAN
df_data['city_level'] = df_data.apply(lambda x: fun(x), axis=1)

求取各城市等级的总消费金额和销售量,并计算出各城市等级占比情况

grouped_city_level = df_data.groupby('city_level').aggregate({'total_money': 'sum', 'item_num': 'sum'})
grouped_city_level['sum_money_city_level_rate'] = grouped_city_level['total_money'] / grouped_city_level['total_money'].sum()
grouped_city_level['sum_item_num_city_level_rate'] = grouped_city_level['item_num'] / grouped_city_level['item_num'].sum()
grouped_city_level = grouped_city_level.sort_values('sum_money_city_level_rate', ascending=False)
print(grouped_city_level)

结果如下

            total_money  item_num  sum_money_city_level_rate  \
city_level                                                     
五线城市           481855.0      1006                   0.244877   
三线城市           429984.0       890                   0.218516   
四线城市           413149.0       892                   0.209961   
新一线城市          246757.0       480                   0.125401   
二线城市           209388.0       424                   0.106410   
一线城市           186610.0       353                   0.094835   

            sum_item_num_city_level_rate  
city_level                                
五线城市                            0.248702  
三线城市                            0.220025  
四线城市                            0.220519  
新一线城市                           0.118665  
二线城市                            0.104821  
一线城市                            0.087268  

可以看出这个店铺在三四五线的销售更优于一二线城市,如果考虑线下销售打造品牌的话,可以优先选择城市等级低的城市作为开店选择。

RFM

获取数据库配置以及获取数据

import matplotlib.pyplot as plt
import pymysql as pymysql
import pandas as pd
import numpy as np

# 数据库连接配置
config = {
    'host': 'localhost',  # 数据库地址
    'port': 3306,  # 端口号
    'user': 'root',  # 数据库用户名
    'password': '***',  # 数据库密码
    'db': 'data',  # 数据库名称
    'charset': 'utf8mb4',  # 字符集
}

# 连接数据库
db = pymysql.connect(**config)

# 对新老客消费比进行分析
df = pd.read_sql(
    "SELECT customer_name,left(order_create_time,10) as order_create_time,left(order_create_time,7) as month,1 AS order_num,cast(total_money as float ) AS total_money FROM order_data WHERE order_status = '交易成功'",
    db)
df['order_create_time'] = pd.to_datetime(df['order_create_time'], format='%Y%m%d ')
df.sort_values('order_create_time', inplace=True)

通过重复订单数据来判断这批订单的新老客

# 查看列customer_name的重复数据数量
duplicates_in_customer_name = df['customer_name'].value_counts()

# 找到重复的数据(即出现次数大于1的数据)
duplicates = duplicates_in_customer_name[duplicates_in_customer_name > 1]
duplicates_no = duplicates_in_customer_name[duplicates_in_customer_name == 1]
print(df)
print(len(duplicates))
print(len(duplicates_no))

结果如下

customer_name order_create_time    month  order_num  total_money
39       幻走外贸男装服饰        2022-01-02  2022-01          1       1965.0
255         拾心少女。        2022-01-05  2022-01          1       4314.0
386            诗呓        2022-01-07  2022-01          1       1501.0
316          你型我溯        2022-01-07  2022-01          1       2750.0
351           莎臣豹        2022-01-09  2022-01          1       4409.0
..            ...               ...      ...        ...          ...
25      斯文g | 斯文m        2023-12-25  2023-12          1       3110.0
287          神都偏爱        2023-12-26  2023-12          1        542.0
105          神都偏爱        2023-12-27  2023-12          1       1644.0
96           悃悃鋩鋩        2023-12-29  2023-12          1        776.0
18          最初的梦想        2023-12-30  2023-12          1        824.0

[507 rows x 5 columns]
142
101

可以看出新老客占比2:3

将数据转化为RFM数据

# RFM
# R(Recency):指客户最近一次消费的时间间隔。这个指标主要反映了客户的活跃程度,R值越小,表示客户的活跃程度越高;R值越大,则表示客户上一次交易的时间越久远,可能面临流失的风险。
# F(Frequency):指客户在最近一段时间内消费的次数。F值越大,说明客户消费频次越高,越活跃。
# M(Monetary):指客户在最近一段时间内消费的金额。M值越高,则客户消费金额越大,可能属于高价值或VIP客户。
rfm = df.pivot_table(index='customer_name',
                        values=['order_num', 'total_money', 'order_create_time'],
                        aggfunc={'order_create_time': 'max',
                                 'total_money': 'sum',
                                 'order_num': 'sum'})

# 到最后一个订单时间距离天数 = R
rfm['R'] = (rfm['order_create_time'].max() - rfm['order_create_time']) / np.timedelta64(1, 'D')

rfm.rename(columns={'order_num': 'F', 'total_money': 'M'}, inplace=True)

rfm[['R', 'F', 'M']].apply(lambda x: x - x.mean())
rfm.reset_index(inplace=True)
data = rfm[['customer_name', 'F', 'M', 'R']]
print(data)

结果如下

customer_name  F        M      R
0         mrゞ唯嗳ガ舞  2   2562.0  225.0
1           ゝ柒彩红艹  3   7207.0  630.0
2           ヾ左岸烟逝  3   6514.0  306.0
3            与我相关  3   3845.0  219.0
4           丫頭╃片子  1   4720.0  128.0
..            ... ..      ...    ...
238        骨子里的高雅  3  11281.0  316.0
239          魔夜战队  2   2904.0  152.0
240          鼻尖触碰  3   6901.0   96.0
241       ︶ㄣ血゛泪Oo  2   3666.0  363.0
242     Johnny.R。  3   7824.0  374.0

[243 rows x 4 columns]

将RFM转化为用户评定

def rfm_func(x):
    level = x.apply(lambda x: '1' if x > 0 else '0')
    label = level.R + level.F + level.M
    d = {
        '111': '重要价值客户',
        '011': '重要保持客户',
        '101': '重要发展客户',
        '001': '重要挽留客户',
        '110': '一般价值客户',
        '010': '一般保持客户',
        '100': '一般发展客户',
        '000': '一般挽留客户',
    }
    result = d[label]
    return result


rfm['label'] = rfm[['R', 'F', 'M']].apply(lambda x: x - x.mean()).apply(rfm_func, axis=1)
rfm.reset_index(level=0, inplace=True)
print(rfm)

结果如下

index customer_name order_create_time  F        M      R   label
0        0       mrゞ唯嗳ガ舞        2023-05-19  2   2562.0  225.0  一般挽留客户
1        1         ゝ柒彩红艹        2022-04-09  3   7207.0  630.0  重要价值客户
2        2         ヾ左岸烟逝        2023-02-27  3   6514.0  306.0  重要价值客户
3        3          与我相关        2023-05-25  3   3845.0  219.0  一般保持客户
4        4         丫頭╃片子        2023-08-24  1   4720.0  128.0  一般挽留客户
..     ...           ...               ... ..      ...    ...     ...
238    238        骨子里的高雅        2023-02-17  3  11281.0  316.0  重要价值客户
239    239          魔夜战队        2023-07-31  2   2904.0  152.0  一般挽留客户
240    240          鼻尖触碰        2023-09-25  3   6901.0   96.0  重要保持客户
241    241       ︶ㄣ血゛泪Oo        2023-01-01  2   3666.0  363.0  一般发展客户
242    242     Johnny.R。        2022-12-21  3   7824.0  374.0  重要价值客户

[243 rows x 7 columns]

绘制散点图

# 对重要价值客户及非重要价值客户的消费时间、消费频率绘制散点图
rfm.loc[rfm.label == '重要价值客户', 'color'] = 'g'
rfm.loc[~(rfm.label == '重要价值客户'), 'color'] = 'r'
rfm.plot.scatter('F', 'R', c=rfm.color)
rmf_group = rfm.groupby('label').agg({'F': 'mean', 'M': 'mean', 'R': 'mean'})
rmf_group.reset_index(level=0, inplace=True)
print(rmf_group)
plt.show()

结果如下

 label         F             M           R
0  一般价值客户  3.000000   4283.000000  495.000000
1  一般保持客户  3.333333   4384.333333  151.333333
2  一般发展客户  1.196970   2756.424242  489.060606
3  一般挽留客户  1.351351   2907.716216  134.891892
4  重要价值客户  3.136364   9010.500000  383.000000
5  重要保持客户  4.250000  11323.850000  100.400000
6  重要发展客户  2.000000   7038.900000  441.600000
7  重要挽留客户  2.000000   6819.760000  122.080000

可以看出重要客户的最后一次购买R平均都超过了100天,甚至有超过一年的,所以客户召回以及挽留动作需要加强,增加客户黏性,重点可以关注保持和价值的客户,这些客户能带来重大的利益。
在这里插入图片描述
可以看出重要价值的客户平均购买频次在3-4次之间,最后一次购买平均为383天,重要客户的最后一次购买R平均都超过了100天,所以客户召回以及挽留动作需要加强,增加客户黏性,重点可以关注保持和价值的客户,这些客户能带来重大的利益。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值