当使用另一个numpy数组时,Numpy结构化数组创建不按预期工作

3zwjbxry  于 2023-10-19  发布在  其他
关注(0)|答案(2)|浏览(89)

我正在尝试从python中的其他数组创建一个numpy结构化数组。然而,这并不像我期望的那样工作:

# this does what I want
In [3]: x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])

In [4]: x['foo']
Out[4]: array([1, 3])

In [5]: x['foo'].shape
Out[5]: (2,)

# when creating the array from another array, the structure is different
In [6]:  y = np.array( np.array([(1, 2), (3, 4)]), dtype=[('foo', 'i8'), ('bar', 'f4')])

In [7]: y['foo'].shape
Out[7]: (2, 2)

# unpacking and packing into a list does not work either
In [8]:   z = np.array([zz for zz in np.array([(1, 2), (3, 4)])], dtype=[('foo', 'i8'), ('bar', 'f4')])

In [9]: z['foo'].shape
Out[9]: (2, 2)

所以x的结构化数组实现了我的期望和要求。但是当你使用另一个numpy数组时,我的应用程序需要它,结构就不同了。实际上你不能像x那样访问轴。
解包值并将其打包也不起作用。
不幸的是,文档不够清楚(至少对我来说),关于如何做到这一点。欢呼

zazmityj

zazmityj1#

有一个你应该熟悉的结构化数组文档页面。我现在就跳过为您查找链接。
通常,结构化数组的数据是作为元组列表提供的:

In [44]: x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])

In [45]: x
Out[45]: array([(1, 2.), (3, 4.)], dtype=[('foo', '<i8'), ('bar', '<f4')])

In [46]: x.tolist()
Out[46]: [(1, 2.0), (3, 4.0)]

此输入回显显示和tolist。常规数组显示为列表的列表。选择元组表示法是为了清楚地定义结构化数组的records
当你尝试生成y时,数组的每个元素都被“复制”以匹配dtype:

In [47]: y = np.array( np.array([(1, 2), (3, 4)]), dtype=[('foo', 'i8'), ('bar', 'f4')])

In [48]: y
Out[48]: 
array([[(1, 1.), (2, 2.)],
       [(3, 3.), (4, 4.)]], dtype=[('foo', '<i8'), ('bar', '<f4')])

In [49]: y.shape
Out[49]: (2, 2)

通常这不是我们想要的。重申一下,内部数组的tolist不是一个元组列表:

In [50]: np.array([(1, 2), (3, 4)]).tolist()
Out[50]: [[1, 2], [3, 4]]

viewastype并没有更好地工作。
然而,结构化数组文档讨论了recfunctions库。大多数是在recarrays更常见的时候写的(现在pandas已经取代了很多时间序列的工作)。
但是最近对结构化数组如何“切片”进行了一些修改,特别是对于多个字段,它增加了一些实用功能:

In [52]: import numpy.lib.recfunctions as rf

In [53]: y = rf.unstructured_to_structured( np.array([(1, 2), (3, 4)]), dtype=[('foo', 'i8'), ('bar', 'f4')])

In [54]: y
Out[54]: array([(1, 2.), (3, 4.)], dtype=[('foo', '<i8'), ('bar', '<f4')])

通常,rf函数创建一个具有所需复合dtype的目标数组,并逐个字段地将数据复制到其中。通常,记录的数量比字段的数量大得多,因此这种迭代复制相对有效。
在任何情况下,在使用结构化数组时都要认真对待元组列表规范。

x6yk4ghg

x6yk4ghg2#

可能有一个更聪明的方法(我从来不喜欢粗糙的或结构化的数组。我使用numpy来处理统一类型数据的传统单体数组。当我需要不同的类型或字段时,我会回到其他东西,比如pandas。所以,再一次,可能有更好的方法)。但在这里,我只是把你的尝试翻译成一个工作:

arr=np.array([(1, 2), (3, 4)])
y=np.array([tuple(zz) for zz in arr], dtype=[('foo', 'i8'), ('bar', 'f4')])

想法很简单:“如果它与元组一起工作,让它们有元组”:D
但是,也许有更好的想法
例如

y=np.empty((len(arr),), dtype=[('foo', 'i8'), ('bar', 'f4')])
for i,k in enumerate(y.dtype.names):
    y[k]=arr[:,i]

也可以。而且可能更快。这是一个python循环。但它只在字段上完成,而前一个是在行上。通常你有更多的行比字段。
至于为什么它不能在数组中工作:理解你想要的结果不是一个2x2的2D数组,就像你的输入数组一样。它是一个2×1阵列,每个单元都是一个结构。为什么它仍然非常有效(numpy可以遍历每个字段,形状和步幅,与另一个数组一样有效。
所以你的第一行从数据开始,元组的一维列表,从中你构建了一个“结构”的一维数组。
您的其他尝试从2D数组开始。

定时

所以,编辑,同时我测试了一些时间,我确认了我的第一个观点:我的第二个代码更快。在我的假设下。行比字段多。从10000×2阵列开始,第一个代码的运行时间为8 ms,而第二个代码的运行时间为29 μs。

hpaulj回答后编辑

所以,正如我所想的,确实有一个更聪明的方法。即使我的python不让我直接运行他的代码,因为它需要一个合适的类型,而不是[('foo', 'i8'), ('bar', 'f4')]。但这很容易解决

rf.unstructured_to_structured(arr, dtype=np.dtype([('foo', 'i8'), ('bar', 'f4')]))

然而,时间方面,虽然几乎一样快,这种方法似乎(奇怪)比我的第二个慢。在我的同一个例子中,它需要39μs,而不是“空然后为字段”的29μs。
尽管如此,这还是在同一个数量级上(与构建元组列表的8毫秒相比),使用标准函数可能比重新发明轮子更好。

相关问题