[转载] 【数据处理】 python 极速极简画图——频数(率)分布直方图

参考链接: Python | 使用XlsxWriter模块在Excel工作表中绘制面积图

说明 

  当我们拿到数据的时候,第一时间就是想知道数据的特点,然鹅单个的数值如平均数、中位数仍不够直观,我们更想得到数据的分布,以便后续的工作,此时就可以采用频数(率)分布直方图。这里以我的一个实际问题为例,一步步讲如何得到想要的图,分为极简版、完整版和进阶版。   画图总结,同系列其他文章请浏览: 

【数据处理】 python 极速极简画图(黑白)——简单条形图、多维并列条形图【数据处理】 python 极速极简画图——频数(率)分布直方图【数据处理】 python 极速极简画图——二维连线、散点图【数据处理】 python 极速极简画图——折线图 

方法 

  用python实现直方图画法有很多种:    1. 纯python自己编写    2. matplotlib.pyplot,调hist    3. pandas 里DataFrame,调hist    4. seaborn里画   … 

  感兴趣的同学移步教你利用Python玩转histogram直方图的五种方法.这里主要展示基于matplotlib.pyplot的画法。 

数据 

  准备好一列数值型的数据,list或numpy.array都行。这里采用的是纽约一段时间内发布事件的生命周期,看其分布。下图为数据展示,147013是数据条数。  

极简版 

  以频数分布直方图为例,若要改成频率分布直方图,plt.hist(x,normed=True)即可,注意纵坐标不是概率,面积才是。 

plt.figure() #初始化一张图

x = life_cycle

plt.hist(x)  #直方图关键操作

plt.grid(alpha=0.5,linestyle='-.') #网格线,更好看 

plt.xlabel('Life Cycle /Month')  

plt.ylabel('Number of Events')  

plt.title(r'Life cycle frequency distribution histogram of events in New York') 

plt.show()

 

   可以看到总体分布特征呈长尾分布,这种分布的数据集中在“头部”,而“尾部”过长,越接近尾部的数量越少。   可以看出不加任何参数控制的极简版画图会有些问题: 

描述能力问题。对数据特征的表达不够细致,只能通过图得知,大部分事件的生命周期在0~20个月里,然鹅这很有可能是描述不准确的,因为看不到0-20月里更细致的分布(有可能大部分集中在更小的区间里)。刻度问题。长尾分布,尾过长导致刻度间隔过大,无法看出直方之间的刻度是多少。 

  下面针对这些问题进行微调。 

极简版微调 

  所有的微调都基于plt.hist()里的参数,所有参数如下: 

    matplotlib.pyplot.hist(  

    x, bins=10, range=None, normed=False,   

    weights=None, cumulative=False, bottom=None,   

    histtype=u'bar', align=u'mid', orientation=u'vertical',   

    rwidth=None, log=False, color=None, label=None, stacked=False,   

    hold=None, **kwargs) 

 

调“直方条”的个数 

  “直方条”的个数由参数bins决定,为了得到更细致的分布,我们调大点,这里我选择bins=50。    果然,更细致的分布往往更有意义,可以看出,数据更集中在最前面。但调整后,整张图显得“画布过大,而内容过少”,这也是由于尾太长导致的,我们可以看到大概55左右,一点蓝色的都没有了,这部分极少数据对我问题的意义不大,下面进行“截尾”。 

截尾 

  这里就选到55进行截尾,其本质就是忽略掉某个刻度以后的数据,这是由参数range来控制的,我们在hist()里添加range=(0,55)。    我们发现,横坐标最多显示的是50,但相应的直方图也发生了变化,看上去46~50部分又没有数据了,说明肉眼观察的始终不如数据输出来真,那么我们有办法知道每一个直方条的长、宽吗?   事实上,hist函数是有返回值的,我们接收一下它的返回值,并输出看看。 

n, bins, patches = plt.hist(x,bins = 50,range=(0,55)) 

print(n)

print(bins)

print(patches)

 

   n对应着纵坐标的值,bins对应横坐标的值,patches是具体的条形对象。根据n和bins我们就可以进行调试,通常而言,我们希望bins对应的是整数,而n不能太小否则显示不出来。   如果bins要是整数,那么直方条数bins和截尾长度range要一致,我们设bins=55,range=(0,55),输出看看。     我们希望刻度也能更细致的显示出来,下面我们来解决刻度问题。 

加刻度 

  刻度得通过plt.xticks调,即加上下面这条语句,表示刻度从0开始,按1构成等差数列一直到55. 

plt.xticks(np.arange(0,55,1))

 

   显然,刻度虽然加上去了,但是因为刻度太密了,图就那么大,都挤在一起非常难看。有多种方法解决: 

把画布变大刻度间隔变大,如0,2,4,6,…继续截尾 

完整版 

  此时,我的做法是结合2、3,继续调整bins和range,并把刻度间隔变为2,在有限的图的大小下,尽量美观。最终调的是bins=40,range=(0,40),np.arrange(0,40,2)。 

plt.figure() #初始化一张图

x = life_cycle

width = 40 #整合成一个参数

n, bins, patches = plt.hist(x,bins = width,range=(0,width)) 

#print(n)

#print(bins)

#print(patches) 

plt.grid(alpha=0.5,linestyle='-.') #网格线,更好看 

plt.xlabel('Life Cycle /Month')  

plt.ylabel('Number of Events')  

plt.title(r'Life cycle frequency distribution histogram of events in New York') #+citys[i])  

plt.xticks(np.arange(0,width,2))

plt.show()

 

   这下我的图从功能上就比较完善了,较好的说明大部分的事件的生命周期都只在2个月内。之前讲了,这个分布是长尾分布,那能不能用一条曲线来拟合我的图像呢? 

进阶之路 

拟合 

  前文讲的这个曲线,就是经常说的“核”,耳熟能详的有什么高斯核、伽马核等等,用核函数进行拟合的话,seaborn比较好用,请移步: Python可视化 | Seaborn5分钟入门(一)——kdeplot和distplot。此处我们仅实现将直方图的中点进行折线连线。关键代码: 

plt.plot(bins[0:width]+((bins[1]-bins[0])/2.0),n,color='red')#利用返回值来绘制区间中点连线

 

  实现方法就是连线,效果如下:    再放一张长尾分布的图进行对比,这就很直观了。  

美颜 

  美颜主要改的要么是颜色,要么是透明度。我的审美em…大家自己调把。 

plt.figure() #初始化一张图

x = life_cycle

width = 40

n, bins, patches = plt.hist(x,bins = width,range=(0,width),color='blue',alpha=0.5) 

#print(n)

#print(bins)

#print(patches)

plt.grid(alpha=0.5,linestyle='-.') #网格线,更好看 

plt.xlabel('Life Cycle /Month')  

plt.ylabel('Number of Events')  

plt.title(r'Life cycle frequency distribution histogram of events in New York') #+citys[i])  

plt.xticks(np.arange(0,width,2))

plt.plot(bins[0:width]+((bins[1]-bins[0])/2.0),n,color='red')#利用返回值来绘制区间中点连线

plt.show()

上一篇:博雅数据机器学习03


下一篇:opencv-直方图