1、数据初步了解
In [1]:
import numpy as np
import pandas as pd
df = pd.read_csv('双十一淘宝美妆数据.csv')
df.head() # 查看数据前五行
Out[1]:
In [2]:
df.info() # 查看数据特征
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 27598 entries, 0 to 27597
Data columns (total 7 columns):
Column Non-Null Count Dtype
0 update_time 27598 non-null object
1 id 27598 non-null object
2 title 27598 non-null object
3 price 27598 non-null float64
4 sale_count 25244 non-null float64
5 comment_count 25244 non-null float64
6 店名 27598 non-null object
dtypes: float64(3), object(4)
memory usage: 1.1+ MB
In [3]:
df.shape # 查看数据量
Out[3]:
(27598, 7)
In [4]:
df.describe() # 查看各数字类型特征的一些统计量
Out[4]:

数据有 27598 条,每条数据有 7 个特征,都是非空的。
2. 数据清洗
2.1 重复值处理
直接删除重复值。
In [5]:
data = df.drop_duplicates(inplace=False) ## 去重
data.reset_index(inplace=True, drop=True) ## 重置行索引
data.shape
Out[5]:
(27512, 7)
看出有 86 条重复数据,删除后得到新的数据
2.2 缺失值处理
通过上面观察数据发现 sale_count,comment_count 存在缺失值,先观察存在缺失值的行的基本情况
In [6]:
data.loc[data['sale_count'].isnull()].head()
Out[6]:

In [196]:
data.loc[data['comment_count'].isnull()].tail()
Out[196]:

存在的缺失值很可能意味着售出的数量为 0 或者评论的数量为 0,所以我们用 0 来填补缺失值。
In [7]:
## 填补缺失值
data = data.fillna(0) # 用0填补缺失值
data.isnull().any() # 查看是否还有空值
Out [7]:
update_time False
id False
title False
price False
sale_count False
comment_count False
店名 False
dtype: bool
2.3 数据挖掘寻找新的特征
In [8]:
## 使用jieba包对title进行分词,进一步了解每一个商品的特征
import jieba
subtitle = []
for each in data['title']:
k = jieba.lcut_for_search(each) ## 搜索引擎模式
subtitle.append(k)
data['subtitle'] = subtitle
data[['title', 'subtitle']].head()
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ASUS\AppData\Local\Temp\jieba.cache
Loading model cost 0.895 seconds.
Prefix dict has been built succesfully.
Out[8]:

