手机销售数据分析
注: 本文基于Python 实战之淘宝手机销售分析 进行修改。 本文数据来源于网友从淘宝爬取的全网手机销售数据,包括cellphone.csv(淘宝网在售的手机商品信息),count_add_comments.csv(手机商品的评价信息),comments.csv(手机商品的具体评价)
数据介绍
查看数据详细信息
import pandas as pd
import numpy as np
import time
phone=pd.read_csv('cellphone.csv')
add_comments=pd.read_csv('count_add_comments.csv')
comments=pd.read_excel('comments.xlsx')
print(phone.info())
print(add_comments.info())
print(comments.info())
cellphone.csv的数据详细信息如下: count_add_comments.csv的数据详细信息如下: comments.csv的数据详细信息如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201013154504555.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzQwMDk3Mw==,size_16,color_FFFFFF,t_70#pic_center)
分析思路
手机销售数据
手机销售数据
度量
分类数据
时间
商品参数
后置摄像头_清晰度
摄像头类型_摄像头个数
网络类型_2g/3g/4g/5g
网络模式_单卡/双卡
运行内存RAM
存储容量
品牌
发货地址
发货城市
发货省份
价格等级
评价内容
爬取时间
商品发布时间
评价时间
累计评价数_总销量
商品现价
商品原价
商品折扣
总销售额
月销量
库存
评分
收藏数
评分
图片
追评
手机品牌统计_词云
手机价格区间统计_直方图
TOP10品牌 手机价格等级构成_堆积柱形图
各个品牌下商品现价与原价对比_分组条形图
各个品牌下收藏数与商品现价的关系_折线图_研究用户是否能突破品牌限制而只关心商品现价而进行收藏商品
各个品牌下销量与商品现价的关系_折线图_研究用户是否能突破品牌限制而只关心商品现价而进行购买商品
各个品牌的总销量_条形图
各发货省销售情况_地图
不同价格等级下的总销量_饼图_从总体上研究价格与销量关系
不同价格等级下的总销售额_饼图_从总体上研究价格与总销售额关系
数据清洗
对导入后的phone数据集进行清洗
由上表,发现存在空白列,并且商品描述、月销量、库存、评分、累计评价数存在缺失值
删除空白列
phone=phone.drop(columns=['Unnamed: 19'])
先获取列名,在此基础上进行更改
phone.columns=['爬取时间', '爬取链接', '商品ID', '商品名称',
'商品描述', '商品参数', '商品现价',
'商品原价', '月销量', '库存',
'发货地址', '商品发布时间',
'店铺ID', '店铺名称', '商品链接URL', '评分',
'收藏数' ,'累计评价数', '商品评价印象标签']
将月销量、库存、累计评价数为null用0填充
phone['月销量']=phone['月销量'].fillna(0)
phone['库存']=phone['库存'].fillna(0)
phone['累计评价数']=phone['累计评价数'].fillna(0)
删除评分为null的样本
phone.dropna(subset=['评分'])
对add_comments和 phone 进行数据合并,并尽心初步清洗
对add_comments和 phone 进行数据合并
df=pd.merge(phone,add_comments,left_on='商品ID',right_on='ID(id)')
print(df.info())
合并后的数据如下: 6. 重命名并删除多余的列
df.columns=['爬取时间', '爬取链接', '商品ID', '商品名称',
'商品描述', '商品参数', '商品现价',
'商品原价', '月销量', '库存',
'发货地址', '商品发布时间',
'店铺ID', '店铺名称', '商品链接URL', '评分',
'收藏数' ,'累计评价数', '商品评价印象标签','图片', '追评', 'ID(id)', 'Unnamed: 3']
df=df.drop(columns=['Unnamed: 3'])
df=df.drop(columns=['ID(id)'])
清洗时间参数
df['商品发布时间']=df['商品发布时间'].apply(lambda op:time.strftime('%Y-%m-%d',time.localtime(op)))
清洗价格数据
def get_prices(s):
price=s.split('-')
l=[float(i) for i in price]
return np.mean(l)
df['商品现价']=df['商品现价'].apply(get_prices)
df['商品原价']=df['商品原价'].apply(get_prices)
清洗发货城市数据
pro_list=['北京',
'天津',
'上海',
'重庆',
'河北',
'山西',
'辽宁',
'吉林',
'黑龙江',
'江苏',
'浙江',
'安徽',
'福建',
'江西',
'山东',
'河南',
'湖北',
'湖南',
'广东',
'海南',
'四川',
'贵州',
'云南',
'陕西',
'甘肃',
'青海',
'台湾',
'内蒙古',
'广西',
'西藏',
'宁夏',
'新疆',
'香港',
'澳门']
def get_city(address):
for i in pro_list:
if i in address:
city=address.replace(i,'')
if len(city)==0:
city=i
return city
def get_province(address):
for i in pro_list:
if i in address:
province=i
return province
df['发货城市']=df['发货地址'].apply(get_city)
df['发货省份']=df['发货地址'].apply(get_province)
价格分箱
def get_price_level(p):
level=p//1000
if level==0:
return '0~999'
if level==1:
return '1000~1999'
if level==2:
return '1999~2999'
if level==3:
return '2999~3999'
if level==4:
return '3999~4999'
if level>=5:
return '5000+'
else:
return '计算出错'
df['价格等级']=df['商品现价'].apply(get_price_level)
手机参数信息提取
target=['后置摄像头',
'摄像头类型',
'视频显示格式',
'分辨率',
'触摸屏类型',
'屏幕尺寸',
'网络类型',
'网络模式',
'键盘类型',
'款式',
'运行内存RAM',
'存储容量',
'品牌',
'华为型号',
'电池类型',
'核心数',
'机身颜色',
'手机类型',
'操作系统',
'CPU品牌',
'产品名称']
for t in target:
def get_pram(p):
for i in eval(p):
if i['label']==t:
return i['value']
df[t]=df['商品参数'].apply(get_pram)
将处理后的数据存储为"处理后的数据.xlsx"
df.to_excel('处理后的数据.xlsx')
数据可视化
导入数据,删除多余的列
import pandas as pd
import numpy as np
data=pd.read_excel('处理后的数据.xlsx')
data.drop(columns=['Unnamed: 0'],inplace=True)
删除价格异常的样本
# 计算下四分位数和上四分位
Q1 = data['商品现价'].quantile(q = 0.25)
Q3 = data['商品现价'].quantile(q = 0.75)
# 基于1.5倍的四分位差计算上下须对应的值
low_whisker = Q1 - 1.5*(Q3 - Q1)
up_whisker = Q3 + 1.5*(Q3 - Q1)
# 寻找异常点
data=data.drop(data['商品现价'][(data['商品现价']> up_whisker) | (data['商品现价'] "rotate":90}))
compare_bar.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
compare_bar.render('compare_bar.html')
结果如下: 由图形可得,华为品牌的手机,原价与现价相比,差距最大。
5. 收藏数与价格的关系(从品牌的角度)
以价格(每一品牌的价格平均值)为x轴,收藏数(每一品牌的收藏数平均值)为y轴,画出收藏数与价格的关系折现图。
# 数据处理(算出每一品牌的商品价格平均值和每一品牌的商品收藏数平均值)
sale=data.groupby('品牌')['商品现价'].mean().reset_index()
store=data.groupby('品牌')['收藏数'].mean().reset_index()
store_sale=pd.merge(sale,store,on='品牌')
store_sale.sort_values(by='商品现价',inplace=True)
store_sale=store_sale.drop(store_sale[store_sale['收藏数']>10000].index)
#画出折线上的折点
store_sale_scatter=Scatter()
store_sale_scatter.add_xaxis(list(np.round(store_sale['商品现价'],2)))
store_sale_scatter.add_yaxis('收藏数',store_sale[['收藏数','品牌']].values.tolist()) #每个折点上的提示框既有商品价格,又有商品收藏数,还有商品品牌
store_sale_scatter.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
store_sale_scatter.set_global_opts(xaxis_opts=opts.AxisOpts(name='商品现价(元)',type_='value'))
#画出折线
store_sale_line=Line()
store_sale_line.add_xaxis(list(np.round(store_sale['商品现价'],2)))
store_sale_line.add_yaxis('收藏数',store_sale['收藏数'].values.tolist())
store_sale_line.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
store_sale_line.set_global_opts(xaxis_opts=opts.AxisOpts(name='商品现价(元)',type_='value'))
store_sale_line.set_global_opts(tooltip_opts=opts.TooltipOpts(formatter='价格,收藏数,品牌:{c}'))
store_sale_line.overlap(store_sale_scatter).render('store_sale_line.html')
结果如下: 有图形可得,对于不同品牌,价格并不是客户收藏的重要关注点。
6. 销量与价格的关系(从品牌的角度)
类似于分析收藏数与价格的关系,同样使用折线图研究销量与价格的关系
sale=data.groupby('品牌')['商品现价'].mean().reset_index()
number=data.groupby('品牌')['累计评价数'].mean().reset_index()
number_sale=pd.merge(sale,number,on='品牌')
number_sale.sort_values(by='商品现价',inplace=True)
number_sale=number_sale.drop(number_sale[number_sale['累计评价数']>10000].index)
number_sale_scatter=Scatter()
number_sale_scatter.add_xaxis(list(np.round(number_sale['商品现价'],2)))
number_sale_scatter.add_yaxis('销量',number_sale[['累计评价数','品牌']].values.tolist())
number_sale_scatter.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
number_sale_scatter.set_global_opts(xaxis_opts=opts.AxisOpts(name='商品现价(元)',type_='value'))
number_sale_line=Line()
number_sale_line.add_xaxis(list(np.round(number_sale['商品现价'],2)))
number_sale_line.add_yaxis('销量',number_sale['累计评价数'].values.tolist())
number_sale_line.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
number_sale_line.set_global_opts(xaxis_opts=opts.AxisOpts(name='商品现价(元)',type_='value'))
number_sale_line.set_global_opts(tooltip_opts=opts.TooltipOpts(formatter='价格,销量,品牌:{c}'))
number_sale_line.overlap(number_sale_scatter).render('number_sale_line.html')
结果如下: 图形证明,并不是所有品牌商品价格越低,销量就越高,只是针对个别品牌,如波导,誉品,纽曼,优思,小辣椒,这几个品牌的平均价格在1000以内,而销售量占所有品牌前五,而小米,荣耀,苹果这样的大品牌虽然价格较高,但是销售量也是居高,可见,品牌知名度对于销量非常重要。
7. 不同品牌手机总销量比较
爬取数据中并未包含总销售量数据,但根据淘宝的系统设置,无论买家是否主动评价,交易成功后将会自动留下评价信息,故在这里可以视‘累计评价数’为总销售量进行分析;
numbers=data.groupby('品牌')['累计评价数'].sum().sort_values()[20:]
number_bar=Bar(init_opts=opts.InitOpts(width='880px',height='600px'))
number_bar.add_xaxis(list(numbers.index))
number_bar.add_yaxis('',list(numbers.values.astype(float)),label_opts = opts.LabelOpts(position='right',font_size=8))
number_bar.set_global_opts(xaxis_opts=opts.AxisOpts(is_show=False),visualmap_opts=opts.VisualMapOpts(max_=26,pos_right=True))
number_bar.reversal_axis()
number_bar.render('number_bar.html')
结果如下: 由图可得,华为的总销量是最高的。
8. 各发货省销售情况
provin=data.groupby('发货省份')['累计评价数'].sum()
geo=Geo(init_opts=opts.InitOpts(width='880px',height='600px'))
geo.add_schema(maptype='china')
geo.add('',[list(z) for z in zip(provin.index,provin.values.astype(float))],type_=ChartType.EFFECT_SCATTER)
geo.set_series_opts(label_opts=opts.LabelOpts(font_size=8,formatter='{b}')) # 显示城市
geo.set_global_opts(visualmap_opts=opts.VisualMapOpts(min_=5000,max_=1000000),title_opts=opts.TitleOpts(title='各发货省销售情况'))
geo.render("geo.html")
结果如下: 由图可得,相比其他城市,广东省和北京市的销量居高,江苏省销售量也不容小觑。
9. 不同价格等级总销量饼图(总体上)
从总体上,分析不同价格等级的销量。
scale_number=data.groupby('价格等级')['累计评价数'].mean()
scale_number_pie=Pie()
scale_number_pie.add("",[list(z) for z in zip(scale_number.index,np.round(scale_number.values,2))])
scale_number_pie.set_global_opts(legend_opts=opts.LegendOpts(pos_bottom=True),title_opts=opts.TitleOpts(title='不同价格等级总销量饼图',pos_left='38%'))
scale_number_pie.render('scale_number_pie.html')
结果如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201031220709342.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzQwMDk3Mw==,size_16,color_FFFFFF,t_70#pic_center)
由图形得出,从总体上,价格较低时,销量会较高。 其中0-999的价格等级销量最高,2999-3999的价格等级销量居第二, 1000-1999的价格等级销量居第三,接下来为1999-2999,3999-4000,5000+。
10. 不同价格等级总销售额饼图
从总体上,分析不同价格等级的销售额。
data['总销售额']=data['商品现价']*data['累计评价数']
scale_sale=data.groupby('价格等级')['总销售额'].mean()
scale_sale_pie=Pie()
scale_sale_pie.add("",[list(z) for z in zip(scale_sale.index,np.round(scale_sale.values,2))],radius=["40%","75%"])
scale_sale_pie.set_global_opts(legend_opts=opts.LegendOpts(pos_bottom=True),title_opts=opts.TitleOpts(title='不同价格等级总销售额玫瑰图',pos_left='38%'))
scale_sale_pie.render('scale_sale_pie.html')
结果如下: 图形显示,价格等级为3999-4999的手机总销售额最高,其次为3999-4999,199-2999,令人惊讶的是,在上轮分析中,0-999的销量是最高的,然而总销售在所有价格等级是最低的。
数据建模
探讨离散变量与月销量的关系 利用卡方统计量筛选出影响月销量的显著离散变量。
from sklearn.preprocessing import LabelEncoder
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#对原数据进行编码
data_copy=data.copy()
le = LabelEncoder()
data_copy.iloc[:, 0] = le.fit_transform(data_copy.iloc[:, 0]) # 月销量
le = LabelEncoder()
data_copy.iloc[:, 1] = le.fit_transform(data_copy.iloc[:, 1]) # 后置摄像头清晰度
le = LabelEncoder()
data_copy.iloc[:, 2] = le.fit_transform(data_copy.iloc[:, 2].astype(str)) # 摄像头类型(有几个摄像头)
le = LabelEncoder()
data_copy.iloc[:, 3] = le.fit_transform(data_copy.iloc[:, 3]) # 网络类型(2g,3g,4g,5g)
le = LabelEncoder()
data_copy.iloc[:, 4] = le.fit_transform(data_copy.iloc[:, 4]) # 网络模式(单卡,双卡)
le = LabelEncoder()
data_copy.iloc[:, 5] = le.fit_transform(data_copy.iloc[:, 5]) # 运行内存RAM
le = LabelEncoder()
data_copy.iloc[:, 6] = le.fit_transform(data_copy.iloc[:, 6]) # 存储容量
le = LabelEncoder()
data_copy.iloc[:, 7] = le.fit_transform(data_copy.iloc[:, 7]) # 品牌
X = data_copy.iloc[:, 1:] #特征
y =data_copy.iloc[:,0] #标签
# 使用卡方统计量进行特征筛选
fs = SelectKBest(score_func=chi2, k='all')
fs.fit_transform(X, y)
for i in range(len(fs.pvalues_)):
print('%s: %f' % (data.columns[i+1], fs.pvalues_[i]))
结果如下: 由图显示,小于0.05的有存储容量,品牌,则这两变量显著影响月销量。
探讨离散变量与月销量的关系 利用热力图筛选出影响月销量的显著连续变量。
#对屏幕尺寸进行处理,转化为浮点型,纳入特征值中
df=df[df['屏幕尺寸'].notnull()]
df['屏幕尺寸']=[float(i.replace('英寸','')) for i in df['屏幕尺寸']]
#绘制热力图
df['商品折扣']=df['商品现价']/df['商品原价']
f=df[['商品折扣','商品现价','商品原价','库存','评分','收藏数','累计评价数','追评','屏幕尺寸','图片','月销量']]
corr=f.corr()
plt.figure(figsize=(12,9))
sns.heatmap(corr,annot=True)
plt.show()
结果如下: 由图显示,收藏数,累积评价数,图片,追评显著影响月销量。
拟合回归模型
# 存储容量,品牌独热编码
data_select=data_copy.iloc[:,6:8]
enc = OneHotEncoder() # 创建独热编码方法
enc.fit(data_select)
enc1 = enc.transform(data_select).toarray()
one_hot=pd.DataFrame(enc1)
# 独热编码后的变量拼接连续变量
data_select1=df[['月销量','图片','追评','收藏数','累计评价数']].fillna(0)
df_all=pd.concat([one_hot,data_select1],axis=1)
建立LASSO回归模型
X = df_all.iloc[:,:].fillna(0).values
Y = df['月销量'].fillna(0).values
# 切分训练集和测试集
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3, random_state=0)
# 将数据集进行标准化
std_x = StandardScaler()
X_train = std_x.fit_transform(X_train)
X_test = std_x.transform(X_test)
# 拟合模型
reg = Lasso(alpha = 0.1)
reg.fit(X_train, Y_train)
# 评价模型
score = reg.score(X_test, Y_test) #R^2值
print(score)
result = reg.predict(X_test)
plt.figure()
plt.plot(np.arange(len(result)), Y_test, "go-", label="True value")
plt.plot(np.arange(len(result)), result, "ro-", label="Predict value")
plt.legend(loc="best")
plt.show()
结果如下: r方值为 拟合图形为 由两结果,拟合效果优。
建立其他模型
def model_test(estimators, x_train, x_test, y_train, y_test):
for key, estimator in estimators.items():
estimator.fit(x_train, y_train)
y_predict = estimator.predict(x_test)
mse = mean_squared_error(y_test, y_predict)
print('----------------MSE of %s-------------' % (key), mse) # 均方误差(有偏估计)
scores = estimator.score(x_test, y_test)
print('----------------Score of %s-------------' % (key), scores) # R方
print('\n')
estimators = {}
estimators['Linear'] = LinearRegression()
estimators['ridge'] = Ridge()
estimators['forest'] = RandomForestRegressor()
estimators['gbdt'] = GradientBoostingRegressor()
x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.3)
std_x = StandardScaler()
std_y = StandardScaler()
x_train = std_x.fit_transform(x_train)
x_test = std_x.transform(x_test)
y_train = std_y.fit_transform(y_train.reshape(-1, 1))
y_test = std_y.transform(y_test.reshape(-1, 1))
model_test(estimators, x_train, x_test, y_train, y_test)
结果如下: 由图显示,岭回归模型的拟合效果同样为优。 有了优秀的模型,只要向模型导入变量,就有很大的可能性能准确预测出手机的月销量。
文本分析
整理出每个评论内容对应的品牌与对应的情感
from snownlp import SnowNLP
import pandas as pd
import numpy as np
df=pd.read_excel("处理后的数据.xlsx")
cmt=pd.read_excel('comments.xlsx')
cmt.columns=['商品ID(id)','评价时间(time)','评价内容(content)','爬取链接(spurl)','爬取时间(sptime)']
cmt=cmt.dropna(subset=['评价内容(content)'])
cmt.index=np.arange(len(cmt))
temp=df[['商品ID','品牌']]
# 通过商品ID,找到评价内容对应的品牌
join_data=pd.merge(cmt,temp,left_on="商品ID(id)",right_on="商品ID")
# 通过情感分析函数,整理出每条评价内容对应的心情
mood=[]
for i in join_data['评价内容(content)']:
s=SnowNLP(str(i)).sentiments
if s > 0.6:
mood.append("positive")
elif s |