向空的NumPy数组追加失败

32

我正在尝试使用append将一个空的(不是np.empty!)数组填充为值,但我遇到了错误:

我的代码如下:

import numpy as np
result=np.asarray([np.asarray([]),np.asarray([])])
result[0]=np.append([result[0]],[1,2])

我得到的结果是:

ValueError: could not broadcast input array from shape (2) into shape (0)
6个回答

71

我可能误解了问题,但如果您想声明一个特定形状的空数组,以下内容可能会有所帮助:

初始化空数组:

>>> a = np.zeros((0,3)) #or np.empty((0,3)) or np.array([]).reshape(0,3)
>>> a
array([], shape=(0, 3), dtype=float64)

现在您可以使用该数组将具有相似形状的行附加到它上面。请记住,numpy数组是不可变的,因此每次迭代都会创建一个新的数组:
>>> for i in range(3):
...     a = np.vstack([a, [i,i,i]])
...
>>> a
array([[ 0.,  0.,  0.],
       [ 1.,  1.,  1.],
       [ 2.,  2.,  2.]])

np.vstack和np.hstack是将numpy数组组合的最常见方法,但是作为Matlab用户,我更喜欢使用np.r_和np.c_:

拼接1d数组:

>>> a = np.zeros(0)
>>> for i in range(3):
...     a = np.r_[a, [i, i, i]]
...
>>> a
array([ 0.,  0.,  0.,  1.,  1.,  1.,  2.,  2.,  2.])

合并行:

>>> a = np.zeros((0,3))
>>> for i in range(3):
...     a = np.r_[a, [[i,i,i]]]
...
>>> a
array([[ 0.,  0.,  0.],
       [ 1.,  1.,  1.],
       [ 2.,  2.,  2.]])

连接列:

>>> a = np.zeros((3,0))
>>> for i in range(3):
...     a = np.c_[a, [[i],[i],[i]]]
...
>>> a
array([[ 0.,  1.,  2.],
       [ 0.,  1.,  2.],
       [ 0.,  1.,  2.]])

6
谢谢您,先生。您是第一个完全理解OP想要什么(对我来说似乎很明显)的人,并且为我们提供了我们最初想要的内容。简而言之,当然有一种方法可以将内容连接到空数组上!再次感谢。 - Neil Traft
3
另一种声明“空”数组的方法是 a = np.zeros((0,3)) - Simon Streicher
很好,这回答了我关于最佳实现方式一直困扰着我的问题。谢谢!这应该被改为原帖的接受答案。 - AN6U5
predictions = np.array([]).reshape(2,2) 数值错误:无法将大小为0的数组重塑为形状(2,2) - Július Marko
1
@CodePope 抱歉我的意思是,你建议使用np.empty是正确的,因为它比整个reshape解决方案更干净、更漂亮。你也是对的,它更高效,但与这种追加方法相比,效率提升微不足道。在这里,你真的没有从np.empty中获得任何好处。你鼓励我改变我的代码来使用np.zeros - 在我看来,这是最可读/最常用的数组创建函数(效率除外)。 - Simon Streicher
显示剩余3条评论

37

numpy.append与Python中的list.append相比有很大的区别。我知道这点会让一些刚接触numpy的程序员感到困惑。numpy.append更像是拼接(concatenate),它创建一个新的数组并将旧数组的值和要附加的新值填入其中。例如:

import numpy

old = numpy.array([1, 2, 3, 4])
new = numpy.append(old, 5)
print old
# [1, 2, 3, 4]
print new
# [1, 2, 3, 4, 5]
new = numpy.append(new, [6, 7])
print new
# [1, 2, 3, 4, 5, 6, 7]

我认为您可以通过以下方式实现您的目标:

result = numpy.zeros((10,))
result[0:2] = [1, 2]

# Or
result = numpy.zeros((10, 2))
result[0, :] = [1, 2]

更新:

如果你需要使用循环创建numpy数组,并且不知道数组的最终大小,你可以这样做:

import numpy as np

a = np.array([0., 1.])
b = np.array([2., 3.])

temp = []
while True:
    rnd = random.randint(0, 100)
    if rnd > 50:
        temp.append(a)
    else:
        temp.append(b)
    if rnd == 0:
         break

 result = np.array(temp)

在我的例子中,结果将是一个(N,2)的数组,其中N是循环运行的次数,但显然,您可以根据自己的需要进行调整。

新的更新

您看到的错误与类型无关,而与您尝试连接的numpy数组的形状有关。如果您执行np.append(a,b),则ab的形状需要匹配。如果您附加一个(2,n)和(n,),则会得到一个(3,n)的数组。您的代码尝试将(1,0)附加到(2,)上。这些形状不匹配,因此会出现错误。


