从列表创建结构化数组

12

我有一个简单的元素列表,我正在尝试将其制作成结构化数组

这种天真的方法失败了:

y = np.array([1,2,3], dtype=[('y', float)])
TypeError: expected an object with a buffer interface

把每个元素放进一个元组中是可行的:

# Manuel way
y = np.array([(1,), (2,), (3,)], dtype=[('y', float)])
# Comprehension
y = np.array([tuple((x,)) for x in [1,2,3]], dtype=[('y', float)])

如果我先从列表中创建一个数组,它也可以正常工作:

y = np.array(np.array([1,2,3]), dtype=[('y', float)])

我有点困惑。为什么后面的方法可行,但是当提供一个简单的列表时,numpy却不能解决问题呢?

有什么推荐的方法吗?创建中间的array可能不会对性能产生很大影响,但这不是最优的方法吧?

我也很惊讶这些方法不起作用:

# All lists
y = np.array([[1,], [2,], [3,]], dtype=[('y', float)])
TypeError: expected an object with a buffer interface
# All tuples
y = np.array(((1,), (2,), (3,)), dtype=[('y', float)])
ValueError: size of tuple must match number of fields.

我对结构化数组还很陌生,而且我似乎不记得numpy对输入类型那么挑剔了。 我肯定是漏了什么。


我对结构化数组还很陌生,而且我不记得numpy对于输入类型如此苛刻。我一定漏了些什么。

因为行必须使用元组进行分配,因为结构化数组的每个元素都是一个“结构体”,所以存在某种复合数据类型。另一种选择是使用缓冲区(这就是为什么np.array有效的原因)。 - juanpa.arrivillaga
这在这里有所记录。 - juanpa.arrivillaga
前面的文档段落提到了“请注意,x是用元组列表创建的。”这种输入样式与显示样式相匹配。我更喜欢您的列表推导方法,或者逐个填充预分配的数组字段。 - hpaulj
2个回答

6

在编译代码中,np.array如何处理各种输入的细节被隐藏了起来。正如许多有关创建对象数据类型数组的问题所显示的那样,这可能会变得复杂和令人困惑。其基本模型是从嵌套列表创建多维数值数组。

np.array([[1,2,3],[4,5,6]])

在实现结构化数组时,开发人员采用了元组作为一种将记录与仅仅另一个嵌套维度区分开的方式。这在结构化数组的显示中是显而易见的。
在定义结构化数组时,元组列表的要求也是必需的,尽管这种要求在文档中有些隐蔽。
In [382]: dt=np.dtype([('y',int)])
In [383]: np.array(alist,dt)

TypeError: a bytes-like object is required, not 'int'

这是我的版本'1.12.0'的错误信息。在你的版本中似乎不同。

正如你所说,列表推导式可以将嵌套列表转换为元组列表。

In [384]: np.array([tuple(i) for i in alist],dt)
Out[384]: 
array([(1,), (2,), (3,)], 
      dtype=[('y', '<i4')])

在回答SO问题时,这是我最常使用的方法。要么这样,要么迭代设置预分配数组的字段(通常记录比字段多得多,因此该循环不会很昂贵)。

看起来,将数组包装在结构化数组调用中等效于astype调用:

In [385]: np.array(np.array(alist),dt)
Out[385]: 
array([[(1,)],
       [(2,)],
       [(3,)]], 
      dtype=[('y', '<i4')])
In [386]: np.array(alist).astype(dt)
Out[386]: 
array([[(1,)],
       [(2,)],
       [(3,)]], 
      dtype=[('y', '<i4')])

但需要注意的是维度数量的变化。元组列表创建了一个 (3,) 的数组。astype 将一个 (3,1) 的数值数组转换成了一个 (3,1) 的结构化数组。
元组的一部分告诉 np.array 在哪里放置数组维度和记录之间的分割线。它会进行解释。
[(3,), (1,), (2,)]
[record, record, record]

相比之下,[[1],[2],[3]] 的自动翻译可能会产生以下结果:

[[record],[record],[record]]

当dtype为数字(非结构化)时,它忽略了列表和元组之间的区别。
In [388]: np.array([tuple(i) for i in alist],int)
Out[388]: 
array([[1],
       [2],
       [3]])

当数据类型为复合型时,开发人员选择使用元组层作为重要信息。


考虑一种更复杂的结构化数据类型。

In [389]: dt1=np.dtype([('y',int,(2,))])
In [390]: np.ones((3,), dt1)
Out[390]: 
array([([1, 1],), ([1, 1],), ([1, 1],)], 
      dtype=[('y', '<i4', (2,))])
In [391]: np.array([([1,2],),([3,4],)])
Out[391]: 
array([[[1, 2]],

       [[3, 4]]])
In [392]: np.array([([1,2],),([3,4],)], dtype=dt1)
Out[392]: 
array([([1, 2],), ([3, 4],)], 
      dtype=[('y', '<i4', (2,))])

显示器(和输入)中包含列表,元组以及列表中的元组。这只是个开始。
In [393]: dt1=np.dtype([('x',dt,(2,))])
In [394]: dt1
Out[394]: dtype([('x', [('y', '<i4')], (2,))])
In [395]: np.ones((2,),dt1)
Out[395]: 
array([([(1,), (1,)],), ([(1,), (1,)],)], 
      dtype=[('x', [('y', '<i4')], (2,))])

将元组列表转换为结构化的numpy数组


1
谢谢您提供这么全面的答案,现在我理解得更清楚了。以我的经验来看,NumPy非常容易上手,就算没有Python背景也能够轻松掌握,但是处理结构化数组需要更多的注意和对内部操作的理解,让我有些惊讶。 - Jérôme

1

np.array()函数接受列表的列表作为输入。因此,如果您想创建一个2 * 2的矩阵,您需要这样做:

X = np.array([[1,2], [3,4]])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接