从.mat文件中删除变量

16

有没有人知道如何从matlab文件中删除变量?我知道可以使用save -append方法向现有的matlab文件添加变量,但是没有关于如何从文件中删除变量的文档。

在有人说“只需保存它”的之前,我想说明一下,因为我正在将中间处理步骤保存到磁盘上以减轻内存问题,在每个分析例程结束时,将有近10 GB的中间数据。谢谢!

4个回答

13

有趣的是,你可以使用 -append 选项和 SAVE 函数“有效地”从 .mat 文件中删除数据。注意文档中的这段摘录(粗体是我添加的):

对于 MAT-文件,-append 会向文件添加新变量或者用工作区中的值替换现有变量的保存值

换句话说,如果你的 .mat 文件中有一个名为 A 的变量,你可以使用 -append 选项将其覆盖为 新的 A 的副本(你已将其设置为 [])。在 .mat 文件中仍然会有一个名为 A 的变量,但它将为空,从而减小总文件大小。

以下是一个示例:

>> A = rand(1000);            %# Create a 1000-by-1000 matrix of random values
>> save('savetest.mat','A');  %# Save A to a file
>> whos -file savetest.mat    %# Look at the .mat file contents
  Name         Size                Bytes  Class     Attributes

  A         1000x1000            8000000  double

文件大小约为7.21 MB。现在,请执行以下操作:

>> A = [];                              %# Set the variable A to empty
>> save('savetest.mat','A','-append');  %# Overwrite A in the file
>> whos -file savetest.mat              %# Look at the .mat file contents
  Name      Size            Bytes  Class     Attributes

  A         0x0                 0  double

现在文件大小将约为169字节。变量仍然存在,但为空。


11

有10 GB的数据?由于MAT格式的开销,更新多变量MAT文件可能会变得昂贵。考虑将数据拆分并将每个变量保存到不同的MAT文件中,必要时使用目录进行组织。即使您有一个方便的函数来从MAT文件中删除变量,这也是低效的。MAT文件中的变量是连续排列的,因此替换一个变量可能需要读取和写入大部分其余内容。如果它们在不同的文件中,则可以只删除整个文件,这很快。

为了看到这一点,请尝试此代码,并在调试器中逐步执行它,同时使用类似于Process Explorer(在Windows上)的工具监视其I/O活动。

function replace_vars_in_matfile

x = 1;
% Random dummy data; zeros would compress really well and throw off results
y = randi(intmax('uint8')-1, 100*(2^20), 1, 'uint8');

tic; save test.mat x y; toc;
x = 2;
tic; save -append test.mat x; toc;
y = y + 1;
tic; save -append test.mat y; toc;
在我的电脑上,结果看起来像这样。(读和写是累积的,时间是每个操作的时间。)
                    Read (MB)      Write (MB)       Time (sec)
before any write:   25             0
first write:        25             105              3.7
append x:           235            315              3.6
append y:           235            420              3.8

注意更新小变量x比更新大变量y更昂贵。大部分的I/O活动是为了维护MAT文件格式的有序性而做的“冗余”工作,如果每个变量都在自己的文件中,则这些工作将会消失。

此外,尽可能将这些文件保存在本地文件系统中;比网络驱动器快得多。如果它们需要放在网络驱动器上,可以考虑在本地临时文件上执行save()和load()操作(可能使用tempname()选择),然后将它们复制到/从网络驱动器上。Matlab的save和load在本地文件系统中往往更快,足以使本地的save/load操作加上复制成为一个实质性的优势。


下面是一个基本实现,可以让您使用熟悉的save()和load()签名将变量保存到单独的文件中。它们的前缀为“d”,表示它们是基于目录的版本。它们使用evalin()和assignin()的一些技巧,因此我认为值得发布完整代码。

function dsave(file, varargin)
%DSAVE Like save, but each var in its own file
%
% dsave filename var1 var2 var3...
if nargin < 1 || isempty(file); file = 'matlab';  end
[tfStruct,loc] = ismember({'-struct'}, varargin);
args = varargin;
args(loc(tfStruct)) = [];
if ~all(cellfun(@isvarname, args))
    error('Invalid arguments. Usage: dsave filename <-struct> var1 var2 var3 ...');
end
if tfStruct
    structVarName = args{1};
    s = evalin('caller', structVarName);
