python-3.x 在numpy中推广到任意大小的数组

qvk1mo1f  于 2023-06-25  发布在  Python
关注(0)|答案(1)|浏览(93)

我目前有一个输入网格和函数,我想传递到网格的每个元素(取决于原始网格的维数)。问题是,我目前有几种不同的传递函数的实现,取决于维度,我想找到一种更好的实现方法。以下是一些例子:

import numpy as np

# 1D

grid1 = np.array(
    [ 1, 2, 3]
)

# Function
foo1 = lambda x: x**2

print(grid1.shape)
print(len(grid1.shape))

f_grid1 = np.zeros_like(grid1)

for i in range(grid1.shape[0]):
    f_grid1[i] = foo1(grid1[i])

print(f_grid1)



# 2D

grid2 = np.array(
    [
        [[1, 0.1], [1, 0.2], [1, 0.3]],
        [[2, 0.1], [2, 0.2], [2, 0.3]],
        [[3, 0.1], [3, 0.2], [3, 0.3]],
        [[4, 0.1], [4, 0.2], [4, 0.3]]
    ]
)

# Function
foo2 = lambda x, y: x**2 - y

print(grid2.shape)
print(len(grid2.shape))

bins = grid2.shape[0] # bins will always the same between dimensions.

f_grid2 = np.empty(grid2.shape[:-1], dtype=np.float64)

for i in range(grid2.shape[0]):
    for j in range(grid2.shape[1]):
        f_grid2[i, j] = foo2(grid2[i, j, 0], grid2[i, j, 1])

print(f_grid2)



# 3D

grid3 = np.array(
    [
        [ [[1, 0.1, 0.0001], [1, 0.2, 0.0001], [1, 0.3, 0.0001]],
          [[2, 0.1, 0.0001], [2, 0.2, 0.0001], [2, 0.3, 0.0001]],
          [[3, 0.1, 0.0001], [3, 0.2, 0.0001], [3, 0.3, 0.0001]] ],

        [ [[1, 0.1, 0.0002], [1, 0.2, 0.0002], [1, 0.3, 0.0002]],
          [[2, 0.1, 0.0002], [2, 0.2, 0.0002], [2, 0.3, 0.0002]],
          [[3, 0.1, 0.0002], [3, 0.2, 0.0002], [3, 0.3, 0.0002]] ],

        [ [[1, 0.1, 0.0003], [1, 0.2, 0.0003], [1, 0.3, 0.0003]],
          [[2, 0.1, 0.0003], [2, 0.2, 0.0003], [2, 0.3, 0.0003]],
          [[3, 0.1, 0.0003], [3, 0.2, 0.0003], [3, 0.3, 0.0003]] ],
    ]
)

# Function
foo3 = lambda x, y, z: x**2 - y + z

bins = grid3.shape[0] # bins will always the same between dimensions.

print(grid3.shape)
print(len(grid3.shape))

f_grid3 = np.empty(grid3.shape[:-1], dtype=np.float64)

for i in range(grid3.shape[0]):
    for j in range(grid3.shape[1]):
        for k in range(grid3.shape[2]):
            f_grid3[i, j, k] = foo3(grid3[i, j, k, 0], grid3[i, j, k, 1], grid3[i, j, k, 2])

print(f_grid3)

如何将其扩展到任意维度?以上各项的产出如下:

(3,)
1
[1 4 9]
(4, 3, 2)
3
[[ 0.9  0.8  0.7]
 [ 3.9  3.8  3.7]
 [ 8.9  8.8  8.7]
 [15.9 15.8 15.7]]
(3, 3, 3, 3)
4
[[[0.9001 0.8001 0.7001]
  [3.9001 3.8001 3.7001]
  [8.9001 8.8001 8.7001]]

 [[0.9002 0.8002 0.7002]
  [3.9002 3.8002 3.7002]
  [8.9002 8.8002 8.7002]]

 [[0.9003 0.8003 0.7003]
  [3.9003 3.8003 3.7003]
  [8.9003 8.8003 8.7003]]]

目前,我认为最终的函数看起来像:

def apply_function(grid, func):
    shape = grid.shape
    if len(shape) == 1:
        grid = grid[np.newaxis].T

    out_grid = np.empty(grid.shape[:-1], dtype=np.float64)

    # calculate the corresponding out_grid here
xdnvmnnf

xdnvmnnf1#

