MATLAB PART 3 - Programming

Dave White

Input output handling

% You can create defaults if no inputs are created
if ~exist('validRange','var') && isempty(validRange)
   validRange=0:1
end

%Sometimes its nice to have variables available outside a function without it being assigned an output
%This is good for me where I have a function that spontaneously crashes before data is saved
%I can send data from function workspace to 'base'/default workspace before I reach that line
%I can then cleanup the data and save it manually
%assignin -> send variable from one workspace to another
assignin('base','A_base',A_fcn)

%Remember how 'plot' has options that can be assigned by a field?
%You can do this same thing with input parsing
functionA(varargin)                      %Varargin is an input variable that allows for an ambiguous number of inputs
p = inputParser('variableA',variableA);  %Essentially just reads through all inputs and organizes them
p.addParameter('variableA','D',@ischar); %Checks to make sure that variable is is of char type, assigns default 'D' if not present
variableA          =p.Results.variableA; %Optional, but easier to use 'variableA' in this format

A mock experiment

Lets build a simple experiment
First lets build a function to act as a template that can run different types of experiments

function R = questionaire(Q,validRange)
%A questionaire that accepts responses in numbers
% Q          - Questions to ask
% validRange - range of numbers that are acceptable
% R          - Responses
assert(iscell(Q),'Questions are not in cell format.')
R=zeros(size(Q));
for i = 1:length(Q)
    clc %clear screen with every new question
    while true

        try
            %PROMPT FOR INPUT
            rsp=input([Q{i} ': '],'s');
            R(i)=str2double(rsp);

            %CHECK IF RESPONSE IS WITHIN RANGE
            fail=~ismember(R(i),validRange);
        catch
            fail=1;
        end

        if fail
            clc
            disp('Invalid response. Try again.')
        else
            break
        end

    end
end
clc
disp('Questionaire Complete. Thank you!')

octave

Now lets build a function to run a specific experiment using the general function


function [] = ADDquestionaire(subj);
if ~exist('subj','var') || isempty(subj)
    error('Requires subject initials.');
end
Q=txt2lines('addquestions.txt');
validRange=1:9;
clc
input(['Welcome.' ...
   newline 'Please rate your response with the following questions with numbers ' num2str(validRange(1)) ' to ' num2str(validRange(end)) '.' ...
   newline 'Press return to start questionaire: '],'s');
R=questionaire(Q,validRange);
dname='ADDquestionaireResponses';
mkdir(dname);
fname=[ dname filesep subj '_addQs'];
save(fname,'R','subj')

%You can run this on your own, but you can generate similar data by running 'makeData'
%Lets build some scripts to analyze this data
%ANALYSIS SCRIPT
try
    dname='ADDquestionaireResponses';
    cd(dname);
    files=dir; %dir returns all contents of a directory
    files={files.name}; %the following process removes any files that don't end with .mat
    ind=contains(files,'mat');
    files=files(ind);
    scores=zeros(length(files),1);
    Subjs=cell(length(files),1);

    %PROCESS BY SUBJECT
    All=[]; %ALL IS A GOOD
    for i = 1:length(files)
        load(files{i});
        Subjs{i}=subj;
        S.(subj).R=R;
        scores(i)=sum(num2str(R));
        S.(subj).score=scores;
        S.(subj).num=i;
        All=[All; R];
    end

    %Z-SCORES
    zscores=zscore(scores);
    for i = 1:length(Subjs)
        S.(Subjs{i}).zscore=zscores(i);
    end

    figure(1)
    plot(zscores)
    title('ADD questionare Z-scores')
    xlabel('Subject number')
    ylabel('Z-Score')

    %Correlation
    n=randn(length(Subjs),1);
    rho=corr(scores,n);

    figure(2)
    plot(scores,n,'k.')
    title(['rho = ' num2str(rho)])

catch ME
    cd ..
    rethrow(ME)
end
cd ..

Try using the variable 'All' in your bootstrap function

ArrayFun

REPLACE LOOPS WITH FUNS
There are a group of functions that can replace loops and will run faster
They are often less intuitive

ARRAYFUN
arrayfun(function,input1,input2,…,Options)
Apply a function to each element

A=1:10
X=arrayfun(@testfun,A)

EXAMPLE
If I needed to get subscripts of each pixel of a patch 7x7 patch in a 1920x1080 image and all I have is patch centers

