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)
我原以为这两个选项都有用.意见?
我在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报告中给出了对此的解释.
我得到的错误与你的不同(使用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
属性?