numpy.数组形状(R,1)和(R,)之间的差异

2024-06-11 10:30:26 发布

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

numpy中,有些操作以(R, 1)形状返回,但有些返回(R,)。由于需要显式reshape,这将使矩阵乘法变得更加乏味。例如,给定一个矩阵M,如果我们想做numpy.dot(M[:,0], numpy.ones((1, R))),其中R是行数(当然,同样的问题也会按列出现)。我们将得到matrices are not aligned错误,因为M[:,0]是形状(R,),而numpy.ones((1, R))是形状(1, R)

所以我的问题是:

  1. 形状(R, 1)(R,)有什么区别。我从字面上知道它是一个数字列表和一个列表,其中所有的列表只包含一个数字。只是想知道为什么不设计numpy以便它更喜欢形状(R, 1),而不是(R,)以便更容易的矩阵乘法。

  2. 上面的例子有更好的方法吗?不需要像这样显式地重塑:numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))


Tags: numpy列表错误onesnot矩阵数字dot
3条回答

(R,)(1,R)之间的区别实际上是需要使用的索引数。ones((1,R))是一个碰巧只有一行的二维数组。ones(R)是一种载体。一般来说,如果变量有多个行/列是没有意义的,那么应该使用向量,而不是具有单一维度的矩阵。

对于您的具体情况,有两种选择:

1)将第二个参数设为向量。以下工作正常:

    np.dot(M[:,0], np.ones(R))

2)如果需要类似于matlab的矩阵运算,请使用类matrix,而不是ndarray。所有矩阵都被强制成为二维数组,运算符*执行矩阵乘法而不是按元素(所以不需要点)。以我的经验,这是更麻烦的是,这是值得的,但它可能是很好的,如果你习惯于matlab。

形状是一个元组。如果只有一个维度,则形状将是一个数字,逗号后为空白。对于2+维度,所有逗号后面都会有一个数字。

# 1 dimension with 2 elements, shape = (2,). 
# Note there's nothing after the comma.
z=np.array([  # start dimension
    10,       # not a dimension
    20        # not a dimension
])            # end dimension
print(z.shape)

(2,)

# 2 dimensions, each with 1 element, shape = (2,1)
w=np.array([  # start outer dimension 
    [10],     # element is in an inner dimension
    [20]      # element is in an inner dimension
])            # end outer dimension
print(w.shape)

(2,1)

一。形状在NumPy

中的意义

你写,“我知道字面上它是一个数字列表和所有列表中只包含一个数字的列表”,但这是一个有点无益的思考方式。

考虑NumPy数组的最好方法是,它们由两部分组成,一个数据缓冲区只是一个原始元素块,另一个视图描述如何解释数据缓冲区。

例如,如果我们创建一个由12个整数组成的数组:

>>> a = numpy.arange(12)
>>> a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

然后a由一个数据缓冲区组成,其排列方式如下:

┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

以及描述如何解释数据的视图:

>>> a.flags
  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)

这里的shape(12,)表示数组由一个从0到11的索引索引。从概念上讲,如果我们将这个索引标记为i,那么数组a如下所示:

i= 0    1    2    3    4    5    6    7    8    9   10   11
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

如果我们reshape一个数组,这不会改变数据缓冲区。相反,它创建了一个新视图,该视图描述了解释数据的不同方式。所以之后:

>>> b = a.reshape((3, 4))

数组ba具有相同的数据缓冲区,但现在它由分别从0到2和从0到3的两个索引索引。如果我们标记这两个索引ij,那么数组b如下所示:

i= 0    0    0    0    1    1    1    1    2    2    2    2
j= 0    1    2    3    0    1    2    3    0    1    2    3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

也就是说:

>>> b[2,1]
9

您可以看到第二个索引变化很快,而第一个索引变化很慢。如果您希望这样做,可以指定order参数:

>>> c = a.reshape((3, 4), order='F')

这会产生一个这样的索引数组:

i= 0    1    2    0    1    2    0    1    2    0    1    2
j= 0    0    0    1    1    1    2    2    2    3    3    3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

也就是说:

>>> c[2,1]
5

现在应该很清楚数组的一个或多个维度为1的形状意味着什么。之后:

>>> d = a.reshape((12, 1))

数组d由两个索引索引,第一个索引从0到11,第二个索引始终为0:

i= 0    1    2    3    4    5    6    7    8    9   10   11
j= 0    0    0    0    0    0    0    0    0    0    0    0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

所以:

>>> d[10,0]
10

长度为1的维度是“自由的”(在某种意义上),所以没有什么能阻止你进城:

>>> e = a.reshape((1, 2, 1, 6, 1))

给出如下索引的数组:

i= 0    0    0    0    0    0    0    0    0    0    0    0
j= 0    0    0    0    0    0    1    1    1    1    1    1
k= 0    0    0    0    0    0    0    0    0    0    0    0
l= 0    1    2    3    4    5    0    1    2    3    4    5
m= 0    0    0    0    0    0    0    0    0    0    0    0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

所以:

>>> e[0,1,0,0,0]
6

有关如何实现数组的详细信息,请参见NumPy internals documentation

2。怎么办?

因为^{}只是创建了一个新视图,所以您不应该害怕在必要时使用它。当你想用不同的方法索引一个数组时,它是正确的工具。

然而,在长时间的计算中,通常可以首先安排构造具有“正确”形状的数组,从而最小化整形和转置的次数。但如果没有看到导致重塑需求的实际背景,就很难说应该改变什么。

你问题的例子是:

numpy.dot(M[:,0], numpy.ones((1, R)))

但这并不现实。首先,这个表达式:

M[:,0].sum()

更简单地计算结果。第二,第0列真的有什么特别之处吗?也许你真正需要的是:

M.sum(axis=0)

相关问题 更多 >