SETUP

s=[100,200];
A=zeros(s);
N=randi(s(1),50,1); %example subscripts
M=randi(s(2),50,1); %example subscripts
indfun=@(x,y) x < 1 | y < 1 | x > s(2) | y > s(1);

ind=sub2ind(size(A),N(:),M(:));
B=A;
B(indo)=1;

imagesc(B)

Advanced arrayfun example

Arrayfun is often easier to figure out than a loop

NON LOOP

fun=@(x,y) meshgrid(x-3:x+3, y-3:y+3);
[X,Y]=arrayfun(fun,M,N,'UniformOutput',false);
X=[X{:}];
Y=[Y{:}];

%Remove values otuside of range
ind=indfun(X,Y);
X(ind)=[];
Y(ind)=[];

%Convert subs 2 ind
ind=sub2ind(size(A),Y(:),X(:));

%VISUALIZE
D=A;
D(ind)=1;
imagesc(D);

LOOP

X=zeros(length(N),7^2)
Y=zeros(length(N),7^2)
for i = 1:length(N)
    Nall=N(i)-3:N(i)+3;
    Mall=M(i)-3:M(i)+3;
    [x,y]=meshgrid(Mall,Nall);
    X(i,:)=x(:);
    Y(i,:)=y(:);
end

%Remove values otuside of range
ind=indfun(X,Y)
X(ind)=[];
Y(ind)=[];

%Convert subs 2 ind
indl=sub2ind(size(A),Y(:),X(:));
C=A;
C(indl)=1;

%VISUALIZE
imagesc(C)

Uniform Output
By default these functions assume that your outputs are matrices. This option assigns to cells instead, allowing for inconsistent dimensions.
The 'UniformOutput' is usually necessary if output is not same size as input

Cellfun

Cells are a pain to deal with

WORKING WITH GROUPS OF STRINGS

data{1}='This is a string';
data{2}='This is also a string';
data{3}='A third string';
fun=@(x) strsplit(x);
cellfun(fun,data,'UniformOutput',false);

Bsxfun

BSXFUN
Array fun but with explicit expansion
Row wise/column wise operations
Looks for matching sizes

GENERATE DATA
different populations down rows
Samples across columns

X=zeros(100,20);
for i = 1:length(X)
    m=rand(1);
    s=rand(1)*3;
    X(i,:)=normrnd(m,s,1,20);
end

Zscore

m=bsxfun(@minus, X,mean(X,2))
size(m)
Z=bsxfun(@rdivide, m,std(X,[],2))

CHECK

mean(Z,2)
std(Z,[],2)

Symbolic solver

%solve system of linear equations
syms x y z
eqn1 = 2*x + y + z == 2;
eqn2 = -x + y - z == 3;
eqn3 = x + 2*y + 3*z == -10;
sol = solve([eqn1, eqn2, eqn3], [x, y, z]);

Use all keys in plotting

GET KEY DEFS

while true
  waitforbuttonpress
  k = double(get(gcf,'CurrentCharacter'));
  disp(num2str(k))
  drawnow
end

GET KEY DEFS

A=rand(100,100,100);

keys.L=28; %LEFT
keys.R=29; %RIGHT
i=1;
while true
  imagesc(A(:,:,i))
  title(num2str(i))

  waitforbuttonpress
  k = double(get(gcf,'CurrentCharacter'));
  switch k
    case keys.L
      i=i-1;
    case keys.R
      i=i+1;
  end
  if i < 1
      i=1;
  elseif i > size(A,3)
    break
  end

end

Intro to object oriented programming

Objects

Object-oriented programming is a newer way of programming that has potential to make things more organized and
objects - variables created from a class
classes - a template for objects -> call on classes to make objects
properties - variables nested within a class (like fields in structs)
modules - subroutines/functions of a class, usually meant to do something to the object its being called from

Matlab has objects, but is only partially object oriented
for example:
varA="this is a string"
python:

