excluindo variáveis de um arquivo .mat
Pergunta
Alguém aqui sabe como excluir uma variável de um arquivo matlab?Eu sei que você pode adicionar variáveis a um arquivo matlab existente usando o save -append
método, mas não há documentação sobre como excluir variáveis do arquivo.
Antes que alguém diga "é só salvar", é porque estou salvando etapas intermediárias de processamento em disco para aliviar problemas de memória, e no final haverá quase 10 GB de dados intermediários por rotina de análise.Obrigado!
Solução
Curiosamente, você pode usar o -append
opção com SALVAR para efetivamente apagar dados de um arquivo .mat.Observe este trecho da documentação (negrito adicionado por mim):
Para arquivos MAT,
-append
adiciona novas variáveis ao arquivo ou substitui os valores salvos de variáveis existentes por valores no espaço de trabalho.
Em outras palavras, se uma variável no seu arquivo .mat for chamada A
, você pode salvar essa variável com um novo cópia de A
(que você definiu para []
) usando o -append
opção.Ainda haverá uma variável chamada A
no arquivo .mat, mas ele estará vazio e, portanto, reduzirá o tamanho total do arquivo.
Aqui está um exemplo:
>> 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
O tamanho do arquivo será de cerca de 7,21 MB.Agora faça isso:
>> 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
E agora o tamanho do arquivo será em torno de 169 bytes.A variável ainda está lá, mas está vazia.
Outras dicas
10 GB de dados?A atualização de arquivos MAT multivariáveis pode ficar cara devido à sobrecarga do formato MAT.Considere dividir os dados e salvar cada variável em um arquivo MAT diferente, usando diretórios para organização, se necessário.Mesmo se você tivesse uma função conveniente para excluir variáveis de um arquivo MAT, ela seria ineficiente.As variáveis em um arquivo MAT são dispostas de forma contígua, portanto, a substituição de uma variável pode exigir a leitura e a gravação de grande parte do restante.Se estiverem em arquivos separados, você pode simplesmente excluir o arquivo inteiro, o que é rápido.
Para ver isso em ação, experimente este código, percorrendo-o no depurador enquanto usa algo como o Process Explorer (no Windows) para monitorar sua atividade de E/S.
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;
Na minha máquina, os resultados são assim.(Leitura e gravação são cumulativas, o tempo é por operação.)
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
Observe que atualizar a variável x pequena é mais caro do que atualizar a variável y grande.Grande parte dessa atividade de E/S é um trabalho de manutenção "redundante" para manter o formato de arquivo MAT organizado e desaparecerá se cada variável estiver em seu próprio arquivo.
Além disso, tente manter esses arquivos no sistema de arquivos local;será muito mais rápido que unidades de rede.Se eles precisarem ir para uma unidade de rede, considere fazer save() e load() em arquivos temporários locais (talvez escolhidos com tempname()) e depois copiá-los de/para a unidade de rede.O salvamento e o carregamento do Matlab tendem a ser muito mais rápidos com sistemas de arquivos locais, o suficiente para que o salvamento/carregamento local mais uma cópia possam ser um ganho líquido substancial.
Aqui está uma implementação básica que permitirá salvar variáveis em arquivos separados usando as conhecidas assinaturas save() e load().Eles são prefixados com “d” para indicar que são versões baseadas em diretório.Eles usam alguns truques com evalin() e assignin(), então achei que valeria a pena postar o código completo.
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
Aqui está o equivalente a 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() é equivalente a whos('-arquivo').
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$', '');
E ddelete() para excluir as variáveis individuais como você pediu.
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
A única maneira de fazer isso que conheço é usar a função API do arquivo MAT matDeleteVariable
.Seria, eu acho, muito fácil escrever uma rotina Fortran ou C para fazer isso, mas parece muito esforço para algo que deveria ser muito mais fácil.
Sugiro que você carregue as variáveis do arquivo .mat que deseja manter e salve-as em um novo arquivo .mat.Se necessário, você pode carregar e salvar (usando '-append'
) em um loop.
S = load(filename, '-mat', variablesYouWantToKeep);
save(newFilename,'-struct',S,variablesYouWantToKeep);
%# then you can delete the old file
delete(filename)