目录
一、导包
二、读取数据
三、画图:
关于hist():
1、hist() 方法的主要参数:
2、hist() 方法的返回值:
四、结果:
五、错误检查:由于数据分布不均匀产生的图像显示问题
六、调整:
(一)调整组距:
(二)自定义图像
我想要达成的目标:
1、将柱子调整,每组柱子添加边框,并将柱子的颜色设置成为绿色:
2、调整横坐标数据,添加标签
问题:Matplotlib绘图无法显示中文字体
解决:通过 plt.rcParams[ ] 方法修改 matplotlib 的配置文件,即 .rc 文件
3、在每组 bin 上添加数据,并将这个视图的 title 改为:“会员消费区间分布”
七、代码
文章记录一次使用 Matplotlib 进行数据可视化操作过程,包括遇到的问题,汇总的代码在文章结尾
一、导包
将可能用到的包导入
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
二、读取数据
data= pd.read_excel(r'C:\Users\yysc\Desktop\tools\近一年会员消费金额.xlsx')
s1=data['消费金额']
print(data)
print(s1)
data:
ID 消费金额
0 2303261145034650 150.2
1 112101100036969 1062.8
2 2305132129519544 146.3
3 2111271415175978 5.7
4 110100800021813 2036.6
... ... ...
288842 2205221256421604 26.1
288843 2112251336015595 176.4
288844 200020204 1455.5
288845 108100100112899 399.4
288846 100028938 522.5
s1:
[288847 rows x 2 columns]
0 150.2
1 1062.8
2 146.3
3 5.7
4 2036.6
...
288842 26.1
288843 176.4
288844 1455.5
288845 399.4
288846 522.5
Name: 消费金额, Length: 288847, dtype: float64
三、画图:
数据是连续型的数据,并没有被分组,我使用条形图,画出一个不同区间的频率直方图,s1的作用是所需要的取数据列
fig,ax=plt.subplots()
n, bins, patches = ax.hist(s1,50,density=True) # density=True纵坐标可以显示概率密度
# print(n,bins,patches)
plt.show()
关于hist():
在这里,只创建了一个Axes,即ax,对其使用 hist() 方法进行绘图,该方法只需要传入x,不用传入y
1、hist() 方法的主要参数:
- x:使用的数据序列。
- bins:可选,它包含整数,序列或字符串,是将横坐标分成了几个区间
- range:可选,是 bins 的上下限。
- density:可选,bool值,为TRUE时显示概率密度
- ...等其他参数可参考官方文档:matplotlib.pyplot.hist — Matplotlib 3.7.2 documentation
2、hist() 方法的返回值:
hist() 方法会有3个返回值
- n:bins的值,每个bin里面元素数量;
- bins:返回每个bin的区间范围;
- patches:如这里的<BarContainer object of 50 artists>
(最后一点不咋明白,但总之 patches 可以是 Container 和多边形列表两种,Container 大概意思就是是一个类,然后包含符合 Artists 范围内的一些概念,如在 bar plots中 的 bars;多边形列表就比如一个包括这个直方图的50个柱子的列表,如有误解望指教改正)
可参考官方文档
四、结果:
五、错误检查:由于数据分布不均匀产生的图像显示问题
可以看出,结果很奇怪,先使用 ticklabel_format() 关闭科学计数法,再放大看看y轴的实际值:
ax.ticklabel_format(useOffset=False, style='plain') # 关闭科学计数法
n, bins, patches = ax.hist(s1,50) # 去掉density=TRUE,竖轴就是频数
plt.show()
这里显示,由0左右到20000这个区间的的数据有25万条以上,考虑是数据分布不均匀的问题,上面说到n可以返回每个 bin 里面元素的数量,输出一下:
print(n)
由上可知,在其他 bins 里面,存在1个或者2个数据,这与28万相比起来实在是太少了,所以在图像里面几乎显示不出来,看起来就只有一根柱子,放大多次之后可以看见其他区间其实还是有高度的:
在此将第一个区间之外的视作异常值,去掉,只筛选2000以内的数据
s1=data[data['消费金额']<=2000]['消费金额']
再进行绘制:
(这下合理多了)
六、调整:
(一)调整组距:
在统计学里确定组距和组数的方法:
组数:用极差/组距,或参考 Sturges 公式:n=1+3.33lgN (N为样本量)
组距:组距 =(最大值-最小值)/组数
数值为负的可能是退单情况,我选择金额在 0-2000 之间的数据进行绘制,分为20组(这个数据就是根据经验公式计算出来的,过程中竟然惊讶地发现很多区间的端点刚好是整百!!!)
s1=data.loc[(data['消费金额']<=2000) & (data['消费金额']>0),['消费金额']]
# 对比:
# s1=data[data.loc[(data['消费金额']<=2000) & (data['消费金额']>0),['消费金额']]]
# 这种会报错,要求里面是表达式,而data.loc[(data['消费金额']<=2000) & (data['消费金额']>0),['消费金额']]输出的是个object
fig,ax=plt.subplots()
n, bins, patches = ax.hist(s1,20,density=True) # density=True纵坐标可以显示概率密度
ax.ticklabel_format(useOffset=False, style='plain')
print(n,bins)
plt.show()
此时图像就基本完成了,
(二)自定义图像
因为 matplotlib 的功能实在过于强大,甚至可以自定义一套属于自己的绘图风格,所以在了解其一些基本概念后决定从实际需求出发来熟悉这个库
我想要达成的目标:
1、将柱子调整,每组柱子添加边框,并将柱子的颜色设置成为绿色:
edgecolor = "black":在hist方法中设置 edgecolor 参数即可设置边框的颜色了
color="lime":该参数控制直方图柱子的颜色
n, bins, patches = ax.hist(s1,20,density=True, edgecolor = "black",color="lime") # density=True纵坐标可以显示概率密度
2、调整横坐标数据,添加标签
- 使用 plt.xticks(ticks, labels) 修改横坐标
主要参数:
ticks:标签显示的位置的列表,传入空列表将清空轴上标签
labels:所给出标签位置上标记的记号,可以是人名,月份等,必须有 ticks 参数才能传入该参数
plt.xticks(range(0,2001,100))
效果如下:
- 使用 plt.xlable()/plt.ylable() 修改坐标轴名字:
plt.xlabel('消费金额')
plt.ylabel('占比')
得到:
介似嘛,中文并没有显示出来?
问题:Matplotlib绘图无法显示中文字体
解决:通过 plt.rcParams[ ] 方法修改 matplotlib 的配置文件,即 .rc 文件
plt.rcParams['font.sans-serif'] = 'SimHei' # 使图形中的中文正常编码显示
.rc 配置文件记录着绘图的风格,可以通过修改这自信文件形成一个属于自己的绘图风格,这里 rcParams 后必须跟 [ ] 不可以使用()号(不是很理解,大概是因为这是配置文件里的一个类似固定属性的东西?)
3、在每组 bin 上添加数据,并将这个视图的 title 改为:“会员消费区间分布”
- 通过 text() 方法添加每组数据的标签:
plt.text(x, y, s, fontsize, verticalalignment,horizontalalignment,rotation , kwargs)
主要参数:
x,y:标签添加的位置,注释文本内容所在位置的横/纵坐标,默认是根据坐标轴的数据来度量的,是绝对值,也就是说图中点所在位置的对应的值
s:标签的符号,字符串格式,比如你想加个“我爱python”,更多的是你标注跟数据有关的主体。
fontsize:加标签字体大小,取整数。
verticalalignment:垂直对齐方式 ,可选 ‘center’ ,‘top’ , ‘bottom’,‘baseline’ 等
horizontalalignment:水平对齐方式 ,可以填 ‘center’ , ‘right’ ,‘left’ 等
rotation:标签的旋转角度,以逆时针计算,取整
family :设置字体
style: 设置字体的风格
weight:设置字体的粗细
bbox:给字体添加框, 如 bbox=dict(facecolor=‘red’, alpha=0.5) 等。
string:注释文本内容
此处纵坐标 y 可以由 n 确定,横坐标 x 可以根据 bins 来确定,n 和 bins 都是数组,代表着什么前面已经了解过了,我现在只需要利用其下表,再写一个循环语句即可在每组 bins 上一个个标注数据了
print(type(bins),len(bins),bins[0],bins[20])
<class 'numpy.ndarray'> 21 6.66133814775094e-16 2000.0
for a in range(len(bins)-1):
ax.text(bins[a],n[a]+0.0001,'{}%'.format(round(n[a]*10000,3)))
注意:
range() 内的数字不要超过 bins 的长度,否则报错
纵坐标的设置要适当,若写为 n[a]+0.1 会距离柱子很远(因为纵坐标数值很小!),后面用格式化字符串传递我想要显示的内容
频率直方图中,纵轴表示频率除以组距的值(即单位距离的频率),每个矩形的高代表频率和组距的商,所以我们如果要显示其频率,则要乘以组间距100,按百分号显示,再乘以100。
关于这个统计小知识可以看看直方图的纵坐标为什么有时候是频率/组距? - 知乎
- 通过 title() 方法添加每组数据的标签:
plt.title('会员消费区间分布')
此时效果:
虽然不完美(可能纵坐标还需要再调整),但基本完成了,就先画到这里吧 !!
七、代码
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
plt.rcParams['font.sans-serif'] = 'SimHei' # 使图形中的中文正常编码显示
data= pd.read_excel(r'C:\Users\yysc\Desktop\tools\近一年会员消费金额.xlsx')
s1=data.loc[(data['消费金额']<=2000) & (data['消费金额']>0),['消费金额']]
fig,ax=plt.subplots()
n, bins, patches = ax.hist(s1,20, density=True,edgecolor = "black",color="lime")
# density=True纵坐标可以显示概率密度
plt.xticks(range(0,2001,100))
ax.ticklabel_format(useOffset=False, style='plain') # 关掉科学计数法
for a in range(len(bins)-1):
ax.text(bins[a],n[a]+0.0001,'{}%'.format(round(n[a]*10000,3)))
plt.xlabel('消费金额')
plt.ylabel('占比')
plt.title('会员消费区间分布')
plt.show()