2010-03-03 79 views
127

我有一组X,Y数据点(大约10k),这些数据点很容易作为散点图进行绘制,但我想将其表示为热图。使用散布数据集在MatPlotLib中生成热图

我翻看了MatPlotLib中的例子,它们似乎都已经开始使用热图单元格值来生成图像。

有没有一种方法可以将一堆x,y,所有不同的东西都转换成热图(其中x,y频率更高的区域会变得更暖和)?

回答

134

如果你不想六边形,您可以使用numpy的的histogram2d功能:

import numpy as np 
import numpy.random 
import matplotlib.pyplot as plt 

# Generate some test data 
x = np.random.randn(8873) 
y = np.random.randn(8873) 

heatmap, xedges, yedges = np.histogram2d(x, y, bins=50) 
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] 

plt.clf() 
plt.imshow(heatmap.T, extent=extent, origin='lower') 
plt.show() 

这使得50×50热图。如果你想要的话,比如512x384,你可以在histogram2d的电话中输入bins=(512, 384)

例子:Matplotlib heat map example

+1

我的意思不是一个白痴,但是你怎么将这个输出结果输出到一个PNG/PDF文件,而不是只显示在一个交互式的IPython会话中?我试图把它作为某种普通的'axes'实例,我可以在其中添加一个标题,轴标签等,然后执行普通的'savefig()',就像我为任何其他典型的matplotlib绘图所做的那样。 – gotgenes 2011-07-15 19:19:45

+3

@gotgenes:不是'plt.savefig('filename.png')'工作吗?如果你想获得一个轴实例,使用Matplotlib的面向对象接口:'fig = plt.figure()''ax = fig.gca()''ax.imshow(...)''fig.savefig(。 ..)' – ptomato 2011-07-16 17:05:32

+1

的确,谢谢!我想我不完全明白'imshow()'与scatter()'在同一类函数上。我真的不明白为什么'imshow()'将一个2d浮点数组转换成适当颜色的块,而我明白'scatter()'应该对这样一个数组做些什么。 – gotgenes 2011-07-21 19:10:24

2

制作一个二维数组,对应于最终图像中的单元格,称为heatmap_cells并将其实例化为全零。

选择两个比例因子,用于定义每个数组元素在实际单位中的差异,如x_scaley_scale。选择这些,使所有的数据点落在热图数组的范围内。

对于每个数据点的原始与x_valuey_value

heatmap_cells[floor(x_value/x_scale),floor(y_value/y_scale)]+=1

+4

numpy的具有该功能... – ptomato 2010-03-17 09:22:34

94

Matplotlib词汇,我想你想一个hexbin情节。

如果你不熟悉这种类型的情节,它只是一个双变量直方图其中xy平面是由一个规则的六边形网格细分。因此,从直方图中,您可以统计每个六边形中落入的点的数量,将绘图区域离散化为一组,将每个点分配给其中一个窗口;最后,将窗口映射到颜色数组,并且您有一个六进制图表。

虽然不太常用的比例如,圆形,或正方形,该六边形是用于合并容器的几何形状较好的选择是直观的:

  • 六边形具有最近邻对称性(例如,方箱不这样做, 例如,从距离一个正方形的边框一个点点 那个广场里面是不是到处相等)和

  • 六边形是给出的最高n多边形普通平面 镶嵌(即,您可以安全地用六角形瓷砖重新塑造您的厨房地板,因为当您完成后,瓷砖之间不会有任何空隙空间 - 对于所有其他更高-n,n> = 7的多边形不是正确的)。

Matplotlib使用术语hexbin情节;所以做(据我所知)所有plotting libraries[R;还有我不知道这是否是对本地块普遍接受的术语类型,但我怀疑这是可能的因为hexbin是短期的六边形合并,这是描述在准备数据用于显示的必要步骤。)


from matplotlib import pyplot as PLT 
from matplotlib import cm as CM 
from matplotlib import mlab as ML 
import numpy as NP 

n = 1e5 
x = y = NP.linspace(-5, 5, 100) 
X, Y = NP.meshgrid(x, y) 
Z1 = ML.bivariate_normal(X, Y, 2, 2, 0, 0) 
Z2 = ML.bivariate_normal(X, Y, 4, 1, 1, 1) 
ZD = Z2 - Z1 
x = X.ravel() 
y = Y.ravel() 
z = ZD.ravel() 
gridsize=30 
PLT.subplot(111) 

# if 'bins=None', then color of each hexagon corresponds directly to its count 
# 'C' is optional--it maps values to x-y coordinates; if 'C' is None (default) then 
# the result is a pure 2D histogram 

PLT.hexbin(x, y, C=z, gridsize=gridsize, cmap=CM.jet, bins=None) 
PLT.axis([x.min(), x.max(), y.min(), y.max()]) 

cb = PLT.colorbar() 
cb.set_label('mean value') 
PLT.show() 

enter image description here

+0

是什么意思说:“六边形有近邻对称性”?你说“从广场边界上的一个点到广场内的一个点的距离并不是每个地方都是平等的”,而是距离什么? – Jaan 2014-04-11 16:04:58

+8

对于一个六边形,从中心到顶点连接两边的距离也比从一边的中心到中间要长,只有比率较小(2/sqrt(3)≈1.15六边形对sqrt(2)≈ 1.41)。从中心到边界上每个点的距离相等的唯一形状是圆。 – Jaan 2014-05-25 18:46:42

+3

@Jaan对于六边形,每个邻居都在相同的距离。 8邻居或4邻居没有问题。没有对角线的邻居,只有一种邻居。 – isarandi 2015-03-08 16:06:10

