电商-用户画像分析

用户画像是通过分析用户的基础信息、特征偏好、社会属性等各维度的数据,刻画出用户的信息全貌,它是建立在一系列属性数据之上的目标用户模型。
用户画像的本质是一个用以描述用户需求的工具。用户画像一般是产品设计、运营人员从用户群体中抽象出来的典型用户,从中可以挖掘用户价值,提供个性化推荐、精准营销等服务。

用户画像主要应用有:

  • 精准营销:精准直邮、短信、App消息推送、个性化广告等。
  • 用户研究:指导产品优化,甚至做到产品功能的私人定制等。
  • 个性服务:个性化推荐、个性化搜索等。
  • 业务决策:排名统计、地域分析、行业趋势、竞品分析等。
电商思维导图.png

【一】数据集概况

  • 导入模块和数据
#导入库
%matplotlib inline
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import matplotlib
import seaborn as sns
# 指定字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei']  
matplotlib.rcParams['font.family']='sans-serif'  
#解决负号'-'显示为方块的问题  
matplotlib.rcParams['axes.unicode_minus'] = False

import pymysql
pymysql.install_as_MySQLdb()
from sqlalchemy import create_engine

import gc
import warnings
warnings.filterwarnings('ignore')
from datetime import datetime

# 导入数据集
df = pd.read_excel("order_data.xlsx")
# 导入用户详情数据集
df_user = pd.read_excel("user_data.xlsx")
订单数据
image.png
订单特征的唯一值数量
df_count=pd.DataFrame()

for i in ['item_id','behavior_type','item_category','time']:
    df_count.loc[i,0]=df[i].nunique()

df_count=df_count.rename(columns={0:'标签数量'},index={'item_id':'小类别编码',
                                                       'behavior_type':'行为类型',
                                                       'item_category':'大类别编码',
                                                       'time':'行为时间'})

from matplotlib.font_manager import FontProperties
myfont=FontProperties(fname=r'C:\\Windows\\Fonts\\simhei.ttf',size=8)
sns.set(font=myfont.get_name())

plt.figure(figsize=(12,5),dpi=200)
g=sns.barplot(data=df_count,x=df_count.index,y='标签数量')
plt.title('订单数据库')
x=df_count.index
y=df_count['标签数量']
plt.yticks(np.arange(0,100001,20000))
for i,row in enumerate(df_count['标签数量']):
    g.text(i,row+3000,str(row)[:-2]+'种',ha='center',va='center')
image.png
 用户数据
image.png
  用户特征的唯一值数量
df_user_count=pd.DataFrame()

for i in df_user.drop('user_id',axis=1):
    df_user_count.loc[i,0]=df_user[i].nunique()

df_user_count=df_user_count.rename(columns={0:'标签数量'},index={'gender':'性别',
                                                       'age':'年龄',
                                                       'city':'城市',
                                                       'province':'省份',
                                                       'marriage':'婚姻状况',
                                                       'eduction':'学历',
                                                       'job':'职业'})
plt.figure(figsize=(10,3),dpi=100)
g=sns.barplot(data=df_user_count,x=df_user_count.index,y='标签数量')
plt.title('用户数据库',fontsize=15)
x=df_user_count.index
y=df_user_count['标签数量']
plt.yticks(np.arange(0,41,5),fontsize=10)
plt.ylabel('标签数量',fontsize=13)
for i,row in enumerate(df_user_count['标签数量']):
    g.text(i,row+1,str(row)[:-2]+'种',ha='center',va='center',fontsize=10)
image.png

城市云词条

#将用户浏览活跃时间段加入到用户标签表中
labels = pd.merge(labels,time_browse_hour,how='left',on='user_id')
labels.rename(columns={'hour':'time_browse'},inplace=True)
#词云
import wordcloud
import jieba
import jieba.analyse
import codecs
from wordcloud import WordCloud,ImageColorGenerator,STOPWORDS #词云,颜色生成器,停止词
import numpy as np #科学计算
from PIL import Image
segments = []
stopwords = [line.strip() for line in codecs.open('中文停用词表.txt', 'r', 'utf-8').readlines()] 
jieba.analyse.set_stop_words('中文停用词表.txt')
labels['city'].to_csv('city.txt',sep='\t',index=False)

text = open("city.txt",encoding='utf8').read()
text = text.replace('\n',"").replace("\u3000","")

text_cut = jieba.lcut(text)
text_cut = ' '.join(text_cut)

background = Image.open("中国地图.jpg")
graph = np.array(background)
word_cloud = WordCloud(font_path="simsun.ttc", 
                       background_color="white", 
                       mask=graph, # 指定词云的形状
                       stopwords=stopwords) 
word_cloud.generate(text_cut)

plt.subplots(figsize=(12,8),dpi=200)
plt.imshow(word_cloud)
plt.axis("off")
image.png
df=labels
user_sex_df = df.groupby("gender")["user_id"].count()
## 利用pd.cut进行数据离散化切分,注意分组标签和分组数要一致
"""
pandas.cut(x,bins,right=True,labels=None,retbins=False,precision=3,include_lowest=False)
x:需要切分的数据
bins:切分区域
right : 是否包含右端点默认True,包含
labels:对应标签,用标记来代替返回的bins,若不在该序列中,则返回NaN
retbins:是否返回间距bins
precision:精度
include_lowest:是否包含左端点,默认False,不包含
"""
bins = [0,20,25,30,35,40,45,50,55,1000]
labels = ["(0-20]","(20-25]","(25-30]","(30-35]","(35-40]","(40-45]","(45-50]","(50-55]","55岁以上"]
df["age_group"] = pd.cut(df["age"],bins=bins,labels=labels)
# 绘制柱状图,,rot=0 表示旋转角度,0-360
age_group_df = df.groupby("age_group").user_id.count().reset_index()
# 按省份分类
pro_user_df = df.groupby("province").user_id.count().reset_index().sort_values(by="user_id",ascending=True)
pro_user_df=pro_user_df.sort_values('user_id',ascending=False)
# 按省份分类
city_user_df = df.groupby("city").user_id.count().reset_index().sort_values(by="user_id",ascending=True)
# 按婚姻分类
marry_df = df.groupby("marriage").user_id.count()
# 按学历分类
eduction_df = df.groupby("eduction").user_id.count().reset_index()
eduction_df=eduction_df.sort_values('user_id',ascending=False)
# 按工作分类
job_df = df.groupby("job").user_id.count().reset_index()
job_df=job_df.sort_values('user_id',ascending=False)

myfont=FontProperties(fname=r'C:\\Windows\\Fonts\\simhei.ttf',size=8)
sns.set(style='darkgrid',font=myfont.get_name())

plt.figure(figsize=(50,70),dpi=100)

plt.subplot(711)
sns.barplot(data=job_df,x='job',y='user_id',palette='Blues_r')
plt.xticks(fontsize=50)
plt.yticks(range(0,81,20),fontsize=50)
plt.xlabel('职业',fontsize=55)
plt.ylabel('人数',fontsize=55)
plt.title('客户职业分布',fontsize=60)
for x,y in enumerate(job_df['user_id']):
    plt.text(x,y+5,y,fontsize=50,ha='center')

plt.subplot(712)
sns.barplot(data=eduction_df,x='eduction',y='user_id')
plt.xticks(fontsize=50)
plt.yticks(range(0,201,50),fontsize=50)
plt.xlabel('学历',fontsize=55)
plt.ylabel('人数',fontsize=55)
plt.title('客户学历分布',fontsize=60)
for x,y in enumerate(eduction_df['user_id']):
    plt.text(x,y+5,y,fontsize=50,ha='center')
    
