Python 在不使用两个for循环的情况下重塑numpy阵列

Python 在不使用两个for循环的情况下重塑numpy阵列,python,arrays,numpy,Python,Arrays,Numpy,我有两个numpy阵列 import numpy as np x = np.linspace(1e10, 1e12, num=50) # 50 values y = np.linspace(1e5, 1e7, num=50) # 50 values x.shape # output is (50,) y.shape # output is (50,) 我想创建一个函数,它返回一个形状为50,50的数组,这样第一个x值x0就可以计算所有y值,等等 我正在使用的当前函数相当复杂,因此让我们使用

我有两个numpy阵列

import numpy as np
x = np.linspace(1e10, 1e12, num=50) # 50 values
y = np.linspace(1e5, 1e7, num=50)   # 50 values
x.shape # output is (50,)
y.shape # output is (50,)
我想创建一个函数,它返回一个形状为50,50的数组,这样第一个x值x0就可以计算所有y值,等等

我正在使用的当前函数相当复杂,因此让我们使用一个更简单的示例。假设函数是

def func(x,y):
    return x**2 + y**2
我如何将其形状设置为50,50数组?目前,它将输出50个值。您会在数组中使用for循环吗

比如:

np.array([[func(x,y) for i in x] for j in y)
但不使用两个for循环。这需要永远运行

编辑:有人要求我分享我复杂的功能。下面是:

有一个数据向量,它是一个包含4000个测量值的1D numpy阵列。还有一个规格化的_矩阵,形状为40004000——它没有什么特别的,只是一个整数输入值在0到1之间的矩阵,例如0.5567878。这是两个给定的输入

我的函数返回TransportAvector*matrix*datavector的矩阵乘积,它是一个值

现在,正如您在代码中看到的,我初始化了两个数组,x和y,它们通过一系列x参数和y参数。也就是说,funcx,y为值x1和值y1返回什么,即funcx1,y1

matrix1的形状是5040004000。matrix2的形状是5040004000。总_矩阵同上

规格化的_矩阵的形状为40004000,id_mat的形状为40004000

normalized_matrix
print normalized_matrix.shape #output (4000,4000)

data_vector = datarr
print datarr.shape #output (4000,)

def func(x, y):
    matrix1 = x [:, None, None] * normalized_matrix[None, :, :]
    matrix2 = y[:, None, None] * id_mat[None, :, :]
    total_matrix = matrix1 + matrix2
    # transpose(datavector) * matrix * datavector
    # by matrix multiplication, equals single value
    return  np.array([ np.dot(datarr.T,  np.dot(total_matrix, datarr) )  ])
如果我尝试使用np.meshgrid,也就是说,如果我尝试

x = np.linspace(1e10, 1e12, num=50) # 50 values
y = np.linspace(1e5, 1e7, num=50)   # 50 values

X, Y = np.meshgrid(x,y)

z = func(X, Y)

我得到以下值错误:ValueError:操作数无法与形状50,1,1,50 140004000一起广播

您对列表的理解是正确的,您只需要添加一个额外的迭代级别:

np.array([[func(i,j) for i in x] for j in y])

我认为有一个更好的办法,就在我嘴边,但作为一项临时措施:

您正在meshgrid的1x2窗口上操作。可以使用从numpy.lib.stride跨步的as_技巧将网格网格重新排列为两个元素窗口,然后将函数应用于结果数组。我喜欢使用通用的nd解决方案,而不是我的来转换数组

import numpy as np
a = np.array([1,2,3])
b = np.array([.1, .2, .3])
z= np.array(np.meshgrid(a,b))
def foo((x,y)):
    return x+y

>>> z.shape
(2, 3, 3)
>>> t = sliding_window(z, (2,1,1))
>>> t
array([[ 1. ,  0.1],
       [ 2. ,  0.1],
       [ 3. ,  0.1],
       [ 1. ,  0.2],
       [ 2. ,  0.2],
       [ 3. ,  0.2],
       [ 1. ,  0.3],
       [ 2. ,  0.3],
       [ 3. ,  0.3]])
>>> v = np.apply_along_axis(foo, 1, t)
>>> v
array([ 1.1,  2.1,  3.1,  1.2,  2.2,  3.2,  1.3,  2.3,  3.3])
>>> v.reshape((len(a), len(b)))
array([[ 1.1,  2.1,  3.1],
       [ 1.2,  2.2,  3.2],
       [ 1.3,  2.3,  3.3]])
>>>
这应该更快一些

您可能需要修改函数的参数签名

如果johnvinyard.com博客的链接中断,我已经在其他SO答案中发布了滑动窗口实现-

四处搜索,你会发现许多其他棘手的解决方案

在numpy中重塑为不同的含义。当您从100开始,并将其更改为5,20或10,10二维阵列时,即“重塑”。有一种功能可以做到这一点

您希望获取2个一维数组,并使用这些数组从函数生成二维数组。这就像取2的外积,将其值的所有组合传递给函数

某种形式的双循环是实现这一点的一种方法,无论是显式循环还是列表理解。但是加速这个过程取决于这个函数

对于x**2+y**2示例,它可以非常容易地“矢量化”:

In [40]: x=np.linspace(1e10,1e12,num=10)
In [45]: y=np.linspace(1e5,1e7,num=5)
In [46]: z = x[:,None]**2 + y[None,:]**2
In [47]: z.shape
Out[47]: (10, 5)
这充分利用了numpy广播的优势。如果为“无”,则x将被重塑为10,1,y将被重塑为1,5,并且+将取外部和

十、 Y=np.meshgridx,Y,indexing='ij'生成两个10,5数组,它们可以以相同的方式使用。查看is文档了解其他参数

因此,如果您的更复杂的函数可以这样用2d数组编写,那么“矢量化”就很容易了

但如果该函数必须接受2个标量,并返回另一个标量,那么您将陷入某种双循环

双循环的列表理解形式为:

np.array([[x1**2+y1**2 for y1 in y] for x1 in x])
另一个是:

z=np.empty((10,5))
for i in range(10):
   for j in range(5):
      z[i,j] = x[i]**2 + y[j]**2
使用np.vectorize可以在一定程度上加快这种双循环。它接受一个用户定义的函数,并返回一个可以接受可广播数组的函数:

In [65]: vprod=np.vectorize(lambda x,y: x**2+y**2)

In [66]: vprod(x[:,None],y[None,:]).shape
Out[66]: (10, 5)
我在过去所做的测试表明,向量化可以在列表理解路径上提高20%左右,但这一改进与首先编写用于处理2d数组的函数完全不同


顺便说一句,这种“矢量化”问题在这么短的时间内已经被问过很多次了。除了这些广泛的例子,我们不能帮助你不知道更多关于更复杂的函数。只要它是一个接受标量的黑盒,我们就可以帮助您实现np.vectorize。您仍然需要了解有无meshgrid帮助的广播。

回答您编辑的问题:

normalized_matrix
print normalized_matrix.shape #output (4000,4000)

data_vector = datarr
print datarr.shape #output (4000,)

def func(x, y):
    matrix1 = x [:, None, None] * normalized_matrix[None, :, :]
    matrix2 = y[:, None, None] * id_mat[None, :, :]
    total_matrix = matrix1 + matrix2
    # transpose(datavector) * matrix * datavector
    # by matrix multiplication, equals single value
    # return  np.array([ np.dot(datarr.T,  np.dot(total_matrix, datarr))])
    return np.einsum('j,ijk,k->i',datarr,total_matrix,datarr)
由于datarr是形状4000,所以转置不起任何作用。我相信你希望这两个点的结果是形状50,。我建议使用einsum。但是它可以用tensordot来完成,或者我认为甚至可以用np.dotnp.dottotal_矩阵,datarr,datarr来完成。使用较小的数组测试表达式,重点是获得正确的形状

x = np.linspace(1e10, 1e12, num=50) # 50 values
y = np.linspace(1e5, 1e7, num=50)   # 50 values
z = func(x,y)

# X, Y = np.meshgrid(x,y)
# z = func(X, Y)
十、 你错了。func取1d的x和y。请注意如何使用[:,无,无]展开尺寸标注。此外,您也不是从x和y的外部组合创建二维阵列。没有你的阵列 在func中为50,50或50,50,。。。。更高的尺寸由nomalied_矩阵和id_mat提供

在向我们显示ValueError时,您还应该指出代码中发生错误的位置。否则我们必须猜测,或者自己重新创建代码

事实上,当我运行编辑过的funcX,Y时,会出现以下错误:

----> 2         matrix1 = x [:, None, None] * normalized_matrix[None, :, :]
      3         matrix2 = y[:, None, None] * id_mat[None, :, :]
      4         total_matrix = matrix1 + matrix2
      5         # transpose(datavector) * matrix * datavector

ValueError: operands could not be broadcast together with shapes (50,1,1,50) (1,400,400) 

请参阅,错误就发生在一开始。标准化的_矩阵扩展到1400400[我使用的是较小的示例]。50,50 X扩展为50,1,1,50。x扩展到50,1,1,可以正常广播。

要解决编辑和编辑中的广播错误,请执行以下操作:

在您的函数中,您正在向数组添加维度,以尝试让它们进行广播

    matrix1 = x [:, None, None] * normalized_matrix[None, :, :]
此表达式看起来像是要用二维数组广播一维数组

网格网格的结果是两个二维阵列:

X,Y = np.meshgrid(x,y)

>>> X.shape, Y.shape
((50, 50), (50, 50))
>>>
当您尝试在广播表达式中使用X时,尺寸不对齐,这就是导致ValueError的原因-请参阅:


我不知道你到底想要什么样的输出。如果输出为50倍;50矩阵是M,你期望M[i,j]的公式是什么?x0在y0到y49处求值,然后x1在y0到y49处求值,等等。这是似曾相识,在它的另一个化身中得到了回答。你尝试了np.meshgrid,但它不起作用?编辑的问题,以及更详细的函数是很有帮助的。但您应该指出ValueError发生的位置。人们喜欢对省略此类信息投反对票。请看我的第二个答案。谢谢你的全面回答。看起来我一直在使用两个for循环,除非我以某种方式更改网格,否则可以轻松地将x和y转换为50,50个数组,但是要将它们组合到所需的输出中,这取决于您的代码。numpy函数和运算符可以快速地对一些常见操作执行此操作,如+、*,以及所有ufunc函数。但诀窍是用这些术语来表达你的函数。我想我上面的函数基本上是一个ufunc,我恐怕我自己有点搞糊涂了。我们基本上有50个矩阵。然后,该函数进行矩阵乘法,得到50个值,每个矩阵一个值,即`funcx1,y1,funcx2,y2,funcx3,y3,等等`我试图找出如何创建形状为50,50的输出,这样funcx1,y1,funcx1,y2,funcx1,y3,。。。funcx2,y1,funcx2,y2,funcx2,y3,。。。funcx3,y1,funcx3,y2,funcx3,y3,。。。。我认为meshgrid是实现这一点的正确方法……无论如何,感谢您的回复。ValueError在包含网格时发生。我不知道为什么你的函数会出错——我不知道。在上面的函数中,matrix1乘以x形50,再乘以规格化的_矩阵形40004000。结果矩阵1的形状为5040004000。当我运行代码时没有问题。啊,我理解你现在说的。x被扩展到50,1,1,这是有效的。使用网格会创建一个50,50形状的输入,但实际上不是这样。我现在明白为什么会有广播错误了。现在,为了弄清楚如何创建输出矩阵,x1,y1,x1,y2,x1,y3,。。。x2,y1,x2,y2,…。@ShanZhengYang请看下面我的另一个答案-使用numpy.meshgrid和基于numpy.lib.stride的滑动窗口函数。
>>> x1 = X[:, np.newaxis, np.newaxis]
>>> nm = normalized_matrix[np.newaxis, :, :]
>>> x1.shape
(50, 1, 1, 50)
>>> nm.shape
(1, 4000, 4000)
>>>