具有相同形状的Numpy点产品形状错误

2024-06-11 19:18:31 发布

您现在位置:Python中文网/ 问答频道 /正文

完全陷入了一个相当愚蠢的问题:我试图计算对象之间某些属性的点积,但不断得到一个值错误-形状不匹配-但形状相同(2,1)和(2,1),因为数组只是同一类的不同实例的属性:

class MyClass(Object):
      def __init__(self, a,b, x,y):
          self.prop_1 = np.array((a,b))
          self.prop_2 = np.array((x,y))

其中a、b、x和y都是标量。然后再往下走,我正在努力

def MyFunction(Obj1, Obj2):
    results = np.dot(Obj1.prop_1 - Obj2.prop_1, Obj2.prop_2 - Obj2.prop_3)

它不断抛出值错误

ValueError: shapes (2,1) and (2,1) not aligned: 1 (dim 1) != 2 (dim 0)

从数学上讲,这个点积应该很好——但是错误消息的最后一位暗示我必须转置其中一个数组。我非常感谢对numpy形状解释的简短解释,以避免这种错误

编辑:

我想我有点说错了。当我通过(案例a)启动我的对象时

a,b = np.random.rand(2)
x,y = np.random.rand(2)
MyClass(a, b, x, y)

每件事都很有魅力。如果相反,我启动as(案例b)

a = np.random.rand(1)
b = np.random.rand(1)
x = np.random.rand(1)
y = np.random.rand(1)
MyClass(a, b, x, y)

由于形状不匹配,点积后来无法工作

我注意到,在案例b中,每个单独的值都是shape(1,),我很清楚,在案例a中,组合这两个值将导致shape(2,1),而不是shape(),但是为什么这两种声明变量的方法会导致不同的形状呢

正如您所知,我对Python比较陌生,并且认为这只是执行多个任务的一种简洁方式——事实证明,这背后有一些进一步的推理,我很想听听这方面的情况


Tags: 对象self属性def错误npmyclassrandom
1条回答
网友
1楼 · 发布于 2024-06-11 19:18:31

第1部分

问题是数组是完整的二维矩阵,而不是^{}理解的一维“向量”。要使乘法工作,您需要(a)将向量转换为向量:

np.dot(a.reshape(-1), b.reshape(-1))

(b)设置矩阵乘法,以便维度工作。记住,两个Nx1矩阵的点积是ATB:

np.dot(a.T, b)

或者(c),使用np.einsum显式设置和的维度:

np.einsum('ij,ij->j', a, b).item()

对于使用dot的所有示例,您可以使用^{}(或等效的@运算符)或^{},因为您有二维数组

通常,在使用dot时,请记住以下规则。表单元格是einsum下标

                                      A
      |         1D        |          2D         |                ND             |
    -+         -+          -+               -+
   1D | i,i->             | ij,j->i             | a...yz,z->a...y               |
    -+         -+          -+               -+
B  2D | i,ij->j           | ij,jk->ik           | a...xy,yz->a...xz             |
    -+         -+          -+               -+
   ND | y,a...xyz->a...xz | ay,b...xyz->ab...xz | a...mxy,n...wyz->a...mxn...wz |
    -+         -+          -+               -+

基本上,dot沿着最后两个维度遵循矩阵乘法的正常规则,但是前导维度总是组合在一起的。如果希望阵列的前导维度一起广播>;2D(即,将矩阵堆栈中的对应元素相乘,而不是所有可能的组合),使用matmul@

第二部分

将输入初始化为a, b = np.random.rand(2)时,将数组的两个元素解压为标量:

>>> a, b = np.random.rand(2)
>>> a
0.595823752387523
>>> type(a)
numpy.float64
>>> a.shape
()

注意,在这种情况下,类型不是numpy.ndarray。但是,当您执行a = np.random.rand(1)时,结果是一个元素的1D数组:

>>> a = np.random.rand(1)a
>>> a
array([0.21983553])
>>> type(a)
numpy.ndarray
>>> a.shape
(1,)

从numpy阵列创建numpy阵列时,结果是二维阵列:

>>> np.array([1, 2]).shape
(2,)
>>> np.array([np.array([1]), np.array([2])]).shape
(2, 1)

展望未来,你有两个选择。您可以更加小心地处理输入,也可以在创建阵列后对其进行清理

可以展开馈入的阵列:

ab = np.random.rand(2)
xy = np.random.rand(2)
MyClass(*ab, *xy)

或者,您也可以在创建阵列后展平/展开阵列:

def __init__(self, a, b, x, y):
     self.prop_1 = np.array([a, b]).ravel()
     self.prop_2 = np.array([x, y]).ravel()

您可以使用....reshape(-1)而不是...ravel()

相关问题 更多 >