命名dtype数组:[0] ['name']和['name'] [0]之间的差异?

时间:2012-02-27 18:58:34

标签: python arrays numpy user-defined-types

我在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

我原以为下面的两个选项都可以使用。 意见?

3 个答案:

答案 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。