In [9]:
# 给商品添加分类
sub_type = [] # 子类别
main_type = [] # 主类别
basic_data = """护肤品 乳液类 乳液 美白乳 润肤乳 凝乳 柔肤液 亮肤乳 菁华乳 修护乳
护肤品 眼部护理类 眼霜 眼部 眼膜
护肤品 面膜类 面膜
护肤品 清洁类 洗面 洁面 清洁 卸妆 洁颜 洗颜 去角质 磨砂
护肤品 化妆水 化妆水 爽肤水 柔肤水 补水露 凝露 柔肤液 精粹水 亮肤水 润肤水 保湿水 菁华水 保湿喷雾 舒缓喷雾
护肤品 面霜类 面霜 日霜 晚霜 柔肤霜 滋润霜 保湿霜 凝霜 日间霜 晚间霜 乳霜 修护霜 亮肤霜 底霜 菁华霜
护肤品 精华类 精华液 精华水 精华露 精华素 精华
护肤品 防晒类 防晒
护肤品 补水类 补水
化妆品 口红类 唇釉 口红 唇彩 唇膏
化妆品 底妆类 散粉 蜜粉 粉底液 定妆粉 气垫 粉饼 BB CC 遮瑕 粉霜 粉底膏 粉底霜
化妆品 眼部彩妆 眉粉 染眉膏 眼线 眼影 睫毛膏 眉笔
化妆品 修容类 鼻影 修容粉 高光 腮红"""
## 主观的分类,不在这些类别里的并入其他类。第一列是大类,第二列是小类,后面都是关键词
In [10]:
dcatg = {}
catg = basic_data.split('\n')
for i in catg:
main_cat = i.strip().split('\t')[0]
sub_cat = i.strip().split('\t')[1]
o_cat = i.strip().split('\t')[2:len(catg)]
for j in o_cat:
dcatg[j] = (main_cat, sub_cat)
dcatg ## 分类字典
Out [10]:
{' 乳液 ': (' 护肤品 ', ' 乳液类 '),
' 美白乳 ': (' 护肤品 ', ' 乳液类 '),
' 润肤乳 ': (' 护肤品 ', ' 乳液类 '),
' 凝乳 ': (' 护肤品 ', ' 乳液类 '),
' 柔肤液 ': (' 护肤品 ', ' 化妆水 '),
' 亮肤乳 ': (' 护肤品 ', ' 乳液类 '),
' 菁华乳 ': (' 护肤品 ', ' 乳液类 '),
' 修护乳 ': (' 护肤品 ', ' 乳液类 '),
' 眼霜 ': (' 护肤品 ', ' 眼部护理类 '),
' 眼部 ': (' 护肤品 ', ' 眼部护理类 '),
' 眼膜 ': (' 护肤品 ', ' 眼部护理类 '),
' 面膜 ': (' 护肤品 ', ' 面膜类 '),
' 洗面 ': (' 护肤品 ', ' 清洁类 '),
' 洁面 ': (' 护肤品 ', ' 清洁类 '),
' 清洁 ': (' 护肤品 ', ' 清洁类 '),
' 卸妆 ': (' 护肤品 ', ' 清洁类 '),
' 洁颜 ': (' 护肤品 ', ' 清洁类 '),
' 洗颜 ': (' 护肤品 ', ' 清洁类 '),
' 去角质 ': (' 护肤品 ', ' 清洁类 '),
' 磨砂 ': (' 护肤品 ', ' 清洁类 '),
' 化妆水 ': (' 护肤品 ', ' 化妆水 '),
' 爽肤水 ': (' 护肤品 ', ' 化妆水 '),
' 柔肤水 ': (' 护肤品 ', ' 化妆水 '),
' 补水露 ': (' 护肤品 ', ' 化妆水 '),
' 凝露 ': (' 护肤品 ', ' 化妆水 '),
' 精粹水 ': (' 护肤品 ', ' 化妆水 '),
' 亮肤水 ': (' 护肤品 ', ' 化妆水 '),
' 润肤水 ': (' 护肤品 ', ' 化妆水 '),
' 保湿水 ': (' 护肤品 ', ' 化妆水 '),
' 菁华水 ': (' 护肤品 ', ' 化妆水 '),
' 面霜 ': (' 护肤品 ', ' 面霜类 '),
' 日霜 ': (' 护肤品 ', ' 面霜类 '),
' 晚霜 ': (' 护肤品 ', ' 面霜类 '),
' 柔肤霜 ': (' 护肤品 ', ' 面霜类 '),
' 滋润霜 ': (' 护肤品 ', ' 面霜类 '),
' 保湿霜 ': (' 护肤品 ', ' 面霜类 '),
' 凝霜 ': (' 护肤品 ', ' 面霜类 '),
' 日间霜 ': (' 护肤品 ', ' 面霜类 '),
' 晚间霜 ': (' 护肤品 ', ' 面霜类 '),
' 乳霜 ': (' 护肤品 ', ' 面霜类 '),
' 修护霜 ': (' 护肤品 ', ' 面霜类 '),
' 精华液 ': (' 护肤品 ', ' 精华类 '),
' 精华水 ': (' 护肤品 ', ' 精华类 '),
' 精华露 ': (' 护肤品 ', ' 精华类 '),
' 精华素 ': (' 护肤品 ', ' 精华类 '),
' 精华 ': (' 护肤品 ', ' 精华类 '),
' 防晒 ': (' 护肤品 ', ' 防晒类 '),
' 补水 ': (' 护肤品 ', ' 补水类 '),
' 唇釉 ': (' 化妆品 ', ' 口红类 '),
' 口红 ': (' 化妆品 ', ' 口红类 '),
' 唇彩 ': (' 化妆品 ', ' 口红类 '),
' 唇膏 ': (' 化妆品 ', ' 口红类 '),
' 散粉 ': (' 化妆品 ', ' 底妆类 '),
' 蜜粉 ': (' 化妆品 ', ' 底妆类 '),
' 粉底液 ': (' 化妆品 ', ' 底妆类 '),
' 定妆粉 ': (' 化妆品 ', ' 底妆类 '),
' 气垫 ': (' 化妆品 ', ' 底妆类 '),
' 粉饼 ': (' 化妆品 ', ' 底妆类 '),
'BB': (' 化妆品 ', ' 底妆类 '),
'CC': (' 化妆品 ', ' 底妆类 '),
' 遮瑕 ': (' 化妆品 ', ' 底妆类 '),
' 粉霜 ': (' 化妆品 ', ' 底妆类 '),
' 粉底膏 ': (' 化妆品 ', ' 底妆类 '),
' 眉粉 ': (' 化妆品 ', ' 眼部彩妆 '),
' 染眉膏 ': (' 化妆品 ', ' 眼部彩妆 '),
' 眼线 ': (' 化妆品 ', ' 眼部彩妆 '),
' 眼影 ': (' 化妆品 ', ' 眼部彩妆 '),
' 睫毛膏 ': (' 化妆品 ', ' 眼部彩妆 '),
' 眉笔 ': (' 化妆品 ', ' 眼部彩妆 '),
' 鼻影 ': (' 化妆品 ', ' 修容类 '),
' 修容粉 ': (' 化妆品 ', ' 修容类 '),
' 高光 ': (' 化妆品 ', ' 修容类 '),
' 腮红 ': (' 化妆品 ', ' 修容类 ')}
给出各个关键词的分类类别
In [11]:
sub_type = [] # 主类
main_type = [] # 次类
for i in range(len(data)):
exist = False
for j in data['subtitle'][i]:
if j in dcatg:
sub_type.append(dcatg[j][1])
main_type.append(dcatg[j][0])
exist = True
break
if not exist:
sub_type.append('其他')
main_type.append('其他')
data['sub_type'] = sub_type
data['main_type'] = main_type
data.loc[data['sub_type'] == '其他'].count() ## 查看分类为其他的有多少商品
Out [11]:
update_time 4273
id 4273
title 4273
price 4273
sale_count 4273
comment_count 4273
店名 4273
subtitle 4273
sub_type 4273
main_type 4273
dtype: int64
由title新生成两列类别
In [15]:
sex = []
for i in range(len(data)):
if '男士' in data['subtitle'][i]:
sex.append('是')
elif '男生' in data['subtitle'][i]:
sex.append('是')
elif '男' in data['subtitle'][i] and '女' not in data['subtitle'][i] and '斩男' not in data['subtitle'][i]:
sex.append('是')
else:
sex.append('否')
data['是否男士专用'] = sex
data.loc[data['是否男士专用'] == '是'].head()
Out[15]:
In [18]:
data['是否男士专用'].value_counts()
Out [18]:
否 25333
是 2179
Name: 是否男士专用, dtype: int64
对每个产品总销量新增销售额这一列
In [19]:
data['销售额'] = data.price * data.sale_count
data.head()
Out[19]:
3. 数据分析及可视化
In [27]:
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = [u'SimHei'] ## 显示中文,设置字体
plt.rcParams['axes.unicode_minus'] = False ## 显示符号
plt.figure(figsize=(12, 10))
# 各店铺的商品数量
plt.subplot(2, 2, 1)
plt.tick_params(labelsize=15)
data['店名'].value_counts().sort_values().plot.bar()
plt.title('各品牌商品数', fontsize=20)
plt.ylabel('商品数量', fontsize=15)
plt.xlabel('店名')
# 各店铺的销量
plt.subplot(2, 2, 2)
plt.tick_params(labelsize=15)
data.groupby('店名').sale_count.sum().sort_values().plot.bar()
plt.title('各品牌所有商品的销量', fontsize=20)
plt.ylabel('商品总销量', fontsize=15)
# 各店铺总销售额
plt.subplot(2, 2, 3)
plt.tick_params(labelsize=15)
data.groupby('店名')['销售额'].sum().sort_values().plot.bar()
plt.title('各品牌总销售额', fontsize=20)
plt.ylabel('商品总销售额', fontsize=15)
# 旋转显示
plt.xticks(rotation=45)
## 补充绘图,挖掘数据,各品牌的平均每单单价,三个销量为0的品牌暂时不考虑
plt.subplot(2, 2, 4)
plt.tick_params(labelsize=15)
avg_price = data.groupby('店名')['销售额'].sum() / data.groupby('店名').sale_count.sum() ### 每个品牌售出的商品的平均单价
avg_price.sort_values().plot.bar()
plt.title('各品牌平均每单单价', fontsize=20)
plt.ylabel('售出商品的平均单价', fontsize=15)
## 自适应调整子图间距
plt.tight_layout()
通过图表不难看出以下几点:
悦诗风吟的商品数量遥遥领先,然而其商品销量只排在第三位,总销售额只排在第五位。
SKII,玉兰油,植村秀商品数量大概都在500-1500的范围,而销量为0。
相宜本草商品数量也只属于中游,但其销量销售额均排在第一位,由于其销量是第二名的大约两倍,而销售额远不到两倍,所以销售额/销量,也就是每一单的均价也是一个值得研究的新指标。
通过加入平均每单单价之后,观察销售额较高的几个品牌相宜本草,欧莱雅,佰草集,悦诗风吟,雅诗兰黛。其中相宜本草,悦诗风吟,欧莱雅都是平均单价200元以下的,佰草集为200-300元区间,雅诗兰黛为大于500元区间。是否能够判断价格亲民的品牌的销售额会相对来说更高?下面根据这里的数据先把平均单价分为几个区间,其中0-100元记为A类,100-200元记为B类,200-300元记为C类,300元及以上记为D类。
In [28]:
A = avg_price[(avg_price <= 100) & (avg_price > 0)].index
B = avg_price[(avg_price <= 200) & (avg_price > 100)].index
C = avg_price[(avg_price <= 300) & (avg_price > 200)].index
D = avg_price[avg_price > 300].index
# 四类ABCD分别代表0-100,100-200,200-300,300以上平均单价区间的各品牌
sum_sale = data.groupby('店名')['销售额'].sum()
plt.figure(figsize=(16, 8))
plt.tick_params(labelsize=10)
### 各类、各品牌的销售额占比
plt.subplot(1, 2, 1)
sum_sale_byprice = sum_sale[A].sort_values().append(sum_sale[B].sort_values()).append(sum_sale[C].sort_values()).append(sum_sale[D].sort_values())
plt.pie(x=sum_sale_byprice, labels=sum_sale_byprice.index, colors=['grey'] * len(A) + ['g'] * len(B) + ['y'] * len(C) + ['m'] * len(D), autopct='%0f%%', pctdistance=0.9)
### 各类的平均每个店销售额
plt.subplot(1, 2, 2)
plt.tick_params(labelsize=15)
plt.bar('均价0-100元', np.mean(sum_sale[A]), color='grey')
plt.bar('均价100-200元', np.mean(sum_sale[B]), color='g')
plt.bar('均价200-300元', np.mean(sum_sale[C]), color='y')
plt.bar('均价300元以上', np.mean(sum_sale[D]), color='m')
plt.title('不同类别的平均每个店销售额', fontsize=20)
plt.ylabel('平均销售额', fontsize=20)
plt.tight_layout()

