如何使用Python获取MATLAB .fig文件中的数据?

15

有人知道如何使用Python从MATLAB fig文件中提取数据吗?我知道这些是二进制文件,但是Python Cookbook中针对.mat文件的方法 http://www.scipy.org/Cookbook/Reading_mat_files 对.fig文件似乎不起作用...

先感谢任何帮助, 丹

6个回答

12

.fig文件是.mat文件(包含一个结构体),请参见http://undocumentedmatlab.com/blog/fig-files-format/

正如您提供的参考文献所述,结构体仅受支持到v7.1:http://www.scipy.org/Cookbook/Reading_mat_files

因此,在MATLAB中使用-v7进行保存:

plot([1 2],[3 4])
hgsave(gcf,'c','-v7');

然后在Python 2.6.4中,我使用:

>>> from scipy.io import loadmat
>>> x = loadmat('c.fig')
>>> x
{'hgS_070000': array([[<scipy.io.matlab.mio5.mat_struct object at 0x1500e70>]], dtype=object), '__version__': '1.0', '__header__': 'MATLAB 5.0 MAT-file, Platform: MACI64, Created on: Fri Nov 18 12:02:31 2011', '__globals__': []}
>>> x['hgS_070000'][0,0].__dict__
{'handle': array([[1]], dtype=uint8), 'children': array([[<scipy.io.matlab.mio5.mat_struct object at 0x1516030>]], dtype=object), '_fieldnames': ['type', 'handle', 'properties', 'children', 'special'], 'type': array([u'figure'], dtype='<U6'), 'properties': array([[<scipy.io.matlab.mio5.mat_struct object at 0x1500fb0>]], dtype=object), 'special': array([], shape=(1, 0), dtype=float64)}

我使用.__dict__来查看如何遍历结构。例如,要获取XDataYData,我可以使用:

>>> x['hgS_070000'][0,0].children[0,0].children[0,0].properties[0,0].XData
array([[1, 2]], dtype=uint8)
>>> x['hgS_070000'][0,0].children[0,0].children[0,0].properties[0,0].YData
array([[3, 4]], dtype=uint8)

在MATLAB中,我使用了plot([1 2],[3 4])来显示图表(子对象是坐标轴,孙对象是线系列)。


11
这是我对Sascha帖子的更新。现在它可以:
  • 显示旋转的文本标签
  • 显示xticks和yticks
  • 更好地处理标记
  • 网格开/关
  • 更好的坐标轴和图例枚举处理
  • 保持图形大小

以下是代码:

from scipy.io import loadmat
import numpy as np
import matplotlib.pyplot as plt

def plotFig(filename,fignr=1):
   d = loadmat(filename,squeeze_me=True, struct_as_record=False)
   matfig = d['hgS_070000']
   childs = matfig.children
   ax1 = [c for c in childs if c.type == 'axes']
   if(len(ax1) > 0):
       ax1 = ax1[0]
   legs = [c for c in childs if c.type == 'scribe.legend']
   if(len(legs) > 0):
       legs = legs[0]
   else:
       legs=0
   pos = matfig.properties.Position
   size = np.array([pos[2]-pos[0],pos[3]-pos[1]])/96
   plt.figure(fignr,figsize=size)
   plt.clf()
   plt.hold(True)
   counter = 0    
   for line in ax1.children:
       if line.type == 'graph2d.lineseries':
           if hasattr(line.properties,'Marker'):
               mark = "%s" % line.properties.Marker
               if(mark != "none"):
                   mark = mark[0]
           else:
               mark = '.'
           if hasattr(line.properties,'LineStyle'):
               linestyle = "%s" % line.properties.LineStyle
           else:
               linestyle = '-'
           if hasattr(line.properties,'Color'):
               r,g,b =  line.properties.Color
           else:
               r = 0
               g = 0
               b = 1
           if hasattr(line.properties,'MarkerSize'):
               marker_size = line.properties.MarkerSize
           else:
               marker_size = -1                
           x = line.properties.XData
           y = line.properties.YData
           if(mark == "none"):
               plt.plot(x,y,linestyle=linestyle,color=[r,g,b])
           elif(marker_size==-1):
               plt.plot(x,y,marker=mark,linestyle=linestyle,color=[r,g,b])
           else:
               plt.plot(x,y,marker=mark,linestyle=linestyle,color=[r,g,b],ms=marker_size)
       elif line.type == 'text':
           if counter == 0:
               plt.xlabel("$%s$" % line.properties.String,fontsize =16)
           elif counter == 1:
               plt.ylabel("$%s$" % line.properties.String,fontsize = 16)
           elif counter == 3:
               plt.title("$%s$" % line.properties.String,fontsize = 16)
           counter += 1        
   plt.grid(ax1.properties.XGrid)
   
   if(hasattr(ax1.properties,'XTick')):
       if(hasattr(ax1.properties,'XTickLabelRotation')):
           plt.xticks(ax1.properties.XTick,ax1.properties.XTickLabel,rotation=ax1.properties.XTickLabelRotation)
       else:
           plt.xticks(ax1.properties.XTick,ax1.properties.XTickLabel)
   if(hasattr(ax1.properties,'YTick')):
       if(hasattr(ax1.properties,'YTickLabelRotation')):
           plt.yticks(ax1.properties.YTick,ax1.properties.YTickLabel,rotation=ax1.properties.YTickLabelRotation)
       else:
           plt.yticks(ax1.properties.YTick,ax1.properties.YTickLabel)
   plt.xlim(ax1.properties.XLim)
   plt.ylim(ax1.properties.YLim)
   if legs:        
       leg_entries = tuple(['$' + l + '$' for l in legs.properties.String])
       py_locs = ['upper center','lower center','right','left','upper right','upper left','lower right','lower left','best','best']
       MAT_locs=['North','South','East','West','NorthEast', 'NorthWest', 'SouthEast', 'SouthWest','Best','none']
       Mat2py = dict(zip(MAT_locs,py_locs))
       location = legs.properties.Location
       plt.legend(leg_entries,loc=Mat2py[location])
   plt.hold(False)
   plt.show()

为python>3.x进行更新(参见@robert-pollak的评论):

from scipy.io import loadmat
import numpy as np
import matplotlib.pyplot as plt

