Fork me on GitHub

kaggle实战-黑色星期图画像分析

kaggle实战:黑色星期五画像分析

本文是对还是kaggle上一份黑色星期五消费数据的分析,主要是针对用户和商品信息的画像分析。

关键词:用户画像、可视化、plotly、Pandas

图形:柱状图、饼图、散点图、小提琴图、桑基图、树状图、漏斗图、多子图等

导入库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.patches as mpatches
import matplotlib
# 中文显示问题
plt.rcParams["font.sans-serif"]=["SimHei"] #设置字体
plt.rcParams["axes.unicode_minus"]=False #正常显示负号

import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
from plotly.offline import init_notebook_mode, iplot


from sklearn.impute import KNNImputer
from sklearn.model_selection import train_test_split

# 忽略notebook中的警告
import warnings
warnings.filterwarnings("ignore")

数据基本信息

导入数据

探索数据的基本信息

In [2]:

1
2
df = pd.read_csv("train.csv")
df

基本信息

In [3]:

1
2
# 1、数据大小
df.shape

Out[3]:

1
(550068, 12)

In [4]:

1
2
# 2、字段类型
df.dtypes

Out[4]:

1
2
3
4
5
6
7
8
9
10
11
12
13
User_ID                         int64
Product_ID object
Gender object
Age object
Occupation int64
City_Category object
Stay_In_Current_City_Years object
Marital_Status int64
Product_Category_1 int64
Product_Category_2 float64
Product_Category_3 float64
Purchase int64
dtype: object

In [5]:

1
2
# 3、缺失值情况
df.isnull().sum()

Out[5]:

1
2
3
4
5
6
7
8
9
10
11
12
13
User_ID                            0
Product_ID 0
Gender 0
Age 0
Occupation 0
City_Category 0
Stay_In_Current_City_Years 0
Marital_Status 0
Product_Category_1 0
Product_Category_2 173638 # 存在缺失值
Product_Category_3 383247
Purchase 0
dtype: int64

In [6]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4、数据信息
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 550068 entries, 0 to 550067
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 User_ID 550068 non-null int64
1 Product_ID 550068 non-null object
2 Gender 550068 non-null object
3 Age 550068 non-null object
4 Occupation 550068 non-null int64
5 City_Category 550068 non-null object
6 Stay_In_Current_City_Years 550068 non-null object
7 Marital_Status 550068 non-null int64
8 Product_Category_1 550068 non-null int64
9 Product_Category_2 376430 non-null float64
10 Product_Category_3 166821 non-null float64
11 Purchase 550068 non-null int64
dtypes: float64(2), int64(5), object(5)
memory usage: 50.4+ MB

缺失值可视化

In [7]:

1
2
import missingno
df.isna().sum() / df.shape[0]

Out[7]:

1
2
3
4
5
6
7
8
9
10
11
12
13
User_ID                       0.000000
Product_ID 0.000000
Gender 0.000000
Age 0.000000
Occupation 0.000000
City_Category 0.000000
Stay_In_Current_City_Years 0.000000
Marital_Status 0.000000
Product_Category_1 0.000000
Product_Category_2 0.315666 # 缺失值字段
Product_Category_3 0.696727
Purchase 0.000000
dtype: float64

In [8]:

可以看到有两个字段存在缺失值

1
2
3
4
# 矩阵图:查看缺失值的分布

missingno.matrix(df)
plt.show()

1
2
3
# 条形图
missingno.bar(df, color="blue")
plt.show()

从下面的图像中也能够观察到只有两个字段存在缺失值

小结1:在我们的数据中包含object、float64和int64共3种数据类型

其中Product_Category_2字段有约31%的缺失占比,Product_Category_3有69%的缺失值占比。

总信息

寻找3个总体的数据信息:

  • 总消费人数
  • 总消费商品种类
  • 总消费金额

In [10]:

1
2
3
# 总消费人数

df["User_ID"].nunique()

Out[10]:

1
5891

In [11]:

1
2
3
# 总消费商品种类

df["Product_ID"].nunique()

Out[11]:

1
3631