plt.subplot(713)
sns.barplot(data=marry_df,x=marry_df.index,y=list(reversed(marry_df.values)))
plt.xticks(fontsize=50)
plt.yticks(range(0,181,90),fontsize=50)
plt.xlabel('婚姻状态',fontsize=55)
plt.ylabel('人数',fontsize=55)
plt.title('客户婚姻状态分布',fontsize=60)
for x,y in enumerate(marry_df.values):
    plt.text(x,y+5,y,fontsize=50,ha='center')
    
plt.subplot(714)
sns.barplot(data=city_user_df,x='city',y='user_id',palette='Greens_r')
plt.xticks(fontsize=50)
plt.yticks(range(0,61,20),fontsize=50)
plt.xlabel('城市',fontsize=55)
plt.ylabel('人数',fontsize=55)
plt.title('客户所在城市分布',fontsize=60)
for x,y in enumerate(city_user_df['user_id']):
    plt.text(x,y+2,y,fontsize=50,ha='center')
    
plt.subplot(715)
sns.barplot(data=pro_user_df,x='province',y='user_id',palette='Oranges_r')
plt.xticks(fontsize=50)
plt.yticks(range(0,61,20),fontsize=50)
plt.xlabel('省份',fontsize=55)
plt.ylabel('人数',fontsize=55)
plt.title('客户所在省份分布',fontsize=60)
for x,y in enumerate(pro_user_df['user_id']):
    plt.text(x,y+2,y,fontsize=50,ha='center')
    
plt.subplot(716)
sns.barplot(data=user_sex_df1,x=user_sex_df1.gender,y=user_sex_df1.user_id)
plt.xticks(fontsize=50)
plt.yticks(range(0,121,60),fontsize=50)
plt.xlabel('省份',fontsize=55)
plt.ylabel('人数',fontsize=55)
plt.title('客户性别分布',fontsize=60)
for x,y in enumerate(user_sex_df1.user_id):
    plt.text(x,y+2,y,fontsize=50,ha='center')
    
plt.subplot(717)
sns.barplot(data=age_group_df,x='age_group',y='user_id',palette='Reds_r')
plt.xticks(fontsize=50)
plt.yticks(range(0,71,20),fontsize=50)
plt.xlabel('年龄',fontsize=55)
plt.ylabel('人数',fontsize=55)
plt.title('客户年龄分布',fontsize=60)
for x,y in enumerate(age_group_df['user_id']):
    plt.text(x,y+2,y,fontsize=50,ha='center')
    
plt.tight_layout()
plt.subplots_adjust(wspace=0.5,hspace=0.5)
plt.savefig('711.png')
711.png

【二】日期和时间段处理

#将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=['凌晨','上午','中午','下午','晚上'])

myfont=FontProperties(fname=r'C:\Windows\Fonts\simhei.ttf',size=8)
sns.set(font=myfont.get_name())

hour_order=[ '上午', '中午', '下午','晚上', '凌晨']
ax1=plt.figure(figsize=(10,3),dpi=100)
plt.title('用户活跃时间分布',fontsize=15)
sns.countplot(data=df,x='hour',order=hour_order,hue='behavior_type')
plt.legend(['浏览','收藏','加购','购买'],fontsize=10)
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.xlabel('时间',fontsize=13)
plt.ylabel('合计数量',fontsize=13)

ax2=plt.figure(figsize=(10,3),dpi=100)
ax2=sns.countplot(df['date'],order=sorted(df['date'].unique()))
plt.xticks(rotation=90,fontsize=15)
ax2.set_title('用户活跃日期分布(不区分具体行为)',fontsize=15,ha='center')
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.xlabel('(年-月-日)',fontsize=13)
plt.ylabel('合计数量',fontsize=13)
  • 一天当中,用户晚上活动最为频繁,其次是下午,凌晨活动频次较少,属于合理范畴。

  • 12月12日为近30日最活跃的日期,原因主要为双12活动。


    image.png
action_hour=pd.DataFrame(df['time'].value_counts()).sort_index(ascending=True)
plt.figure(figsize=(10,3),dpi=100)
plt.title('用户活跃时段',fontsize=15)
plt.plot(action_hour.index,action_hour['time'],'-o')
plt.xticks(range(0,24,1),fontsize=10)
plt.yticks(fontsize=10)
plt.xlabel('(时间)',fontsize=13)
plt.ylabel('浏览等行为合计数量',fontsize=13)

一天当中,用户活跃时段集中在晚上18:00~22:00。


image.png

【三】制作用户标签表

一、用户活跃时间

  • 1.1、用户浏览活跃时间段
  • 1.2、用户购买活跃时间段

a、用户浏览活跃时间段

计算加工流程:

  • a、提取 behavior_type=1 的用户浏览数据
  • b、然后根据 用户id+时间段 分组计数,并且求出最大值
  • c、获取 用户id+最活跃时间段,如果有多个最活跃时间段,则进行逗号拼接

behavior_type 的内容

  • 浏览:behavior_type=1
  • 收藏:behavior_type=2
  • 加购:behavior_type=3
  • 购买:behavior_type=4
#对用户和时段分组,统计浏览次数
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()
count_browse_hour=time_browse_hour.groupby('hour').count()
count_browse_hour2=count_browse_hour.reset_index()
count_browse_hour2['hour']=count_browse_hour2['hour'].astype('category')
count_browse_hour2['hour'].cat.reorder_categories([ '上午', '中午', '下午','晚上', '凌晨','下午,晚上'], inplace=True)
#画图
hour_order=[ '上午', '中午', '下午','晚上', '凌晨','下午,晚上']
ax1=plt.figure(figsize=(10,3),dpi=100)
plt.title('各用户浏览时间最长的时段分布',fontsize=15)
g=sns.barplot(data=count_browse_hour2,x='hour',y='user_id',order=hour_order)
plt.xticks(fontsize=10)
plt.yticks(range(0,200,50),fontsize=10)
plt.xlabel('时段',fontsize=13)
plt.ylabel('合计数量',fontsize=13)
for x,y in enumerate(count_browse_hour2['user_id']):
    g.text(x,y+5,str(y)+'人',ha='center',fontsize=10)
  • 共有121人喜欢在晚上浏览购买商品,只有少数人喜欢在凌晨和中午浏览电商平台


    image.png

b、用户购买活跃时间段

计算加工流程:

  • a、提取 behavior_type=4 的用户购买数据
  • b、然后根据 用户id+时间段 分组计数,并且求出最大值
  • c、获取 用户id+最活跃时间段,如果有多个最活跃时间段,则进行逗号拼接
#生成逻辑与浏览活跃时间段相同
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()

#将用户购买活跃时间段加入到用户标签表中
labels = pd.merge(labels,time_buy_hour,how='left',on='user_id')
labels.rename(columns={'hour':'time_buy'},inplace=True)
count_time_buy=labels.groupby('time_buy').agg({'time_buy':'count'})

#画图
ax1=plt.figure(figsize=(10,3),dpi=100)
plt.title('各用户主要购买时间的时段分布',fontsize=15)
g=sns.barplot(data=count_time_buy,x=count_time_buy.index,y='time_buy')
plt.xticks(fontsize=10,rotation=90)
plt.yticks(range(0,81,20),fontsize=10)
plt.xlabel('时段',fontsize=13)
plt.ylabel('合计数量',fontsize=13)
for x,y in enumerate(count_time_buy['time_buy']):
    g.text(x,y+5,str(y)+'人',ha='center',fontsize=10)
  • 人们喜欢在下午或者晚上购物,合计113人,占比50.82%。


    image.png

二、关于类目的用户行为

  • 2.1、浏览最多的类目
  • 2.2、收藏最多的类目
  • 2.3、加购最多的类目
  • 2.4、购买最多的类目

a、浏览最多的类目

