我有一组X,Y数据点(大约10k),很容易绘制成散点图,但我想用热图来表示。

我查看了Matplotlib中的示例,它们似乎都已经从热图单元值开始生成图像。

有没有一种方法可以将一堆不同的x, y转换为热图(其中x, y频率较高的区域会“更温暖”)?


当前回答

编辑:为了更好地近似Alejandro的答案,请看下面。

我知道这是一个老问题,但想在Alejandro的回答中添加一些东西:如果你想要一个漂亮的平滑图像而不使用py-sphviewer,你可以使用np。Histogram2d,并对热图应用高斯滤波器(from scipy. nmage .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()

生产:

Agape Gal'lo的散点图和s=16相互叠加(点击查看更好的视图):


我注意到我的高斯滤波方法和亚历杭德罗的方法的一个区别是,他的方法显示局部结构比我的好得多。因此,我在像素级上实现了一个简单的最近邻方法。该方法为每个像素计算数据中n个最近点距离的逆和。这种方法的分辨率很高,计算成本很高,我认为有更快的方法,所以如果你有任何改进,请告诉我。

更新:正如我所怀疑的,有一个更快的方法使用Scipy的Scipy . ckdtree。关于实现,请参阅Gabriel的回答。

总之,这是我的代码:

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


def data_coord2view_coord(p, vlen, pmin, pmax):
    dp = pmax - pmin
    dv = (p - pmin) / dp * vlen
    return dv


def nearest_neighbours(xs, ys, reso, n_neighbours):
    im = np.zeros([reso, reso])
    extent = [np.min(xs), np.max(xs), np.min(ys), np.max(ys)]

    xv = data_coord2view_coord(xs, reso, extent[0], extent[1])
    yv = data_coord2view_coord(ys, reso, extent[2], extent[3])
    for x in range(reso):
        for y in range(reso):
            xp = (xv - x)
            yp = (yv - y)

            d = np.sqrt(xp**2 + yp**2)

            im[y][x] = 1 / np.sum(d[np.argpartition(d.ravel(), n_neighbours)[:n_neighbours]])

    return im, extent


n = 1000
xs = np.random.randn(n)
ys = np.random.randn(n)
resolution = 250

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

for ax, neighbours in zip(axes.flatten(), [0, 16, 32, 64]):
    if neighbours == 0:
        ax.plot(xs, ys, 'k.', markersize=2)
        ax.set_aspect('equal')
        ax.set_title("Scatter Plot")
    else:
        im, extent = nearest_neighbours(xs, ys, resolution, neighbours)
        ax.imshow(im, origin='lower', extent=extent, cmap=cm.jet)
        ax.set_title("Smoothing over %d neighbours" % neighbours)
        ax.set_xlim(extent[0], extent[1])
        ax.set_ylim(extent[2], extent[3])
plt.show()

结果:

其他回答

恐怕我来晚了一点,但我之前也有一个类似的问题。接受的答案(@ptomato)帮助了我,但我也想张贴这个,以防它对某人有用。


''' I wanted to create a heatmap resembling a football pitch which would show the different actions performed '''

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

#fixing random state for reproducibility
np.random.seed(1234324)

fig = plt.figure(12)
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)

#Ratio of the pitch with respect to UEFA standards 
hmap= np.full((6, 10), 0)
#print(hmap)

xlist = np.random.uniform(low=0.0, high=100.0, size=(20))
ylist = np.random.uniform(low=0.0, high =100.0, size =(20))

#UEFA Pitch Standards are 105m x 68m
xlist = (xlist/100)*10.5
ylist = (ylist/100)*6.5

ax1.scatter(xlist,ylist)

#int of the co-ordinates to populate the array
xlist_int = xlist.astype (int)
ylist_int = ylist.astype (int)

#print(xlist_int, ylist_int)

for i, j in zip(xlist_int, ylist_int):
    #this populates the array according to the x,y co-ordinate values it encounters 
    hmap[j][i]= hmap[j][i] + 1   

#Reversing the rows is necessary 
hmap = hmap[::-1]