18

如果您正在使用的1.2.x

 
x = randn(100000) 
y = randn(100000) 
hist2d(x,y,bins=100); 

enter image description here

20

而不是使用np.hist2d,这在一般产生相当难看直方图的,我想回收py-sphviewer,一个用于使用自适应平滑内核渲染粒子模拟的python包,可以从pip轻松安装(请参阅网页文档)。考虑下面的代码,这是基于例如:

import numpy as np 
import numpy.random 
import matplotlib.pyplot as plt 
import sphviewer as sph 

def myplot(x, y, nb=32, xsize=500, ysize=500): 
    xmin = np.min(x) 
    xmax = np.max(x) 
    ymin = np.min(y) 
    ymax = np.max(y) 

    x0 = (xmin+xmax)/2. 
    y0 = (ymin+ymax)/2. 

    pos = np.zeros([3, len(x)]) 
    pos[0,:] = x 
    pos[1,:] = y 
    w = np.ones(len(x)) 

    P = sph.Particles(pos, w, nb=nb) 
    S = sph.Scene(P) 
    S.update_camera(r='infinity', x=x0, y=y0, z=0, 
        xsize=xsize, ysize=ysize) 
    R = sph.Render(S) 
    R.set_logscale() 
    img = R.get_image() 
    extent = R.get_extent() 
    for i, j in zip(xrange(4), [x0,x0,y0,y0]): 
     extent[i] += j 
    print extent 
    return img, extent 

fig = plt.figure(1, figsize=(10,10)) 
ax1 = fig.add_subplot(221) 
ax2 = fig.add_subplot(222) 
ax3 = fig.add_subplot(223) 
ax4 = fig.add_subplot(224) 


# Generate some test data 
x = np.random.randn(1000) 
y = np.random.randn(1000) 

#Plotting a regular scatter plot 
ax1.plot(x,y,'k.', markersize=5) 
ax1.set_xlim(-3,3) 
ax1.set_ylim(-3,3) 

heatmap_16, extent_16 = myplot(x,y, nb=16) 
heatmap_32, extent_32 = myplot(x,y, nb=32) 
heatmap_64, extent_64 = myplot(x,y, nb=64) 

ax2.imshow(heatmap_16, extent=extent_16, origin='lower', aspect='auto') 
ax2.set_title("Smoothing over 16 neighbors") 

ax3.imshow(heatmap_32, extent=extent_32, origin='lower', aspect='auto') 
ax3.set_title("Smoothing over 32 neighbors") 

#Make the heatmap using a smoothing over 64 neighbors 
ax4.imshow(heatmap_64, extent=extent_64, origin='lower', aspect='auto') 
ax4.set_title("Smoothing over 64 neighbors") 

plt.show() 

产生如下图:

enter image description here

正如你看到的,图像看起来相当不错,我们能够确定不同的子结构。这些图像被构造为对于某个域内的每个点扩展给定的权重,由平滑长度定义,这又由邻近的距离给出(我已经选择了16,32和64的例子)。因此,与较低密度区域相比,较高密度区域通常遍布较小区域。

函数myplot只是一个非常简单的函数,我为了给x,y数据提供py-sphviewer来做魔术。

+1

对于任何想在OSX上安装py-sphviewer的人的评论:我有很多困难,请参阅:https://github.com/alejandrobll/py-sphviewer/issues/3 – 2017-06-27 12:11:22

9

Seaborn现在有jointplot function应该很好的工作在这里:

import numpy as np 
import seaborn as sns 
import matplotlib.pyplot as plt 

# Generate some test data 
x = np.random.randn(8873) 
y = np.random.randn(8873) 

sns.jointplot(x=x, y=y, kind='hex') 
plt.show() 

demo image

+0

简单,漂亮和分析有用。 – ryanjdillon 2017-03-10 09:30:30

+0

@wordsforthewise如何使用此功能使600k数据在视觉上可读? (如何调整大小) – nrmb 2017-05-22 09:43:29

+0

我不太清楚你的意思;也许最好你问一个单独的问题,并把它链接到这里。你的意思是调整整个无花果?首先用'fig = plt.figure(figsize =(12,12))'制作图形,然后用'ax = plt.gca()'获得当前轴,然后将参数'ax = ax'添加到' jointplot'功能。 – wordsforthewise 2017-05-22 21:11:47

3

我知道这是一个老问题,但想添加一些亚历杭德罗的anwser:如果你想有一个很好的平滑没有使用py-sphviewer的图像,你可以改为使用np.histogram2d并应用高斯过滤器(从scipy.ndimage.filters)到热图:

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.cm as cm 
from scipy.ndimage.filters import gaussian_filter 


def myplot(x, y, s, bins=1000): 
    heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins) 
    heatmap = gaussian_filter(heatmap, sigma=s) 

    extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] 
    return heatmap.T, extent 


fig, axs = plt.subplots(2, 2) 

# Generate some test data 
x = np.random.randn(1000) 
y = np.random.randn(1000) 

sigmas = [0, 16, 32, 64] 

for ax, s in zip(axs.flatten(), sigmas): 
    if s == 0: 
     ax.plot(x, y, 'k.', markersize=5) 
     ax.set_title("Scatter plot") 
    else: 
     img, extent = myplot(x, y, s) 
     ax.imshow(img, extent=extent, origin='lower', cmap=cm.jet) 
     ax.set_title("Smoothing with $\sigma$ = %d" % s) 

plt.show() 

产地:

Output images

+1

喜欢这个。图表和Alejandro的答案一样好,但不需要新的软件包。 – 2017-11-30 21:16:03