73

I think everyone would agree that the MATLAB language is not pretty, or particularly consistent. But nevermind! We still have to use it to get things done.

What are your favourite tricks for making things easier? Let's have one per answer so people can vote them up if they agree. Also, try to illustrate your answer with an example.

Matthew Simoneau
  • 6,199
  • 6
  • 35
  • 46
Matt
  • 5,522
  • 5
  • 29
  • 24

31 Answers31

39

Using the built-in profiler to see where the hot parts of my code are:

profile on
% some lines of code
profile off
profile viewer

or just using the built in tic and toc to get quick timings:

tic;
% some lines of code
toc;
Jason Sundram
  • 12,225
  • 19
  • 71
  • 86
31

Directly extracting the elements of a matrix that satisfy a particular condition, using logical arrays:

x = rand(1,50) .* 100;
xpart = x( x > 20 & x < 35);

Now xpart contains only those elements of x which lie in the specified range.

Sundar R
  • 13,776
  • 6
  • 49
  • 76
  • in matlab, you can use the function find to do basically the same thing. – devin Oct 28 '09 at 21:54
  • 10
    But find is MUCH slower. Logical Indexing is way faster, unless you need to know the indices of the matches. – Marc Apr 26 '10 at 13:23
28

Provide quick access to other function documentation by adding a "SEE ALSO" line to the help comments. First, you must include the name of the function in all caps as the first comment line. Do your usual comment header stuff, then put SEE ALSO with a comma separated list of other related functions.

function y = transmog(x)
%TRANSMOG Transmogrifies a matrix X using reverse orthogonal eigenvectors
%
% Usage:
%   y = transmog(x)
%
% SEE ALSO
% UNTRANSMOG, TRANSMOG2

When you type "help transmog" at the command line, you will see all the comments in this comment header, with hyperlinks to the comment headers for the other functions listed.

Scottie T
  • 11,729
  • 10
  • 45
  • 59
23

Turn a matrix into a vector using a single colon.

x = rand(4,4);
x(:)
Scottie T
  • 11,729
  • 10
  • 45
  • 59
  • 1
    How would yo do it for a sub matrix? Let's say: x = rand(20, 20); I want to turn x(1:10, 1:10) into a vector. Are y=reshape(x(:10, 1:10), [], 1) or y=x(1:10, 1:10)-> y=y(:) my only options? Needless to say that x(1:10, 1:10)(:) won't work. – Royi Jan 20 '10 at 10:36
  • @Drazick, you can access the elements of x using multiple dimensional indicies, or a single dimensional index. `myElems = [1:10 21:30 31:40...181:190]; y = x(myElems);` – Scottie T Jan 20 '10 at 16:30
  • Let's say I have an image - I. to calculate it variance I would do: var(I(:)). What if I want to calculate the variance of part of it - I(1:20, 1:20). var(var(I(1:20, 1:20)) won't do it (It's wrong). The options I know about, y = I(1:20, 1:20) -> var(y(:)) or y=reshape(I(1:20, 1:20), [], 1) -> var(y(:)). What I'm asking is there a way to apply the colon operator on sub matrices of a matrix without reallocating it? Thanks. – Royi Jan 20 '10 at 17:54
  • @Drazick: you should make that a separate question. Also, maybe `sub2ind` could help – Tobias Kienzler Feb 04 '11 at 10:24
  • @Drazick: feval(@(x) x(:), x(1:3,1:3)) – Szymon Bęczkowski Sep 30 '12 at 18:41
22

Vectorizing loops. There are lots of ways to do this, and it is entertaining to look for loops in your code and see how they can be vectorized. The performance is astonishingly faster with vector operations!

Jason Sundram
  • 12,225
  • 19
  • 71
  • 86
  • is this still the case now that Matlab has a JIT compiler? It would be interesting to see. – Matt Oct 20 '08 at 13:39
20

Anonymous functions, for a few reasons:

  1. to make a quick function for one-off uses, like 3x^2+2x+7. (see listing below) This is useful for functions like quad and fminbnd that take functions as arguments. It's also convenient in scripts (.m files that don't start with a function header) since unlike true functions you can't include subfunctions.
  2. for closures -- although anonymous functions are a little limiting as there doesn't seem to be a way to have assignment within them to mutate state.

.

% quick functions
f = @(x) 3*x.^2 + 2*x + 7;
t = (0:0.001:1);
plot(t,f(t),t,f(2*t),t,f(3*t));