观察饼图,不难发现A类平均单价的品牌所占的销售额比例最高,D类最少,并且D类中的一半以上收入都来自于雅诗兰黛品牌,而最受欢迎的相宜本草由于其价格便宜,销售额也最高。综合分析来说,在价格方面,价格越高的一般来说销量就会越低,反之亦有所相似之处。销量前四的品牌都属于A类,而属于D类的五个品牌销量都在最后七名之中。但
收益来说也有很大的区别。从每个类中每个店的平均销售额也能看出,定价越低则平均销售额越高。
比如A类中的美加净,销售额很低,观察分析其是平均单价最低的品牌,而且销量也并不算高,所以可以考虑几点:1.商品质量问题,虽然便宜但是是否产品质量过低降低了用户使用体验?2.知名度问题,是否需要提高知名度获取更多点击率以提高销量?3.定价问题,在质量过关的前提下,是否因为定价过低而降低了收益?能否在不大幅影响销量的情况下涨价?
事实上,D类中,也就是高端商品中,雅诗兰黛占据了主要份额,一方面可能由于品牌效应,就个人主观的分析,雅诗兰黛为人熟知,在推广营销方面做的很好,所以销量尚可。另一方面,可能雅诗兰黛虽然相对价格较高,但是给使用者带来的使用体验非常好,所以很多消费者宁愿多花钱也要选择雅诗兰黛。这也可能是雅诗兰黛平均单价最高,然而销量却在D类中也最高的原因。
接下来考虑各个类别的销售情况
In [19]:
# 大类销售量、销售额的占比
plt.figure(figsize=(12, 12))
# 销售量
plt.subplot(2, 2, 1)
data.groupby('main_type').sale_count.sum().plot.pie(autopct='%0f%%', title='各大类销售量占比')
# 销售额
plt.subplot(2, 2, 2)
data.groupby('main_type')['销售额'].sum().plot.pie(autopct='%0f%%', title='各大类销售额占比')
# 小类销售量、销售额的占比
plt.subplot(2, 2, 3)
data.groupby('sub_type').sale_count.sum().plot.pie(autopct='%0f%%', title='各小类销售量占比')
plt.subplot(2, 2, 4)
data.groupby('sub_type')['销售额'].sum().plot.pie(autopct='%0f%%', title='各小类销售额占比')
plt.tight_layout()
通过观察比较各个类销售量与销售额占比的关系,基本可以判断他们是正相关的。这也是符合常识的。
在大类中,护肤品的销量远胜化妆品以及其他商品。而在小类之中,清洁类的护肤品销量最高,其次是补水类的护肤品。