我在numpy中遇到了以下奇怪的事情,这可能是也可能不是一个错误:
import numpy as np
dt = np.dtype([('tuple', (int, 2))])
a = np.zeros(3, dt)
type(a['tuple'][0]) # ndarray
type(a[0]['tuple']) # ndarray
a['tuple'][0] = (1,2) # ok
a[0]['tuple'] = (1,2) # ValueError: shape-mismatch on array construction
我原以为下面的两个选项都可以使用。 意见?
答案 0 :(得分:9)
我在numpy讨论列表上问过这个问题。 Travis Oliphant回答here。
引用他的回答:
简短的回答是,这不是一个“正常”的错误,但它可能被视为“设计”错误(尽管问题可能不是直接解决的)。这意味着它可能在短期内不会改变 - 你应该只使用第一个拼写。
由于几个原因,结构化数组可能是NumPy的一个令人困惑的区域。你已经构建了一个涉及其中几个的例子。您有一个数据类型,它是一个带有一个成员(“元组”)的“结构”数组。该成员包含一个2向量的整数。
首先,重要的是要记住使用Python,做
a ['tuple'] [0] =(1,2)
相当于
b = a ['tuple']; b [0] =(1,2)
以同样的方式,
a [0] ['tuple'] =(1,2)
相当于
b = a [0]; b ['tuple'] =(1,2)
要理解这种行为,我们需要剖析代码路径和发生的事情。你在'a'中构建了这些元素的(3,)数组。当你写b = a ['tuple']你应该得到一个(3,)数组的(2,) - 整数,但因为目前没有正式的dtype支持(n,) - 整数作为一般dtype在NumPy中,你得到一个(3,2)整数数组,这是NumPy可以给你的最接近的东西。通过
设置此对象的[0]行a ['tuple'] [0] =(1,2)
工作得很好,做你想要的。
另一方面,当您键入:
b = a [0]
你得到一个数组标量,这是一种特别有趣的数组标量,可以保存记录。这个新对象正式为numpy.void类型,它包含任何适合“VOID”基本dtype的“标量表示”。
出于某种原因:
b ['tuple'] = [1,2]
无效。在我的系统上,我遇到了一个不同的错误:TypeError:'int'类型的对象没有len()
我认为这应该作为问题跟踪器上的错误存档,暂时在这里:http://projects.scipy.org/numpy
如果有人想要调查,问题最终是在voidtype_setfields中调用void-> copyswap函数。我认为这种行为应该有效。
a numpy bug report中给出了对此的解释。
答案 1 :(得分:8)
我得到了一个与你不同的错误(使用numpy 1.7.0.dev):
ValueError: setting an array element with a sequence.
因此下面的解释可能对您的系统不正确(或者甚至可能是我所看到的错误解释)。
首先,请注意索引structured array的一行会为您提供numpy.void
个对象(请参阅data type docs)
import numpy as np
dt = np.dtype([('tuple', (int, 2))])
a = np.zeros(3, dt)
print type(a[0]) # = numpy.void
据我所知,void
有点像Python列表,因为它可以容纳不同数据类型的对象,这是有道理的,因为结构化数组中的列可以是不同的数据类型。
如果您切出第一行而不是编制索引,则会获得ndarray
:
print type(a[:1]) # = numpy.ndarray
这类似于Python列表的工作方式:
b = [1, 2, 3]
print b[0] # 1
print b[:1] # [1]
Slicing返回原始序列的缩短版本,但索引会返回一个元素(此处为int
;上方为void
类型)。
因此,当您切入结构化数组的行时,您应该期望它的行为与原始数组一样(只有较少的行)。继续您的示例,您现在可以分配到第一行的“元组”列:
a[:1]['tuple'] = (1, 2)
那么,......为什么a[0]['tuple'] = (1, 2)
不起作用?
好吧,请记住a[0]
会返回void
个对象。所以,当你打电话
a[0]['tuple'] = (1, 2) # this line fails
您将tuple
分配给该void
对象的'tuple'元素。 注意:尽管您已将此索引称为“元组”,但它仍存储为ndarray
:
print type(a[0]['tuple']) # = numpy.ndarray
因此,这意味着需要将元组强制转换为ndarray
。 但是,void
对象无法转换赋值(这只是猜测),因为它可以包含任意数据类型,因此它不知道要转换为什么类型。为了解决这个问题,您可以自己投射输入:
a[0]['tuple'] = np.array((1, 2))
我们得到不同的错误这一事实表明上述行可能不适合您,因为转换会解决我收到的错误 - 而不是您收到的错误。
<强>附录:强>
那么为什么以下工作呢?
a[0]['tuple'][:] = (1, 2)
在这里,当您添加[:]
时,您正在为数组建立索引,但如果没有这个,您将索引到void
对象。换句话说,a[0]['tuple'][:]
表示“替换存储数组的元素”(由数组处理),a[0]['tuple']
表示“替换存储的数组”(由void
处理)。
<强>尾声:强>
奇怪的是,访问行(即使用0索引)似乎丢弃了基本数组,但它仍然允许您分配给基数组。
print a['tuple'].base is a # = True
print a[0].base is a # = False
a[0] = ((1, 2),) # `a` is changed
也许void
实际上不是一个数组,所以它没有基数组,但是为什么它有一个base
属性呢?
答案 2 :(得分:1)
这是一个上游错误,已修复为NumPy PR #5947,并修复了1.9.3。