本实践项目基于一个月的淘宝用户行为,给用户打标签 此项目为上部,包含用户属性标签和用户行为标签。 文末可下载文中所需的数据集。
标签总览
由于数据集的局限,此项目的画像标签只是庞大用户画像的一部分,基于已有的数据集进行制作
导入所需库
#导入库
%matplotlib inline
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import gc
import warnings
warnings.filterwarnings('ignore')
from datetime import datetime
导入数据集
#导入数据集
df_orginal = pd.read_csv('/home/kesci/input/mydata9388/taobao_persona.csv')
数据预处理
数据抽样
数据集太大,为了提高运行效率,只随机抽取20%的数据
#数据集太大,为了提高运行效率,只随机抽取20%的数据
df = df_orginal.sample(frac=0.2,random_state=None)
#回收内存
del df_orginal
gc.collect()
输出结果:21
缺失值处理
df.info()
输出:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 4658205 entries, 13077081 to 21758719
Data columns (total 6 columns):
user_id int64
item_id int64
behavior_type int64
user_geohash object
item_category int64
time object
dtypes: int64(4), object(2)
memory usage: 248.8+ MB
df.shape
输出:(4658205, 6)
#查看各字段的缺失值数量
df.isnull().sum()
输出:
user_id 0
item_id 0
behavior_type 0
user_geohash 3183516
item_category 0
time 0
dtype: int64
只有user_geohash有缺失值,且缺失的比例很高,无统计分析的意义,将此列删除
输入:
df.drop('user_geohash',axis=1,inplace=True)
日期与时段处理
输入:
#将time字段拆分为日期和时段
df['date'] = df['time'].str[0:10]
df['date'] = pd.to_datetime(df['date'],format='%Y-%m-%d')
df['time'] = df['time'].str[11:]
df['time'] = df['time'].astype(int)
输入:
#将时段分为'凌晨'、'上午'、'中午'、'下午'、'晚上'
df['hour'] = pd.cut(df['time'],bins=[-1,5,10,13,18,24],labels=['凌晨','上午','中午','下午','晚上'])
制作用户标签表
输入:
#生成用户标签表,制作好的标签都加入这个表中
users = df['user_id'].unique()
labels = pd.DataFrame(users,columns=['user_id'])
用户行为标签
用户浏览活跃时间段
输入:
#对用户和时段分组,统计浏览次数
time_browse = df[df['behavior_type']==1].groupby(['user_id','hour']).item_id.count().reset_index()
输入:
time_browse.rename(columns={'item_id':'hour_counts'},inplace=True)
输入:
#统计每个用户浏览次数最多的时段
time_browse_max = time_browse.groupby('user_id').hour_counts.max().reset_index()
输入:
time_browse_max.rename(columns={'hour_counts':'read_counts_max'},inplace=True)
输入:
time_browse = pd.merge(time_browse,time_browse_max,how='left',on='user_id')
输入:
#选取各用户浏览次数最多的时段,如有并列最多的时段,用逗号连接
time_browse_hour = time_browse.loc[time_browse['hour_counts']==time_browse['read_counts_max'],'hour'].groupby(time_browse['user_id']).aggregate(lambda x:','.join(x)).reset_index()
输入:
time_browse_hour.head()
输出:
#将用户浏览活跃时间段加入到用户标签表中
labels = pd.merge(labels,time_browse_hour,how='left',on='user_id')
labels.rename(columns={'hour':'time_browse'},inplace=True)
用户购买活跃时间段
输入:
#生成逻辑与浏览活跃时间段相同
time_buy = df[df['behavior_type']==4].groupby(['user_id','hour']).item_id.count().reset_index()
time_buy.rename(columns={'item_id':'hour_counts'},inplace=True)
time_buy_max = time_buy.groupby('user_id').hour_counts.max().reset_index()
time_buy_max.rename(columns={'hour_counts':'buy_counts_max'},inplace=True)
time_buy = pd.merge(time_buy,time_buy_max,how='left',on='user_id')
time_buy_hour = time_buy.loc[time_buy['hour_counts']==time_buy['buy_counts_max'],'hour'].groupby(time_buy['user_id']).aggregate(lambda x:','.join(x)).reset_index()
输入:
time_buy_hour.head()
输出:
输入:
#将用户购买活跃时间段加入到用户标签表中
labels = pd.merge(labels,time_buy_hour,how='left',on='user_id')
labels.rename(columns={'hour':'time_buy'},inplace=True)
输入:
del time_browse
del time_buy
del time_browse_hour
del time_browse_max
del time_buy_hour
del time_buy_max
gc.collect()
输出:168
关于类目的用户行为
输入:
df_browse = df.loc[df['behavior_type']==1,['user_id','item_id','item_category']]
df_collect = df.loc[df['behavior_type']==2,['user_id','item_id','item_category']]
df_cart = df.loc[df['behavior_type']==3,['user_id','item_id','item_category']]
df_buy = df.loc[df['behavior_type']==4,['user_id','item_id','item_category']]
浏览最多的类目
输入:
#对用户与类目进行分组,统计浏览次数
df_cate_most_browse = df_browse.groupby(['user_id','item_category']).item_id.count().reset_index()
输入:
df_cate_most_browse.rename(columns={'item_id':'item_category_counts'},inplace=True)
输入:
#统计每个用户浏览次数最多的类目
df_cate_most_browse_max = df_cate_most_browse.groupby('user_id').item_category_counts.max().reset_index()
输入:
df_cate_most_browse_max.rename(columns={'item_category_counts':'item_category_counts_max'},inplace=True)
输入:
df_cate_most_browse = pd.merge(df_cate_most_browse,df_cate_most_browse_max,how='left',on='user_id')
输入:
df_cate_most_browse['item_category'] = df_cate_most_browse['item_category'].astype(str)
输入:
#选取各用户浏览次数最多的类目,如有并列最多的类目,用逗号连接
df_cate_browse = df_cate_most_browse.loc[df_cate_most_browse['item_category_counts']==df_cate_most_browse['item_category_counts_max'],'item_category'].groupby(df_cate_most_browse['user_id']).aggregate(lambda x:','.join(x)).reset_index()
输入:
df_cate_browse.head()
输出:
输入:
#将用户浏览最多的类目加入到用户标签表中
labels = pd.merge(labels,df_cate_browse,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_browse'},inplace=True)
收藏最多的类目
输入:
#生成逻辑与浏览最多的类目相同
df_cate_most_collect = df_collect.groupby(['user_id','item_category']).item_id.count().reset_index()
df_cate_most_collect.rename(columns={'item_id':'item_category_counts'},inplace=True)
df_cate_most_collect_max = df_cate_most_collect.groupby('user_id').item_category_counts.max().reset_index()
df_cate_most_collect_max.rename(columns={'item_category_counts':'item_category_counts_max'},inplace=True)
df_cate_most_collect = pd.merge(df_cate_most_collect,df_cate_most_collect_max,how='left',on='user_id')
df_cate_most_collect['item_category'] = df_cate_most_collect['item_category'].astype(str)
df_cate_collect = df_cate_most_collect.loc[df_cate_most_collect['item_category_counts']==df_cate_most_collect['item_category_counts_max'],'item_category'].groupby(df_cate_most_collect['user_id']).aggregate(lambda x:','.join(x)).reset_index()
输入:
df_cate_collect.head()
输出:
输入:
labels = pd.merge(labels,df_cate_collect,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_collect'},inplace=True)
加购最多的类目
输入:
#生成逻辑与浏览最多的类目相同
df_cate_most_cart = df_cart.groupby(['user_id','item_category']).item_id.count().reset_index()
df_cate_most_cart = df_cart.groupby(['user_id','item_category']).item_id.count().reset_index()
df_cate_most_cart.rename(columns={'item_id':'item_category_counts'},inplace=True)
df_cate_most_cart_max = df_cate_most_cart.groupby('user_id').item_category_counts.max().reset_index()
df_cate_most_cart_max.rename(columns={'item_category_counts':'item_category_counts_max'},inplace=True)
df_cate_most_cart = pd.merge(df_cate_most_cart,df_cate_most_cart_max,how='left',on='user_id')
df_cate_most_cart['item_category'] = df_cate_most_cart['item_category'].astype(str)
df_cate_cart = df_cate_most_cart.loc[df_cate_most_cart['item_category_counts']==df_cate_most_cart['item_category_counts_max'],'item_category'].groupby(df_cate_most_cart['user_id']).aggregate(lambda x:','.join(x)).reset_index()
输入:
df_cate_cart.head()
输出:
输入:
labels = pd.merge(labels,df_cate_cart,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_cart'},inplace=True)
购买最多的类目
输入:
#生成逻辑与浏览最多的类目相同
df_cate_most_buy = df_buy.groupby(['user_id','item_category']).item_id.count().reset_index()
df_cate_most_buy = df_buy.groupby(['user_id','item_category']).item_id.count().reset_index()
df_cate_most_buy.rename(columns={'item_id':'item_category_counts'},inplace=True)
df_cate_most_buy_max = df_cate_most_buy.groupby('user_id').item_category_counts.max().reset_index()
df_cate_most_buy_max.rename(columns={'item_category_counts':'item_category_counts_max'},inplace=True)
df_cate_most_buy = pd.merge(df_cate_most_buy,df_cate_most_buy_max,how='left',on='user_id')
df_cate_most_buy['item_category'] = df_cate_most_buy['item_category'].astype(str)
df_cate_buy = df_cate_most_buy.loc[df_cate_most_buy['item_category_counts']==df_cate_most_buy['item_category_counts_max'],'item_category'].groupby(df_cate_most_buy['user_id']).aggregate(lambda x:','.join(x)).reset_index()
输入:
df_cate_buy.head()
输出:
输入:
labels = pd.merge(labels,df_cate_buy,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_buy'},inplace=True)
输入:
del df_browse
del df_collect
del df_cart
del df_buy
del df_cate_most_browse
del df_cate_most_collect
del df_cate_most_buy
del df_cate_most_cart
del df_cate_most_browse_max
del df_cate_most_collect_max
del df_cate_most_cart_max
del df_cate_most_buy_max
del df_cate_browse
del df_cate_collect
del df_cate_cart
del df_cate_buy
gc.collect(0)
输出:112
30天用户行为
数据集中的数据正好是一个月,30天的数据即整个数据集的数据
近30天购买次数
输入:
#将购买行为按用户进行分组,统计次数
df_counts_30_buy = df[df['behavior_type']==4].groupby('user_id').item_id.count().reset_index()
输入:
df_counts_30_buy.head()
输出:
输入:
labels = pd.merge(labels,df_counts_30_buy,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_30_buy'},inplace=True)
近30天加购次数
输入:
#将加购行为按用户进行分组,统计次数
df_counts_30_cart = df[df['behavior_type']==3].groupby('user_id').item_id.count().reset_index()
输入:
df_counts_30_cart.head()
输出:
输入:
labels = pd.merge(labels,df_counts_30_cart,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_30_cart'},inplace=True)
近30天活跃天数
输入:
#对用户进行分组,统计活跃的天数,包括浏览、收藏、加购、购买
counts_30_active = df.groupby('user_id')['date'].nunique()
输入:
counts_30_active.head()
输出:
user_id
492 11
3726 15
19137 6
36465 7
37101 19
Name: date, dtype: int64
输入:
labels = pd.merge(labels,counts_30_active,how='left',on='user_id')
labels.rename(columns={'date':'counts_30_active'},inplace=True)
输入:
del df_counts_30_buy
del df_counts_30_cart
del counts_30_active
gc.collect()
输出:238
7天用户行为
输入:
#数据集中的最后日期是12月18号,统计11号之后的用户行为
df_near_7 = df[df['date']>datetime.strptime('2014-12-11', '%Y-%m-%d')]
近7天购买次数
输入:
df_counts_7_buy = df_near_7[df_near_7['behavior_type']==4].groupby('user_id').item_id.count().reset_index()
输入:
df_counts_7_buy.head()
输出:
输入:
labels = pd.merge(labels,df_counts_7_buy,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_7_buy'},inplace=True)
近7天加购次数
输入:
df_counts_7_cart = df_near_7[df_near_7['behavior_type']==3].groupby('user_id').item_id.count().reset_index()
输入:
df_counts_7_cart.head()
输出:
输入:
labels = pd.merge(labels,df_counts_7_cart,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_7_cart'},inplace=True)
近7天活跃天数
输入:
counts_7_active = df_near_7.groupby('user_id')['date'].nunique()
输入:
counts_7_active.head()
输出:
user_id
492 4
3726 5
19137 1
36465 2
37101 5
Name: date, dtype: int64
输入:
labels = pd.merge(labels,counts_7_active,how='left',on='user_id')
labels.rename(columns={'date':'counts_7_active'},inplace=True)
输入:
del df_counts_7_buy
del df_counts_7_cart
del counts_7_active
gc.collect()
输出:112
最后一次行为距今天数
上次浏览距今天数
输入:
days_browse = df[df['behavior_type']==1].groupby('user_id')['date'].max().apply(lambda x:(datetime.strptime('2014-12-19','%Y-%m-%d')-x).days)
输入:
days_browse.head()
输出:
user_id
492 1
3726 1
19137 7
36465 3
37101 3
Name: date, dtype: int64
输入:
labels = pd.merge(labels,days_browse,how='left',on='user_id')
输入:
labels.rename(columns={'date':'days_browse'},inplace=True)
上次加购距今天数
输入:
days_cart = df[df['behavior_type']==3].groupby('user_id')['date'].max().apply(lambda x:(datetime.strptime('2014-12-19','%Y-%m-%d')-x).days)
输入:
days_cart.head()
输出:
user_id
3726 1
37101 8
45561 1
59436 4
61797 18
Name: date, dtype: int64
输入:
labels = pd.merge(labels,days_cart,how='left',on='user_id')
labels.rename(columns={'date':'days_cart'},inplace=True)
上次购买距今天数
输入:
days_buy = df[df['behavior_type']==4].groupby('user_id')['date'].max().apply(lambda x:(datetime.strptime('2014-12-19','%Y-%m-%d')-x).days)
输入:
days_buy.head()
输出:
user_id
38745 23
45561 6
53394 22
59436 7
100605 7
Name: date, dtype: int64
输入:
labels = pd.merge(labels,days_buy,how='left',on='user_id')
labels.rename(columns={'date':'days_buy'},inplace=True)
输入:
del days_browse
del days_buy
del days_cart
gc.collect()
输出:42
最近两次购买间隔天数
输入:
df_interval_buy = df[df['behavior_type']==4].groupby(['user_id','date']).item_id.count().reset_index()
输入:
interval_buy = df_interval_buy.groupby('user_id')['date'].apply(lambda x:x.sort_values().diff(1).dropna().head(1)).reset_index()
输入:
interval_buy['date'] = interval_buy['date'].apply(lambda x : x.days)
输入:
interval_buy.drop('level_1',axis=1,inplace=True)
输入:
interval_buy.rename(columns={'date':'interval_buy'},inplace=True)
输入:
interval_buy.head()
输出:
输入:
labels = pd.merge(labels,interval_buy,how='left',on='user_id')
输入:
del df_interval_buy
gc.collect()
输出:70
是否浏览未下单
输入:
df_browse_buy = df.loc[(df['behavior_type']==1) | (df['behavior_type']==4),['user_id','item_id','behavior_type','time']]
输入:
browse_not_buy = pd.pivot_table(df_browse_buy,index=['user_id','item_id'],columns=['behavior_type'],values=['time'],aggfunc=['count'])
输入:
browse_not_buy.columns = ['browse','buy']
输入:
browse_not_buy.fillna(0,inplace=True)
输入:
browse_not_buy['browse_not_buy'] = 0
输入:
browse_not_buy.loc[(browse_not_buy['browse']>0) & (browse_not_buy['buy']==0),'browse_not_buy'] = 1
输入:
browse_not_buy = browse_not_buy.groupby('user_id')['browse_not_buy'].sum().reset_index()
输入:
browse_not_buy.head()
输出:
输入:
labels = pd.merge(labels,browse_not_buy,how='left',on='user_id')
输入:
labels['browse_not_buy'] = labels['browse_not_buy'].apply(lambda x: '是' if x>0 else '否')
是否加购未下单
输入:
df_cart_buy = df.loc[(df['behavior_type']==3) | (df['behavior_type']==4),['user_id','item_id','behavior_type','time']]
cart_not_buy = pd.pivot_table(df_cart_buy,index=['user_id','item_id'],columns=['behavior_type'],values=['time'],aggfunc=['count'])
cart_not_buy.columns = ['cart','buy']
cart_not_buy.fillna(0,inplace=True)
cart_not_buy['cart_not_buy'] = 0
cart_not_buy.loc[(cart_not_buy['cart']>0) & (cart_not_buy['buy']==0),'cart_not_buy'] = 1
cart_not_buy = cart_not_buy.groupby('user_id')['cart_not_buy'].sum().reset_index()
输入:
cart_not_buy.head()
输出:
输入:
labels = pd.merge(labels,cart_not_buy,how='left',on='user_id')
输入:
labels['cart_not_buy'] = labels['cart_not_buy'].apply(lambda x: '是' if x>0 else '否')
用户属性标签
是否复购用户
输入:
buy_again = df[df['behavior_type']==4].groupby('user_id')['item_id'].count().reset_index()
输入:
buy_again.rename(columns={'item_id':'buy_again'},inplace=True)
输入:
buy_again.head()
输出:
输入:
labels = pd.merge(labels,buy_again,how='left',on='user_id')
输入:
labels['buy_again'].fillna(-1,inplace=True)
输入:
#未购买的用户标记为‘未购买’,有购买未复购的用户标记为‘否’,有复购的用户标记为‘是’
labels['buy_again'] = labels['buy_again'].apply(lambda x: '是' if x>1 else '否' if x==1 else '未购买')
访问活跃度
输入:
user_active_level = labels['counts_30_active'].value_counts().sort_index(ascending=False)
输入:
plt.figure(figsize=(16,9))
user_active_level.plot(title='30天内访问次数与访问人数的关系',fontsize=18)
plt.ylabel('访问人数',fontsize=14)
plt.xlabel('访问次数',fontsize=14)
输出:
Text(0.5, 0, '访问次数')
(30天内访问次数与访问人数的关系)
总体上看,访问次数多的访客比访问次数少的访客数量多,且以15次左右为拐点,因此定义访问次数小于等于16次的为低活跃,访问次数大于16次的定义为高活跃
此定义只是从用户的分布角度出发,工作中当从业务出发定义是否活跃
PS:访问次数多的访客比访问次数少的访客数量多,与绝大多数的产品访问规律相反,从侧面反映了淘宝的用户黏性之强
输入:
labels['user_active_level'] = '高'
labels.loc[labels['counts_30_active']<=16,'user_active_level'] = '低'
购买活跃度
输入:
buy_active_level = labels['counts_30_buy'].value_counts().sort_index(ascending=False)
输入:
plt.figure(figsize=(16,9))
buy_active_level.plot(title='30天内购买次数与购买人数的关系',fontsize=18)
plt.ylabel('购买人数',fontsize=14)
plt.xlabel('购买次数',fontsize=14)
输出:
Text(0.5, 0, '购买次数')
(30天内购买次数与购买人数的关系)
14次左右是个拐点,因此定义购买次数小于等于14次为低活跃,大于14次为高活跃
此定义只是从用户的分布角度出发,工作中当从业务出发定义是否活跃
输入:
labels['buy_active_level'] = '高'
labels.loc[labels['counts_30_buy']<=14,'buy_active_level'] = '低'
购买的品类是否单一
输入:
buy_single = df[df['behavior_type']==4].groupby('user_id').item_category.nunique().reset_index()
输入:
buy_single.rename(columns={'item_category':'buy_single'},inplace=True)
输入:
labels = pd.merge(labels,buy_single,how='left',on='user_id')
输入:
labels['buy_single'].fillna(-1,inplace=True)
输入:
labels['buy_single'] = labels['buy_single'].apply(lambda x: '是' if x>1 else '否' if x==1 else '未购买' )
用户价值分组(RFM)
last_buy_days = labels['days_buy'].value_counts().sort_index()
输入:
plt.figure(figsize=(16,9))
last_buy_days.plot(title='最后一次购买距今天数与购买人数的关系',fontsize=18)
plt.ylabel('购买人数',fontsize=14)
plt.xlabel('距今天数',fontsize=14)
输出:
Text(0.5, 0, '距今天数')
(最后一次购买距今天数与购买人数的关系)
注:访问异常的那天为双12
输入:
labels['buy_days_level'] = '高'
labels.loc[labels['days_buy']>8,'buy_days_level'] = '低'
输入:
labels['rfm_value'] = labels['buy_active_level'].str.cat(labels['buy_days_level'])
输入:
def trans_value(x):
if x == '高高':
return '重要价值客户'
elif x == '低高':
return '重要深耕客户'
elif x == '高低':
return '重要唤回客户'
else:
return '即将流失客户'
输入:
labels['rfm'] = labels['rfm_value'].apply(trans_value)
输入:
labels.drop(['buy_days_level','rfm_value'],axis=1,inplace=True)
输入:
labels['rfm'].value_counts()
输出:
重要深耕客户 7167
重要价值客户 7142
即将流失客户 5631
重要唤回客户 16
Name: rfm, dtype: int64
输入:
labels.head()
输出: