import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
plt.style.use('ggplot')
columns = ['user_id', 'order_dt', 'order_products', 'order_amount']
df = pd.read_table('CDNOW_master.txt', names=columns, sep='\s+')
df.head()
df.info()
df.describe()
df['order_dt'] = pd.to_datetime(df.order_dt, format="%Y%m%d") # 解析日期
df['month'] = df.order_dt.values.astype('datetime64[M]') # 生成month变量
df.head()
grouped_month = df.groupby('month') # 按月聚合
order_month_amount = grouped_month.order_amount.sum() # 加总每月消费额
order_month_amount.head()
# 每月总金额折线图
order_month_amount.plot()
由上图可知,消费金额在前三个月达到最高峰,后续消费额较为稳定,有轻微下降趋势
# 每月消费订单数
grouped_month.user_id.count().plot()
前三个月消费订单数在10000笔左右,后续月份的平均消费人数在2500人左右
# 每月消费商品总数
grouped_month.order_products.sum().plot()
# 每月消费人数
# user_id 作为输入,去重
df.groupby('month').user_id.apply(lambda x:len(x.drop_duplicates())).plot()
# 另一种方法去重
(df.groupby(['month', 'user_id']).count().reset_index()).groupby('month').user_id.count().plot()
# 使用数据透视表
df.pivot_table(index = 'month',
values = ['order_products', 'order_amount', 'user_id'],
aggfunc = {'order_products': 'sum',
'order_amount': 'sum',
'user_id': 'count'}).head()
grouped_user = df.groupby('user_id')
grouped_user.sum().describe()
grouped_user.sum().plot.scatter(x='order_amount', y='order_products')
# order_amount < 4000的数据
grouped_user.sum().query('order_amount < 4000').plot.scatter(x='order_amount', y='order_products')
# 消费金额分布
grouped_user.sum().order_amount.plot.hist(bins=20)
从直方图可知,用户消费金额,绝大部分呈现集中趋势,小部分异常值干扰了判断,可以使用过滤操作排除异常
# 购买产品数
grouped_user.sum().query('order_products < 100').order_products.hist(bins=20)
user_cumsum = grouped_user.sum().sort_values('order_amount').apply(lambda x:x.cumsum() / x.sum())
user_cumsum.head()
user_cumsum.tail()
user_cumsum.reset_index().order_amount.plot()
按用户消费金额进行升序排列,由图可知50%的用户仅贡献了15%的消费额度
grouped_user.min().order_dt.value_counts().plot()
grouped_user.max().order_dt.value_counts().plot()
user_life = grouped_user.order_dt.agg(['min', 'max'])
user_life.head()
(user_life['min'] == user_life['max']).value_counts()
rfm = df.pivot_table(index = 'user_id',
values = ['order_products', 'order_amount', 'order_dt'],
aggfunc = {'order_dt': 'max',
'order_amount': 'sum',
'order_products': 'sum'})
rfm.head()
# 距今天数 转换时间格式 把单位消除
rfm['R'] = -(rfm.order_dt - rfm.order_dt.max()) / np.timedelta64(1, 'D')
# 重命名
rfm.rename(columns= {'order_products': 'F', 'order_amount': 'M'}, inplace=True)
rfm.head()
rfm[['R', 'F', 'M']].apply(lambda x:x-x.mean()).head() # 负号表示小于平均值
def rfm_func(x):
level = x.apply(lambda x:'1' if x>=0 else '0') # x大于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.head()
rfm.groupby('label').sum()
rfm.loc[rfm.label == '重要价值客户', 'color'] = 'coral'
rfm.loc[~(rfm.label == '重要价值客户'), 'color'] = 'c' # 除了重要价值客户之外的客户
rfm.plot.scatter('F', 'R', c=rfm.color)
rfm.head()
从RFM分层可知,大部分用户为重要保持客户,但是这是由于极值的影响,所以RFM的划分标准
pivoted_counts = df.pivot_table(index = 'user_id',
columns = 'month',
values = 'order_dt',
aggfunc = 'count').fillna(0)
pivoted_counts.head()
df_purchase = pivoted_counts.applymap(lambda x: 1 if x > 0 else 0)
df_purchase.tail()
def active_status(data):
status = []
for i in range(18):
# 若本月没有消费
if data[i] == 0:
if len(status) > 0: # 如果有记录
if status[i-1] == 'unreg': # 如果前一个月未注册
status.append('unreg') # 那么就为未注册
else:
status.append('unactive') # 否则就为不活跃
else: # 如果没有记录就为未注册
status.append('unreg')
# 若本月消费
else:
if len(status) == 0: # 如果记录为空,那么就为本月第一次消费
status.append('new') # 该用户为新用户
else: # 如果记录不为空
if status[i-1] == 'unactive': # 如果前一个月不活跃
status.append('return') # 该用户为回流用户
elif status[i-1] == 'unreg': # 如果未注册
status.append('new') # 该用户为新用户
else:
status.append('active') # 否则该用户为活跃用户
return pd.Series(status)
若本月没有消费
若本月有消费
print(df_purchase.columns)
purchase_stats = df_purchase.apply(active_status, axis=1)
purchase_stats.columns = df_purchase.columns
purchase_stats.head()
purchase_stats.tail()
purchase_stats_ct = purchase_stats.replace('unreg', np.NaN).apply(lambda x:pd.value_counts(x))
purchase_stats_ct.head()
purchase_stats_ct.fillna(0).T.head()
purchase_stats_ct.fillna(0).T.apply(lambda x:x/x.sum(), axis=1)
purchase_stats_ct.fillna(0).T.plot.area()
order_diff = grouped_user.apply(lambda x:x.order_dt - x.order_dt.shift())
order_diff.head(10)
df.order_dt.head(10)
df.order_dt.shift().head(10) # 把所有数据往下移动一位
order_diff.describe()
(order_diff / np.timedelta64(1, 'D')).hist(bins=20)
(user_life['max'] - user_life['min']).describe()
((user_life['max'] - user_life['min']) / np.timedelta64(1, 'D')).hist(bins= 40)
u_1 = ((user_life['max'] - user_life['min']).reset_index()[0] / np.timedelta64(1, 'D'))
u_1[u_1 > 0].hist(bins = 40)
pivoted_counts.head()
# 大于1 -> 1
# 等于1 -> 0
# 等于0 -> NaN
purchase_r = pivoted_counts.applymap(lambda x: 1 if x > 1 else np.NaN if x == 0 else 0)
purchase_r.head()
(purchase_r.sum() / purchase_r.count()).plot(figsize=(10, 4))
复购率稳定在20%左右,前三个月因为有大量新用户涌入,而这批用户只购买了一次,所以导致复购率降低
df_purchase.head()
# 1 -> 当月消费过,次月依旧消费
# 0 -> 当月消费过,次月没有消费
# NaN -> 当月没有消费
def purchase_back(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)
return pd.Series(status)
purchase_b = df_purchase.apply(purchase_back, axis=1)
purchase_b.head(5)
(purchase_b.sum() / purchase_b.count()).plot(figsize=(10, 4))