1
那对我没有帮助。因为我想要动态地将数字附加到我的数组中! - Cupitor
我不确定你所说的“动态”的意思,也许你正在寻找一个动态数组?在任何语言中,我使用过的最好的动态数组实现是Python列表。不幸的是,NumPy数组不是动态数组,但你可以通过复制来添加元素。 - Bi Rico
是的,那正是我的意思。但我可以理解Python的这个错误并不那么有意义,就像我在上面的评论中所说的一样,因为它是无类型的!但还是谢谢你!我会使用列表。 - Cupitor
3
@Naji Python并不是无类型的,它是动态类型的。而且许多(大多数?)动态语言都有一些数据结构是不可变的或者在使用时有一定限制。 - Free Monica Cellio

3
这个错误是由于您试图将一个形状为(0,)的对象定义为一个形状为(2,)的对象。如果您在不强制它等于result[0]的情况下添加所需内容,则不会出现任何问题:
b = np.append([result[0]], [1,2])

但是当您定义result [0] = b时,您正在等同于不同形状的对象,这是不允许的。您想要做什么?

这很有道理,尽管看起来很糟糕,你不能在分配后更改变量的类型,考虑到Python是无类型的事实! - Cupitor
我不知道你想做什么,但我认为你可能需要使用列表而不是NumPy数组。 列表更加灵活。 - Alejandro
我想做的是创建一个空的二维数组,并动态地填充它,其中包含索引为0或1的值。 - Cupitor
@Naji,你可以通过将其分配给不同的值来更改变量的类型。通常情况下,创建后无法更改的类型。特别是对于numpy数组,由于性能原因使用了C实现的数据结构,这增加了一些复杂性。 - Free Monica Cellio
1
np.arrays最好被视为固定大小的容器。虽然它们可以从Python列表构建,但它们并不是为增长而设计的。 - hpaulj

3
这是在Ipython中运行你的代码后的结果。请注意,`result`是一个`(2,0)`数组,即2行,0列,0个元素。`append`生成了一个`(2,)`数组。`result[0]`是一个`(0,)`数组。你的错误信息与试图将这个2项数组分配到大小为0的插槽有关。由于`result`的类型是`dtype=float64`,因此只能将标量赋值给它的元素。
In [65]: result=np.asarray([np.asarray([]),np.asarray([])])

In [66]: result
Out[66]: array([], shape=(2, 0), dtype=float64)

In [67]: result[0]
Out[67]: array([], dtype=float64)

In [68]: np.append(result[0],[1,2])
Out[68]: array([ 1.,  2.])

np.array并不是Python列表,它的所有元素都是相同类型(由dtype指定)。同时请注意,result不是一个数组的数组。

也可以将结果构建为

ll = [[],[]]
result = np.array(ll)

当...的时候
ll[0] = [1,2]
# ll = [[1,2],[]]

对于结果而言,情况并非如此。

np.zeros((2,0)) 也会生成你的 result

实际上,result 还有另一个怪异之处。

result[0] = 1

这并不改变result的值。它接受了赋值,但由于它有0列,没有地方放置1。如果result创建为np.zeros((2,1)),那么这个赋值会起作用。但是它仍然无法接受列表。

但是如果result有2列,那么您可以将一个2元素列表分配给其中一行。

result = np.zeros((2,2))
result[0] # == [0,0]
result[0] = [1,2]

append操作之后,您希望result具体看起来是什么样子?


点赞。但我认为你已经写的内容在其他答案中已经涵盖了。无论如何,谢谢。 - Cupitor

2

numpy.append 在追加新值之前总是会复制数组。您的代码等同于以下内容:

import numpy as np
result = np.zeros((2,0))
new_result = np.append([result[0]],[1,2])
result[0] = new_result # ERROR: has shape (2,0), new_result has shape (2,)

也许你的意思是要这样做?
import numpy as np
result = np.zeros((2,0))
result = np.append([result[0]],[1,2])

实际上,结果有一个元素零...但是你建议的点是完全不同的方式! - Cupitor
np.append 就是一个 concatenate: return concatenate((arr, values), axis=axis) - hpaulj

1

SO线程'逐元素相乘两个数组,其中一个数组的元素是数组'有一个构建由数组组成的数组的示例。如果子数组大小相同,numpy会创建一个二维数组。但是如果它们的长度不同,则会创建一个dtype=object的数组,并且子数组保留其身份。

在此基础上,您可以像这样做:

In [5]: result=np.array([np.zeros((1)),np.zeros((2))])

In [6]: result
Out[6]: array([array([ 0.]), array([ 0.,  0.])], dtype=object)

In [7]: np.append([result[0]],[1,2])
Out[7]: array([ 0.,  1.,  2.])

In [8]: result[0]
Out[8]: array([ 0.])

In [9]: result[0]=np.append([result[0]],[1,2])

In [10]: result
Out[10]: array([array([ 0.,  1.,  2.]), array([ 0.,  0.])], dtype=object)

然而,我并没有立即看出它相比于纯Python列表或列表的优势。它不像一个2D数组那样工作。例如,我必须使用result [0] [1]而不是result [0,1]。如果子数组的长度都相同,我必须使用np.array(result.tolist())来生成一个2D数组。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接