% closures (linfunc below is a function that returns a function,
% and the outer functions arguments are held for the lifetime
% of the returned function.
linfunc = @(m,b) @(x) m*x+b;
C2F = linfunc(9/5, 32);
F2C = linfunc(5/9, -32*5/9);
Ricardo Altamirano
  • 14,650
  • 21
  • 72
  • 105
Jason S
  • 184,598
  • 164
  • 608
  • 970
19

Matlab's bsxfun, arrayfun, cellfun, and structfun are quite interesting and often save a loop.

M = rand(1000, 1000);
v = rand(1000,    1);
c = bsxfun(@plus, M, v);

This code, for instance, adds column-vector v to each column of matrix M.

Though, in performance critical parts of your application you should benchmark these functions versus the trivial for-loop because often loops are still faster.

ymihere
  • 381
  • 1
  • 4
  • 11
18

LaTeX mode for formulas in graphs: In one of the recent releases (R2006?) you add the additional arguments ,'Interpreter','latex' at the end of a function call and it will use LaTeX rendering. Here's an example:

t=(0:0.001:1);
plot(t,sin(2*pi*[t ; t+0.25]));
xlabel('t'); 
ylabel('$\hat{y}_k=sin 2\pi (t+{k \over 4})$','Interpreter','latex');
legend({'$\hat{y}_0$','$\hat{y}_1$'},'Interpreter','latex');

Not sure when they added it, but it works with R2006b in the text(), title(), xlabel(), ylabel(), zlabel(), and even legend() functions. Just make sure the syntax you are using is not ambiguous (so with legend() you need to specify the strings as a cell array).

Ricardo Altamirano
  • 14,650
  • 21
  • 72
  • 105
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • Matlab will throw an error with your example, though, because the vectors passed to the `plot` command are not the same length. I presume you're trying to plot two lines, right? You need a semicolon in the matrix passed to your plot command so Matlab knows it's two separate series, i.e. like this: `plot(t,sin(2*pi*[t ; t+0.25]));` – Ricardo Altamirano Feb 07 '13 at 16:06
17

Using xlim and ylim to draw vertical and horizontal lines. Examples:

  1. Draw a horizontal line at y=10:

    line(xlim, [10 10])

  2. Draw vertical line at x=5:

    line([5 5], ylim)

rescdsk
  • 8,739
  • 4
  • 36
  • 32
Samil
  • 958
  • 1
  • 17
  • 20
  • 1
    This doesn't always work. The limits are not updated in real time. In that case, calling `drawnow` will force it to update them. – Memming Apr 20 '12 at 19:27
16

Here's a quick example:

I find the comma separated list syntax quite useful for building function calls:

% Build a list of args, like so:
args = {'a', 1, 'b', 2};
% Then expand this into arguments:
output = func(args{:})
Matt
  • 5,522
  • 5
  • 29
  • 24
  • 1
    Not sure about MATLAB but in Octave you can assign values to multiple variables in a similar way: `[one two three four] = {1 2 3 4}{:}` – Griffin Mar 14 '13 at 14:17
11

Here's a bunch of nonobvious functions that are useful from time to time:

  • mfilename (returns the name of the currently running MATLAB script)
  • dbstack (gives you access to the names & line numbers of the matlab function stack)
  • keyboard (stops execution and yields control to the debugging prompt; this is why there's a K in the debug prompt K>>
  • dbstop error (automatically puts you in debug mode stopped at the line that triggers an error)
Jason S
  • 184,598
  • 164
  • 608
  • 970
10

Invoking Java code from Matlab

petrichor
  • 6,459
  • 4
  • 36
  • 48
Rorick
  • 8,857
  • 3
  • 32
  • 37
10

Using nargin to set default values for optional arguments and using nargout to set optional output arguments. Quick example

function hLine=myplot(x,y,plotColor,markerType)
% set defaults for optional paramters
if nargin<4, markerType='none'; end
if nargin<3, plotColor='k'; end

hL = plot(x,y,'linetype','-', ...  
              'color',plotColor, ...
              'marker',markerType, ...
              'markerFaceColor',plotColor,'markerEdgeColor',plotColor);

% return handle of plot object if required
if nargout>0, hLine = hL; end
Azim J
  • 8,260
  • 7
  • 38
  • 61
  • 8
    I find functions easier to maintain if they use `if exist('plotColor', 'var') ...`, because then you're using the name of the argument and not just its argument number. – rescdsk May 10 '11 at 15:36
10

I like using function handles for lots of reasons. For one, they are the closest thing I've found in MATLAB to pointers, so you can create reference-like behavior for objects. There are a few neat (and simpler) things you can do with them, too. For example, replacing a switch statement:

switch number,
  case 1,
    outargs = fcn1(inargs);
  case 2,
    outargs = fcn2(inargs);
  ...
end
%
%can be turned into
%
fcnArray = {@fcn1, @fcn2, ...};
outargs = fcnArray{number}(inargs);

I just think little things like that are cool.

gnovice
  • 125,304
  • 15
  • 256
  • 359
5

cellfun and arrayfun for automated for loops.

KennyMorton
  • 681
  • 1
  • 4
  • 9
5

The colon operator for the manipulation of arrays.

@ScottieT812, mentions one: flattening an array, but there's all the other variants of selecting bits of an array:


x=rand(10,10);
flattened=x(:);
Acolumn=x(:,10);
Arow=x(10,:);

y=rand(100);
firstSix=y(1:6);
lastSix=y(end-5:end);
alternate=y(1:2:end);
Ian Hopkinson
  • 3,412
  • 4
  • 24
  • 28
5

Oh, and reverse an array

v = 1:10;
v_reverse = v(length(v):-1:1);
Robert Van Hoose
  • 203
  • 1
  • 11
  • 6
    Hmm. I'd just use `flipud()` or `fliplr()` to do this. However, combined with steps, this is more useful. e.g. v(end:-4:1) for example. – Matt Oct 20 '08 at 13:40
  • I like my way vs. flipud()/fliplr() because you don't have to know whether you have a column vector or a row vector. – Robert Van Hoose Oct 20 '08 at 14:15
  • 13
    You can drop the length() call and write v_reverse = v(end:-1:1); – Jonas Dec 22 '09 at 18:41
5

conditional arguments in the left-hand side of an assignment:

t = (0:0.005:10)';
x = sin(2*pi*t);
x(x>0.5 & t<5) = 0.5;
% This limits all values of x to a maximum of 0.5, where t<5
plot(t,x);
Jason S
  • 184,598
  • 164
  • 608
  • 970
5

In order to be able to quickly test a function, I use nargin like so:

function result = multiply(a, b)
if nargin == 0 %no inputs provided, run using defaults for a and b
    clc;
    disp('RUNNING IN TEST MODE')
    a = 1;
    b = 2;
end

result = a*b;

Later on, I add a unit test script to test the function for different input conditions.

Samil
  • 958
  • 1
  • 17
  • 20
5

Know your axis properties! There are all sorts of things you can set to tweak the default plotting properties to do what you want:

set(gca,'fontsize',8,'linestyleorder','-','linewidth',0.3,'xtick',1:2:9);

(as an example, sets the fontsize to 8pt, linestyles of all new lines to all be solid and their width 0.3pt, and the xtick points to be [1 3 5 7 9])

Line and figure properties are also useful, but I find myself using axis properties the most.

Jason S
  • 184,598
  • 164
  • 608
  • 970
5

Be strict with specifying dimensions when using aggregation functions like min, max, mean, diff, sum, any, all,...

For instance the line:

reldiff = diff(a) ./ a(1:end-1)

might work well to compute relative differences of elements in a vector, however in case the vector degenerates to just one element the computation fails:

>> a=rand(1,7);
>> diff(a) ./ a(1:end-1)

ans =
   -0.5822   -0.9935  224.2015    0.2708   -0.3328    0.0458

>> a=1;
>> diff(a) ./ a(1:end-1)
??? Error using ==> rdivide
Matrix dimensions must agree.

If you specify the correct dimensions to your functions, this line returns an empty 1-by-0 matrix, which is correct:

>> diff(a, [], 2) ./ a(1, 1:end-1)

ans =

   Empty matrix: 1-by-0

>> 

The same goes for a min-function which usually computes minimums over columns on a matrix, until the matrix only consists of one row. - Then it will return the minimum over the row unless the dimension parameter states otherwise, and probably break your application.

I can almost guarantee you that consequently setting the dimensions of these aggregation functions will save you quite some debugging work later on.

At least that would have been the case for me. :)

ymihere
  • 381
  • 1
  • 4
  • 11
4

Using ismember() to merge data organized by text identfiers. Useful when you are analyzing differing periods when entries, in my case company symbols, come and go.

%Merge B into A based on Text identifiers
UniverseA = {'A','B','C','D'};
UniverseB = {'A','C','D'};

DataA = [20 40 60 80];
DataB = [30 50 70];

MergeData = NaN(length(UniverseA),2);

MergeData(:,1) = DataA;

[tf, loc] = ismember(UniverseA, UniverseB);

MergeData(tf,2) = DataB(loc(tf));

 MergeData =

20    30
40   NaN
60    50
80    70
Robert Van Hoose
  • 203
  • 1
  • 11
4

Asking 'why' (useful for jarring me out of a Matlab runtime-fail debugging trance at 3am...)

lightw8
  • 3,262
  • 2
  • 25
  • 35
3

Vectorization:

function iNeedle = findClosest(hay,needle)
%FINDCLOSEST find the indicies of the closest elements in an array.
% Given two vectors [A,B], findClosest will find the indicies of the values
% in vector A closest to the values in vector B.
[hay iOrgHay] = sort(hay(:)');  %#ok must have row vector

% Use histogram to find indices of elements in hay closest to elements in
% needle. The bins are centered on values in hay, with the edges on the
% midpoint between elements.
[iNeedle iNeedle] = histc(needle,[-inf hay+[diff(hay)/2 inf]]); %#ok

% Reversing the sorting.
iNeedle = iOrgHay(iNeedle);
Scottie T
  • 11,729
  • 10
  • 45
  • 59
user36927
  • 41
  • 1
  • 1
  • 4
3

Executing a Simulink model directly from a script (rather than interactively) using the sim command. You can do things like take parameters from a workspace variable, and repeatedly run sim in a loop to simulate something while varying the parameter to see how the behavior changes, and graph the results with whatever graphical commands you like. Much easier than trying to do this interactively, and it gives you much more flexibility than the Simulink "oscilloscope" blocks when visualizing the results. (although you can't use it to see what's going on in realtime while the simulation is running)

A really important thing to know is the DstWorkspace and SrcWorkspace options of the simset command. These control where the "To Workspace" and "From Workspace" blocks get and put their results. Dstworkspace defaults to the current workspace (e.g. if you call sim from inside a function the "To Workspace" blocks will show up as variables accessible from within that same function) but SrcWorkspace defaults to the base workspace and if you want to encapsulate your call to sim you'll want to set SrcWorkspace to current so there is a clean interface to providing/retrieving simulation input parameters and outputs. For example:

function Y=run_my_sim(t,input1,params)
% runs "my_sim.mdl" 
% with a From Workspace block referencing I1 as an input signal
% and parameters referenced as fields of the "params" structure
% and output retrieved from a To Workspace block with name O1.
opt = simset('SrcWorkspace','current','DstWorkspace','current');
I1 = struct('time',t,'signals',struct('values',input1,'dimensions',1));
Y = struct;
Y.t = sim('my_sim',t,opt);
Y.output1 = O1.signals.values;
Jason S
  • 184,598
  • 164
  • 608
  • 970
3

Contour plots with [c,h]=contour and clabel(c,h,'fontsize',fontsize). I usually use the fontsize parameter to reduce the font size so the numbers don't run into each other. This is great for viewing the value of 2-D functions without having to muck around with 3D graphs.

Jason S
  • 184,598
  • 164
  • 608
  • 970
3

Using persistent (static) variables when running an online algorithm. It may speed up the code in areas like Bayesian machine learning where the model is trained iteratively for the new samples. For example, for computing the independent loglikelihoods, I compute the loglikelihood initially from scratch and update it by summing this previously computed loglikelihood and the additional loglikelihood.

Instead of giving a more specialized machine learning problem, let me give a general online averaging code which I took from here:

function av = runningAverage(x)
% The number of values entered so far - declared persistent.
persistent n;
% The sum of values entered so far - declared persistent.
persistent sumOfX;
if x == 'reset' % Initialise the persistent variables.
    n = 0;
    sumOfX = 0;
    av = 0;
else % A data value has been added.
    n = n + 1;
    sumOfX = sumOfX + x;
    av = sumOfX / n; % Update the running average.
end

Then, the calls will give the following results

runningAverage('reset')
ans = 0
>> runningAverage(5)
ans = 5
>> runningAverage(10)
ans = 7.5000
>> runningAverage(3)
ans = 6
>> runningAverage('reset')
ans = 0
>> runningAverage(8)
ans = 8
petrichor
  • 6,459
  • 4
  • 36
  • 48
  • `persistent` is dangerous because you can't directly set the internal state, which means that you can't test properly. Also, it means that you can only use the function in one place at a time. For example, if you wanted to compute running averages of two separate quantities, then you'd need to copy the file in order to separate the states. – Nzbuu Oct 09 '11 at 19:08
  • It is true that we should avoid using it if it doesn't help since it may lead to unexpected problems which are difficult to notice. In my problem, I do some online modifications to few variables so it improved the speed of the code considerably. One should use it with caution. – petrichor Oct 13 '11 at 11:55
2

-You can make a Matlab shortcut to an initialization file called startup.m. Here, I define formatting, precision of the output, and plot parameters for my Matlab session (for example, I use a larger plot axis/font size so that .fig's can be seen plainly when I put them in presentations.) See a good blog post from one of the developers about it http://blogs.mathworks.com/loren/2009/03/03/whats-in-your-startupm/ .

-You can load an entire numerical ascii file using the "load" function. This isn't particularly fast, but gets the job done quickly for prototyping (shouldn't that be the Matlab motto?)

-As mentioned, the colon operator and vectorization are lifesavers. Screw loops.

temp2290
  • 1,222
  • 2
  • 13
  • 17
2

x=repmat([1:10],3,1); % say, x is an example array of data

l=x>=3; % l is a logical vector (1s/0s) to highlight those elements in the array that would meet a certain condition.

N=sum(sum(l));% N is the number of elements that meet that given condition.

cheers -- happy scripting!

Community
  • 1
  • 1
Y.T.
  • 200
  • 3
2

Here's what I use frequently:

% useful abbreviations

flat=@(x) x(:);

% print basic statistics
stats=@(x) sprintf('mean +/- s.d. \t= %f +/- %f\nmin, max \t\t= %f, %f\nmedian, mode \t= %f, %f', ...
    mean(flat(x)), std(flat(x)), min(flat(x)), max(flat(x)), median(flat(x)), mode(flat(x)) );

nrows=@(x) size(x,1);
ncols=@(x) size(x,2);
nslices=@(x) size(x,3);

% this is just like ndims except it returns 0 for an empty matrix and
% ignores dimensions of size 0.
ndim=@(x) length(find(size(x)));

These abbreviations are useful for finding the mean and standard deviation of pixel values in a small area of an image. I would use the following logic:

phantomData = phantom();

stats( phantomData(50:80, 50:80) )

What if I wanted to put the size of an image in its title?

imagesc( phantomData );

title( sprintf('The image size is %d by %d by %d.', nrows(phantomData), ncols(phantomData), nslices(phantomData)) )

user244795
  • 698
  • 6
  • 14
2

I'm surprised that while people mentioned the logical array approach of indexing an array, nobody mentioned the find command.

e.g. if x is an NxMxO array

x(x>20) works by generating an NxMxO logical array and using it to index x (which can be bad if you have large arrays and are looking for a small subset

x(find(x>20)) works by generating list (i.e. 1xwhatever) of indices of x that satisfy x>20, and indexing x by it. "find" should be used more than it is, in my experience.

More what I would call 'tricks'

you can grow/append to arrays and cell arrays if you don't know the size you'll need, by using end + 1 (works with higher dimensions too, so long as the dimensions of the slice match -- so you'll have to initialize x to something other than [] in that case). Not good for numerics but for small dynamic lists of things (or cell arrays), e.g. parsing files.

e.g.

>> x=[1,2,3]
x =  1     2     3
>> x(end+1)=4
x =  1     2     3     4

Another think many people don't know is that for works on any dim 1 array, so to continue the example

>> for n = x;disp(n);end
     1
     2
     3
     4

Which means if all you need is the members of x you don't need to index them.

This also works with cell arrays but it's a bit annoying because as it walks them the element is still wrapped in a cell:

>> for el = {1,2,3,4};disp(el);end
    [1]
    [2]
    [3]
    [4]

So to get at the elements you have to subscript them

>> for el = {1,2,3,4};disp(el{1});end
     1
     2
     3
     4

I can't remember if there is a nicer way around that.

simon
  • 7,044
  • 2
  • 28
  • 30
  • Using `find` in this situations is a bad idea because it's redundant and slower. Personally, I find the logical approach clearer, because it avoids the additional clutter too. – Nzbuu Oct 09 '11 at 19:05