varA="this is a string"
out=varA.split()
#out=["this", "is", "a, "string"]
varA='some string';
out=strsplit(varA)
%out=["this", "is", "a, "string"]

General form for

classdef exampleClassName
 properties
    var1
    var2=1
    var3
 end
 methods
   function obj = exampleClassName(obj,var1)
    %Contructors are methods that create objects
       obj.var1=var1
   end
   function obj = method2(obj,val)
   %Contructors are methods that create objects
       obj.var3=3
   end
 end

%To create an object from this class
obj=exampleClassName(60)
obj.var1
obj.var2
obj.var3

%To call one of its methods
obj=object1.method2
obj.var3

Class/Object example

classdef experiment < handle
properties
  name
  author
  type
  subjects
end
methods
  function obj=expermient(name)
    obj.name();
  end
  function obj=change_properties(obj,author,type,nblocks)
    obj.author=author;
    obj.type=type;
    obj.nblocks=nblocks
    obj.save();
  end
  function obj=add_subject2exp(obj,subjName)
    obj.subjName{end+1}=subjName;
    obj.save();
  end
  function subj=get_subject(obj,subjName)
    subj=subject('subjName')
    subj.load();
  end
  function obj=run_subj(obj,subjName)
    if ~isfield(obj.subjects,'subjName')
      obj.add_subject2exp(subjName)
    end
    subj=get_subject(subjName);
    block=subj.get_next_block();
    data=obj.run(); %notice not in this class
    subj.data.(obj.name).block=data
    subj.save();
    subj.update_completion();
  end
  function obj=save(obj)
    save(obj.name,'obj')
  end
  function obj=load(obj)
    load(obj.name)
  end
  function obj=run(obj)
    %SOME EXPERIMENT CODE HERE
  end
end
end
function experment1 < handle & experiment
methods
  function obj=run(obj)
    %SOME EXPERIMENT CODE HERE
  end
end
end
classdef subject < handle
properties
  age
  name
  sex
  status
  experiments %blocks
  data
end
methods
  function obj=subject(obj)
  end
  function obj=get_next_exp(obj)
  end
  function obj=get_next_block(obj,expName)
  end
  function obj=change_status
  end
  function obj=change_status_to_train
  end
  function obj=change_status_to_run
  end
  function obj=update_completion
  end
  function obj=load(obj)
    obj.check()
    load(obj.name)
  end
  function obj=check()
  end
  function obj=save()
    save(obj.name,'obj')
  end
  function obj=show_completed()
  end
end

Advanced figure properties

special objects for figures
gcr
gcf
gca

all graphics functions use get & set implicitly

%Making advanced changes to properties of figure
f=figure(1)
%whether you assign to a variable, your figure is an object with properties
%you can change these through f, or if you havn't assigned a variable, you can use gcf
fig=gcf %get current figure

%To make some advanced changes to your axes, you need to adjust the properties of your axes object
axes=gca %get current axes

%Not all properties are listed.  See "Figure Properties" in online documentation

Parallelization

parfor is a for loop, but will run each iteration on its own processor core
rules:
Only 1 parfor at a time
parfor; parfor; end; end
independent (variables must not depend on previous interation)
parfor t=1:10; x=(t-1); end
increasing, positive, & integer, iterator
no breaks

Error handling

%You can have code that doesn't error-out if something goes wrong
try
%Run Something that may break
catch ME; %ME is struct that contains the error message
%do something, like save data
  rethrow(ME); %rethrows error
end

try
%Warning %Disp with 'Warning:' attached to the beginning & can trigger debugging tools (next section)
%Error - create your own errors!
%assert -> throw error if condition is not met - like an if statement and 'error' combined

a=nan;
b=1;
try
  z(:,:,i)=imresize(A,[a,b])
catch
  z(:,:,i)=zeros(a,b);
end

'System'

%You can tell matlab to run a system command line function
program='matlab'
command=['ps aux | grep ' program] %You can pass matlab variables to it;
[status,out]=system(command)
%On linux/mac, status will return
%  1 if error
%  0 if successful
%If using logical indexing, always use & and |
%When using if statements, && and || are prefferable
% && and || do what is called 'short-circuiting'
if exist('variableA','var') && exist(variableA)
%if statement 1 fails, it will short-circuit, meaning that it won't test statement2
if exist('variableA','var') & exist(variableA)
%if statement 1 fails, it will test statement2 in this case, and error-out

#! commands

Path & startup

path %list path
g=genpath('/home') %returns all directiories within /home
addpath(g)         %add all directiroies within home to path

Startup
There is a file called startup.m located in (Your matlab install directory)/toolbox/local/
This file will run everytime you start matlab