else
    varNames = args;
    if isempty(args)
        w = evalin('caller','whos');
        varNames = { w.name };
    end
    captureExpr = ['struct(' ...
        join(',', cellfun(@(x){sprintf('''%s'',{%s}',x,x)}, varNames)) ')'];
    s = evalin('caller', captureExpr);
end

% Use Java checks to avoid partial path ambiguity
jFile = java.io.File(file);
if ~jFile.exists()
    ok = mkdir(file);
    if ~ok; 
        error('failed creating dsave dir %s', file);
    end
elseif ~jFile.isDirectory()
    error('Cannot save: destination exists but is not a dir: %s', file);
end
names = fieldnames(s);
for i = 1:numel(names)
    varFile = fullfile(file, [names{i} '.mat']);
    varStruct = struct(names{i}, {s.(names{i})});
    save(varFile, '-struct', 'varStruct');
end

function out = join(Glue, Strings)
Strings = cellstr(Strings);
if length( Strings ) == 0
    out = '';
elseif length( Strings ) == 1
    out = Strings{1};
else
    Glue = sprintf( Glue ); % Support escape sequences
    out = strcat( Strings(1:end-1), { Glue } );
    out = [ out{:} Strings{end} ];
end

这是load()的等效方法。

function out = dload(file,varargin)
%DLOAD Like load, but each var in its own file
if nargin < 1 || isempty(file); file = 'matlab'; end
varNames = varargin;
if ~exist(file, 'dir')
    error('Not a dsave dir: %s', file);
end
if isempty(varNames)
    d = dir(file);
    varNames = regexprep(setdiff(ls(file), {'.','..'}), '\.mat$', '');
end

out = struct;
for i = 1:numel(varNames)
    name = varNames{i};
    tmp = load(fullfile(file, [name '.mat']));
    out.(name) = tmp.(name);
end

if nargout == 0
    for i = 1:numel(varNames)
        assignin('caller', varNames{i}, out.(varNames{i}));
    end
    clear out
end

Dwhos()等同于whos('-file')。

function out = dwhos(file)
%DWHOS List variable names in a dsave dir
if nargin < 1 || isempty(file); file = 'matlab'; end
out = regexprep(setdiff(ls(file), {'.','..'}), '\.mat$', '');

并且使用 ddelete() 函数来删除像您所要求的单个变量。

function ddelete(file,varargin)
%DDELETE Delete variables from a dsave dir
if nargin < 1 || isempty(file); file = 'matlab'; end
varNames = varargin;
for i = 1:numel(varNames)
    delete(fullfile(file, [varNames{i} '.mat']));
end

趣事是,我正在使用一种特定的数据分析工具箱,它们建议按照你所说的方法进行操作;每个变量使用单独的文件。我没有这样做,因为我没有理解其原因,但是你详细的帖子解释了这一点。谢谢! - eykanal

1
我所知道的唯一方法是使用MAT文件API函数matDeleteVariable。我猜,编写Fortran或C例程来完成这个任务可能会很容易,但这似乎需要付出很多努力,而本应更容易些。

哇,我希望那不是唯一的方法,尽管感谢你指出来。我已经很久没有写过C语言了... 我有点想尝试写一下你提到的C例程,只是为了好玩。 - eykanal
@eykanal:我也希望不止这一种方法。但是还没有人告诉我们如何更直接地从命令窗口执行此操作。 - High Performance Mark
2
可能是有意为之,不让这个更方便。MAT文件格式的连续布局意味着当您删除一个变量时,您要么需要将“垃圾”留在原地并浪费磁盘空间,要么可能需要重写文件的大部分内容。有点像数组中元素的O(n)删除成本,但会产生磁盘I/O成本。公开deletevariable函数可能会邀请不成熟的用户意外进行大量不必要的I/O操作。 - Andrew Janke

0

我建议您从要保留的.mat文件中加载变量,并将它们保存到一个新的.mat文件中。如有必要,您可以在循环中加载和保存(使用'-append')。

S = load(filename, '-mat', variablesYouWantToKeep);
save(newFilename,'-struct',S,variablesYouWantToKeep);
%# then you can delete the old file
delete(filename)

这就是我正在做的事情,但这是一个笨拙的解决方法。我真的很惊讶似乎没有直接的方法来做到这一点。 - eykanal
@eykanal:显然,从未需要这样功能的人并不足够多。 - Jonas

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