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

时间:2014-02-26 20:55:36

标签: python numpy matrix multidimensional-array

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

7 个答案:

答案 0 :(得分:453)

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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

如果我们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.reshape只是创建了一个新视图,因此您不应该害怕在必要时使用它。当您想以不同的方式索引数组时,它是正确的工具。

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

您问题中的示例是:

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

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

M[:,0].sum()

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

M.sum(axis=0)

答案 1 :(得分:13)

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

对于您的具体情况,有以下几种选择:

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

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

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

答案 2 :(得分:6)

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

答案 3 :(得分:4)

对于它的基类数组,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,3) v (3,1)。你多久写一次[1,2,3].'?我打算写row vectorcolumn vector,但是使用那个2d约束,MATLAB中没有任何向量 - 至少在向量的数学意义上不是1d。

你看过np.atleast_2d(也是_1d和_3d版本)吗?

答案 4 :(得分:0)

1)不喜欢(R, 1)超过(R,)形状的原因是它不必要地使事情复杂化。此外,为什么最好为长度为R的向量而不是(R, 1)设置(1, R)形状?当你需要额外的尺寸时,最好保持简单明了。

2)对于您的示例,您正在计算外部产品,因此您可以使用reshape进行np.outer调用而无需执行此操作:

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

答案 5 :(得分: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数组

但是如果我们将reshape添加到a

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

这正常工作!

答案 6 :(得分:0)

形状(n,)的数据结构称为1级数组。它不能作为行向量或列向量始终保持一致,这使其某些操作和效果变得不直观。如果采用此(n,)数据结构的转置,它将看起来完全相同,并且点积将为您提供数字而不是矩阵。 形状为(n,1)或(1,n)行或列向量的向量更加直观和一致。