编程语言
首页 > 编程语言> > python – 通过从3D数组中采样和分组来创建热图

python – 通过从3D数组中采样和分组来创建热图

作者:互联网

我有一些像这样存在的实验数据:

x = array([1, 1.12, 1.109, 2.1, 3, 4.104, 3.1, ...])
y = array([-9, -0.1, -9.2, -8.7, -5, -4, -8.75, ...])
z = array([10, 4, 1, 4, 5, 0, 1, ...])

如果方便的话,我们可以假设数据存在为3D数组甚至是pandas DataFrame:

df = pd.DataFrame({'x': x, 'y': y, 'z': z})

解释是,对于每个位置x [i],y [i],某个变量的值是z [i].这些不是均匀采样的,因此将存在一些“密集采样”的部分(例如,在x中介于1和1.2之间)和其他非常稀疏的部分(例如,在x中介于2和3之间).因此,我不能将它们放入pcolormesh或contourf中.

我想做的是在某个固定的时间间隔内均匀地重新采样x和y,然后聚合z的值.根据我的需要,z可以求和或平均得到有意义的值,所以这不是问题.我天真的尝试是这样的:

X = np.arange(min(x), max(x), 0.1)  
Y = np.arange(min(y), max(y), 0.1)
x_g, y_g = np.meshgrid(X, Y)
nx, ny = x_g.shape
z_g = np.full(x_g.shape, np.nan)

for ix in range(nx - 1):
    for jx in range(ny - 1):
        x_min = x_g[ix, jx]
        x_max = x_g[ix + 1, jx + 1]
        y_min = y_g[ix, jx]
        y_max = y_g[ix + 1, jx + 1]
        vals = df[(df.x >= x_min) & (df.x < x_max) & 
                  (df.y >= y_min) & (df.y < y_max)].z.values
        if vals.any():
            z_g[ix, jx] = sum(vals)

这是有效的,我得到了我想要的输出,用plt.contourf(x_g,y_g,z_g),但它很慢!我有大约20k个样本,然后我将其子样本分成~x个中的~800个样本和y中的~500个样本,意味着for循环长度为400k.

有没有办法对它进行矢量化/优化?如果有一些功能已经做到这一点,那就更好了!

(还将其标记为MATLAB,因为numpy / MATLAB之间的语法非常相似,我可以访问这两个软件.)

解决方法:

这是一个矢量化的Python解决方案,采用NumPy broadcasting和矩阵乘法,np.dot为减少部分 –

x_mask = ((x >= X[:-1,None]) & (x < X[1:,None]))
y_mask = ((y >= Y[:-1,None]) & (y < Y[1:,None]))

z_g_out = np.dot(y_mask*z[None].astype(np.float32), x_mask.T)

# If needed to fill invalid places with NaNs
z_g_out[y_mask.dot(x_mask.T.astype(np.float32))==0] = np.nan

请注意,我们在那里避免使用meshgrid.因此,在那里节省内存作为使用meshgrid创建的网格将是巨大的,并且在此过程中希望获得性能改进.

标杆

# Original app
def org_app(x,y,z):    
    X = np.arange(min(x), max(x), 0.1)  
    Y = np.arange(min(y), max(y), 0.1)
    x_g, y_g = np.meshgrid(X, Y)
    nx, ny = x_g.shape
    z_g = np.full(np.asarray(x_g.shape)-1, np.nan)

    for ix in range(nx - 1):
        for jx in range(ny - 1):
            x_min = x_g[ix, jx]
            x_max = x_g[ix + 1, jx + 1]
            y_min = y_g[ix, jx]
            y_max = y_g[ix + 1, jx + 1]
            vals = z[(x >= x_min) & (x < x_max) & 
                      (y >= y_min) & (y < y_max)]
            if vals.any():
                z_g[ix, jx] = sum(vals)
    return z_g

# Proposed app
def app1(x,y,z):
    X = np.arange(min(x), max(x), 0.1)  
    Y = np.arange(min(y), max(y), 0.1)
    x_mask = ((x >= X[:-1,None]) & (x < X[1:,None]))
    y_mask = ((y >= Y[:-1,None]) & (y < Y[1:,None]))

    z_g_out = np.dot(y_mask*z[None].astype(np.float32), x_mask.T)

    # If needed to fill invalid places with NaNs
    z_g_out[y_mask.dot(x_mask.T.astype(np.float32))==0] = np.nan
    return z_g_out

如图所示,对于公平的基准测试,我使用原始方法的数组值,因为从数据帧中获取值可能会减慢速度.

时间和验证 –

In [143]: x = np.array([1, 1.12, 1.109, 2.1, 3, 4.104, 3.1])
     ...: y = np.array([-9, -0.1, -9.2, -8.7, -5, -4, -8.75])
     ...: z = np.array([10, 4, 1, 4, 5, 0, 1])
     ...: 

# Verify outputs
In [150]: np.nansum(np.abs(org_app(x,y,z) - app1(x,y,z)))
Out[150]: 0.0

In [145]: %timeit org_app(x,y,z)
10 loops, best of 3: 19.9 ms per loop

In [146]: %timeit app1(x,y,z)
10000 loops, best of 3: 39.1 µs per loop

In [147]: 19900/39.1  # Speedup figure
Out[147]: 508.95140664961633

标签:python,matplotlib,matlab,numpy,contourf
来源: https://codeday.me/bug/20190627/1306409.html