def plotFig(filename,fignr=1, subfig=1):
   d = loadmat(filename,squeeze_me=True, struct_as_record=False)
   matfig = d['hgS_070000']
   childs = matfig.children

   sfig = max(0, subfig - 1)
   
   ax1 = [c for c in childs if c.type == 'axes']
   if(len(ax1) > 0):
       sfig = min(sfig, len(ax1) - 1)
       ax1 = ax1[sfig]
   
   legs = [c for c in childs if c.type == 'scribe.legend']
   if(len(legs) > 0):
       legs = legs[sfig]
   else:
       legs=0
   pos = matfig.properties.Position
   size = np.array([pos[2]-pos[0],pos[3]-pos[1]])/96
   plt.figure(fignr,figsize=size)
   plt.clf()
   #plt.hold(True)
   counter = 0    
   for line in ax1.children:
       if line.type == 'graph2d.lineseries':
           if hasattr(line.properties,'Marker'):
               mark = "%s" % line.properties.Marker
               if(mark != "none"):
                   mark = mark[0]
           else:
               mark = '.'
           if hasattr(line.properties,'LineStyle'):
               linestyle = "%s" % line.properties.LineStyle
           else:
               linestyle = '-'
           if hasattr(line.properties,'Color'):
               r,g,b =  line.properties.Color
           else:
               r = 0
               g = 0
               b = 1
           if hasattr(line.properties,'MarkerSize'):
               marker_size = line.properties.MarkerSize
           else:
               marker_size = -1                
           x = line.properties.XData
           y = line.properties.YData
           if(mark == "none"):
               plt.plot(x,y,linestyle=linestyle,color=[r,g,b])
           elif(marker_size==-1):
               plt.plot(x,y,marker=mark,linestyle=linestyle,color=[r,g,b])
           else:
               plt.plot(x,y,marker=mark,linestyle=linestyle,color=[r,g,b],ms=marker_size)
       elif line.type == 'text':
           if counter == 0:
               plt.xlabel("$%s$" % line.properties.String,fontsize =16)
           elif counter == 1:
               plt.ylabel("$%s$" % line.properties.String,fontsize = 16)
           elif counter == 3:
               plt.title("$%s$" % line.properties.String,fontsize = 16)
           counter += 1        
   plt.grid(ax1.properties.XGrid)

   if(hasattr(ax1.properties,'XTick')):
       if(hasattr(ax1.properties,'XTickLabelRotation')):
           plt.xticks(ax1.properties.XTick,ax1.properties.XTickLabel,rotation=ax1.properties.XTickLabelRotation)
       else:
           plt.xticks(ax1.properties.XTick,ax1.properties.XTickLabel)
   if(hasattr(ax1.properties,'YTick')):
       if(hasattr(ax1.properties,'YTickLabelRotation')):
           plt.yticks(ax1.properties.YTick,ax1.properties.YTickLabel,rotation=ax1.properties.YTickLabelRotation)
       else:
           plt.yticks(ax1.properties.YTick,ax1.properties.YTickLabel)
   plt.xlim(ax1.properties.XLim)
   plt.ylim(ax1.properties.YLim)
   if legs:        
       leg_entries = tuple(['$' + l + '$' for l in legs.properties.String])
       py_locs = ['upper center','lower center','right','left','upper right','upper left','lower right','lower left','best','best']
       MAT_locs=['north','south','east','west','northeast', 'northwest', 'southeast', 'southwest','best','none']
       Mat2py = dict(zip(MAT_locs,py_locs))
       location = legs.properties.Location.lower()
       plt.legend(leg_entries,loc=Mat2py[ location ])
   #plt.hold(False)
   plt.show()

你会如何运行这个脚本来转换或打开.fig文件? - August
我更喜欢使用WinPython和Spyder进行开发:http://winpython.github.io/ - johnml1135
3
这个脚本很好用,唯一需要更改的是要检查XGrid属性是否存在: if hasattr(ax1.properties, 'XGrid'): plt.grid(ax1.properties.XGrid) - Matt Williams
2
@MattWilliams 是的,而且在 matplotlib 3 中必须删除 plt.hold 调用。此外,在我的情况下,我还必须将 MAT_locs 条目更改为小写。 - Robert Pollak
@DanteSmith - 我的最佳猜测是迭代ax1.children并查看您是否可以确定绘制数据的类型。一步一步地解决它,然后随时发布您的更新代码。 - johnml1135
显示剩余3条评论

8

我觉得Alex的答案很吸引人,但我稍微改了他的代码。首先,我加入了导言部分,以显示图形、纵标签等来自哪里。其次,我加入了图例! 由于我对Python还比较新,所以非常欢迎任何改进建议。

