# 整体结构为《python数据分析与挖掘实战》第三章内容,在其代码内容及演示上有所修改及补充。
# 具体问题具体分析,请结合实际情况食用
前言
在收集到初步的样本数据之后,接下来该考虑的问题有:
(1)样本数据集的数量和质量是否满足模型构建的要求。
(2)是否出现从未设想过的数据状态。
(3)是否有明显的规律和趋势。
(4)各因素之间有什么样的关联性。
解决方案:检验数据集的数据质量、绘制图表、计算某些特征量等,对样本数据集的结构和规律进行分析。
从数据质量分析和数据特征分析两个角度出发。
3.1 数据质量分析
数据质量分析师数据预处理的前提,是数据挖掘分析结论有效性和准确性的基础。
主要任务是检查数据中是否存在脏数据,包括以下内容:
(1)缺失值
(2)异常值
(3)不一致的值
(4)重复数据及含有特殊符号(#、¥、*等)的数据
3.1.1 缺失值分析
产生原因:(1)信息无法获取;(2)信息被遗漏;(3)属性值不存在(如未婚者的配偶姓名)。
影响:(1)丢失有用信息;(2)模型不确定性更加显著;(3)建模混乱,输出不可靠。
分析:含有缺失值属性的个数,每个属性的未缺失数、缺失数与缺失率等。
处理:删除、对可能值进行插补或不处理(4.1.1节)
3.1.2 异常值(离群点)分析
(1)简单统计量分析
最大值、最小值。
(2)3σ原则
如果数据服从正态分布:异常值被定义为一组测定值中与平均值的偏差超过3倍标准差的值。
正态分布假设下,距离平均值3σ之外的值出现的概率为P(|x-μ|>3σ)≤0.003,属于极个别的小概率事件。
如果数据不服从正态分布,也可以用远离平均值的多少倍标准差来描述。
(3)箱型图分析
箱型图识别异常值的结果比较客观,在识别异常值方面有一定优越性。
import pandas as pd
# 读取餐饮数据
file_path = r'D:\pyproject\data_analysis_mining\01-数据和代码\chapter3\demo\data\catering_sale.xls'
data = pd.read_excel(file_path, index_col=u'日期') # 指定日期作为索引列
data_describe = data.describe()
print(data_describe)
销量
count 200.000000
mean 2755.214700
std 751.029772
min 22.000000
25% 2451.975000
50% 2655.850000
75% 3026.125000
max 9106.440000
其中count是非空值数,len(data)得出的数据条是201,因此缺失值为1。
import pandas as pd
import matplotlib.pyplot as plt
# 读取餐饮数据
file_path = r'D:\pyproject\data_analysis_mining\01-数据和代码\chapter3\demo\data\catering_sale.xls'
data = pd.read_excel(file_path, index_col=u'日期') # 指定日期作为索引列
data_describe = data.describe()
print(data_describe)
# ----------------------------------------------------------------------
# 制作箱型图
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
plt.figure() # 建立图像
p = data.boxplot(return_type='dict') # DataFrame的方法画箱型图,返回字典格式
x = p['fliers'][0].get_xdata() # 'fliers' 为异常值标签
y = p['fliers'][0].get_ydata()
y.sort() # 从小到大排序
# 用annotate添加注释
for i in range(len(x)):
if i > 0:
plt.annotate(y[i], xy=(x[i], y[i]),
xytext=(x[i]+0.05-0.8/(y[i]-y[i-1]), y[i]))
else:
plt.annotate(y[i], xy=(x[i], y[i]),
xytext=(x[i]+0.08, y[i]))
plt.show()
结合业务,将4065.2、4060.3、865.0归为正常值,其他五点归为异常值。
最后确定过滤规则:日销量在400以下5000以上则属于异常数据,编写过滤程序(写个判断就行),进行后续处理。
3.1.3 一致性分析
主要发生在数据集成的过程中,可能是重复存放的数据未能进行一致性更新造成的。
3.2 数据特征分析
3.2.1 分布特征
对于定量数据:频率分布表、频率分布直方图、茎叶图。
对于定性分类数据:饼图、条形图。
1.定量数据的分布分析
步骤:1)求极差;2)决定组距与组数;3)决定分点;4)列出频率分布表;5)绘制频率分布直方图。
遵循原则:1)各组之间必须互相排斥;2)各组必须将所有的数据包含在内;3)各组的组宽最好相等。
依旧以3.1.2的数据源为实例(下载的配套数据集没找到书上的数据):
# 注:书上这部分没有列代码,因此实现过程可能略有不同
"""
数据集为定量数据
通过以下步骤进行分布分析
1)求极差
2)决定组距与组数
3)决定分点
4)列出频率分布表
5)绘制频率分布直方图
"""
import pandas as pd
import math
import matplotlib.pyplot as plt
# 调整df列宽
pd.set_option('display.unicode.ambiguous_as_wide', True)
pd.set_option('display.unicode.east_asian_width', True)
file_path = r'D:\pyproject\data_analysis_mining\01-数据和代码\chapter3\demo\data\catering_sale.xls'
data = pd.read_excel(file_path, index_col=u'日期')
# 过滤
data = data[(data[u'销量'] > 400) & (data[u'销量'] < 5000)]
statistics = data.describe() # 保存基本统计量
# 1)求极差
statistics.loc['range'] = statistics.loc['max'] - statistics.loc['min'] # 极差=3200.200000
print(statistics)
# 2)分组
# 根据业务,取组距为500
# 组数=(极差/组距)+1,这里书上有误
group_num = math.ceil((3200.2 / 500) + 1)
print(f'组数为:', group_num) # 8
# 3)决定分点
# 分布区间
start, end = 0, 500
for i in range(group_num):
if i in range(group_num)[:-2]:
print(f'[{start}, {end})', end=',')
else:
print(f'[{start}, {end})')
start += 500
end += 500
'''
[0, 500),[500, 1000),[1000, 1500),[1500, 2000),[2000, 2500),
[2500, 3000),[3000, 3500),[3500, 4000)
'''
# 绘制频率分布表
start, end = 0, 500
fre_table = pd.DataFrame(data=None, columns=['组段', '组中值', '频数', '频率'])
for i in range(group_num + 1):
group_segment = f'[{start}, {end})' # 组段
median = sum([start, end]) // 2 # 中间值
f1 = len(data[(data[u'销量'] >= start) & (data[u'销量'] < end)]) # 频数
f2 = f1 / len(data)
f2 = '{:.2f}%'.format(f2 * 100) # 频率
fre_table.loc[i + 1] = [group_segment, median, f1, f2]
start += 500
end += 500
print(fre_table)
'''
组段 组中值 频数 频率
1 [0, 500) 250 0 0.00%
2 [500, 1000) 750 1 0.51%
3 [1000, 1500) 1250 0 0.00%
4 [1500, 2000) 1750 1 0.51%
5 [2000, 2500) 2250 53 27.18%
6 [2500, 3000) 2750 87 44.62%
7 [3000, 3500) 3250 44 22.56%
8 [3500, 4000) 3750 7 3.59%
9 [4000, 4500) 4250 2 1.03%
'''
# ----------------------------------------------------------------------
# 4)制作箱频率分布直方图
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
plt.figure(figsize=(10, 6))
x = fre_table['组段'].tolist()
y = fre_table['频数'].tolist()
plt.bar(x, y)
plt.title('销售额频率分布直方图')
plt.show()
# 为了方便这里绘制的是频数分布直方图
2.定性数据的分布特征
采用饼图、条形图描述定性变量的分布
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_excel(r'D:\pyproject\data_analysis_mining\01-数据和代码\chapter3\demo\data\catering_sale_all.xls')
data = data.dropna() # 去除缺失值
labels = data.columns[1:]
sizes = []
for dish in data.columns[1:]:
size = sum(data[dish].tolist())
sizes.append(size)
print(sizes)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
plt.pie(
x=sizes,
labels=labels,
autopct="%.0f%%",
)
plt.title('菜品销售分布(饼图)')
plt.show()
# -------------------------------------------------------------
# 条形图
plt.title('菜品销售分布(条形图))')
plt.bar(
x=labels,
height=sizes
)
plt.show()
3.2.2 对比分析
适用于指标间的横纵向比较、时间序列的比较分析。
主要有两种形式:(1)绝对数比较;(2)相对数比较
一般为组合折线图。
3.2.3 统计量分析
1.集中趋势度量
均值、中位数、众数。
2.离中趋势度量
极差、标准差、变异系数、四分位数间距
# -*- coding: utf-8 -*-
import pandas as pd
catering_sale = r'D:\pyproject\data_analysis_mining\01-数据和代码\chapter3\demo\data\catering_sale.xls'
data = pd.read_excel(catering_sale, index_col=u'日期')
data = data[(data[u'销量'] > 400) & (data[u'销量'] < 5000)]
statistics = data.describe()
# 极差、变异系数、四分位数间距
statistics.loc['range'] = statistics.loc['max'] - statistics.loc['min']
statistics.loc['var'] = statistics.loc['std'] / statistics.loc['mean']
statistics.loc['dis'] = statistics.loc['75%'] - statistics.loc['25%']
print(statistics)
count 195.000000
mean 2744.595385
std 424.739407
min 865.000000
25% 2460.600000
50% 2655.900000
75% 3023.200000
max 4065.200000
range 3200.200000
var 0.154755
dis 562.600000
3.2.4 周期性分析
一般为时间序列数据,表示为折线图。
pass
3.2.5 贡献度分析
贡献度分析原理是帕累托法则,又称20/80定律。
对于餐饮企业来讲,应用贡献度分析可以重点改善某菜系盈利最高的前80%的菜品,或重点发展综合影响最高的80%的部门。
# -*- coding: utf-8 -*-
import pandas as pd
import matplotlib.pyplot as plt
# 初始化plt参数
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
# -----------------------------------------------------------------------
# 准备数据源
data = pd.read_excel(
r'D:\pyproject\data_analysis_mining\01-数据和代码\chapter3\demo\data\catering_dish_profit.xls',
index_col='菜品名')
data = data['盈利'].copy()
# 降序排序
data.sort_values(ascending=False)
print(data)
# -----------------------------------------------------------------------
# 绘制图像
plt.figure(figsize=(10, 8))
data.plot(kind='bar')
plt.xlabel('菜品名')
plt.ylabel('盈利(元)')
# 求盈利比例=累计销售额/总销售额
p = data.cumsum() / data.sum()
p.plot(color='r', secondary_y=True, style='-o', linewidth=2)
# 增加注释
plt.annotate(
text=format(p[6], '.4%'), # 注释文本内容
xy=(6, p[6]), # 被注释的坐标点
xytext=(6*0.9, p[6]*0.9), # 注释文字的坐标位置
arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=.2') # 箭头参数,参数类型为字典dict
)
plt.ylabel('盈利(比例)')
plt.show()
由上图可知,菜品A1-A7占菜品种类数的70%,总盈利额占该月盈利额的85.0033%。根据帕累托法则,应该增加对菜品A1~A7的成本投入,减少A8~A10的投入以获得更高的盈利额。
# 书上的代码写的奇奇怪怪的,计算盈利比例的时候前面还乘了个1.0,不知道是为了转换成浮点数还是啥。
# 书上的plt.annotate那段既有位置参数又有关键字参数,看的怪怪的,这里把关键字加上去了(顺便写了下注释)。
3.2.6 相关性分析
1.直接绘制散点图
2.绘制散点图矩阵
3.计算相关系数
# -*- coding: utf-8 -*-
import pandas as pd
# 调整df列宽
pd.set_option('display.unicode.ambiguous_as_wide', True)
pd.set_option('display.unicode.east_asian_width', True)
data = pd.read_excel(r'D:\pyproject\data_analysis_mining\01-数据和代码\chapter3\demo\data\catering_sale_all.xls')
# 相关系数矩阵
print(data.corr(), end='\n')
'''
日期 百合酱蒸凤爪 ... 香煎罗卜糕 原汁原味菜心
日期 1.000000 -0.400896 ... -0.259884 -0.398426
百合酱蒸凤爪 -0.400896 1.000000 ... -0.090276 0.428316
翡翠蒸香茜饺 -0.292033 0.009206 ... 0.270276 0.020462
金银蒜汁蒸排骨 -0.272744 0.016799 ... 0.077808 0.029074
乐膳真味鸡 -0.052438 0.455638 ... -0.030222 0.421878
蜜汁焗餐包 -0.232917 0.098085 ... 0.171005 0.527844
生炒菜心 0.121191 0.308496 ... 0.049898 0.122988
铁板酸菜豆腐 -0.404750 0.204898 ... 0.157958 0.567332
香煎韭菜饺 -0.024573 0.127448 ... 0.178336 0.049689
香煎罗卜糕 -0.259884 -0.090276 ... 1.000000 0.088980
原汁原味菜心 -0.398426 0.428316 ... 0.088980 1.000000
'''
# 只显示“百合酱蒸凤爪”与其他菜品的相关关系
print(data.corr()['百合酱蒸凤爪'], end='\n')
'''
[11 rows x 11 columns]
日期 -0.400896
百合酱蒸凤爪 1.000000
翡翠蒸香茜饺 0.009206
金银蒜汁蒸排骨 0.016799
乐膳真味鸡 0.455638
蜜汁焗餐包 0.098085
生炒菜心 0.308496
铁板酸菜豆腐 0.204898
香煎韭菜饺 0.127448
香煎罗卜糕 -0.090276
原汁原味菜心 0.428316
Name: 百合酱蒸凤爪, dtype: float64
'''
# 计算“百合酱蒸凤爪”与“翡翠蒸香茜饺”的相关关系
print(data['百合酱蒸凤爪'].corr(data['翡翠蒸香茜饺']))
'''
0.009205803051836475
'''
从结果得,如果顾客点了百合酱蒸凤爪,则和点乐膳真味鸡、生炒菜心、原汁原味菜心的相关性比较高,和点其他的相关性较低。
补充下不同程度线性相关的说明:
|r|<=0.3不存在线性相关
0.3<|r|<=0.5为低度线性相关
0.5<|r|<=0.8为显著线性相关
|r|>0.8为高度线性相关
(r>0为正相关,r<0为负相关)