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

Nic*_*mer 16 python arrays numpy user-defined-types

我在numpy中遇到了以下奇怪的东西,可能是也可能不是bug:

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
Run Code Online (Sandbox Code Playgroud)

我原以为这两个选项都有用.意见?

bmu*_*bmu 9

我在numpy讨论列表上问过这个问题.Travis Oliphant 在这里回答.

引用他的回答:

简短的回答是,这不是一个"正常"的错误,但它可以被认为是一个"设计"错误(虽然问题可能不是直接解决).这意味着它可能在短期内不会改变 - 你应该只使用第一个拼写.

由于几个原因,结构化数组可能是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函数.我认为这种行为应该有效.

numpy bug报告中给出了对此的解释.


Ton*_* Yu 8

我得到的错误与你的不同(使用numpy 1.7.0.dev):

ValueError: setting an array element with a sequence.
Run Code Online (Sandbox Code Playgroud)

所以下面的解释对你的系统可能不正确(或者甚至可能是我所看到的错误解释).

首先,请注意索引一行结构化数组会为您提供一个numpy.void对象(请参阅数据类型docs)

import numpy as np
dt = np.dtype([('tuple', (int, 2))])
a = np.zeros(3, dt)
print type(a[0]) # = numpy.void
Run Code Online (Sandbox Code Playgroud)

据我所知,void它有点像Python列表,因为它可以容纳不同数据类型的对象,这是有道理的,因为结构化数组中的列可以是不同的数据类型.

如果您切出第一行而不是索引,则得到ndarray:

print type(a[:1]) # = numpy.ndarray
Run Code Online (Sandbox Code Playgroud)

这类似于Python列表的工作方式:

b = [1, 2, 3]
print b[0] # 1
print b[:1] # [1]
Run Code Online (Sandbox Code Playgroud)

切片返回原始序列的缩短版本,但索引返回一个元素(这里是一个int;上面是一个void类型).

因此,当您切入结构化数组的行时,您应该期望它的行为与原始数组一样(只有较少的行).继续您的示例,您现在可以分配到第一行的"元组"列:

a[:1]['tuple'] = (1, 2)
Run Code Online (Sandbox Code Playgroud)

那么,......为什么不起作用a[0]['tuple'] = (1, 2)

好吧,回想一下,a[0]返回一个void对象.所以,当你打电话的时候

a[0]['tuple'] = (1, 2) # this line fails
Run Code Online (Sandbox Code Playgroud)

你正在tuple为该void对象的'tuple'元素赋值.注意:尽管您已将此索引称为"元组",但它仍存储为ndarray:

print type(a[0]['tuple']) # = numpy.ndarray
Run Code Online (Sandbox Code Playgroud)

所以,这意味着元组需要被强制转换为ndarray.但是,void对象不能转换赋值(这只是一个猜测),因为它可以包含任意数据类型,因此它不知道要转换为什么类型.要解决这个问题,您可以自己投射输入:

a[0]['tuple'] = np.array((1, 2))
Run Code Online (Sandbox Code Playgroud)

我们得到不同错误这一事实表明上述行可能不适合您,因为转换会解决我收到的错误 - 而不是您收到的错误.

附录:

那么为什么以下工作呢?

a[0]['tuple'][:] = (1, 2)
Run Code Online (Sandbox Code Playgroud)

在这里,您在添加时索引数组[:],但没有它,您将索引到该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
Run Code Online (Sandbox Code Playgroud)

也许void不是一个真正的数组,所以它没有一个基数组,...但是为什么它有一个base属性?


Nic*_*mer 2

这是一个上游错误,从NumPy PR #5947开始修复,并在 1.9.3 中修复。