def plotFig(filename,fignr=1):
    from scipy.io import loadmat
    from numpy import size
    from matplotlib.pyplot import plot,figure,hold,xlabel,ylabel,show,clf,xlim,legend
    d = loadmat(filename,squeeze_me=True, struct_as_record=False)
    ax1 = d['hgS_070000'].children
    if size(ax1) > 1:
        legs= ax1[1]
        ax1 = ax1[0]
    else:
        legs=0
    figure(fignr)
    clf()
    hold(True)
    counter = 0    
    for line in ax1.children:
        if line.type == 'graph2d.lineseries':
            if hasattr(line.properties,'Marker'):
                mark = "%s" % line.properties.Marker
                mark = mark[0]
            else:
                mark = '.'
            if hasattr(line.properties,'LineStyle'):
                linestyle = "%s" % line.properties.LineStyle
            else:
                linestyle = '-'
            if hasattr(line.properties,'Color'):
                r,g,b =  line.properties.Color
            else:
                r = 0
                g = 0
                b = 1
            if hasattr(line.properties,'MarkerSize'):
                marker_size = line.properties.MarkerSize
            else:
                marker_size = 1                
            x = line.properties.XData
            y = line.properties.YData
            plot(x,y,marker=mark,linestyle=linestyle,color=color(r,g,b),markersize=marker_size)
        elif line.type == 'text':
            if counter < 1:
                xlabel("%s" % line.properties.String,fontsize =16)
                counter += 1
            elif counter < 2:
                ylabel("%s" % line.properties.String,fontsize = 16)
                counter += 1        
    xlim(ax1.properties.XLim)
    if legs:        
        leg_entries = tuple(legs.properties.String)
        py_locs = ['upper center','lower center','right','left','upper right','upper left','lower right','lower left','best']
        MAT_locs=['North','South','East','West','NorthEast', 'NorthWest', 'SouthEast', 'SouthWest','Best']
        Mat2py = dict(zip(MAT_locs,py_locs))
        location = legs.properties.Location
        legend(leg_entries,loc=Mat2py[location])
    hold(False)
    show()

2
当您保存MATLAB图形时,它会将Handle Graphics层次结构转储到一个结构体中,将其保存为.mat文件,并将扩展名更改为.fig。因此,.fig文件只是.mat文件,如果您要查找的数据存储在原始图形的某个位置,则将在其中。如果您手动将扩展名更改回.mat,则可以将其加载到MATLAB中并查看。
很抱歉,我不太了解如何从Python中读取.mat文件,但如果您有一般的方法进行读取,您也应该能够读取.fig文件。

2
这里有一种更简单的方法。它基于更新的Scipy和loadmat: http://answerpot.com/showthread.php?3707193-loadmat+and+figure 对于简单的2D线条,我进行了一些小扩展:
from scipy.io import loadmat



d = loadmat('../impulse_all.fig',squeeze_me=True, struct_as_record=False)
# d = loadmat('R11_resuspension.fig',squeeze_me=True, struct_as_record=False)
ax1 = d['hgS_070000'].children
if size(ax1) > 1:
    ax1 = ax1[0]

figure
hold(True)
counter = 0
for line in ax1.children:
    if line.type == 'graph2d.lineseries':
        marker = "%s" % line.properties.Marker
        linestyle = "%s" % line.properties.LineStyle
        r,g,b =  line.properties.Color
        marker_size = line.properties.MarkerSize
        x = line.properties.XData
        y = line.properties.YData
        plot(x,y,marker,linestyle=linestyle,color = (r,g,b),markersize=marker_size)
    elif line.type == 'text':
        if counter < 1:
            xlabel("%s" % line.properties.String,fontsize =16)
            counter += 1
        elif counter < 2:
            ylabel("%s" % line.properties.String,fontsize = 16)
            counter += 1



hold(False)

只含有 figure 的那一行是什么意思? - user647772
这是创建一个新图形的代码,相当于matplotlib中的plt.figure()函数。 - Alex
你的回答中的链接已经失效。 - Richard

1
使用Sascha的文章,我只想提取存储在.fig文件中的x数据和y数据。 下面是我的Python函数,它是Sascha函数的简化版本,旨在仅提取数据。 输出是一个字典。其键是图中数据的相应标签。 我把它放在那里。如果这能为其他人节省几分钟,我会很高兴!
import numpy
from scipy.io import loadmat

def read_fig(filename):
    output = {}
    d = loadmat(filename, squeeze_me=True, struct_as_record=False)
    matfig = d['hgS_070000']
    childs = matfig.children
    ax1 = [c for c in childs if c.type == 'axes'][0]
    for line in ax1.children:
        try:
            if line.type == 'graph2d.lineseries':
                x = line.properties.XData
                y = line.properties.YData
                leg = line.properties.DisplayName
                print leg
                output[leg] = numpy.column_stack((x, y))
        except:
            print 'One children is ignored...'
    return output

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