我有两个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)
的数组的函数,以便为所有y值等评估第一个x值x0
。
我使用的当前功能相当复杂,所以让我们使用一个更简单的例子。让我们说功能是
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数组。还有一个" normalized_matrix",其形状为(4000,4000)---它没有什么特别的,只是一个矩阵,其入口值为0到1之间的整数,例如: 0.5567878。这是两个"给出"投入。
我的函数返回transpose(datavector)* matrix * datavector的矩阵乘法乘积,它是单个值。
现在,正如您在代码中看到的,我已经初始化了两个数组x和y,它们通过了一系列" x参数"和" y参数"。也就是说,func(x,y)
返回值x1
和值y1
,即func(x1,y1)
?
matrix1
的形状是(50,4000,4000)。 matrix2
的形状是(50,4000,4000)。同上total_matrix
。
normalized_matrix
形状(4000,4000),id_mat
形状(4000,4000)。
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: operands could not be broadcast together with shapes (50,1,1,50) (1,4000,4000)
。
答案 0 :(得分:3)
reshape
中的{p> numpy
具有不同的含义。当您从(100,)
开始并将其更改为(5,20)
或(10,10)
2d数组时,即可重新设置. There is a
numpy`函数来执行此操作。
您想要获取2个1d数组,并使用它们从函数生成2d数组。这就像获取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
广播。使用None
,x
将重新转换为(10,1)
,y
转换为(1,5)
,而+
将获得outer
总和。
X,Y=np.meshgrid(x,y,indexing='ij')
生成两个(10,5)
数组,可以使用相同的方式。看看是其他参数的文档。
因此,如果你的更复杂的函数可以采用像这样的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)
测试我过去做过的表明,vectorize
可以通过类似20%的事情改进列表理解路线,但改进与编写你的函数来处理2d数组无关。第一名。
顺便说一下,这种矢量化'关于SO numpy已多次询问过这个问题。除了这些广泛的例子之外,我们无法在不了解更复杂功能的情况下帮助您。只要它是一个带有标量的黑盒子,我们可以帮助你的最好的是np.vectorize
。你还需要了解广播(有或没有meshgrid
帮助)。
答案 1 :(得分:0)
我认为有更好的方法,这是我的舌头,但作为一个临时措施:
您正在使用网格网格的1x2窗口进行操作。您可以使用as_strided
中的numpy.lib.stride_tricks
将meshgrid
重新排列为两个元素的窗口,然后将您的函数应用于结果数组。我喜欢使用通用的nd解决方案,sliding_windows
(http://www.johnvinyard.com/blog/?p=268)(不是我的)来转换数组。
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 blog
的链接中断,我已将sliding_window
实施内容发布在其他SO答案中 - https://stackoverflow.com/a/22749434/2823755
搜索并找到许多其他棘手的 as_strided
解决方案。
答案 2 :(得分:0)
回答您编辑过的问题:
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,)
,因此转置不执行任何操作。我相信您希望2 dots
的结果为(50,)
形状。我建议使用einsum
。但是可以使用tensordot
完成,或者我认为np.dot(np.dot(total_matrix, 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)
X,Y
错了。 func
x
和y
为1d
。请注意您如何使用[:, None, None]
扩展维度。您也无法从outer
和x
的{{1}}组合创建二维数组。 y
中的所有数组都不是func
或(50,50)
。较高的尺寸由(50,50,...)
和nomalied_matrix
提供。
向我们展示id_mat
时,您还应指明代码中的位置。否则我们必须猜测,或者自己重新创建代码。
事实上,当我运行已编辑的ValueError
时,我收到此错误:
func(X,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)
已扩展为normalized_matrix
[我使用较小的示例]。 (1,400,400)
(50,50)
已扩展为X
。 (50,1,1,50)
扩展为x
,播放效果很好。
答案 3 :(得分:0)
解决编辑中的编辑和广播错误:
在您的函数中,您正在向数组添加维度以尝试让它们进行广播。
matrix1 = x [:, None, None] * normalized_matrix[None, :, :]
此表达式看起来像是要广播带有2d数组的1d数组。
meshgrid的结果是两个2d数组:
X,Y = np.meshgrid(x,y)
>>> X.shape, Y.shape
((50, 50), (50, 50))
>>>
当您尝试在广播表达式中使用X
时,维度不会排列,这就是导致ValueError
的原因 - 请参阅{{3 }}:
>>> x1 = X[:, np.newaxis, np.newaxis]
>>> nm = normalized_matrix[np.newaxis, :, :]
>>> x1.shape
(50, 1, 1, 50)
>>> nm.shape
(1, 4000, 4000)
>>>
答案 4 :(得分:-1)
您使用列表理解进入了正确的轨道,您只需添加额外的迭代级别:
np.array([[func(i,j) for i in x] for j in y])