Saturday, February 2, 2008

Plotting in MATLAB

Further searching in the murky depths of MATLAB’s documentation revealed some useful information regarding the plot function. Whilst it may be common knowledge that plotting is fairly simple, there are some nice additions that can be made to a default-style plot that can enhance its appearance.

  1. The first is the popular grid function. It’s simple and does what it says - adds a grid to the plot. Most people either state grid or grid on, but if you fancy being different, try grid minor
  2. The second is the box function. Again, its name suggests that it adds (or removes) the box around a plot. Again, it’s possible to enter box or box off. For most, if not all, plots the box function removes the thin black frame - essentially enabling you to blend the plot into the background.
  3. Next on my list is the axis function. MATLAB has the tendency to fit all the data on a plot and disregards scaling by default. In order to correct this, you can issue the axis equal command which sets the aspect ratio so that the data units are the same in every direction. I, personally, have found this useful on many occasions as a plot is no longer distorted.

But then, MATLAB’s plot function only plots what it knows how to plot. So the structure that you created which encapsulates virtually every data type available cannot be plotted by simply typing in plot(myStructure). There are, however, several ways for you to overcome this. You could either rewrite the plot function (not recommended) or simply overload the plot function. Overloading the function will involve some programming on your part, but it can be done.

How you overload it is up to you. I have two methods that overload the plot function. One will operate when only one input is entered (it can be adapted to multiple inputs if necessary) and the other will work with any given number of inputs (basically, a method that uses a sledgehammer to crack a walnut).

My first proposal (my apologies for the formatting by the way - I have yet to find a way to control this): (Edit 02/02/2008: this appears to have only worked the once, so please refer to the second long-winded proposal)

function plot(p)

% Decide if the argument is a structure

if ~isstruct(p)

% Not a structure so run built-in code
disp('Executing built-in code');
builtin('plot',p);

else

% Is a structure so run built-in code
disp(’Executing custom code’);

end

So what’s gone on here, you may ask. Good question. This function is only called when one input is specified (and is called if the file falls into one of the directories added using the addpath method or is in be current directory). Should the first input be of type structure, it runs some custom code (to, perhaps, verify the input data before ripping it apart in order to plot it). If, on the other hand, the input is of some data type that isn’t a structure (e.g. of type double or of type char), then the built-in/hard-wired function is executed using the builtin function.

Should you be feeling particularly adventurous, you could follow proposal two (although this is not recommended as there are definitely better ways in accomplishing this task). So, for novelty value, I have included below the method that uses the sledgehammer to crack one rotten walnut.

My second proposal:

function plot(varargin)

% Criteria for executing custom code

if 3 > length(varargin{1}) && nargin == 1

disp('My own defined function');

if isstruct(varargin{1})

% Run custom code here
% Note: You could still call the plot command here - as long as it
% eventually calls the built-in function

error('Unexpected input: expected a structured object');

end

else

disp(’Running built-in parent function’);

% You may want to cover the structured case below using the
% "isstruct()" function

if isnumeric(varargin{1})

% It appears that numeric data is entered
tempstring = mat2str(varargin{1});

else

% Not numeric
tempstring = ['’ varargin{1} ‘’];

end

for i = 2:length(varargin)

if isnumeric(varargin{i})

% Numeric data entered - put into matrix form
tempstring = [tempstring ', ' mat2str(varargin{i})];

else

% Not a string - just save as text
tempstring = [tempstring ‘, ‘'’ varargin{i} ‘'’ ‘];

end

end

%
% Uncomment for debugging (lets you see the run string):
%[’builtin('’plot'’,’ tempstring ‘)’]

% Render the command using the tempstring as arguments
eval(['builtin(''plot'',' tempstring ')'])

end

The above takes every input possible and, according to your “run my custom code” criteria will run your custom code and take any of the other inputs. In this example, it takes the first input and checks to see if it’s of size 1 (i.e. not multi-dimensional - the code’s bad enough as it is) and that there is only one input. It then moves onto the next step: determining if the first input is a structure. That’s all this function does. I am aware that it probably won’t accept every form of input either - that’s for version 2.
The remainder of the above reforms the original expression (i.e. plot(input1, input2, ..., etc); ) and uses the eval command to execute the builtin command to plot the data. Whilst this is absolutely pointless for most purposes, it merely works as a proof of concept (well, that’s the excuse I’m sticking to) and could be useful for amending the plot function for multiple inputs (particularly where one is unable to know in advance the number of input arguments that will be used).

So, in conclusion, if you wanted to override the built-in plot function, it’s an idea that you know the number of inputs beforehand and then only override that instance of the plot function. Otherwise, your work suddenly becomes all the more complicated… or interesting as I prefer to call it.




Update: 2nd February 2008
After searching for an alternative solution having found the first proposal no longer worked, I found a way to reduce the content of the second version which should also take care of possible problems. The first few lines are the same (i.e. the code that determines whether to execute the custom code which, in this case, is determined by a structured input) but it's the parsing part that has been hugely altered.


And the part you've been waiting for...


function plot(varargin)

% Criteria for executing custom code

if 3 > length(varargin{1}) && nargin == 1

disp('My own defined function');

if isstruct(varargin{1})

% Run custom code here
% Note: You could still call the plot command here - as long as it
% eventually calls the built-in function

error('Unexpected input: expected a structured object');

end

else

disp(’Running built-in parent function’);

builtin('plot', varargin{:})

end


It works as before (using the builtin function to use the original plot function so functionality isn't lost). It's simple and there's no complex parsing or reconstruction of inputs (shame), so I don't know why I didn't think of this in the first instance.


0 comments: