使用scipy.io.savemat保存嵌套列表

3
这与我最近的一个问题有关,可以在这里找到。我正在处理类似于我在该链接中描述的markerList列表 - 即具有三个级别的列表。我需要将此信息保存为.mat文件,但我无法以正确的类型保存它。当使用scipy.io.savemat时,它将该列表保存为200x40x2 single,而应该是一组包含40x2单元格的200个单元格集合。我用来保存的代码是:
matdict = dict(markers = (markerList), sorted = (finalStack))
scipy.io.savemat('C:\pathname\\sortedMarkers.mat', matdict)

我感到困惑的是,它以正确的格式保存了markerList(1x200单元格,每个单元格大小不同),但未能保存finalStack(保存为200 x 40 x 2单精度)。更令人困惑的是,在我弄清这段代码的其余部分之前,它可以正确地保存finalStack - 这使我认为,当保存的数据大小不一致时,它会将其保存为单元格。(finalStack的大小是一致的;markerList的大小不一致。)
有没有办法将这样一个复杂的数据结构保存为.mat文件?

我必须询问一下,只是为了确认这不是一个XY问题,你会在Matlab中使用它吗?还是你只是将scipy.io.savemat作为保存Python工作空间的通用库?因为如果是后者,有更好的替代方案。 - Tasos Papastylianou
但是,用 savemat 保存一个形状为 (200,40,2) 的数组到一个期望形状为 (20,) 的 (40,2) 数组的位置不会有问题。如果我的诊断正确,那么这是因为 NumPy 将一组数组转换为一个数组时出现了问题。 - hpaulj
Tasos- 这不是一个 XY 问题。基本上我和所有一起工作的人都使用 Matlab,而单元格数组是我所知道的最类似于嵌套列表的东西。此外,我已经编写了脚本来批处理我的数据,一旦它以适当的格式保存(这并不是不能重新做,但仍然有动力)。任何其他格式在保存大量嵌套数据时似乎非常笨重和难以操作。 - godfreap
2个回答

6
根据savemat文档,将其转换为一个numpy对象数组:(链接)
from scipy.io import savemat
import numpy

a = numpy.array([[1,2,3],[1,2,3]])
b = numpy.array([[2,3,4],[2,3,4]])
c = numpy.array([[3,4,5],[3,4,5]])
L = [a,b,c]

FrameStack = numpy.empty((len(L),), dtype=numpy.object)
for i in range(len(L)):
    FrameStack[i] = L[i]

savemat("myfile.mat", {"FrameStack":FrameStack})

在Octave中:

>> load myfile.mat 

>> whos FrameStack
Variables in the current scope:

   Attr Name            Size                     Bytes  Class
   ==== ====            ====                     =====  ===== 
        FrameStack      1x3                        144  cell

Total is 3 elements using 144 bytes

>> whos FrameStack{1}
Variables in the current scope:

   Attr Name               Size                     Bytes  Class
   ==== ====               ====                     =====  ===== 
        FrameStack{1}      2x3                         48  int64

Total is 6 elements using 48 bytes

但是当 A,B,C 的形状都相同时会发生什么? - hpaulj
我明白你的意思,但是我的列表内容是相关且相互依赖的。也就是说,我不能只是将finalStack [0] [1] [0]分解为其单独的元素; 在那一点上它是没有意义的。无论如何,我明白你的意思,我可能能够从这里找到答案... - godfreap
我意识到我需要创建一个Python对象,其中finalStack [0] [0]在Matlab中为{1}(1),但我无法弄清楚,因此提出了问题。将finalStack附加到列表(在范围内使用b.append(finalStack[i]),其中b = [])无法解决问题-它们仍然保存为单个。(对于所有元素i,finalStack[i]是一个40x2 np.array。)仍在努力中,但还没有成功。 - godfreap
我同意。我认为现在更快地完成这项任务的最简单方法是直接操作 mat 文件,而不是尝试修改 Python 解释器。本来想尽量减少 Matlab 端的工作量,但生活就是这样。感谢您的帮助! - godfreap
@godfreap 对不起,我刚刚重新运行了你的示例,我明白你所说的相同形状导致连接数组的问题。解决方法是使用numpy.object数组;这在savemat文档中已经说明。我会更新我的答案,向你展示一个例子。 - Tasos Papastylianou
显示剩余2条评论

1

我猜测您之前的问题是由于现在的numpy.array从子列表或数组的列表中创建数组引起的。

您注意到markerList已按预期保存,并且单元格大小不同。

请尝试:

np.array(markerList)

看一下它的形状和数据类型。我猜它会是一个1d数组 (200,),且数据类型为object。

np.array(finalStack)

另一方面,它可能会保存3D数组。"savemat"被设置为保存numpy数组,而不是Python字典和列表——毕竟,它正在与MATLAB通信,而在那里,所有东西都曾经是2D矩阵。MATLAB单元格可以概括这一点;它们更像是dtype对象的2D numpy数组。经常出现从大小统一的元素创建对象数组的问题。通常的解决方案是创建所需大小(例如(200,))和对象类型的“空”数组,并将子数组加载到其中。

https://dev59.com/KZnga4cB1Zd3GeqPaZA-#38776674

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

我来演示一下。创建3个数组,其中2个大小相同,第三个不同:

In [59]: from scipy import io

In [60]: A=np.ones((40,2))    
In [61]: B=np.ones((40,2))
In [62]: C=np.ones((30,2))

保存两个列表,一个只有两个数组,另一个包含全部三个:
In [63]: io.savemat('test.mat', {'AB':[A,B],'ABC':[A,B,C]})

加载它回来;我可以在octave中完成这个操作:

In [65]: D=io.loadmat('test.mat')

In [66]: D.keys()
Out[66]: dict_keys(['ABC', '__header__', 'AB', '__globals__', '__version__'])

ABC 是一个有3个元素的2D数组

In [68]: D['ABC'].shape
Out[68]: (1, 3)
In [71]: D['ABC'][0,0].shape
Out[71]: (40, 2)

但是 AB 已经被转换成了一个三维数组:
In [69]: D['AB'].shape
Out[69]: (2, 40, 2)
In [70]: np.array([A,B]).shape
Out[70]: (2, 40, 2)

如果我改为使用一个一维对象数组来保存A和B,则它会被保留:
In [72]: AB=np.empty((2,),object)
In [73]: AB[...]=[A,B]
In [74]: AB.shape
Out[74]: (2,)

In [75]: io.savemat('test.mat', {'AB':AB,'ABC':[A,B,C]})
In [76]: D=io.loadmat('test.mat')

In [77]: D['AB'].shape
Out[77]: (1, 2)
In [78]: D['AB'][0,0].shape
Out[78]: (40, 2)

一个好的替代方案是将数组保存为字典的项。
io.savemat('test.mat',{'A':A, 'B':B, 'C':C})

鉴于将MATLAB结构体转换为numpy结构体并返回的困难,最好保持简单扁平化的结构,而不是创建复合对象在两边都有用。

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

我安装了 Octave。加载这个 test.mat 文件:

Octave

io.savemat('test.mat', {'AB':AB,'ABs':[A,B]})

提供

>> whos
Variables in the current scope:

   Attr Name        Size                     Bytes  Class
   ==== ====        ====                     =====  =====
        AB          1x2                       1280  cell
        ABs         2x40x2                    1280  double

一个对象数据类型的数组被保存为Matlab的cell;其他数组被保存为Matlab矩阵。(我需要回顾之前的答案才能想起Matlab结构的等效表示)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接