繁体   English   中英

在NumPy 1.14中将结构化数组的切片转换为常规NumPy数组

[英]Convert a slice of a structured array to regular NumPy array in NumPy 1.14

注意1:在我的情况下, 对此问题没有给出任何答案。

注意2:解决方案必须在NumPy 1.14中运行。

假设我有以下结构化数组:

arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b', 'f4'), ('c', 'f4'), ('d', 'f4')])

现在,我将像这样分割为结构化数据类型:

arr2 = arr[['a', 'b']]

现在,我正在尝试将该切片转换为常规数组:

out = arr2[0].view((np.float32, 2))

导致

ValueError: Changing the dtype of a 0d array is only supported if the itemsize is unchanged

我想要得到的只是一个常规数组,如下所示:

[105.0, 34.0]

注意,该示例被简化以使其最小。 在我的实际用例中,我显然不是在处理包含一个元素的数组。

我知道此解决方案有效:

out = np.asarray(list(arr2[0]))

但是我认为必须有一个比将NumPy数组中已经存在的数据复制到列表然后再返回到数组更有效的解决方案。 我认为有一种方法可以保留在NumPy中,也许根本不复制任何数据,我只是不知道该怎么做。

一维数组的确使用view转换:

In [270]: arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b','f4'), ('c', 'f4'), ('d', 'f4')])
In [271]: arr
Out[271]: 
array([(105., 34., 145., 217.)],
      dtype=[('a', '<f4'), ('b', '<f4'), ('c', '<f4'), ('d', '<f4')])
In [272]: arr.view('<f4')
Out[272]: array([105.,  34., 145., 217.], dtype=float32)

当我们尝试转换单个元素时,会出现以下错误:

In [273]: arr[0].view('<f4')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-273-70fbab8f61ba> in <module>()
----> 1 arr[0].view('<f4')

ValueError: Changing the dtype of a 0d array is only supported if the itemsize is unchanged

早期的view通常需要对尺寸进行调整。 我怀疑最近对结构化数组的处理发生了变化(一次索引多个字段时最明显),此错误是有意还是无意的结果。

在整个数组的情况下,它将1d,4字段数组更改为1d,4元素数组(1)到(4,)。 但是更改元素从()到(4,)。

过去,我建议使用tolist作为解决view (和astype )问题的最可靠方法:

In [274]: arr[0].tolist()
Out[274]: (105.0, 34.0, 145.0, 217.0)
In [279]: list(arr[0].tolist())
Out[279]: [105.0, 34.0, 145.0, 217.0]
In [280]: np.array(arr[0].tolist())
Out[280]: array([105.,  34., 145., 217.])

item也是从其numpy结构中拉出元素的好方法:

In [281]: arr[0].item()
Out[281]: (105.0, 34.0, 145.0, 217.0)

tolostitem的结果是一个元组。

您担心速度。 但是,您只是在转换一个元素。 在1000个项目的数组上使用tolist时,担心速度是一回事,而在处理1个元素时,则是另一回事。

In [283]: timeit arr[0]
131 ns ± 1.31 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [284]: timeit arr[0].tolist()
1.25 µs ± 11.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [285]: timeit arr[0].item()
1.27 µs ± 2.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [286]: timeit arr.tolist()
493 ns ± 17.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [287]: timeit arr.view('f4')
1.74 µs ± 18.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

您可以采用不会将尺寸减小到0的方式来索引元素(不是它对速度有很大帮助):

In [288]: arr[[0]].view('f4')
Out[288]: array([105.,  34., 145., 217.], dtype=float32)
In [289]: timeit arr[[0]].view('f4')
6.54 µs ± 15.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [290]: timeit arr[0:1].view('f4')
2.63 µs ± 105 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [298]: timeit arr[0][None].view('f4')
4.28 µs ± 160 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

view仍然需要改变形状; 考虑一个大数组:

In [299]: arrs = np.repeat(arr, 10000)
In [301]: arrs.view('f4')
Out[301]: array([105.,  34., 145., ...,  34., 145., 217.], dtype=float32)
In [303]: arrs.shape
Out[303]: (10000,)
In [304]: arrs.view('f4').shape
Out[304]: (40000,)

视图仍然是1d,因为我们可能想要一个(10000,4)形状的2d数组。

更好的视图更改:

In [306]: arrs.view(('f4',4))
Out[306]: 
array([[105.,  34., 145., 217.],
       [105.,  34., 145., 217.],
       [105.,  34., 145., 217.],
       ...,
       [105.,  34., 145., 217.],
       [105.,  34., 145., 217.],
       [105.,  34., 145., 217.]], dtype=float32)
In [307]: _.shape
Out[307]: (10000, 4)

这适用于1元素数组,无论是1d还是0d:

In [308]: arr.view(('f4',4))
Out[308]: array([[105.,  34., 145., 217.]], dtype=float32)
In [309]: _.shape
Out[309]: (1, 4)
In [310]: arr[0].view(('f4',4))
Out[310]: array([105.,  34., 145., 217.], dtype=float32)
In [311]: _.shape
Out[311]: (4,)

这是您的链接中的答案之一所建议的: https : //stackoverflow.com/a/10171321/901925

与您的评论相反,它对我有用:

In [312]: arr[0].view((np.float32, len(arr.dtype.names)))
Out[312]: array([105.,  34., 145., 217.], dtype=float32)
In [313]: np.__version__
Out[313]: '1.14.0'

进行编辑:

In [84]: arr = np.array([(105.0, 34.0, 145.0, 217.0)], dtype=[('a', 'f4'), ('b','f4'), ('c', 'f4'), ('d', 'f4')])
In [85]: arr2 = arr[['a', 'b']]
In [86]: arr2
Out[86]: 
array([(105., 34.)],
      dtype={'names':['a','b'], 'formats':['<f4','<f4'], 'offsets':[0,4], 'itemsize':16})

In [87]: arr2.view(('f4',2))
...
ValueError: Changing the dtype to a subarray type is only supported if the total itemsize is unchanged

请注意, arr2 dtype包含offsets值。 在最新的numpy版本中,多字段选择已更改。 现在,它是一个真实的视图,保留了原始数据-所有数据,而不仅仅是选定字段。 项目大小不变:

In [93]: arr.itemsize
Out[93]: 16
In [94]: arr2.itemsize
Out[94]: 16

arr.view(('f4',4)arr2.view(('f4',4))产生相同的结果。

因此,您无法view (更改dtype)部分字段。 您必须首先view整个数组的view ,然后选择行/列,或者使用tolist

我正在使用1.14.0 1.14.1发行说明说:

在1.14.0中,结构化数组的多字段索引返回了一个视图,而不是一个副本,该更改已还原,但仍在NumPy 1.15上正常进行。 受影响的用户应阅读《 1.14.1 Numpy用户指南》中的“基本/结构化数组/访问多个字段”部分,以获取有关如何管理此过渡的建议。

https://docs.scipy.org/doc/numpy-1.14.2/user/basics.rec.html#accessing-multiple-fields

这仍在开发中。 该文档提到了repack_fields函数,但尚不存在。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM