大家好,本文以真實案為例手把手教你搭建電商系統(tǒng)的用戶畫像。
先來看該電商用戶畫像用到的標簽。
數(shù)據(jù)內(nèi)容包括user_id(用戶身份)、item_id(商品)、IDbehavior_type(用戶行為類型,包含點擊、收藏、加購物車、支付四種行為,分別用數(shù)字1、2、3、4表示)、user_geohash(地理位置)、item_category(品類ID,即商品所屬的品類)、Time(用戶行為發(fā)生的時間),其中user_id和item_id因為涉及隱私,做了脫敏處理,顯示的是數(shù)字編號。
下面是具體的代碼實現(xiàn)過程。
導入庫
本示例除了用到numpy、pandas、matplotlib,還用到其他一些模塊。
# 導入所需的庫
%matplotlib inline
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from datetime import datetime
參數(shù)說明如下。
- %matplotlib inline: 一個魔法函數(shù),由于%matplotlib inline的存在,當輸入plt.plot()后,不必再輸入plt.show(),圖像將自動顯示出來。
- datetime: 用來顯示時間的模塊。
數(shù)據(jù)準備
# 導入數(shù)據(jù)集
df_orginal = pd.read_csv('./taobao_persona.csv')
# 抽取部分數(shù)據(jù)
df = df_orginal.sample(frac=0.2,random_state=None)
此處使用Pandas的read_csv方法讀取數(shù)據(jù)文件,由于數(shù)據(jù)集太大,為了提高運行效率,使用sample函數(shù)隨機抽取20%的數(shù)據(jù)。
DataFrame.sample()是Pandas中的函數(shù),DataFrame是一種數(shù)據(jù)格式,代指df_orginal。frac(fraction)是抽取多少數(shù)據(jù),random_state是隨機數(shù)種子,目的是保證每次隨機抽取的數(shù)據(jù)一樣,防止執(zhí)行命令時使用不一樣的數(shù)據(jù)。
數(shù)據(jù)預處理
# 查看其中是否有缺失值,統(tǒng)計各字段缺失值
df.isnull().any().sum()
# 發(fā)現(xiàn)只有user_geohash有缺失值,且缺失的比例很高,無統(tǒng)計分析的意義,將此列刪
df.drop('user_geohash',axis=1,inplace=True)
# 將time字段拆分為日期和時段
df['date'] = df['time'].str[0:10]
df['time'] = df['time'].str[11:]
df['time'] = df['time'].astype(int)
# date用str方法取0-9位的字符,time取11位到最后一位,將time轉(zhuǎn)化成int類型。
# 將時段分為'凌晨','上午','中午','下午','晚上'
df['hour'] = pd.cut(df['time'],bins=[-1,5,10,13,18,24],labels=['凌晨','上午','中午','下午','晚上'])
結果如圖1所示。
圖1 數(shù)據(jù)預處理結果
# 生成用戶標簽表,制作好的標簽都加入這個表中
users = df['user_id'].unique()
labels = pd.DataFrame(users,columns=['user_id'])
- pd.DataFrame(): 其中數(shù)據(jù)填充的是users,列名為user_id。
結果如圖2所示。
圖2 制作好的用戶ID
之后分析后的內(nèi)容都會放置在此表中,相當于建立了一個空白表,將自己分析后的結論一一加入。
數(shù)構建用戶行為標簽
1)對用戶瀏覽時間段進行分析
選取出各用戶瀏覽次數(shù)最多的時段,看看用戶到底在什么時間瀏覽商品比較多。
# 對用戶和時段分組,統(tǒng)計瀏覽次數(shù)
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)
# 統(tǒng)計每個用戶瀏覽次數(shù)最多的時段
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')
# 之前已經(jīng)按照user_id和hour進行了瀏覽物品次數(shù)的計數(shù)統(tǒng)計,現(xiàn)在借用瀏覽次數(shù)統(tǒng)計user_id在
# 哪個時間段瀏覽次數(shù)最多,并將其作為該用戶的瀏覽時間標簽的代表。
# 選取各用戶瀏覽次數(shù)最多的時段,如有并列最多的時段,用逗號連接
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)
# labels相當于一張考試卷紙,上面展示的都是最后處理好的結果
結果如圖3所示。
圖3 用戶瀏覽時間段
- groupby([‘key1’,‘key2’]): 多列聚合,分組鍵為列名。
- reset_index(): 默認drop=False,可以獲得新的index,原來的index變成數(shù)據(jù)列保留下來,第一列會添加計數(shù)的數(shù)字,不會使用數(shù)據(jù)中的index。
- rename(): 進行重命名,此處將item_id替換成hour_counts,inplace為是否原地填充。
- pd.merge(): 將兩個表合并在一起,橫向合并,on代表通過某個主鍵,how指左合并,每行一一對應。
- loc函數(shù): 通過行索引Index中的具體值來取指定數(shù)據(jù)。
- aggregate函數(shù): groupby分組之后會返回多個子數(shù)據(jù)幀,該函數(shù)可以實現(xiàn)數(shù)據(jù)聚合,可以得到每個子數(shù)據(jù)幀的某些列的某些信息。
- lambda函數(shù): 可以定義一個匿名函數(shù),lambda [arg1[, arg2, … argN]]: expression,其中參數(shù)是函數(shù)的輸入,是可選的,后面的表達式則為輸出,此處和join()函數(shù)一起用,其中每個x值能被“,”隔開;使用類似的代碼可以生成瀏覽活躍時間段,此處就不再贅述。
2)關于類目的用戶行為。
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']]
根據(jù)不同的用戶行為,如瀏覽、收藏等,分別導出數(shù)據(jù)進行分析。
# 對用戶與類目進行分組,統(tǒng)計瀏覽次數(shù)
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)
# 統(tǒng)計每個用戶瀏覽次數(shù)最多的類目
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')
# 將item_category的數(shù)字類型改為字符串型
df_cate_most_browse['item_category'] = df_cate_most_browse['item_category'].astype(str)
# 選取各用戶瀏覽次數(shù)最多的類目,如有并列最多的類目,用逗號連接
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()
# 將用戶瀏覽最多的類目加入用戶標簽表中
labels = pd.merge(labels,df_cate_browse,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_browse'},inplace=True)
labels.head(5)
用戶瀏覽最多的類目如圖4所示。
圖4 瀏覽最多的類目
收藏、加購和購買最多的類目生成邏輯相同,重復操作后結果如圖5所示。
圖5 關于類目的用戶行為
從整理的數(shù)據(jù)中可以看出,瀏覽、加購物車、收藏、購買之前其實不一定存在明顯的必然關系,我們還需要進一步分析得到一些規(guī)律。
3)近30天用戶行為分析。
近30天購買次數(shù):
# 將購買行為按用戶進行分組,統(tǒng)計次數(shù)
df_counts_30_buy = df[df['behavior_type']==4].groupby('user_id').item_id.count().reset_index()
labels = pd.merge(labels,df_counts_30_buy,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_30_buy'},inplace=True)
近30天加購次數(shù):
# 將加購行為按用戶進行分組,統(tǒng)計次數(shù)
df_counts_30_cart = df[df['behavior_type']==3].groupby('user_id').item_id.count().reset_index()
labels = pd.merge(labels,df_counts_30_cart,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_30_cart'},inplace=True)
近30天活躍天數(shù):
# 對用戶進行分組,統(tǒng)計活躍的天數(shù),包括瀏覽、收藏、加購、購買
counts_30_active = df.groupby('user_id')['date'].nunique()
labels = pd.merge(labels,counts_30_active,how='left',on='user_id')
labels.rename(columns={'date':'counts_30_active'},inplace=True)
結果如圖6所示。
圖6 近30天用戶行為
近30天用戶行為分析屬于中長期的用戶行為,我們可以依此判斷是否需要調(diào)整營銷策略,類似可以得到短期的7天用戶行為分析,觀察中短期或一個小周期內(nèi),用戶的行為是何種情況。
4)最后一次行為距今天數(shù)。
分析上次和本次用戶行為的時間差值可以實現(xiàn)精確推薦分析,下面我們來看看具體如何實現(xiàn)。
上次瀏覽距今天數(shù):
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)
labels = pd.merge(labels,days_browse,how='left',on='user_id')
labels.rename(columns={'date':'days_browse'},inplace=True)
- datetime.strptime(‘2014-12-19’,’%Y-%m-%d’)-x).days: 該部分屬于lambda中的函數(shù)表達式部分,即計算規(guī)則,此處最后取相減后的天數(shù)總和。
- apply(): 格式為apply(func,*args,**kwargs),當一個函數(shù)的參數(shù)存在于一個元組或者一個字典中時,可間接調(diào)用這個函數(shù),并將元組或者字典中的參數(shù)按照順序傳遞給該函數(shù),返回值就是func函數(shù)的返回值。相當于循環(huán)遍歷,起到處理每一條數(shù)據(jù)的效果。
類似可以生成上次加購、購買距今天數(shù),分析得到用戶的活躍情況,如圖7所示,如果長時間沒有活躍,則需要推送一些內(nèi)容,或者發(fā)放優(yōu)惠券刺激用戶。
圖7 最后一次行為距今天情況統(tǒng)計
5)最近兩次購買間隔天數(shù)。
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)
labels = pd.merge(labels,interval_buy,how='left',on='user_id')
用購買間隔數(shù)分析用戶的購買頻率,方便確定用戶的消費活躍等級,精準制定營銷方式。結果如圖8所示。
圖8 最近兩次購買間隔天數(shù)統(tǒng)計
6)是否瀏覽未下單。
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,初始值為0。
browse_not_buy['browse_not_buy'] = 0
# 瀏覽數(shù)>0,購買數(shù)=0的數(shù)據(jù)輸出1.
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()
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 '否')
- |: 在Python語句中表示或,&表示且。
- pd.pivot_table(): 透視表功能,df_browse_buy為data塊,values可以對需要的計算數(shù)據(jù)進行篩選,aggfunc參數(shù)可以設置我們對數(shù)據(jù)聚合時進行的函數(shù)操作。
- fillna: 會填充NaN數(shù)據(jù),返回填充后的結果,inplace=True代表原地填充。
結果如圖9所示。
圖9 是否瀏覽未下單情況統(tǒng)計
針對瀏覽未下單的用戶要加大推廣力度,可以增加優(yōu)惠券的發(fā)放次數(shù),促進購物。
7)是否加購未下單。
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()
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 '否')
結果如圖10所示。
圖10 是否加購未下單情況統(tǒng)計
制定營銷策略時,要重點注意這部分人群,因為加購未下單的購買轉(zhuǎn)化率是最大的,有成功下單、最大潛力的客戶就在這里。
構建用戶屬性標簽
1)是否復購用戶:
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)
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 '未購買')
結果如圖11所示。
圖11 是否復購用戶統(tǒng)計
2)訪問活躍度:
user_active_level = labels['counts_30_active'].value_counts().sort_index(ascending=False)
plt.figure(figsize=(16,9))
user_active_level.plot(title='30天內(nèi)訪問次數(shù)與訪問人數(shù)的關系',fontsize=18)
plt.ylabel('訪問人數(shù)',fontsize=14)
plt.xlabel('訪問次數(shù)',fontsize=14)
# 用于顯示中文
plt.rcParams["font.sans-serif"] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 先將user_active_level全部設置成高,再搜索數(shù)值<16的部分,設置成低
labels['user_active_level'] = '高'
labels.loc[labels['counts_30_active']<=16,'user_active_level'] = '低'
結果如圖12所示。
圖12 30天內(nèi)訪問次數(shù)與訪問人數(shù)的關系
- value_counts(): 查看表格某列中有多少個不同值,并計算每個不同值在該列中有多少重復值。
- sort_index(): 按照某一列的大小進行排序,ascending=False是按照從大到小排序。
- plt.figure(figsize=(a,b)): 創(chuàng)建畫板,figsize代表寬為a,高為b的圖形,單位為英寸。
- plt.ylabel: 設置y軸,fontsize是字體大小。
- plt.xlabel: 設置x軸。
通過圖12可以看出,訪問次數(shù)多的用戶比訪問次數(shù)少的用戶數(shù)量多,且以15次左右為拐點,因此定義訪問次數(shù)小于等于16次的用戶為低活躍用戶,訪問次數(shù)大于16次的用戶定義為高活躍用戶,此定義只是從用戶的角度出發(fā),工作中當從業(yè)務角度定義。訪問次數(shù)多的訪客比訪問次數(shù)少的訪客數(shù)量多,與絕大多數(shù)的產(chǎn)品訪問規(guī)律相反,從側面反映了用戶黏性之強。
3)購買活躍度:
buy_active_level = labels['counts_30_buy'].value_counts().sort_index(ascending= False)
plt.figure(figsize=(16,9))
buy_active_level.plot(title='30天內(nèi)購買次數(shù)與購買人數(shù)的關系',fontsize=18)
plt.ylabel('購買人數(shù)',fontsize=14)
plt.xlabel('購買次數(shù)',fontsize=14)
labels['buy_active_level'] = '高'
labels.loc[labels['counts_30_buy']<=14,'buy_active_level'] = '低'
結果如圖13所示。
圖13 30天內(nèi)購買次數(shù)與購買人數(shù)的關系
由圖13可知,14次左右是個拐點,因此定義購買次數(shù)小于等于14次的用戶為低活躍用戶,大于14次的用戶為高活躍用戶。
4)購買的品類是否單一:
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 '未購買' )
結果如圖14所示。
圖14 購買品類單一情況統(tǒng)計
了解用戶購買的品類有利于構建用戶群體行為,比如該群體統(tǒng)一對化妝品消費占比巨大,則該用戶群體的主要特征標簽之一就是化妝品。
5)用戶價值分組(RFM模型):
last_buy_days = labels['days_buy'].value_counts().sort_index()
plt.figure(figsize=(16,9))
last_buy_days.plot(title='最后一次購買距今天數(shù)與購買人數(shù)的關系',fontsize=18)
plt.ylabel('購買人數(shù)',fontsize=14)
plt.xlabel('距今天數(shù)',fontsize=14)
結果如圖15所示。
圖15 最后購買行為距今天數(shù)與購買人數(shù)的關系
使用RFM模型分析:
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)
# 此處的apply()調(diào)用了一個自己定義(def)的函數(shù)
labels.drop(['buy_days_level','rfm_value'],axis=1,inplace=True)
labels['rfm'].value_counts()
結果如圖16所示。
圖16 RFM模型分析結果
- str.cat() 是指將兩個獨立的字符串拼接,此處將
- ’buy_active_level‘和’buy_days_level’ 拼接。如果要在兩個合并的列中間加一個分隔符號,可在cat括號內(nèi)加:sep=’-’,用-連接合并內(nèi)容。
將buy_active_level和buy_days_level組合,形成“高高”或者“高低”等。將兩個重要指標合并后,每個user_id進入不同的分類組。RFM模型是衡量客戶價值和客戶創(chuàng)利能力的重要工具和手段,其中,R(recently):最近一次消費;F(Frequently):消費頻率;M(Monetary):消費金額。
對最后輸出的用戶群體制定不同的營銷策略。針對重要價值客戶要予以關注并維護;針對重要深耕用戶,予以相應的價格刺激,如折扣和捆綁銷售等增加用戶的購買頻率,提高黏性;針對重要喚回用戶,要在特定時間點進行刺激,比如進行產(chǎn)品賣點刺激、品牌灌輸?shù)?,不斷加強他們對品牌的認可,提高忠誠度;針對流失客戶,在此例中,因其數(shù)量占三分之一左右,需進一步分析得出流失原因。
技術交流
歡迎轉(zhuǎn)載、收藏、有所收獲點贊支持一下!
目前開通了技術交流群,群友已超過2000人,添加時最好的備注方式為:來源+興趣方向,方便找到志同道合的朋友
方式③、微信搜索公眾號:Python學習與數(shù)據(jù)挖掘,后臺回復:加群
本文摘自 :https://blog.51cto.com/u