【问题标题】:Efficient way to map 3D function to a meshgrid with NumPy使用 NumPy 将 3D 函数映射到网格网格的有效方法
【发布时间】:2021-12-27 17:56:20
【问题描述】:

我有一组标量 3D 函数的数据值,它们排列为形状为 (n,3) 的数组中的输入 x,y,z 和形状为 (n,) 的数组中的函数值 f(x,y,z)

编辑:例如,考虑以下简单函数

data = np.array([np.arange(n)]*3).T
F = np.linalg.norm(data,axis=1)**2

我想将此函数与球核进行卷积,以执行 3D 平滑。我发现执行此操作的最简单方法是将函数值映射到 3D 空间网格中,然后使用我想要的内核应用 3D 卷积。

这很好用,但是将 3D 函数映射到 3D 网格的部分非常慢,因为我没有找到仅使用 NumPy 的方法。下面的代码是我的实际实现,其中data(n,3) 数组,其中包含函数被评估的3D 位置,F(n,) 数组,其中包含函数的相应值,M 是包含 3D 空间网格的 (N,N,N) 数组。

step = 0.1

# Create meshgrid
xmin = data[:,0].min()
xmax = data[:,0].max()
ymin = data[:,1].min()
ymax = data[:,1].max()
zmin = data[:,2].min()
zmax = data[:,2].max()

x = np.linspace(xmin,xmax,int((xmax-xmin)/step)+1)
y = np.linspace(ymin,ymax,int((ymax-ymin)/step)+1)
z = np.linspace(zmin,zmax,int((zmax-zmin)/step)+1)


# Build image
M = np.zeros((len(x),len(y),len(z)))

for l in range(len(data)):
    for i in range(len(x)-1):
        if x[i] < data[l,0] < x[i+1]:
            for j in range(len(y)-1):
                if y[j] < data[l,1] < y[j+1]:
                    for k in range(len(z)-1):
                        if z[k] < data[l,2] < z[k+1]:
                            M[i,j,k] = F[l]

有没有更有效的方法来用 3D 函数的值填充 3D 空间网格?

【问题讨论】:

  • 我不是很清楚这个问题,没有一些数据样本很难进行一些测试..但是您是否尝试过np.meshgrid创建de 3D网格?
  • “3D 功能”在哪里?所有这些if 将使执行“整个数组”操作变得困难。这些本质上是标量。
  • 我已经更新了我的问题,希望现在可以解决。 “3D 函数”是指以 3D 向量作为输入的标量函数。
  • 请注意,由于条件:The truth value of an array with more than one element is ambiguous. Use a.any() or a.all(),当前代码不起作用。
  • @JérômeRichard 此代码在我的机器上执行没有问题(刚刚测试过)。

标签: python numpy performance


【解决方案1】:

对于每一项数据,您都在扫描长方体的像素以检查它是否在里面。有一个选项可以跳过此扫描。您可以自己计算这些像素的对应索引,例如:

data = np.array([[1, 2, 3], #14 (corner1)
                 [4, 5, 6], #77 (corner2)
                 [2.5, 3.5, 4.5], #38.75 (duplicated pixel)
                 [2.9, 3.9, 4.9], #47.63 (duplicated pixel)
                 [1.5, 2, 3]]) #15.25 (one step up from [1, 2, 3])
step = 0.5                            
data_idx = ((data - data.min(axis=0))//step).astype(int)
M = np.zeros(np.max(data_idx, axis=0) + 1)
x, y, z = data_idx.T
M[x, y, z] = F

请注意,只有一个重复像素值被映射到 M。

【讨论】:

  • 你能详细说明一下这是做什么的吗?我对其进行了测试,但无法达到与上面的代码相同的结果。
  • 我觉得和原代码的区别在于对比。此方法使用截断来有效地设置值。这相当于检查x[i] &lt;= data[l,0] &lt; x[i+1](其他维度也一样)。原代码测试 if x[i] &lt; data[l,0] &lt; x[i+1]。请注意缺少的下限=。我不确定这是有意的,但我觉得奇怪的是原始代码排除了值,因为输入只包含整数。结果M在计算原始代码后总是为零(即循环什么都不做)......
  • @user14900935 的回答基本一样,但是帮我澄清了你的解决方案,谢谢你们!
【解决方案2】:

您只需将F[:, 3](仅 f(x, y, z))重塑为网格。没有样本数据很难更精确:

如果数据没有排序,则需要排序:

F_sorted = F[np.lexsort((F[:,0], F[:,1], F[:,2]))]  # sort by x, then y, then z

只选择 f(x, y, z)

F_values = F_sorted[:, 3]

最后,将数据重塑为网格:

M = F_sorted.reshape(N, N, N)

【讨论】:

  • 您的答案提供了一种方法,可以将形状 (N,) 的函数值通过 3D 向量转换为形状规则的网格 (N,3) 到排序的 3D 网格 (n,n,n),其中 n=N**(1/3) .虽然我的问题有点不同,但这可能对其他人有用。
【解决方案3】:

这种方法比原来的方法更快(大约快 20 倍):

step = 0.1
mins = np.min(data, axis=0)
maxs = np.max(data, axis=0)
ranges = np.floor((maxs - mins) / step + 1).astype(int)
indx = np.zeros(data.shape,dtype=int)
for i in range(3):
    x = np.linspace(mins[i], maxs[i], ranges[i])
    indx[:,i] = np.argmax(data[:,i,np.newaxis] <= (x[np.newaxis,:]), axis=1) -1
    
M = np.zeros(ranges)
M[indx[:,0],indx[:,1],indx[:,2]] = F

第一部分设置所需的网格变量。 argmax 函数提供了一种简单(快速)的方法来查找广播数组的第一个真值。这将为每个函数值生成一组 x、y 和 z 方向的索引。

结果数组M 与原始代码生成的数组不同,因为原始代码会丢失数据。 y[j] &lt; data[l,1] &lt; y[j+1] 的逻辑,其中y 是使用linspace 生成的向量,意味着将丢失每个方向的最小值和最大值(data[l,1] 可能等于y[j]y[j+1]!)。使用包含两个值的数据集运行它,每个值都有自己的坐标,M 数组将全为零。

【讨论】:

  • 谢谢您,您的解决方案实际上与@mathfux 相同,但更清晰。既然你们都提供了一个可行的解决方案,我会给你赏金并接受另一个解决方案。
  • 谢谢。我猜他们的速度要快一些,因为它删除了循环和 linspace 命令,但很乐意提供帮助。
猜你喜欢
  • 2018-08-31
  • 1970-01-01
  • 1970-01-01
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
  • 2020-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多