In [12]:

1
2
# 总消费金额
sum(df["Purchase"])

Out[12]:

1
5095812742

商品类别

In [13]:

1
2
3
# 总消费商品种类

df["Product_Category_1"].nunique()

Out[13]:

1
20

In [14]:

1
df["Product_Category_2"].nunique()

Out[14]:

1
17

画像1:消费金额Top10

In [15]:

不同用户的消费金额对比

1
2
3
4
df1 = df.groupby("User_ID")["Purchase"].sum().reset_index()

df2 = df1.sort_values("Purchase", ascending=False)
df2["User_ID"] = df2["User_ID"].apply(lambda x: "id_"+str(x))

1
2
3
4
5
6
fig = px.bar(df2[:10],   # 选择前10人
x="User_ID",
y="Purchase",
text="Purchase")

fig.show()

可以看到最高的一位用户花费了1千多万!

画像2:高频消费前10

In [19]:

1
2
3
4
5
6
7
8
df3 = df.groupby("User_ID").size().reset_index()

# Number表示购买次数
df3.columns = ["User_ID", "Number"]

# 降序排列
df4 = df3.sort_values("Number", ascending=False)
df4.head(10)

1
2
3
4
5
6
7
8
9
10
df4["User_ID"] = df4["User_ID"].apply(lambda x: "id_"+str(x))

fig = px.bar(df4[:10], # 选择前10人
x="User_ID",
y="Number",
text="Number",
color="Number"
)

fig.show()

画像3:人均消费金额Top10

In [23]:

1
2
3
4
5
6
df5 = pd.merge(df2, df4)
# 人均消费金额

df5["Average"] = df5["Purchase"] / df5["Number"]
df5["Average"] = df5["Average"].apply(lambda x: round(x,2))
df5.head()

1
2
3
4
5
6
fig = px.scatter(df5,
x="User_ID",
y="Average",
color="Average")

fig.show()

1
px.violin(df5["Average"])

从散点分布图和小提琴图中能够看到,大部分用户的平均消费金额在8k到10k之间

画像4:男女消费对比

In [28]:

1
2
df6 = df.groupby("Gender").agg({"User_ID":"nunique", "Purchase":"sum"}).reset_index()
df6

Out[28]:

Gender User_ID Purchase
0 F 1666 1186232642
1 M 4225 3909580100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
labels = df6["Gender"].tolist()

# 制作子图:'domain' 表示饼图
fig = make_subplots(
rows=1,
cols=2,
subplot_titles=["男女消费次数","男女消费金额"],
specs=[[{'type':'domain'},
{'type':'domain'}]])

# 两个子图
fig.add_trace(go.Pie(
labels=labels,
values=df6["User_ID"].tolist(),
name="UserID"),
row=1,
col=1)

fig.add_trace(go.Pie(
labels=labels,
values=df6["Purchase"].tolist(),
name="Purchase"),
1, 2)

fig.show()

总结:男士才是消费主力军!

画像5:不同年龄的消费人数和金额

In [30]:

1
2
df7 = df.groupby("Age").agg({"User_ID":"nunique", "Purchase":"sum"}).reset_index()
df7

Out[30]:

Age User_ID Purchase
0 0-17 218 134913183
1 18-25 1069 913848675
2 26-35 2053 2031770578
3 36-45 1167 1026569884
4 46-50 531 420843403
5 51-55 481 367099644
6 55+ 372 200767375

In [31]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
labels = df7["Age"].tolist()

# 制作子图:'domain' 表示饼图
fig = make_subplots(
rows=1,
cols=2,
subplot_titles=["不同age消费次数","不同age消费金额"],
specs=[[{'type':'domain'},
{'type':'domain'}]])

# 两个子图
fig.add_trace(go.Pie(
labels=labels,
values=df7["User_ID"].tolist(),
name="UserID"),
row=1,
col=1)

fig.add_trace(go.Pie(
labels=labels,
values=df7["Purchase"].tolist(),
name="Purchase"),
1, 2)

fig.update_layout(height=500,width=800)

fig.show()

