python - 来自数组和 np.dtype 对象的结构化数组
问题描述
以下代码使用 dtype 对象构造一个 NumPy 数组:
dt = np.dtype([
("index", np.int32),
("timestamp", np.int32),
("volume", np.float32)
])
arr = np.array([
[0, 20, 3],
[1, 21, 2],
[2, 23, 8],
[3, 26, 5],
[4, 31, 9]
]).astype(dt)
的预期结果arr
将是:
>>> arr
array([[ 0, 20, 334.],
[ 1, 21, 254.],
[ 2, 23, 823.],
[ 3, 26, 521.],
[ 4, 31, 943.]])
>>> arr[0]
array([ 0, 20, 334.])
但是上面的代码实际上是这样创建的:
>>> arr
array([[( 0, 0, 0.), ( 20, 20, 20.), (334, 334, 334.)],
[( 1, 1, 1.), ( 21, 21, 21.), (254, 254, 254.)],
[( 2, 2, 2.), ( 23, 23, 23.), (823, 823, 823.)],
[( 3, 3, 3.), ( 26, 26, 26.), (521, 521, 521.)],
[( 4, 4, 4.), ( 31, 31, 31.), (943, 943, 943.)]],
dtype=[('index', '<i4'), ('timestamp', '<i4'), ('volume', '<f4')])
>>> arr[0]
array([( 0, 0, 0.), ( 20, 20, 20.), (334, 334, 334.)],
dtype=[('index', '<i4'), ('timestamp', '<i4'), ('volume', '<f4')])
为什么 NumPy 为每种数据类型创建每个值的版本,而不是将每一列映射到它自己的数据类型(并且只有这个)?我猜我在那里做错了什么。有没有办法达到我期望的结果?
解决方案
这里的问题是,对于结构化数组的创建,您需要一个元组列表。这在Structured Datatype Creation中提到,其中指出在其他不太常见的数组创建方法中,输入数据必须是元组列表,每个字段一个元组。
所以你可以做的就是把你的数组变成一个元组列表(zip
在这里会很方便)并使用它构建结构化数组np.fromiter
并指定dt
为dtype
:
np.fromiter(zip(*arr.T), dtype=dt)
array([(0, 20, 3.), (1, 21, 2.), (2, 23, 8.), (3, 26, 5.), (4, 31, 9.)],
dtype=[('index', '<i4'), ('timestamp', '<i4'), ('volume', '<f4')])
@hpaulj 在评论中提到的另一种(鲜为人知的)方法是 using np.lib.recfunctions.unstructured_to_structured
,它可用于直接从arr
dtype 对象构造结构化数组:
np.lib.recfunctions.unstructured_to_structured(a, dt)
array([(0, 20, 3.), (1, 21, 2.), (2, 23, 8.), ..., (2, 23, 8.),
(3, 26, 5.), (4, 31, 9.)],
dtype=[('index', '<i4'), ('timestamp', '<i4'), ('volume', '<f4')])
或者基于this other post还有可能创建一个记录数组,一个ndarray子类,在使用方面与结构化数组非常相似,它带有几个相关的帮助函数,例如np.core.records.fromarrays
可用于创建数组以一种简单的方式:
np.core.records.fromarrays(arr.T,
names='index, timestamp, volume',
formats = '<i4, <i4, <f4')
rec.array([(0, 20, 3.), (1, 21, 2.), (2, 23, 8.), (3, 26, 5.),
(4, 31, 9.)],
dtype=[('index', '<i4'), ('timestamp', '<i4'), ('volume', '<f4')])
或者从np.dtype
对象创建它:
names, dtypes = list(zip(*dt.descr))
np.core.records.fromarrays(arr.transpose(),
names= ', '.join(names),
formats = ', '.join(dtypes))
比较上述方法和其他一些可能方法的时间安排:
a = np.concatenate([arr]*1000, axis=0)
%%timeit
np.core.records.fromarrays(a.T,
names='index, timestamp, volume',
formats = '<i4, <i4, <f4')
# 57.9 µs ± 1.18 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit np.lib.recfunctions.unstructured_to_structured(a, dt)
# 79.6 µs ± 1.32 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit np.fromiter(zip(*a.T), dtype=dt)
#2.1 ms ± 69.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit np.fromiter(map(tuple, a), dtype=dt)
#6.34 ms ± 65.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit np.array(list(zip(*a.T)), dtype=dt)
# 2.17 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
推荐阅读
- javascript - 无法使用 userchrome.js 在 Firefox 中禁用 ctrl-w
- python - Pandas - 用列名标记时间序列图的 x 轴
- python - 添加第二个搜索条件 Json API Python
- c# - 获取 C# 中的分配总数
- bash - 使用 GNU 并行读取文件
- csv - 使用 csv 文件将现有客户导入 Shopify 时出错
- wordpress - 为所有用户添加指向统计站点的链接
- eclipse - Eclipse MCU J-Link 调试器在 ldrb r3,[r7,#8] 上崩溃。地址有效 - [编辑 - 硬件问题]
- xml - 从字符串转换为 xml 的异常
- c - 将 RTOS 整合到一个简单的传感器接口项目中以用于学习目的的想法