在 中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)。
numpy
(R, 1)
(R,)
reshape
M
numpy.dot(M[:,0], numpy.ones((1, R)))
R
matrices are not aligned
M[:,0]
numpy.ones((1, R))
(1, R)
我的问题是:
numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))
你写道,“我知道它实际上是数字列表和列表的列表,其中所有列表都只包含一个数字”,但这是一种无益的思考方式。
思考 NumPy 数组的最佳方式是它们由两部分组成,一个是数据缓冲区(只是一块原始元素),另一个是描述如何解释数据缓冲区的视图。
例如,如果我们创建一个包含 12 个整数的数组:
>>> a = numpy.arange(12) >>> a array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
然后a由一个数据缓冲区组成,其排列方式如下:
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,)
这里的形状 (12,)表示数组由从 0 到 11 的单个索引进行索引。从概念上讲,如果我们标记这个单个索引i,则数组a如下所示:
(12,)
i
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 │ └────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
如果我们重塑数组,这不会改变数据缓冲区。相反,它会创建一个新的视图,描述解释数据的不同方式。因此之后:
>>> b = a.reshape((3, 4))
该数组b具有与 相同的数据缓冲区,但现在它由两个a索引进行索引,这两个索引分别从 0 到 2 和 0 到 3。如果我们将这两个索引标记为和,则数组如下所示:i``j``b
b
i``j``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:
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:
d
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 内部文档。
由于numpy.reshape只是创建了一个新视图,因此您不必担心在必要时使用它。当您想要以不同的方式索引数组时,它是正确的工具。
numpy.reshape
然而,在长时间计算中,通常可以首先安排构建具有“正确”形状的数组,从而最大限度地减少重塑和转置的次数。但如果不了解导致需要重塑的实际背景,就很难说应该改变什么。
你问题中的例子是:
但这不现实。首先,这个表达式:
M[:,0].sum()
计算结果更简单。其次,第 0 列真的有什么特别之处吗?也许你真正需要的是:
M.sum(axis=0)