总结:26到35岁的人群,年轻人,大部分都是工作多年的用户,有家庭和经济基础,成为了消费主力军

画像6:不同性别+年龄的消费人数、金额

In [32]:

1
2
df8 = df.groupby(["Gender","Age"]).agg({"User_ID":"nunique", "Purchase":"sum"}).reset_index()
df8

1
2
3
4
5
6
7
8
9
10
11
fig = px.treemap(
df8, # 传入数据
path=[px.Constant("all"),"Gender","Age"],
values="Purchase" # 消费金额
)

fig.update_traces(root_color="lightskyblue")

fig.update_layout(margin=dict(t=30,l=20,r=25,b=30))

fig.show()

画像7:不同城市、年龄消费金额

In [34]:

1
2
df9 = df.groupby(["City_Category","Age"]).agg({"User_ID":"nunique", "Purchase":"sum"}).reset_index()
df9.head()

1
2
3
4
5
6
7
8
9
10
11
fig = px.bar(df9,
x="City_Category",
y="Purchase",
color="Age",
barmode="group",
text="Purchase"
)

fig.update_layout(title="不同城市不同年龄段的消费金额")

fig.show()

从3个城市来看,26-35一直都是消费主力

从上面的柱状图来看,在不同的年龄段,一直保持A < B < C的现象。C城市果真是消费的主要城市

画像8:不同婚姻状态的消费次数和金额

In [37]:

1
2
df10 = df.groupby(["Marital_Status"]).agg({"User_ID":"nunique", "Purchase":"sum"}).reset_index()
df10

Out[37]:

Marital_Status User_ID Purchase
0 0 3417 3008927447
1 1 2474 2086885295

In [38]:

1
df10["Marital_Status"] = df10["Marital_Status"].map({0:"未婚",1:"已婚"})

In [39]:

1
2
3
4
5
6
7
8
9
10
fig = px.pie(names=df10["Marital_Status"],
values=df10["Purchase"]
)

fig.update_traces(
textposition='inside', # 文本显示位置:['inside', 'outside', 'auto', 'none']
textinfo='percent+label'
)

fig.show()

总结:单身自由,就是要买

画像9:城市停留时间

In [41]:

1
2
3
px.violin(df,
y="Purchase",
color="Stay_In_Current_City_Years")

1
2
3
4
5
df11 = (df.groupby(["Stay_In_Current_City_Years"])
.agg({"User_ID":"nunique", "Purchase":"sum"})
.reset_index())
df12 = df11.sort_values("User_ID",ascending=False)
df12

1
2
3
4
5
6
stages = df12["Stay_In_Current_City_Years"].tolist()

fig = px.funnel_area(values = df12["User_ID"].tolist(),
names = stages
)
fig.show()

在一个城市停留1-2年内的用户更容易成为消费主力

画像10:销售额Top20商品

In [46]:

1
2
3
4
5
6
7
8
df13 = df.groupby(["Product_ID"]).agg({"Purchase":"sum"}).reset_index()
# 改成以万为单位
df13["Purchase"] = df13["Purchase"].apply(lambda x: round(x / 10000,2))

# 销售额降序
df13.sort_values("Purchase", ascending=False, inplace=True)

df13

1
2
3
4
5
6
7
8
9
fig = px.bar(df13[:10],   # 选择前10人
x="Product_ID",
y="Purchase",
color="Purchase",
text="Purchase")

fig.update_layout(title="Top20商品销售额对比图(万)")

fig.show()

画像11:二八法则

In [49]:

1
2
3
4
# 销售额

top20 = int(df13["Product_ID"].nunique() * 0.2)
top20

Out[49]:

1
726

In [50]:

1
sum(df13[:top20]["Purchase"]) / sum(df13["Purchase"])

Out[50]:

1
0.7325143993257992

通过计算的结果发现:销售额排名前20的商品其总销售额占据整体的73%,基本上是符合我们听到的二八法则

画像11:商品种类

In [51]:

1
2
3
4
5
6
7
8
9
10
11
12
13
df14 = df.groupby(["Product_Category_1"]).agg({"Purchase":"sum"}).reset_index()

