在 NumPy 1.14 中将结构化数组的一部分转换为常规 NumPy 数组
Convert a slice of a structured array to regular NumPy array in NumPy 1.14
注 1:对 this question 的回答中有 None 对我有效。
注意 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)
tolost
和 item
的结果是一个元组。
你担心速度。但你只是在转换一个元素。在 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,)
视图仍然是一维的,我们可能想要一个 (10000,4) 形状的二维数组。
更好的视图更改:
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,)
这是在您的 link 中的一个答案中提出的:
与你在那里的评论相反,它对我有用:
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 版本中,多字段 selection 已更改。它现在是一个真实的视图,保留了原始数据——所有数据,而不仅仅是 selected 字段。项目大小不变:
In [93]: arr.itemsize
Out[93]: 16
In [94]: arr2.itemsize
Out[94]: 16
arr.view(('f4',4)
和 arr2.view(('f4',4))
产生相同的结果。
因此您不能 view
(更改数据类型)部分字段集。你必须先取整个数组的 view
,然后 select rows/columns,或者使用 tolist
.
我正在使用 1.14.0
。 1.14.1
的发行说明说:
The change in 1.14.0 that multi-field indexing of structured arrays returns a
view instead of a copy has been reverted but remains on track for NumPy 1.15.
Affected users should read the 1.14.1 Numpy User Guide section
"basics/structured arrays/accessing multiple fields" for advice on how to
manage this transition.
https://docs.scipy.org/doc/numpy-1.14.2/user/basics.rec.html#accessing-multiple-fields
这仍在开发中。该文档提到了一个 repack_fields
函数,但该函数尚不存在。
注 1:对 this question 的回答中有 None 对我有效。
注意 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)
tolost
和 item
的结果是一个元组。
你担心速度。但你只是在转换一个元素。在 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,)
视图仍然是一维的,我们可能想要一个 (10000,4) 形状的二维数组。
更好的视图更改:
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,)
这是在您的 link 中的一个答案中提出的:
与你在那里的评论相反,它对我有用:
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 版本中,多字段 selection 已更改。它现在是一个真实的视图,保留了原始数据——所有数据,而不仅仅是 selected 字段。项目大小不变:
In [93]: arr.itemsize
Out[93]: 16
In [94]: arr2.itemsize
Out[94]: 16
arr.view(('f4',4)
和 arr2.view(('f4',4))
产生相同的结果。
因此您不能 view
(更改数据类型)部分字段集。你必须先取整个数组的 view
,然后 select rows/columns,或者使用 tolist
.
我正在使用 1.14.0
。 1.14.1
的发行说明说:
The change in 1.14.0 that multi-field indexing of structured arrays returns a view instead of a copy has been reverted but remains on track for NumPy 1.15. Affected users should read the 1.14.1 Numpy User Guide section "basics/structured arrays/accessing multiple fields" for advice on how to manage this transition.
https://docs.scipy.org/doc/numpy-1.14.2/user/basics.rec.html#accessing-multiple-fields
这仍在开发中。该文档提到了一个 repack_fields
函数,但该函数尚不存在。