【问题标题】:Named dtype array: Difference between a[0]['name'] and a['name'][0]?命名 dtype 数组:a[0]['name'] 和 a['name'][0] 之间的区别?
【发布时间】:2012-03-17 06:08:08
【问题描述】:

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

我原以为以下两个选项都有效。 意见?

【问题讨论】:

  • 提示:将代码发布到 SO 时,请发布我们可以剪切和粘贴的位;对于 Python,这意味着使用 # 插入 cmets,而不是 %
  • 有趣的东西,我也看到使用 1.6.1 给出相同的结果...
  • 有点奇怪,但a[0]['tuple'][:] = (1,2) 有效,也许那里有线索......
  • 显式切片也有效 a[0]['tuple'][0:2] = (1,2)

标签: python arrays numpy user-defined-types


【解决方案1】:

我在 numpy-discussion 列表上问过这个问题。 Travis Oliphant 回答了here

引用他的回答:

简短的回答是,这并不是一个真正的“正常”错误,但它可以被视为“设计”错误(尽管这些问题可能不容易解决)。这意味着它可能不会在短期内改变——你应该只使用第一个拼写。

由于多种原因,结构化数组可能是 NumPy 的一个令人困惑的领域。您已经构建了一个涉及其中几个的示例。您有一个数据类型,它是一个具有一个成员(“元组”)的“结构”数组。该成员包含一个 2 整数向量。

首先,重要的是要记住,用 Python,做

a['tuple'][0] = (1,2)

等价于

b = a['元组']; b[0] = (1,2)

同理,

a[0]['tuple'] = (1,2)

等价于

b = a[0]; b['元组'] = (1,2)

要了解这种行为,我们需要剖析代码路径和发生的情况。您在“a”中构建了一个 (3,) 数组。当您编写 b = a['tuple'] 时,您可能应该得到一个 (3,) 数组 (2,)-integers,但由于目前没有正式的 dtype 支持 (n,)-integers 作为一般 dtype在 NumPy 中,你会得到一个 (3,2) 整数数组,这是 NumPy 能给你的最接近的东西。通过

设置此对象的 [0] 行

a['tuple'][0] = (1,2)

工作得很好,可以满足您的期望。

另一方面,当你输入:

b = a[0]

你得到了一个数组标量,它是一种特别有趣的数组标量,可以保存记录。这个新对象的正式类型为 numpy.void ,它包含任何适合“VOID”基本 dtype 的“标量表示”。

出于某种原因:

b['tuple'] = [1,2]

不工作。在我的系统上我得到一个不同的错误:TypeError: object of type 'int' has no len()

我认为这应该作为问题跟踪器上的错误提交,暂时在这里:http://projects.scipy.org/numpy

如果有人想调查,问题最终是在 voidtype_setfields 中调用了 void->copyswap 函数。我认为这种行为应该有效。

a numpy bug report 对此进行了解释。

【讨论】:

    【解决方案2】:

    我得到一个与你不同的错误(使用 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]
    

    切片返回原始序列的缩短版本,但索引返回一个元素(此处为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 对象的“元组”元素。 注意:尽管您将此索引称为“元组”,但它仍存储为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 属性?

    【讨论】:

      【解决方案3】:

      这是一个上游错误,自 NumPy PR #5947 起已修复,已在 1.9.3 中修复。

      【讨论】:

        猜你喜欢
        • 2023-03-15
        • 1970-01-01
        • 2016-01-13
        • 2019-03-13
        • 2021-02-14
        • 1970-01-01
        • 2013-08-24
        • 2019-03-19
        • 1970-01-01
        相关资源
        最近更新 更多