numpy.array形状(R,1)和(R,)之间的区别


319

进入时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)))


3
可能会有所帮助。虽然找不到可行的解决方案。
keyer 2014年

1
正确的解决方案:numpy.ravel(M [:,0])-将形状从(R,1)转换为(R,)
Andi R

Answers:


544

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,)

这里的形状 (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 
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

如果我们调整数组的形状,则不会更改数据缓冲区。相反,它创建一个新视图,该视图描述了另一种解释数据的方式。所以之后:

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

该数组b具有与相同的数据缓冲区a,但是现在它由两个索引分别从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内部文档

2.怎么办?

由于numpy.reshape只是创建了一个新视图,因此不必在必要时使用它。当您想以其他方式索引数组时,它是使用的正确工具。

但是,在较长的计算中,通常可能首先要安排构造具有“正确”形状的数组,这样就可以最大程度地减少变形和转置的次数。但是,在没有看到导致需要重塑的实际环境的情况下,很难说应该改变什么。

您问题中的示例是:

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

但这是不现实的。首先,此表达式:

M[:,0].sum()

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

M.sum(axis=0)

33
这对于考虑如何存储数组非常有帮助。谢谢!访问(2-d)矩阵的一列(或行)以进行进一步的矩阵计算很不方便,因为我总是必须适当地重塑该列的形状。每次我必须将形状从(n,)更改为(n,1)。
OfLettersAndNumbers

3
@SammyLee:使用newaxis,如果你需要另一个轴,例如a[:, j, np.newaxis]j的列a,并且a[np.newaxis, i]i第i行。
加里斯·里斯

我正在尝试绘制索引以通过此模型在纸上获得更好的理解,但我似乎没有得到,如果我的形状为2 x 2 x 4,我理解前2个可以理解为0000000011111111,后4个可以理解为理解为0123012301230123中间的会发生什么?
PirateApp '18

3
想到这一点的一种简单方法是numpy的工作完全符合此处的预期,但是Python打印的元组可能会产生误导。在这种(R, )情况下,的形状ndarray是一个包含单个元素的元组,因此由Python用尾随逗号打印。如果没有多余的逗号,括号中的表达式将变得模棱两可ndarray具有单一维度的A 可以作为length的列向量R。在该(R, 1)情况下,元组具有两个元素,所以可以被认为是作为一个行向量(或具有1行长度的矩阵R
迈克尔杨

1
@ Alex-droidAD:请参阅此问题及其答案。
加雷斯·里斯

16

(R,)和之间的区别(1,R)实际上是您需要使用的索引数。 ones((1,R))是一个二维数组,碰巧只有一行。 ones(R)是一个向量。通常,如果变量的行数/列数不超过一个,则应该使用向量,而不是单维度的矩阵。

对于您的特定情况,有两种选择:

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

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

2)如果您想要矩阵等矩阵运算,请使用类matrix代替ndarray。所有*矩阵都被强制为二维数组,并且运算符执行矩阵乘法而不是按元素进行乘法(因此您不需要点)。以我的经验,这是值得解决的麻烦,但是如果您习惯使用matlab可能会很好。


是。我期待一个更像matlab的行为。我来看看matrix课。matrixBTW类有什么麻烦?
clwen 2014年

2
问题matrix在于它仅是2D,而且由于它会重载运算符'*',因此ndarray如果在上使用,则为其编写的函数可能会失败matrix
伊万2014年

11

形状是一个元组。如果只有一维,则形状将是一个数字,并且逗号后仅是空白。对于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)


5

对于其基本数组类,2d数组不比1d或3d数组更特殊。有一些操作可以保留尺寸,一些可以减小尺寸,其他可以组合甚至扩展尺寸。

M=np.arange(9).reshape(3,3)
M[:,0].shape # (3,) selects one column, returns a 1d array
M[0,:].shape # same, one row, 1d array
M[:,[0]].shape # (3,1), index with a list (or array), returns 2d
M[:,[0,1]].shape # (3,2)

In [20]: np.dot(M[:,0].reshape(3,1),np.ones((1,3)))

Out[20]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

In [21]: np.dot(M[:,[0]],np.ones((1,3)))
Out[21]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

其他给出相同数组的表达式

np.dot(M[:,0][:,np.newaxis],np.ones((1,3)))
np.dot(np.atleast_2d(M[:,0]).T,np.ones((1,3)))
np.einsum('i,j',M[:,0],np.ones((3)))
M1=M[:,0]; R=np.ones((3)); np.dot(M1[:,None], R[None,:])

MATLAB最初只是2D阵列。较新的版本允许更大的尺寸,但保留2的下限。但是,您仍然必须注意行矩阵和列1之间的差异,即形状为(1,3)v的列(3,1)。你多久写一次[1,2,3].'?我将要编写row vectorcolumn vector,但是受2d约束,MATLAB中没有任何矢量-至少从矢量的数学意义上讲不是1d。

您是否看过np.atleast_2d(还有_1d和_3d版本)?


1

1)不喜欢的形状的原因(R, 1)(R,)在于,它不必要地复杂的事情。此外,为什么最好在(R, 1)长度R向量上默认使用形状而不是(1, R)?当您需要其他尺寸时,最好使其简单明了。

2)以您的示例为例,您正在计算外部产品,因此可以reshape通过使用np.outer以下命令来执行此操作而无需致电

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

感谢您的回答。1)M[:,0]本质上是使用第一个元素获取所有行,因此拥有(R, 1)比具有更有意义(1, R)。2)并非总是可替换的np.outer,例如,点的形状为(1,R)然后是(R,1)。
clwen 2014年

1)是的,这可能是约定,但是在其他情况下它不太方便。约定也可能是让M [1,1]返回形状(1,1)数组,但是通常也比标量方便。如果您真的想要类似矩阵的行为,那么最好使用一个matrix对象。2)实际上,np.outer作品不管形状是否(1, R)(R, 1)或两者的组合。
bogatron

0

这里已经有很多好的答案。但是对我来说,很难找到一些例子,其中形状或数组会破坏所有程序。

所以这是一个:

import numpy as np
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])


from sklearn.linear_model import LinearRegression
regr = LinearRegression()
regr.fit(a,b)

这将因错误而失败:

ValueError:预期的2D数组,取而代之的是1D数组

但是如果我们添加reshapea

a = np.array([1,2,3,4]).reshape(-1,1)

这正常工作!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.