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

2024-12-11 08:48:00
admin
原创
145
摘要:问题描述:在 中numpy,一些操作返回形状为(R, 1),但一些操作返回(R,)。这将使矩阵乘法更加繁琐,因为reshape需要显式地 。例如,给定一个矩阵M,如果我们想做numpy.dot(M[:,0], numpy.ones((1, R)))其中R是行数(当然,同样的问题也发生在列上)。我们会得到mat...

问题描述:

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


解决方案 1:

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。如果我们将这两个索引标记为和,则数组如下所示: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

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

解决方案 2:

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

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

1) 只需将第二个参数设为向量即可。以下方法有效:

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

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

解决方案 3:

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

解决方案 4:

对于其基数组类,二维数组并不比一维或三维数组更特殊。有些操作会保留维度,有些会减少维度,有些会合并甚至扩展维度。

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 最初仅支持二维数组。较新的版本允许更多维度,但保留 2 的下限。但您仍需注意行矩阵和第一列矩阵(形状为(1,3)v )之间的区别(3,1)。您写过多少次[1,2,3].'?我本来打算写row vectorcolumn vector,但由于该二维约束,MATLAB 中不存在任何向量 - 至少在数学意义上不存在一维向量。

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

在较新的 Python/numpy 中有一个matmul运算符

In [358]: M[:,0,np.newaxis]@np.ones((1,3))
Out[358]: 
array([[0., 0., 0.],
       [3., 3., 3.],
       [6., 6., 6.]])

numpy某种意义上说,元素乘法比矩阵乘法更基本。对于大小为 1 的维度上的乘积和,无需使用dot/matmul

In [360]: M[:,0,np.newaxis]*np.ones((1,3))
Out[360]: 
array([[0., 0., 0.],
       [3., 3., 3.],
       [6., 6., 6.]])

这使用了一直以来都具有broadcasting的强大功能numpy。MATLAB 最近才添加了它。

解决方案 5:

这里已经有很多很好的答案了。但对我来说,很难找到一些示例,其中形状或数组可以破坏整个程序。

以下是其中的一个:

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:

形状为 (n,) 的数据结构称为秩 1 数组。它的行为与行向量或列向量不一致,这使得它的某些操作和效果不直观。如果您对此 (n,) 数据结构进行转置,它看起来完全相同,并且点积将给出一个数字而不是矩阵。形状为 (n,1) 或 (1,n) 的行向量或列向量更加直观和一致。

解决方案 7:

1) 不选择(R, 1)大于的形状的原因(R,)是它不必要地使事情复杂化。此外,为什么(R, 1)默认长度为 R 的向量的形状比更好(1, R)?最好保持简单,并在需要额外维度时明确说明。

2)对于您的示例,您正在计算外积,因此您可以reshape使用以下命令无需调用即可完成此操作np.outer

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

解决方案 8:

为了清楚起见,我们讨论的是:

  • NumPy 数组,也称为numpy.ndarray

  • 已知数组的形状numpy.ndarray.shape

  • 问题假设一些未知数numpy.ndarray,其形状为(R,),其中R应理解为其各自维度的长度

NumPy 数组具有形状。它.shape由一个元组表示,其中元组中的每个元素告诉我们该维度的长度。为了简单起见,我们坚持使用行和列。虽然我们的值numpy.ndarray在以下示例中不会改变,但形状会改变。

让我们考虑一个包含值 1、2、3 和 4 的数组。

我们的示例将包括以下.shape表示:

(4,)  # 1-dimensional array with length 4
(1,4) # 2-dimensional array with row length 1, column length 4
(4,1) # 2-dimensional array with row length 4, column length 1

我们可以用变量a和来更抽象地思考这个问题b

(a,)  # 1-dimensional array with length a
(b,a) # 2-dimensional array with row length b, column length a
(a,b) # 2-dimensional array with row length a, column length b

对我来说,“手动”构建它们以更好地了解它们的尺寸含义是有帮助的。

>> # (4,)
>> one_dimensional_vector = np.array(
    [1, 2, 3, 4]
)

>> # (1,4)
>> row_vector = np.array(
    [
        [1, 2, 3, 4]
    ]
)

>> # (4,1)
>> column_vector = np.array(
    [
        [1], 
        [2], 
        [3], 
        [4]
    ]
)

那么,第一个问题的答案是:

  1. 形状 (R, 1) 和 (R,) 之间有什么区别?

答案:它们有不同的维度。a是一维的长度,而b另一维的长度分别.shape(a, b)和。恰好是 1。一种思考方式是,如果行的长度为 1,那么它就是行向量。如果列的长度为 1,那么它代表的就是列向量。(a,)`ba = 1b = 1`numpy.ndarray

  1. 对于上面的例子,还有更好的方法吗?

答案:假设我们有一个上面用作示例的数组,其值为 1、2、3 和 4。一种方便的获取方法(R,)(R, 1)

>> one_dimensional_array = np.array([1,2,3,4])
>> one_dimensional_array.shape
(4,)
>> row_vector = one_dimensional_array[:, None]
>> row_vector.shape
(4, 1)

资源

  1. NumPy — ndarrays — https://numpy.org/doc/stable/reference/arrays.ndarray.html

  2. 交叉验证@unutbu — 维度技巧 — https://stats.stackexchange.com/a/285005

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用