将 numpy 数组转换为结构化数组
Convert a numpy array to a structured array
鉴于 numpy 数组中行的字节宽度与 dtype 定义的结构中字段的总宽度相同,是否有一种简单的方法可以将这种 numpy 数组转换为结构化数组?
例如,my_type
定义了一种数据类型,在所有字段中每个数据元素有 5 个字节:[('checksum','u2'), ('word', 'B', (3,))]
。然后我想将 numpy 数组 [[ 1 2 3 4 5] [ 11 12 13 14 15]]
转换为结构化数组 [( 258, [ 3, 4, 5]) (2828, [13, 14, 15])]
.
我最初的尝试是这样的:
import numpy as np
from random import randint
# generate data
array = np.array([(1,2,3,4,5),
(11,12,13,14,15)], dtype = np.uint8)
# format data
my_type = np.dtype([('checksum','u2'), ('word', 'B', (3,))])
structured_array = np.array([array], dtype=my_type)
但是,正如预期的那样,由于 numpy broadcasting rules,我得到以下信息:
[[[( 1, [ 1, 1, 1]) ( 2, [ 2, 2, 2]) ( 3, [ 3, 3, 3])
( 4, [ 4, 4, 4]) ( 5, [ 5, 5, 5])]
[( 11, [ 11, 11, 11]) (12, [12, 12, 12]) (13, [13, 13, 13])
(14, [14, 14, 14]) (15, [15, 15, 15])]]]
我目前不太优雅的解决方案是遍历数组的行并将它们映射到结构:
structured_array = np.zeros(array.shape[0], dtype=my_type)
for idx, row in enumerate(array):
for key, value in my_type.fields.items():
b = row[value[1]:value[1]+value[0].itemsize]
if len(structured_array[idx][key].shape):
structured_array[idx][key] = b
else:
structured_array[idx][key] = int.from_bytes(b, byteorder='big', signed=False)
所以问题是是否有一种简单的单行解决方案可以针对结构化数组的任意数据类型执行此任务,而无需解析 numpy 数组的字节?
In [222]: x = np.array([[ 0, 2, 3, 4, 5], [ 0, 12, 13, 14, 15]])
In [223]: dt = np.dtype([('checksum','u2'), ('word', 'B', (3,))])
我从过去的使用中知道,genfromtxt
可以处理相对复杂的数据类型:
In [224]: np.savetxt('temp', x[:,1:], fmt='%d')
In [225]: cat temp
2 3 4 5
12 13 14 15
In [226]: data = np.genfromtxt('temp', dtype=dt)
In [227]: data
Out[227]:
array([( 2, [ 3, 4, 5]), (12, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
但我还没有深入研究它的代码以了解它如何将平面行数据映射到数据类型。
但事实证明我在评论中提到的 unstructured_to_structured
可以处理您的数据类型:
In [228]: import numpy.lib.recfunctions as rf
In [229]: rf.unstructured_to_structured(x[:,1:],dtype=dt)
Out[229]:
array([( 2, [ 3, 4, 5]), (12, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
但对于更简单的 dtype,我和其他人经常建议将列表列表转换为元组列表。
In [230]: [tuple(row) for row in x[:,1:]]
Out[230]: [(2, 3, 4, 5), (12, 13, 14, 15)]
许多 recfunctions
使用逐字段复制
In [231]: res = np.zeros(2, dtype=dt)
In [232]: res
Out[232]:
array([(0, [0, 0, 0]), (0, [0, 0, 0])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
In [233]: res['checksum']= x[:,1]
In [234]: res['word']
Out[234]:
array([[0, 0, 0],
[0, 0, 0]], dtype=uint8)
In [235]: res['word'] = x[:,2:]
In [236]: res
Out[236]:
array([( 2, [ 3, 4, 5]), (12, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
字节视图
我错过了您想要重新打包字节的事实。我上面的答案将输入行视为 4 numbers/ints ,它将分配给复合数据类型中的 4 个插槽。但是对于 uint8
输入,以及 u2
和 u1
插槽,你想要 view
具有新数据类型的 5 个字节,而不是创建一个新数组。
In [332]: dt
Out[332]: dtype([('checksum', '<u2'), ('word', 'u1', (3,))])
In [333]: arr = np.array([(1,2,3,4,5),
...: (11,12,13,14,15)], dtype = np.uint8)
In [334]: arr.view(dt)
Out[334]:
array([[( 513, [ 3, 4, 5])],
[(3083, [13, 14, 15])]],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
view
添加了一个我们需要删除的维度:
In [335]: _.shape
Out[335]: (2, 1)
In [336]: arr.view(dt).reshape(2)
Out[336]:
array([( 513, [ 3, 4, 5]), (3083, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
并更改 u2
字段的结尾:
In [337]: dt = np.dtype([('checksum','>u2'), ('word', 'B', (3,))])
In [338]: arr.view(dt).reshape(2)
Out[338]:
array([( 258, [ 3, 4, 5]), (2828, [13, 14, 15])],
dtype=[('checksum', '>u2'), ('word', 'u1', (3,))])
鉴于 numpy 数组中行的字节宽度与 dtype 定义的结构中字段的总宽度相同,是否有一种简单的方法可以将这种 numpy 数组转换为结构化数组?
例如,my_type
定义了一种数据类型,在所有字段中每个数据元素有 5 个字节:[('checksum','u2'), ('word', 'B', (3,))]
。然后我想将 numpy 数组 [[ 1 2 3 4 5] [ 11 12 13 14 15]]
转换为结构化数组 [( 258, [ 3, 4, 5]) (2828, [13, 14, 15])]
.
我最初的尝试是这样的:
import numpy as np
from random import randint
# generate data
array = np.array([(1,2,3,4,5),
(11,12,13,14,15)], dtype = np.uint8)
# format data
my_type = np.dtype([('checksum','u2'), ('word', 'B', (3,))])
structured_array = np.array([array], dtype=my_type)
但是,正如预期的那样,由于 numpy broadcasting rules,我得到以下信息:
[[[( 1, [ 1, 1, 1]) ( 2, [ 2, 2, 2]) ( 3, [ 3, 3, 3])
( 4, [ 4, 4, 4]) ( 5, [ 5, 5, 5])]
[( 11, [ 11, 11, 11]) (12, [12, 12, 12]) (13, [13, 13, 13])
(14, [14, 14, 14]) (15, [15, 15, 15])]]]
我目前不太优雅的解决方案是遍历数组的行并将它们映射到结构:
structured_array = np.zeros(array.shape[0], dtype=my_type)
for idx, row in enumerate(array):
for key, value in my_type.fields.items():
b = row[value[1]:value[1]+value[0].itemsize]
if len(structured_array[idx][key].shape):
structured_array[idx][key] = b
else:
structured_array[idx][key] = int.from_bytes(b, byteorder='big', signed=False)
所以问题是是否有一种简单的单行解决方案可以针对结构化数组的任意数据类型执行此任务,而无需解析 numpy 数组的字节?
In [222]: x = np.array([[ 0, 2, 3, 4, 5], [ 0, 12, 13, 14, 15]])
In [223]: dt = np.dtype([('checksum','u2'), ('word', 'B', (3,))])
我从过去的使用中知道,genfromtxt
可以处理相对复杂的数据类型:
In [224]: np.savetxt('temp', x[:,1:], fmt='%d')
In [225]: cat temp
2 3 4 5
12 13 14 15
In [226]: data = np.genfromtxt('temp', dtype=dt)
In [227]: data
Out[227]:
array([( 2, [ 3, 4, 5]), (12, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
但我还没有深入研究它的代码以了解它如何将平面行数据映射到数据类型。
但事实证明我在评论中提到的 unstructured_to_structured
可以处理您的数据类型:
In [228]: import numpy.lib.recfunctions as rf
In [229]: rf.unstructured_to_structured(x[:,1:],dtype=dt)
Out[229]:
array([( 2, [ 3, 4, 5]), (12, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
但对于更简单的 dtype,我和其他人经常建议将列表列表转换为元组列表。
In [230]: [tuple(row) for row in x[:,1:]]
Out[230]: [(2, 3, 4, 5), (12, 13, 14, 15)]
许多 recfunctions
使用逐字段复制
In [231]: res = np.zeros(2, dtype=dt)
In [232]: res
Out[232]:
array([(0, [0, 0, 0]), (0, [0, 0, 0])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
In [233]: res['checksum']= x[:,1]
In [234]: res['word']
Out[234]:
array([[0, 0, 0],
[0, 0, 0]], dtype=uint8)
In [235]: res['word'] = x[:,2:]
In [236]: res
Out[236]:
array([( 2, [ 3, 4, 5]), (12, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
字节视图
我错过了您想要重新打包字节的事实。我上面的答案将输入行视为 4 numbers/ints ,它将分配给复合数据类型中的 4 个插槽。但是对于 uint8
输入,以及 u2
和 u1
插槽,你想要 view
具有新数据类型的 5 个字节,而不是创建一个新数组。
In [332]: dt
Out[332]: dtype([('checksum', '<u2'), ('word', 'u1', (3,))])
In [333]: arr = np.array([(1,2,3,4,5),
...: (11,12,13,14,15)], dtype = np.uint8)
In [334]: arr.view(dt)
Out[334]:
array([[( 513, [ 3, 4, 5])],
[(3083, [13, 14, 15])]],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
view
添加了一个我们需要删除的维度:
In [335]: _.shape
Out[335]: (2, 1)
In [336]: arr.view(dt).reshape(2)
Out[336]:
array([( 513, [ 3, 4, 5]), (3083, [13, 14, 15])],
dtype=[('checksum', '<u2'), ('word', 'u1', (3,))])
并更改 u2
字段的结尾:
In [337]: dt = np.dtype([('checksum','>u2'), ('word', 'B', (3,))])
In [338]: arr.view(dt).reshape(2)
Out[338]:
array([( 258, [ 3, 4, 5]), (2828, [13, 14, 15])],
dtype=[('checksum', '>u2'), ('word', 'u1', (3,))])