Load .mat file from inside function - matlab

In Matlab, I have a little function init() placed inside "init.m" that I use to load all the data I need when I start up matlab. Files loaded include .mat files and a .png file. To load the .png file, another function is called, importfile(filename). When I place the two functions in seperated files, everything works great. However, when I place this second function inside the file "init.m", and I call init() from the command line, only the png appears in my workspace variables. I know that the first function in a .m file is the main function and additional functions are considered local functions.
Can anyone explain this behavior? I am used to C++ and it would be helpful to precisely understand how Matlab handles paths, workspaces and multiple functions inside files.
Here are the relevant functions:
function init()
cd('~/thesis/data/');
files = dir('*.mat');
for i=1:length(files)
disp(files(i).name);
load(files(i).name);
end
importfile('./K2.png');
end
function importfile(fileToRead1)
%IMPORTFILE(FILETOREAD1)
% Imports data from the specified file
% FILETOREAD1: file to read
% Auto-generated by MATLAB on 06-Jan-2015 12:10:28
% Import the file
rawData1 = importdata(fileToRead1);
% For some simple files (such as a CSV or JPEG files), IMPORTDATA might
% return a simple array. If so, generate a structure so that the output
% matches that from the Import Wizard.
[~,name] = fileparts(fileToRead1);
newData1.(genvarname(name)) = rawData1; %#ok<DEPGENAM>
% Create new variables in the base workspace from those fields.
vars = fieldnames(newData1);
for i = 1:length(vars)
assignin('base', vars{i}, newData1.(vars{i}));
end
end