fig = px.pie(names=df14["Product_Category_1"],
values=df14["Purchase"],
hole=0.5
)

fig.update_traces(
textposition='inside',
textinfo='percent+label'
)

fig.show()

1
2
3
4
px.box(df,
x="Product_Category_1",
y="Purchase",
color="Product_Category_1")

画像12:不同性别不同种类销售额

In [54]:

1
2
3
4
5
6
7
8
9
10
df15 = (df.groupby(["Gender","Product_Category_1","Product_Category_2","Product_Category_3"])
.agg({"Purchase":"sum"})
.reset_index())

Product_Category = ["Product_Category_1","Product_Category_2","Product_Category_3"]

for i, v in enumerate(Product_Category):
df15[v] = df15[v].apply(lambda x: f"c{i+1}_"+str(int(x)))

df15.head()

第一级

In [56]:

1
2
df16 = df15.groupby("Gender")["Purchase"].sum().reset_index()
df16

Out[56]:

Gender Purchase
0 F 416719106
1 M 1528099293

In [57]:

1
df16["Total"] = "总销售额"

In [58]:

1
2
3
4
5
df16 = df16[["Total","Gender","Purchase"]]

df16.columns = ["父类","子类","数据"]

df16

Out[58]:

父类 子类 数据
0 总销售额 F 416719106
1 总销售额 M 1528099293

第二级

In [59]:

1
2
df17 = df15.groupby(["Gender","Product_Category_1"])["Purchase"].sum().reset_index()
df17.head()

第三级

In [61]:

1
2
df18 = df15.groupby(["Product_Category_1","Product_Category_2"])["Purchase"].sum().reset_index()
df18.head()

Out[61]:

In [62]:

1
df18.columns = ["父类","子类","数据"]

第四级

In [63]:

1
2
df19 = df15.groupby(["Product_Category_2","Product_Category_3"])["Purchase"].sum().reset_index()
df19.head()

Out[63]:

In [64]:

1
df19.columns = ["父类","子类","数据"]

数据合并

In [65]:

1
2
df20 = pd.concat([df16,df17,df18,df19],axis=0).reset_index(drop=True)
df20.head()

Out[65]:

子类和父类元素个数

In [66]:

1
2
labels = list(set(df20["父类"].tolist() + df20["子类"].tolist()))
labels[:5]

Out[66]:

1
['c1_3', 'c3_14', 'c3_18', 'c2_4', 'c2_12']

元素字典设置

In [67]:

1
2
3
number = list(range(0, len(labels)))

index = dict(list(zip(labels, number)))

配对

In [68]:

1
2
3
df20["父类索引"] = df20["父类"].map(index)
df20["子类索引"] = df20["子类"].map(index)
df20

Out[68]:

绘图

In [69]:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import plotly.graph_objects as go

label = list(index.keys())
# 父类和子类
source = df20["父类索引"].tolist()
target = df20["子类索引"].tolist()
# 流量的值
value = df20["数据"].tolist()

# 生成绘图需要的字典数据
link = dict(source = source, target = target, value = value)
node = dict(label = label,
pad=20,
thickness=60,
line = dict(color = "black", width = 0.5))

data = go.Sankey(link = link, node=node)

fig = go.Figure(data)

fig.update_layout(title=dict(text="黑色星期五消费数据",x=0.5,y=0.97))
fig.show()

从性别、3个不同的商品类别来看:

  • 男性的消费能力远高于女性
  • 在商品1中,1号品类是一个高需求的物品
  • 在商品2中,2号最高,8号其次
  • 在商品3中,头部需求的物品差距缩小,整体的需求更均衡

本文标题:kaggle实战-黑色星期图画像分析

发布时间:2022年05月26日 - 21:05

原始链接:http://www.renpeter.cn/2022/05/26/kaggle%E5%AE%9E%E6%88%98-%E9%BB%91%E8%89%B2%E6%98%9F%E6%9C%9F%E4%BA%94%E7%94%BB%E5%83%8F%E5%88%86%E6%9E%90.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Coffee or Tea