我有一些像这样存在的实验数据:
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之间)和其他非常稀疏的部分(例如2到3之间)在x
)。因此,我不能将它们放入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之间的语法非常相似,我可以访问这两个软件。)
答案 0 :(得分:2)
这是一个矢量化的Python解决方案,使用NumPy broadcasting
和matrix multiplication
,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
答案 1 :(得分:1)
这是一个MATLAB解决方案:
X = min(x)-1 :.1:max(x)+1; % the grid needs to be expanded slightly beyond the min and max
Y = min(y)-1 :.1:max(y)+1;
x_o = interp1(X, 1:numel(X), x, 'nearest');
y_o = interp1(Y, 1:numel(Y), y, 'nearest');
z_g = accumarray([x_o(:) y_o(:)], z(:),[numel(X) numel(Y)]);