正如我在评论最近类似的问题时所说(我记得@hpaulj也评论过),真正的问题是:你真的需要这个吗你必须意识到,无论是否特定于维度,任何将python函数应用于数组的所有元素的方法都将是非常低效的方法。这是否定整个麻木点。整个numpy点是如何不这样做(经验法则:如果你有一个for循环,你可能做错了)
在你的例子中

f_grid=foo1(grid)
f_grid=foo2(grid[:,:,0], grid[:,:,1])
f_grid=foo3(grid[:,:,:,0], grid[:,:,:,1], grid[:,:,:,2])

是应用于整个向量的方式,而不需要for循环(至少不需要显式循环)。显然,它们被numpy代码中的隐式代码所取代。但这些都是C语言,所以速度更快)。
还要注意的是,你似乎把数组的维数(也就是你要应用foo的N × N × N数据的维数k)和foo函数的变量数对齐了。这可能有一个应用的原因(我们不知道,这是你的应用)。但没有数学或无聊的理由。
例如,1D网格可以包含x,y,z值,如下所示:

foo=lambda x,y,z: x**2+y-z.

其可以沿着1维阵列应用。要么与

for i in range(len(grid)): 
    f_grid[i] =foo(grid[i][0], grid[i][1], grid[i][2])

用你的方法或者与

f_grid=foo(grid[:,0], grid[:,1], grid[:,2])

使用我的(好吧,这不是我的:每个人都在做同样的事情)。所以,你想调整代码的不是网格的维数,而是foo函数的变量数(也就是网格最后一个轴的大小,换句话说,也不是强制的其他维数)
同样,如果函数是向量化的(并且你所有的例子都是;大多数数学函数只从一堆独立的标量中返回一个标量),那么,适应网格的维数根本不是问题;你什么都没做这已经被矢量化处理了。矢量化函数不关心它们是否应用于标量,一维数组,二维数组等。他们只是重复所有数据沿所有轴所需的时间。
例如,1参数函数foo可以应用于3D网格的所有数据:

foo=lambda x: x**2 
f_grid=foo(grid)

无论网格的尺寸如何
所以,让我们忘记一次你的目标网格的维数。要关注你的函数的变量的数量,也就是说,在多个一维网格中,最后一个轴的大小。好吧,让我们不要忘记它,因为尽管如此,第二个问题(事实上K个变量是沿着最后一个轴),导致了一个问题(代码是不完全相同的取决于维数N-再次没有理由假设N = K:我们需要在代码中N :
但是@hpaulj给出了摆脱那些N :的方法。将我的3个示例的初始代码替换为

f_grid=foo1(grid)
f_grid=foo2(grid[...,0], grid[...,1])
f_grid=foo3(grid[...,0], grid[...,1], grid[...,2])

你看,现在维数N没有影响。只有最后一个轴的大小K,即参数的数量。除了1D的情况,这是不同的。我们更愿意称之为

f_grid=foo1(grid[...,0])

坚持相同的模式。这很容易解决:确保在1D情况下,网格是2D阵列(尺寸为1作为最后一个轴),与您的3D示例完全相同,网格是一个4D阵列(以尺寸3作为最后一个轴),或者一般来说,在K个参数的函数的情况下,生成N维的网格,grid是(N +1)D数组,最后一个轴的大小为K(在所有K = N的例子中,大小为N)。
因为你保证了。或者是因为我们添加了一个代码来处理那个特殊的情况

grid=grid if len(grid.shape)>1 else grid[:,None]
f_grid=foo1(grid[...,0])
f_grid=foo2(grid[...,0], grid[...,1])
f_grid=foo3(grid[...,0], grid[...,1], grid[...,2])

这段代码的第一行添加了一个假的、大小为1的轴,当网格是1D时,这样,和所有其他示例一样,网格有1个额外的维度来保存K个变量。
现在,你可以看到共同的模式开始变得明显。
我们可以用这种方法产生所有维度的呼叫

grid=grid if len(grid.shape)>1 else grid[:,None]
f_grid=foo(*(grid[...,i] for i in range(grid.shape[-1])))

这与参数的数量K=grid.shape[-1]和维数无关。
我知道,我说了“不要循环”。但这一点并不重要:它仅沿着foo的参数的数量迭代。除非你缩进一个foo函数,它有无数的参数(在这种情况下,你有一个问题),否则for循环是可以忽略的。它不迭代数据。只是数据的维度。它只是1、2或3次迭代。

相关问题