#print(hmap)
im = ax2.imshow(hmap)


这是结果

下面是我在100万个点集上做的一个,有3个类别(红色、绿色和蓝色)。如果您想尝试这个功能,这里有一个到存储库的链接。Github回购

histplot(
    X,
    Y,
    labels,
    bins=2000,
    range=((-3,3),(-3,3)),
    normalize_each_label=True,
    colors = [
        [1,0,0],
        [0,1,0],
        [0,0,1]],
    gain=50)

如果您正在使用1.2.x

import numpy as np
import matplotlib.pyplot as plt

x = np.random.randn(100000)
y = np.random.randn(100000)
plt.hist2d(x,y,bins=100)
plt.show()

最初的问题是…如何将散点值转换为网格值? Histogram2d确实计算每个单元格的频率,但是,如果每个单元格除了频率之外还有其他数据,则需要做一些额外的工作。

x = data_x # between -10 and 4, log-gamma of an svc
y = data_y # between -4 and 11, log-C of an svc
z = data_z #between 0 and 0.78, f1-values from a difficult dataset

我有一个数据集,X和Y坐标的z结果。然而,我计算的是兴趣区域之外的几个点(大的差距),而在一个小的兴趣区域内的一堆点。

是的,在这里它变得更困难,但也更有趣。一些库(抱歉):

from matplotlib import pyplot as plt
from matplotlib import cm
import numpy as np
from scipy.interpolate import griddata

Pyplot是我今天的图形引擎, Cm是一个彩色地图的范围,有一些有趣的选择。 Numpy来计算, 和griddata用于将值附加到固定网格。

最后一点很重要,因为xy点的频率在我的数据中不是均匀分布的。首先,让我们从适合我的数据和任意网格大小的边界开始。原始数据的数据点也在这些x和y边界之外。

#determine grid boundaries
gridsize = 500
x_min = -8
x_max = 2.5
y_min = -2
y_max = 7

所以我们已经定义了一个在x和y的最小值和最大值之间有500像素的网格。

在我的数据中,在高度感兴趣的领域,有超过500个可用值;而在低兴趣区域,整个网格中甚至没有200个值;在x_min和x_max的图形边界之间就更少了。

因此,要得到一张漂亮的图片,任务就是求出高兴趣值的平均值,并填补其他地方的空白。

我现在定义我的网格。对于每一对xx-yy,我想有一个颜色。

xx = np.linspace(x_min, x_max, gridsize) # array of x values
yy = np.linspace(y_min, y_max, gridsize) # array of y values
grid = np.array(np.meshgrid(xx, yy.T))
grid = grid.reshape(2, grid.shape[1]*grid.shape[2]).T

为什么会有这么奇怪的形状?scipy。griddata需要一个(n, D)的形状。

Griddata通过预定义的方法计算网格中的每个点的值。 我选择“最近”-空网格点将被来自最近邻居的值填充。这看起来好像信息较少的区域有更大的细胞(即使事实并非如此)。人们可以选择插值“线性”,那么信息较少的区域看起来不那么清晰。这是品味问题,真的。

points = np.array([x, y]).T # because griddata wants it that way
z_grid2 = griddata(points, z, grid, method='nearest')
# you get a 1D vector as result. Reshape to picture format!
z_grid2 = z_grid2.reshape(xx.shape[0], yy.shape[0])

跳跃时,我们交给matplotlib来显示图

fig = plt.figure(1, figsize=(10, 10))
ax1 = fig.add_subplot(111)
ax1.imshow(z_grid2, extent=[x_min, x_max,y_min, y_max,  ],
            origin='lower', cmap=cm.magma)
ax1.set_title("SVC: empty spots filled by nearest neighbours")
ax1.set_xlabel('log gamma')
ax1.set_ylabel('log C')
plt.show()

在v型的尖端部分,你可以看到,我在寻找最佳点的过程中做了很多计算,而几乎所有其他地方的不太有趣的部分都有较低的分辨率。

Seaborn现在有了jointplot函数,它应该在这里工作得很好:

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()