计算加工流程:

  • a、提取 behavior_type=1 的用户购买数据
  • b、然后根据 用户id+类目 分组计数,并且求出最大值
  • c、获取 用户id+浏览最多的类目,如果有多个类目,则进行逗号拼接
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()
#将用户浏览最多的类目加入到用户标签表中
labels = pd.merge(labels,df_cate_browse,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_browse'},inplace=True)

b、收藏最多的类目

  • a、提取 behavior_type=2 的用户购买数据
  • b、然后根据 用户id+类目 分组计数,并且求出最大值
  • c、获取 用户id+收藏最多的类目,如果有多个类目,则进行逗号拼接
#生成逻辑与浏览最多的类目相同
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()

labels = pd.merge(labels,df_cate_collect,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_collect'},inplace=True)

c、加购最多的类目

  • a、提取 behavior_type=3 的用户购买数据
  • b、然后根据 用户id+类目 分组计数,并且求出最大值
  • c、获取 用户id+加购最多的类目,如果有多个类目,则进行逗号拼接
# 生成逻辑与浏览最多的类目相同
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()

labels = pd.merge(labels,df_cate_cart,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_cart'},inplace=True)

d、购买最多的类目

  • a、提取 behavior_type=4 的用户购买数据
  • b、然后根据 用户id+类目 分组计数,并且求出最大值
  • c、获取 用户id+购买最多的类目,如果有多个类目,则进行逗号拼接
#生成逻辑与浏览最多的类目相同
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()

labels = pd.merge(labels,df_cate_buy,how='left',on='user_id')
labels.rename(columns={'item_category':'cate_most_buy'},inplace=True)
  • 浏览、收藏、加购、购买最多前十种类目产品可视化展示
# 先获取各个需要的数据集
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']]

def top10_purchase(df,action):
    df_top10=df.groupby('item_id').count().sort_values('user_id',ascending=False)
    df_top10['item_category']=round(df_top10['user_id']/df_top10['user_id'].sum()*100,3)
    df_top10['item_category']=df_top10['item_category'].apply(lambda x:str(x)+'%')
    df_top10=df_top10.head(10)
    ax=plt.figure(figsize=(10,3),dpi=100)
    plt.title('用户'+action+'产品TOP10(次数/占比)',fontsize=15)
    g=sns.barplot(data=df_top10,x=df_top10.index,y='user_id',order=df_top10.index)
    plt.xticks(fontsize=10,rotation=45)
    plt.yticks(range(0,max(df_top10['user_id'].max()//10*13+1,df_top10['user_id'].max()+2),max(df_top10['user_id'].max()//10*10//5,1)),fontsize=10)
    plt.xlabel('产品编号',fontsize=13)
    plt.ylabel(action+'次数',fontsize=13)
    for x,y in enumerate(df_top10['user_id']):
        g.text(x,y+y*0.05,str(y)+'次',ha='center',fontsize=10)
    for x,y in enumerate(df_top10['item_category']):
        g.text(x,df_top10['user_id'].min()/2,y,ha='center',fontsize=10)

top10_purchase(df_browse,'浏览')
top10_purchase(df_collect,'收藏')
top10_purchase(df_cart,'加购')
image.png
image.png
image.png
image.png

三、30天用户行为

  • 3.1 近30天购买次数
  • 3.2 近30天加购次数
  • 3.3 近30天活跃天数

3.1 近30天购买次数

  • a、提取 behavior_type=4 的用户购买数据
  • b、然后根据 用户id 分组计数,得到结果
#将购买行为按用户进行分组,统计次数
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)

3.2 近30天加购次数

  • a、提取 behavior_type=3 的用户购买数据
  • b、然后根据 用户id 分组计数,得到结果
#将加购行为按用户进行分组,统计次数
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)

labels=labels.iloc[:,~labels.columns.duplicated()]

3.3 近30天活跃天数

  • a、提取所有用户购买数据
  • b、然后根据 用户id 分组计数,统计不同日期的天数
#对用户进行分组,统计活跃的天数,包括浏览、收藏、加购、购买
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)

四、7天用户行为

  • 4.1 近7天购买次数
  • 4.2 近7天加购次数
  • 4.3 近7天活跃次数
#数据集中的最后日期是12月18号,统计11号之后的用户行为
df_near_7 = df[df['date']>'2020-12-11']

4.1 近7天购买次数

  • a、提取 behavior_type=4 的用户购买数据
  • b、然后根据 用户id 分组计数,得到结果
df_counts_7_buy = df_near_7[df_near_7['behavior_type']==4].groupby('user_id').item_id.count().reset_index()

labels = pd.merge(labels,df_counts_7_buy,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_7_buy'},inplace=True)

4.2 近7天加购次数

  • a、提取 behavior_type=3 的用户加购数据
  • b、然后根据 用户id 分组计数,得到结果
df_counts_7_cart = df_near_7[df_near_7['behavior_type']==3].groupby('user_id').item_id.count().reset_index()

labels = pd.merge(labels,df_counts_7_cart,how='left',on='user_id')
labels.rename(columns={'item_id':'counts_7_cart'},inplace=True)

4.3 近7天活跃天数

  • a、获取每个用户近7天里面的日期数据,不区分用户行为有记录就算
  • b、去重统计有多少个日期
counts_7_active = df_near_7.groupby('user_id')['date'].nunique()
labels = pd.merge(labels,counts_7_active,how='left',on='user_id')
labels.rename(columns={'date':'counts_7_active'},inplace=True)

五、最后一次行为距今天数

  • 5.1 上次浏览距今天数
  • 5.2 上次加购距今天数
  • 5.3 上次购买距今天数

5.1 上次浏览距今天数

  • a、提取 behavior_type=1 的用户浏览数据,
  • b、获取里面最大的一个日期时间,然后用现在时间相减得到天数
  • c、添加到标签内容里面去
days_browse = df[df['behavior_type']==1].groupby('user_id')['date'].max().apply(lambda x:(datetime.strptime('2020-12-19','%Y-%m-%d')-datetime.strptime(x,'%Y-%m-%d')).days)
labels = pd.merge(labels,days_browse,how='left',on='user_id')
labels.rename(columns={'date':'days_browse'},inplace=True)

5.2 上次加购距今天数

  • a、提取 behavior_type=3 的用户加购数据,
  • b、获取里面最大的一个日期时间,然后用现在时间(2020-12-19)相减得到天数
  • c、添加到标签内容里面去
days_cart = df[df['behavior_type']==3].groupby('user_id')['date'].max().apply(lambda x:(datetime.strptime('2020-12-19','%Y-%m-%d')-datetime.strptime(x,'%Y-%m-%d')).days)

labels = pd.merge(labels,days_cart,how='left',on='user_id')
labels.rename(columns={'date':'days_cart'},inplace=True)

5.3 上次购买距今天数

  • a、提取 behavior_type=4 的用户购买数据,
  • b、获取里面最大的一个日期时间,然后用现在时间(2020-12-19)相减得到天数
  • c、添加到标签内容里面去
days_buy = df[df['behavior_type']==4].groupby('user_id')['date'].max().apply(lambda x:(datetime.strptime('2020-12-19','%Y-%m-%d')-datetime.strptime(x,'%Y-%m-%d')).days)

labels = pd.merge(labels,days_buy,how='left',on='user_id')
labels.rename(columns={'date':'days_buy'},inplace=True)

六、最近两次购买间隔天数

  • a、提取 behavior_type=4 的用户购买数据,
  • b、对 用户id+日期 进行分组,统计记录数
  • c、再对上面得到的数据根据 用户id 进行分组,然后对日期进行一个偏移,获取
  • d、添加到标签内容里面去
df_interval_buy = df[df['behavior_type']==4].groupby(['user_id','date']).item_id.count().reset_index()
df_interval_buy['date']=df_interval_buy['date'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d'))
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')

七、是否浏览未下单

  • a、提取 behavior_type=1 的浏览数据 和 behavior_type=4 的用户购买数据,
  • b、根据 用户id+商品id+用户行为 来进行分组,统计每个用户对每个商品的浏览情况和购买情况
  • c、判断每一个用户对于每一个商品是否存在,浏览但是未购买的行为,如果有那新建一个字段的值记为1,没有则为0
  • d、再根据 用户id 进行分组,统计每个用户的 浏览但未购买 的次数,如果大于0则为 是,否则为 否
df_browse_buy = df.loc[(df['behavior_type']==1) | (df['behavior_type']==4),['user_id','item_id','behavior_type','time']]\
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()
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 '否')

八、是否加购未下单

  • a、提取 behavior_type=3 的加购数据 和 behavior_type=4 的用户购买数据,
  • b、根据 用户id+商品id+用户行为 来进行分组,统计每个用户对每个商品的加购情况和购买情况
  • c、判断每一个用户对于每一个商品是否存在,加购但是未购买的行为,如果有那新建一个字段的值记为1,没有则为0
  • d、再根据 用户id 进行分组,统计每个用户的 加购但未购买 的次数,如果大于0则为 是,否则为 否
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')

九、用户属性标签

  • 9.1 是否复购用户
  • 9.2 访问活跃度
  • 9.3 购买的品类是否单一
  • 9.4 用户价值分组

9.1 是否复购用户

  • a、提取 behavior_type=4 的用户购买数据
  • b、如果计数大于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 '未购买')

9.2 访问活跃度

  • a、获取标签的统计值 counts_30_active 近30天活跃人数情况
  • b、对活跃次数分组计数,也就是得到了访问次数和访问人数的情况
  • c、进行绘图得到趋势图
  • d、如果访问活跃度超过20,则定义为“高”,否则定义为“低”
user_active_level = labels['counts_30_active'].value_counts().sort_index(ascending=False)

plt.figure(figsize=(10,3),dpi=100)
plt.plot(user_active_level.index,user_active_level.values,'--o')
plt.title('30天内访问天数与访问人数的关系',fontsize=15)
plt.xticks(range(0,32,5),fontsize=10)
plt.yticks(range(0,21,5),fontsize=10)
plt.xlabel('访问天数',fontsize=13)
plt.ylabel('访问人数',fontsize=13)
for x,y in enumerate(list(reversed(user_active_level.values))):
    plt.text(x+1,y+1,y,ha='center',fontsize=10)
image.png

总体上看,访问天数多的访客比访问天数少的访客数量多,且以20次左右为拐点,因此定义访问天数小于20次的为低活跃,访问天数大于等于20次的定义为高活跃。此定义只是从用户的分布角度出发,工作中当从业务出发定义是否活跃。

labels['buy_active_level'] = '高'
labels.loc[labels['counts_30_buy']<=19,'buy_active_level'] = '低'

9.3 购买的品类是否单一

  • a、提取 behavior_type=4 的用户购买数据
  • b、根据 用户id 进行分组,统计 类目的数量
  • c、判断计数是否大于1,大于1的
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 '未购买' )

ax1=plt.figure(figsize=(10,3),dpi=100)
plt.title('是否只购买了一次',fontsize=15)
g=sns.countplot(data=labels,x='buy_single')
plt.xticks(fontsize=13)
plt.yticks(range(0,201,20),fontsize=10)
plt.xlabel(' ')
plt.ylabel('人数',fontsize=13,rotation=90)
for x,y in enumerate([180,29,13]):
    g.text(x,y+5,str(y)+'人',ha='center',fontsize=10)
image.png

9.4 用户价值分组(RFM)

  • a、获取标签的统计值 days_buy 得到上次距今购买天数
  • b、对购买天数进行统计计数,得到 buy_days_level
  • c、绘制购买天数buy_days_level 和 购买人数的图形走势
  • d、对购买天数进行分类,如果大于8天则为“低”,反之为“高”

RFM:
最近一次消费 (Recency)
消费频率 (Frequency)
消费金额 (Monetary)

last_buy_days = labels['days_buy'].value_counts().sort_index()

from matplotlib.font_manager import FontProperties
myfont=FontProperties(fname=r'C:\\Windows\\Fonts\\simhei.ttf',size=8)
sns.set(font=myfont.get_name())

plt.figure(figsize=(10,3),dpi=100)
plt.plot(last_buy_days.index,last_buy_days.values,'--o')
plt.title('最后一次购买距今天数与人数的关系',fontsize=15)
plt.xticks(range(0,32,5),fontsize=10)
plt.yticks(range(0,41,5),fontsize=10)
plt.xlabel('距今天数',fontsize=13)
plt.ylabel('购买人数',fontsize=13)
for x,y in enumerate(last_buy_days.values):
    plt.text(x+1,y+1,y,ha='center',fontsize=10)
image.png

注:访问异常的那天为双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)
RFM=labels['rfm'].value_counts()

ax1=plt.figure(figsize=(10,3),dpi=100)
plt.title('RFM模型',fontsize=15)
g=sns.barplot(y=labels['rfm'].value_counts().index,x=labels['rfm'].value_counts().values)
plt.xticks(range(0,141,20),fontsize=10)
plt.yticks(fontsize=10)
plt.xlabel('人数',fontsize=13)
plt.ylabel('客户RFM类型',fontsize=13,rotation=90)
for x,y in enumerate(labels['rfm'].value_counts().values):
    g.text(y+5,x,str(y)+'人',ha='center',fontsize=10)
image.png
# 导入订单数据
order_df = pd.read_excel("order_data.xlsx")
order_df['time']=order_df['time'].apply(lambda x:'2020'+x[4:])
# 把订单日期转换为时间格式
order_df["time"]=pd.to_datetime(order_df["time"])
# 提取时间点
order_df["time_hour"]=order_df["time"].dt.hour
# 为了能按星期统计情况,需要把时间转换成星期几,默认周一是从0计数
order_df["time_week"]=order_df["time"].dt.dayofweek+1
#统计星期购物的情况
week_order_df = order_df.groupby("time_week").user_id.count().reset_index()

myfont=FontProperties(fname=r'C:\\Windows\\Fonts\\simhei.ttf',size=8)
sns.set(font=myfont.get_name())

plt.figure(figsize=(10,3),dpi=100)
plt.plot(week_order_df['time_week'],week_order_df['user_id'],'--o')
plt.title('每周购物情况',fontsize=15)
plt.xticks(range(1,8,1),fontsize=10)
plt.yticks(range(30000,42001,2000),fontsize=10)
plt.xlabel('第N周',fontsize=13)
plt.ylabel('购买次数',fontsize=13)
for x,y in enumerate(week_order_df['user_id']):
    plt.text(x+1,y+500,y,ha='center',fontsize=10)
image.png
# 统计时间点购物情况
hour_order_df = order_df.groupby("time_hour").user_id.count().reset_index()

myfont=FontProperties(fname=r'C:\\Windows\\Fonts\\simhei.ttf',size=8)
sns.set(font=myfont.get_name())

plt.figure(figsize=(10,3),dpi=100)
plt.plot(hour_order_df['time_hour'],hour_order_df['user_id'],'--o')
plt.title('一天购物时间分布',fontsize=15)
plt.xticks(range(0,24,1),fontsize=10)
plt.yticks(range(0,25001,5000),fontsize=10)
plt.xlabel('24小时',fontsize=13)
plt.ylabel('购买次数',fontsize=13)
for x,y in enumerate(hour_order_df['user_id']):
    plt.text(x,y+1000,y,ha='center',fontsize=10)
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容