Python 使用numpy的嵌套结构化数组字段访问

Python 使用numpy的嵌套结构化数组字段访问,python,numpy,scipy,Python,Numpy,Scipy,我正在用Python解析Matlab结构化数组。为了简单起见,数据结构最终由3个字段组成,即header、body和trailer。在Matlab中创建一些数据,例如: header_data = {100, 100, 100}; body_data = {1234, 100, 4321}; trailer_data = {1001, 1001, 1001}; data = struct('header', header_data, 'body', body_data, 'trailer', t

我正在用Python解析Matlab结构化数组。为了简单起见,数据结构最终由3个字段组成,即header、body和trailer。在Matlab中创建一些数据,例如:

header_data = {100, 100, 100};
body_data = {1234, 100, 4321};
trailer_data = {1001, 1001, 1001};
data = struct('header', header_data, 'body', body_data, 'trailer', trailer_data);
生成一个1x3结构数组

然后用Python读取该数据,如下所示:

import scipy.io as sio
import numpy as np

matlab_data = sio.loadmat('data.mat', squeeze_me=True)
data = matlab['data']
这使得
data
成为一个大小为3的一维
numpy.ndarray
,具有
dtype=dtype([('header','O'),('body','O'),('trailer','O')])
,我可以愉快地使用
numpy.nditer
进行迭代,并从每个结构中提取和解析数据

我试图克服的问题是,不幸的是(在我无法控制的情况下)在我需要解析的一些文件中,上面定义的结构数组本身是另一个结构数组的成员,其中包含一个字段
msg
。继续我在Matlab中的示例:

messages = struct('msg', {data(1), data(2), data(3)});
用Python中的
scipy.loadmat
加载该文件时,会生成大小为3的一维
numpy.ndarray
,其中
dtype=dtype([('msg','O')])
。为了重用相同的函数来解析数据字段,我需要有逻辑来检测
msg
字段(如果存在),然后从那里提取每个
numpy.void
,然后调用该函数来解析单个标题、正文和尾部字段

在Matlab中,这是很容易克服的,因为可以通过执行以下操作从带有单个
msg
字段的1x3结构数组中提取包含三个字段的原始1x3结构数组:
[messages.msg]
,从而生成包含标头、正文和尾部字段的1x3结构数组。如果我尝试将其转换为numpy,下面的命令将显示原始
numpy.ndarray
,它不是一个结构(
dtype=dtype('O')


我试图找出是否有一种类似的方法,使用
numpy
从包含单个
msg
字段的结构数组中恢复包含三个字段的结构数组,就像我在Matlab中所做的那样,或者我是否真的需要迭代每个值,并在使用公共解析函数之前从
msg
字段手动提取它。同样,Matlab输入文件的格式超出了我的控制范围,我无法更改它们;与我需要从Matlab数据中提取的嵌套字段数量相比,我这里的示例仅是微不足道的。

尝试使用Octave(使用-v7保存)重新创建文件,在Ipython会话中,我得到:

In [190]: data = io.loadmat('test.mat')
In [191]: data
Out[191]: 
{'__globals__': [],
 '__header__': b'MATLAB 5.0 MAT-file, written by Octave 4.0.0, 2016-10-04 20:54:53 UTC',
 '__version__': '1.0',
 'body_data': array([[array([[ 1234.]]), array([[ 100.]]), array([[ 4321.]])]], dtype=object),
 'data': array([[([[100.0]], [[1234.0]], [[1001.0]]),
         ([[100.0]], [[100.0]], [[1001.0]]),
         ([[100.0]], [[4321.0]], [[1001.0]])]], 
       dtype=[('header', 'O'), ('body', 'O'), ('trailer', 'O')]),
 'header_data': array([[array([[ 100.]]), array([[ 100.]]), array([[ 100.]])]], dtype=object),
 'messages': array([[([[(array([[ 100.]]), array([[ 1234.]]), array([[ 1001.]]))]],),
         ([[(array([[ 100.]]), array([[ 100.]]), array([[ 1001.]]))]],),
         ([[(array([[ 100.]]), array([[ 4321.]]), array([[ 1001.]]))]],)]], 
       dtype=[('msg', 'O')]),
 'trailer_data': array([[array([[ 1001.]]), array([[ 1001.]]), array([[ 1001.]])]], dtype=object)}
body\u data
header\u data
trailer\u data
是倍频程单元,在
numpy
中,倍频程单元是包含二维元素的二维对象数组

In [194]: data['trailer_data'][0,0]
Out[194]: array([[ 1001.]])
In [195]: data['trailer_data'][0,0][0,0]
Out[195]: 1001.0
数据
是一个包含3个字段的结构化数组(1,3)

In [198]: data['data']['header'][0,0][0,0]
Out[198]: 100.0
消息
是(1,3)和1个字段,与
数据
一样进一步嵌套

In [208]: data['messages']['msg'][0,0]['header'][0,0][0,0]
Out[208]: 100.0
(这可能重复了您所描述的内容,但我只想澄清一下数据结构)

================

玩了一会,我发现,我可以去掉
msg
(1,3)
形状,并进行索引和连接:

In [241]: np.concatenate(data['messages']['msg'][0])
Out[241]: 
array([[([[100.0]], [[1234.0]], [[1001.0]])],
       [([[100.0]], [[100.0]], [[1001.0]])],
       [([[100.0]], [[4321.0]], [[1001.0]])]], 
      dtype=[('header', 'O'), ('body', 'O'), ('trailer', 'O')])
In [242]: data['data']
Out[242]: 
array([[([[100.0]], [[1234.0]], [[1001.0]]),
        ([[100.0]], [[100.0]], [[1001.0]]),
        ([[100.0]], [[4321.0]], [[1001.0]])]], 
      dtype=[('header', 'O'), ('body', 'O'), ('trailer', 'O')])
这看起来与
数据
相同


出于某种原因,我必须先将其缩减为(3,)`数组,然后才能进行连接。我还没有完全考虑这些细节。

在Ipython会话中,尝试使用Octave(使用-v7保存)重新创建文件时,我得到:

In [190]: data = io.loadmat('test.mat')
In [191]: data
Out[191]: 
{'__globals__': [],
 '__header__': b'MATLAB 5.0 MAT-file, written by Octave 4.0.0, 2016-10-04 20:54:53 UTC',
 '__version__': '1.0',
 'body_data': array([[array([[ 1234.]]), array([[ 100.]]), array([[ 4321.]])]], dtype=object),
 'data': array([[([[100.0]], [[1234.0]], [[1001.0]]),
         ([[100.0]], [[100.0]], [[1001.0]]),
         ([[100.0]], [[4321.0]], [[1001.0]])]], 
       dtype=[('header', 'O'), ('body', 'O'), ('trailer', 'O')]),
 'header_data': array([[array([[ 100.]]), array([[ 100.]]), array([[ 100.]])]], dtype=object),
 'messages': array([[([[(array([[ 100.]]), array([[ 1234.]]), array([[ 1001.]]))]],),
         ([[(array([[ 100.]]), array([[ 100.]]), array([[ 1001.]]))]],),
         ([[(array([[ 100.]]), array([[ 4321.]]), array([[ 1001.]]))]],)]], 
       dtype=[('msg', 'O')]),
 'trailer_data': array([[array([[ 1001.]]), array([[ 1001.]]), array([[ 1001.]])]], dtype=object)}
body\u data
header\u data
trailer\u data
是倍频程单元,在
numpy
中,倍频程单元是包含二维元素的二维对象数组

In [194]: data['trailer_data'][0,0]
Out[194]: array([[ 1001.]])
In [195]: data['trailer_data'][0,0][0,0]
Out[195]: 1001.0
数据
是一个包含3个字段的结构化数组(1,3)

In [198]: data['data']['header'][0,0][0,0]
Out[198]: 100.0
消息
是(1,3)和1个字段,与
数据
一样进一步嵌套

In [208]: data['messages']['msg'][0,0]['header'][0,0][0,0]
Out[208]: 100.0
(这可能重复了您所描述的内容,但我只想澄清一下数据结构)

================

玩了一会,我发现,我可以去掉
msg
(1,3)
形状,并进行索引和连接:

In [241]: np.concatenate(data['messages']['msg'][0])
Out[241]: 
array([[([[100.0]], [[1234.0]], [[1001.0]])],
       [([[100.0]], [[100.0]], [[1001.0]])],
       [([[100.0]], [[4321.0]], [[1001.0]])]], 
      dtype=[('header', 'O'), ('body', 'O'), ('trailer', 'O')])
In [242]: data['data']
Out[242]: 
array([[([[100.0]], [[1234.0]], [[1001.0]]),
        ([[100.0]], [[100.0]], [[1001.0]]),
        ([[100.0]], [[4321.0]], [[1001.0]])]], 
      dtype=[('header', 'O'), ('body', 'O'), ('trailer', 'O')])
这看起来与
数据
相同


出于某种原因,我必须先将其缩减为(3,)`数组,然后才能进行连接。我还没有完全考虑这些细节。

看来这样行得通。我曾尝试用
squeze\u me=True
加载Matlab数据,因为我能够使用
[()]
提取值,这似乎比
[0,0]
更为简单,因为它们不再存储为1x1数组,而是它所能提供的一切。看起来这会起作用。我曾尝试用
squeze\u me=True
加载Matlab数据,因为我能够使用
[()]
提取值,这似乎比
[0,0]
更简单,因为它们不再存储为1x1数组,但这一切都让我明白了。