load() only loads data into your function workspace. When you had init() on its own I'm assuming you had it as a script and not as a function.
e.g.
% Script
cd('~/thesis/data/');
files = dir('*.mat');
for i=1:length(files)
disp(files(i).name);
load(files(i).name);
end
importfile('./K2.png');
vs.
function init()
% Function
cd('~/thesis/data/');
files = dir('*.mat');
for i=1:length(files)
disp(files(i).name);
load(files(i).name);
end
importfile('./K2.png');
end
A script's workspace is the base MATLAB workspace, so it's functioning as intended. When changed to a function it loads your data into the function workspace and discards it when the funciton completes execution. The reason the image still loads correctly is because you're explicitly assigning it to the base workspace.
To get around this, you could change init() to:
function init()
% Function
cd('~/thesis/data/');
files = dir('*.mat');
for i=1:length(files)
disp(files(i).name);
evalin('base', strcat('load(''', files(ii).name, ''')'));
end
importfile('./K2.png');
end
And keep importfile as a local function, which produces what I believe you're expecting. I'm not a huge fan of the syntax and blindly assigning workspace variables but it achieves the goal.

My apologies, the answer is contained in the (MATLAB autogenerated) code:
% Create new variables in the base workspace from those fields.
vars = fieldnames(newData1);
for i = 1:length(vars)
assignin('base', vars{i}, newData1.(vars{i}));
end
The code also failed if I place it inside a seperate function; only not placing it in a function at all worked, as matlab considers this environment local.

Related

Performing operations on a variable number of workspace elements in MATLAB

new MATLAB user here so apologies if this seems like a silly question. I have the following list of variables (doubles) in my workspace:
E1_01Strain E1_06Strain E1_07Strain E1_08Strain E1_09Strain E1_10Strain
E1_01Stress E1_06Stress E1_07Stress E1_08Stress E1_09Stress E1_10Stress
These are lists of numbers. I would like to remove the last n elements from each variable. I can do it with the command
E1_01Strain = E1_01Strain(1:end-100)
but it's impractical because later I'm going to have to do it on many, many more similar variables. Therefore I wanted to write a function that accepts as inputs a list of the workspace variables (as in, I highlight the variables I want and drag and drop into the function input) and removes from each one n elements.
I understand that I can write a function like this:
function [X1, X2, X3, X4] = Remove_n_elements[n, X1, X2, X3, X4]
X1 = X1(1:end-100);
X2 = X2(1:end-100);
X3 = X3(1:end-100);
X4= X4(1:end-100);
end
but that would mean that I would have to change the number of inputs, outputs, and the lines of code in the function every time. I'm sure there's a better way to do it but I can't figure it out.
I keep thinking that there might be a way to do it by looping over all the inputs but I can't get it to work since (as far as I know) I need to create a list of the inputs and then the operation is performed only on the elements of that list, not the inputs themselves.
I was looking at Passing A Variable Number of Arguments into a Function and from that using inputParser from https://www.mathworks.com/help/matlab/matlab_prog/parse-function-inputs.html but since I'm new to MATLAB I'm not sure how to use it for my case.
I used the code provided by il_raffa for a bit but followed his advice and went back and reconsidered how the script functions. After some more digging I wrote the following script that does exactly what I need. This script extracts the columns des_cols from all .csv files in a folder and plots them together. It then makes another plot of the averages.
files = dir('*.csv'); % navigate to the folder that you want to run the script on in MATLAB
avgStress = [];
avgStrain = [];
set(groot, 'DefaultLegendInterpreter', 'none') % the names of my .csv files have underscores that I want to see in the legend, if you don't want this then comment this line
hold on; %comment this and hold off further down if you want separate plots for every .csv
for file = files'
csv = xlsread(file.name);
[n,s,r] = xlsread(file.name);
des_cols = {'Stress','Ext.1(Strain)'}; % type here the names of the columns you want to extract
colhdrs = s(2,:);
[~,ia] = intersect(colhdrs, des_cols);
colnrs = flipud(ia);
file.name = n(:, colnrs);
file.name = file.name(1:end-600,:); % I wanted to remove the last 600 rows but if you want them all, remove the -600
plot(file.name(:,2),file.name(:,1),'DisplayName',s{1,1});
avgStress = [avgStress file.name(1:1500,1)]; % calculates the average stress for the first 1500 points, you can change it to whatever you want
avgStrain = [avgStrain file.name(1:1500,2)];
end
ylabel({'Stress (MPa)'}); % y-axis label
xlabel({'Strain (%)'}); %x-axis label
title({'E_2'}); % title of the plot
legend('show');
hold off; % commment this if you want different plots for all .csv files
avgStress = mean(avgStress,2);
avgStrain = mean(avgStrain,2);
plot(avgStrain,avgStress);
This creates two plots, one with all the raw data and another with just the averages. I hope this helps anyone that might have a similar issue.
The best thing you can do is to review the architecture of your SW in order to avoid the needs to perform such operations on the Workspace variables.
That is: how those variables are created? Are these variables loaded from a ".mat" file? etc.
Anyway, in order to avoid using the eval function and given your situation, a possible approach could be:
identify the names of the varailbes by using the function who. You can specify in the call to who the root name of the varaibles and use the * as, for example, who('E1*'). Make sure it fit wiht the desired variables. You can also use regexp to better refine the selection of the variables
save these varaibles in a temporary .mat file: the name (including the path of the temporary file can be created with the function tempname
load the temporary .mat file: this will create a struct in the Workspace whose fields are the variables you want to midify
call the function to remove the undesired elements form the fields of the struct. The function have to return the updated struct
save the updated struct in the temporary file
load again the temporary file by specifying the option -struct which allows loading the content of the file as single varaibles
The function to remove the undesired elements can be made as follows:
get the nams of the struct's fields by using the function fieldnames
loop over the filed of the struct by using the dynamic field names property
remove the undesired elements form the fields
return the updated struct
A possible implementatin could be:
Code "before" the call to the function
% Get the names of the variables
list_var=who('E1*')
% Define the name of a temporary ".mat" file
tmp_file=tempname
% Save the variables in the temporary ".mat" file
save(tmp_file,list_var{:});
% Load the variables in a struct
sel_vars=load(tmp_file);
% Call the function to remove the elements
out_str=Remove_n_elements(8,sel_vars)
Function to remove the undesired elements
function sel_vars=Remove_n_elements(n,sel_vars)
% Get the names of the fields of the struct
var_names=fieldnames(sel_vars)
% Loop over the fields and remove the undesired elements
for i=1:length(var_names)
sel_vars.(var_names{i})=sel_vars.(var_names{i})(1:end-n)
end
Code "after" the call to the function
% Save the updated struct in the temporary ".mat" file
save(tmp_file,'-struct','out_str')
% Load the updated struct as separate variables
load(tmp_file)

How to clear variables of a mat file?

I have a mat file, named save.mat, which has two variables. I want to clear the variables and add some other variables. How could I clear/add variables inside a mat file?
Best
You can do this two ways, either through the Matlab GUI or through a script.
To do this within Matlab itself, simply navigate to the .mat file's directory, run 'clear' within your command window (make sure to save your current workspace if you need that data!), the double-click the .mat file to load it. From here you can use the workspace to delete and add variables to your heart's content.
If you want to do this in a script, set up something like this:
clear
load('save'); % load the .mat file
clear foo bar % removes the variables 'foo' and 'bar'
baz = 3; % adds the variable 'baz'
save('save'); % save over the old .mat file
To add one o more variables to an existing .mat file, you can use the save function by specifying the option -append.
To remove one or more variables from an existing .mat file you can:
load the .mat file into a struct using the load function: each read variable will be stored in a field of the struct
remove from that struct the the field corresponding to the varaible you want to delete from the original mat file by using the rmfield function
save the struct using the save function by specifying the option -struct: this will allow save the fields of a structure as individual variables
This is an example of the implementation:
% Define some varaible to be saved
a=1
b=2
c=3
% Save the varaibles
save('add_del.mat','a','b','c')
% Define an additional variable
d=4
% Add the new varaible to the ".mat" file
save('add_del.mat','d','-append')
% Load the ".mat" file into a struct
str=load('add_del.mat')
% Delete the field corresponding to the varaible to delete
str=rmfield(str,'b')
% Save the fields of a structure as individual variables
save('add_del.mat','-struct','str')
Hope this helps.
Qapla'
#excaza has made a good recommendation, I think. To verify that the matfile command works as suggested, try the following:
filename = ('file.mat');
m = matfile(filename,'Writable',true);
y = 23;
m.y = y;
clear y
load(filename);
display(y);
y = 24;
m.y = y;
clear y
load(filename);
display(y);

How to delete only variables created by a specific matlab script

Is there some way to delete only the variables generated in a matlab script at the end of the script and not any other variables of the workspace which are not generated in the script?
Note : The script is not a function.
Basically I want to do the following in one line
save abc.mat % saves the whole workspace
some_script % call the script
clear % deletes the variables created by the script along with the whole workspace
load abc.mat % again loads the whole earlier workspace
Use who before the script, then after the script; compare the results (setdiff) to detect variables created in the script, and then clear only those.
Variable names varsbefore, varsafter and varsnew in the following code should be guaranteed not to be used before the script or within the script.
varsbefore = who; %// get names of current variables (note 1)
some_script
varsafter = []; %// initiallize so that this variable is seen by next 'who'
varsnew = []; %// initiallize too.
varsafter = who; %// get names of all variables in 'varsbefore' plus variables
%// defined in the script, plus 'varsbefore', 'varsafter' and 'varsnew'
varsnew = setdiff(varsafter, varsbefore); %// variables defined in the script
%// plus 'varsbefore', 'varsafter' and 'varsnew'
clear(varsnew{:}) %// (note 2)
Notes about the code:
who used with an output argument returns a cell array of strings containing the names of all variables.
The functional form of clear is used, with input arguments in the form of a comma-separated list generated from a cell array.
You can create structures of variables in this way:
FOO_STRUCT.foo_var = var_from_abc.mat + rand(1);
FOO_STRUCT.foo_var2 = 10*log10(FOO_STRUCT.foo_var);
FOO_STRUCT.foo_var3 = FOO_STRUCT.foo_var2 + var_from_abc.mat;
The upper case part is the structure name. The lower case part is the variable.
You can use variables from the workspace in your script, make your work and at the end of the script you delete the entire structure
clear FOO_STRUCT

How to wrap an already existing function with a new function of the same name

Is it possible to create a wrapper around a function that has the exact same name as the original function?
This would be very useful in circumstances where the user wants to do some additional checks on input variables before they are passed on to the built in function How to interrupt MATLAB IDE when it hangs on displaying very large array?
Actually alternatively to slayton's answer you don't need to use openvar. If you define a function with the same name as a matlab function, it will shadow that function (i.e. be called instead).
To then avoid recursively calling your own function, you can call the original function from within the wrapper by using builtin.
e.g.
outputs = builtin(funcname, inputs..);
Simple example, named rand.m and in the matlab path:
function out = main(varargin)
disp('Test wrapping rand... calling rand now...');
out = builtin('rand', varargin{:});
Note that this only works for functions that are found by builtin. For those that are not, slayton's approach is likely necessary.
Yes this is possible but it requires a bit of hacking. It requires that you copy around some function handles.
Using the example provided in the question I will show how to wrap the function openvar in a user defined function that checks the size of the input variable and then allows the user to cancel any open operation for variables that are too large.
Additionally, this should work when the user double clicks a variable in the Workspace pane of the Matlab IDE.
We need to do three things.
Get a handle to the original openvar function
Define the wrapper function that calls openvar
Redirect the original openvar name to our new function.
Example Function
function openVarWrapper(x, vector)
maxVarSize = 10000;
%declare the global variable
persistent openVarHandle;
%if the variable is empty then make the link to the original openvar
if isempty(openVarHandle)
openVarHandle = #openvar;
end
%no variable name passed, call was to setup connection
if narargin==0
return;
end
%get a copy of the original variable to check its size
tmpVar = evalin('base', x);
%if the variable is big and the user doesn't click yes then return
if prod( size( tmpVar)) > maxVarSize
resp = questdlg(sprintf('Variable %s is very large, open anyway?', x));
if ~strcmp(resp, 'Yes')
return;
end
end
if ischar(x) && ~isempty(openVarHandle);
openVarHandle(x);
end
end
Once this function is defined then you simply need to execute a script that
Clears any variables named openvar
run the openVarWrapper script to setup the connection
point the original openVar to openVarWrapper
Example Script:
clear openvar;
openVarWrapper;
openvar = #openVarWrapper;
Finally when you want to clean everything up you can simply call:
clear openvar;
I prefer jmetz's approach using builtin() when it can be applied, because it is clean and to the point. Unfortunately, many many functions are not found by builtin().
I found that I was able to wrap a function using a combination of the which -all and cd commands. I suspect that this approach can be adapted to a wide variety of applications.
In my example case, I wanted to (temporarily) wrap the interp1 function so that I could check for NaN output values. (The interp1 function will, by default, return a NaN under some conditions, such as if a query point is larger than the largest sample point.) Here's what I came up with:
function Vq = interp1(varargin)
persistent interp1_builtin;
if (isempty(interp1_builtin)) % first call: handle not set
toolbox = 'polyfun';
% get a list of all known instances of the function, and then
% select the first such instance that contains the toolbox name
% in its path
which_list = which('interp1','-all');
for ii = 1:length(which_list)
if (strfind(which_list{ii}, [filesep, toolbox, filesep]))
base_path = fileparts(which_list{ii}); % path to the original function
current_path = pwd;
cd(base_path); % go to the original function's directory
interp1_builtin = #interp1; % create a function handle to the original function
cd(current_path); % go back to the working directory
break
end
end
end
Vq = interp1_builtin(varargin{:}); % call the original function
% test if the output was NaN, and print a message
if (any(isnan(Vq)))
dbstack;
disp('ERROR: interp1 returned a NaN');
keyboard
end
end
See also: How to use MATLAB toolbox function which has the same name of a user defined function

Is there a way to push a MATLAB workspace onto a stack?

Does anyone know if it's possible to have a stack of workspaces in MATLAB? It would be very convenient, to say the least.
I need this for research. We have several scripts which interact in interesting ways. Functions have local variables, but not scripts...
The regular Matlab function call stack is itself a stack of workspaces. Just using functions is the easiest way to use one, and Matlab's copy-on-write makes this reasonably efficient. But that's probably not what you're asking.
There's a natural correspondence between workspaces and structs, since the same identifiers are valid for variable names and struct fields. They're both essentially identifier => Mxarray mappings.
You can use whos and evalin to capture workspace state to a struct. Use a cell vector to implement a stack of them. (A struct array won't work because it requires homogeneous field names.) The stack could be stored in appdata to prevent it from appearing in a workspace itself.
Here are push and pop functions for this technique.
function push_workspace()
c = getappdata(0, 'WORKSPACE_STACK');
if isempty(c)
c = {};
end
% Grab workspace
w = evalin('caller', 'whos');
names = {w.name};
s = struct;
for i = 1:numel(w)
s.(names{i}) = evalin('caller', names{i});
end
% Push it on the stack
c{end+1} = s;
setappdata(0, 'WORKSPACE_STACK', c);
function pop_workspace()
% Pop last workspace off stack
c = getappdata(0, 'WORKSPACE_STACK');
if isempty(c)
warning('Nothing on workspace stack');
return;
end
s = c{end};
c(end) = [];
setappdata(0, 'WORKSPACE_STACK', c);
% Do this if you want a blank slate for your workspace
evalin('caller', 'clear');
% Stick vars back in caller's workspace
names = fieldnames(s);
for i = 1:numel(names)
assignin('caller', names{i}, s.(names{i}));
end
It sounds like you'd like to switch back and forth between workspaces of variables. The best way I can think to do this is to use the SAVE, CLEAR, and LOAD commands to move sets of variables back and forth between MAT-files and the workspace:
save workspace_1.mat %# Save all variables in the current workspace
%# to a .mat file
clear %# Clear all variables in the current workspace
load workspace_2.mat %# Load all variables from a .mat file into the
%# current workspace
Wonderful. (Haven't found using 0 with getappdata documented anywhere tho...so this could might away in the future.) Have added push & pop to my util library, and also the following:
pop_workspace(keep_current)
% keep_current: bool: if true, current vars retained after pop
. . .
if (~keep_current)
evalin('caller','clear');
end
A little creativity and one could retain only selected vars, and avoid overwriting on a pop. I found I also need the following function in my work:
function pull_workspace(names)
% pulls variablesin cell array names{} into workspace from stack without
% popping the workspace stack
%
% pulled variable will be a local copy of the stack's variable,
% so modifying it will leave the stack's variable untouched.
%
if (~exist('names','var') || isempty(names))
pull_all = true;
else
pull_all = false;
% if names is not a cell array, then user gave us
% just 1 var name as a string. make it a cell array.
if (~iscell(names))
names = {names};
end
end
% Peek at last workspace on stack
c = getappdata(0, 'WORKSPACE_STACK');
if isempty(c)
warning('Nothing on workspace stack');
return;
end
s = c{end};
% Stick vars back in caller's workspace
if (pull_all)
names = fieldnames(s);
end
for i = 1:numel(names)
assignin('caller', names{i}, s.(names{i}));
end
end