[英]Efficiently select elements from an (x,y) field with a 2D mask in Python
我有一个很大的二维位置数据字段,以两个数组x
和y
,其中len(x) == len(y)
。 我想返回索引idx_masked
的数组,其中(x[idx_masked], y[idx_masked])
被称为mask
的 N x N int
数组mask
。 也就是说, mask[x[idx_masked], y[idx_masked]] == 1
。 mask
数组仅由0
s 和1
s 组成。
我想出了以下解决方案,但它(特别是下面的最后一行)非常慢,因为我有 N x N = 5000 x 5000,重复了 1000 次:
import numpy as np
import matplotlib.pyplot as plt
# example mask of one corner of a square
N = 100
mask = np.zeros((N, N))
mask[0:10, 0:10] = 1
# example x and y position arrays in arbitrary units
x = np.random.uniform(0, 1, 1000)
y = np.random.uniform(0, 1, 1000)
x_bins = np.linspace(np.min(x), np.max(x), N)
y_bins = np.linspace(np.min(y), np.max(y), N)
x_bin_idx = np.digitize(x, x_bins)
y_bin_idx = np.digitize(y, y_bins)
idx_masked = np.ravel(np.where(mask[y_bin_idx - 1, x_bin_idx - 1] == 1))
plt.imshow(mask[::-1, :])
plt.scatter(x, y, color='red')
plt.scatter(x[idx_masked], y[idx_masked], color='blue')
有没有更有效的方法来做到这一点?
鉴于该mask
用相同大小的 bin 覆盖了您的字段,您无需明确定义 bin。 *_bin_idx
可以通过简单的楼层划分在每个位置确定,因为您知道每个 bin 的大小为1 / N
我建议使用1 - 0
作为总宽度(你传递给np.random.uniform
)而不是x.max() - x.min()
,当然如果你知道范围的预期大小。
x0 = 0 # or x.min()
x1 = 1 # or x.max()
x_bin = (x1 - x0) / N
x_bin_idx = ((x - x0) // x_bin).astype(int)
# ditto for y
这将比数字化更快、更简单,并避免在开始时出现额外的 bin。
大多数情况下,您不需要np.where
。 90% 的问题(包括这个)不应该使用where
。 如果您想快速访问x
和y
的必要元素,只需使用布尔掩码。 面膜简直了
selction = mask[x_bin_idx, y_bin_idx].astype(bool)
如果mask
已经是一个布尔值(无论如何它应该是),表达式mask[x_bin_idx, y_bin_idx]
就足够了。 它会生成一个与x_bin_idx
和y_bin_idx
大小相同的数组(与x
和y
大小相同),其中包含每个点的掩码值。 您可以将面膜用作
x[selection] # Elements of x in mask
y[selection] # Elements of y in mask
如果您绝对需要整数索引,那么where
不是您的最佳选择。
indices = np.flatnonzero(selection)
或者
indices = selection.nonzero()[0]
如果您的目标只是从x
和y
提取值,我建议将它们堆叠到一个数组中:
coords = np.stack((x, y), axis=1)
这样,您不必两次应用索引,只需使用以下命令即可提取值
coords[selection, :]
或者
coords[indices, :]
根据mask
和x
和y
的相对密度,布尔掩码或线性索引可能更快。 您将不得不计时一些相关案例以获得更好的直觉。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.