PlottingEMA Receiver Positions Using Matlab
Here we develop a Matlab function that takes x, y values of one or several receivers and plots a figure like the one shown below. The palate, which is another x, y vector, could be an optional argument.
Outline of Steps:
- Create a toy text file similar to the one shown below.
Note: Be sure to save this in the same path that you will be running your function from otherwise you will have to type in the whole path file in the function call.
- Assume that the x, y values represent the x, y positions of some receiver, say, TT (TongueTip) extracted from labels placed in some data files using mview. So we assume some post-mview labeling has taken place and from that processing we have extracted these data.
X Y
-27.64 -14.85
-26.96 -13.98
-27.2 -17.00
-26.38 -16.42
Of course, in an expanded version of this we should also be able to give the receiver name these x,y come from, say, TT or TB, and it would also be good to allow for more than one receiver, but this will suffice for a simple example of what we are looking to implement.
- Then, let’s use a function readColData.m, from an on-line source on Matlab and included below, to read in the label names and values from the file. Specifically, I give the following command in Matlab’s command window.
[labels,x,y] = readColData('C:\Documents and Settings\Adamantios Gafos\Desktop\xydata.txt')
This results in the following.
labels =
X
Y
x =
-27.6400
-26.9600
-27.2000
-26.3800
y =
-14.8500
-13.9800
-17.0000
-16.4200
- Then plotted by entering the following in the command window.
> plot(x,y,'ro');
> axis([-40 20 -25 20])
> xlabel('mm'), ylabel('mm');
> gtext('Some receiver positions')
Overall, then, the goal is to construct a matlab function that allows one to do this in a single call. We’ll need to learn a bit more about Matlab before doing this.
Functions of Interest
axis
Sets the scaling for the x and y axes of your plot. The syntax is axis([xMin xMax yMin yMax]).
fopen
This is the Matlab command to open a file for read access. Typically it would be set up fid = fopen(filename) where filename is the path to the file you want read. If it is in the same path as the function that you are coding, you need only type the name of the file, otherwise you will need to do the full path.
fid is the file identifier. Once you have opened the file, you can use fid as the argument to other routines such as fread and fclose. Note: You don’t necessarily need to use fid. You can name this whatever you want, but try to keep with typical naming conventions for the sake of ease of reading.
You can also choose the permissions you want the program to have on your file. These are done by entering them as a single character string after the filename. fid = fopen(filename, ‘permission character’). There are a number of possible permissions, all available in the Matlab help file, but for the sake of this assignment, you only need ‘r’ which gives the program permission to read the file without altering it.
fscanf
This function reads formatted data from a file. It is formatted arrayName = fscanf(fid, format). Again, it doesn’t necessarily have to be fid, it can be whatever id name you used when you opened the file with fopen, but I keep with the conventions used in the Matlab help file for the sake of continuity. Essentially, the function reads data from the specified file, converts it to the specified format and returns it in the arrayName. The format is specified by the % symbol and the character that represents the conversion you desire. There are a number of conversions available but for this assignment we use %f which returns the file’s contents as floating-point numbers.
help ____
If you find yourself coming across a function in someone else’s code and are unsure of what it does. You can type in help functionNameand Matlab will display its help file providing it is not a user-made function. This is invaluable when trying to read Matlab code as a beginner.
length
length(x) returns the length of x. For example, if an array has 10 items, it will return 10.
mean
mean(values) will take the average of the values. You don’t need to input the values separately, if you plug in an array’s name it will run through the contents of the array and return the average of all of the contents.
nargin
Will return the number of input arguments. For example, say you are writing code for fakeFunction. If a user runs your code with the line below and for whatever reason you have coded it to display the number of arguments by typing nargin somewhere in your code, it will output 4. Notice the lack of a semicolon(;). When included at the end of a line, the semicolon suppresses output.
fakeFunction (file.m, 5, 2, ‘test’)
plot
plot is relatively self-explanatory. The usual setup is plot(xCoordinate, yCoordinate, ‘scheme’). By scheme, I mean the color you want the points to be, the shape of the points, and whether or not you want a line. The possible schemes are below:
b blue. point- solid
g greeno circle: dotted
r redx x-mark-. dashdot
c cyan+ plus -- dashed
m magenta* star (none) no line
y yellows square
k blackd diamond
w whitev triangle (down)
^ triangle (up)
< triangle (left)
> triangle (right)
p pentagram
h hexagram
The scheme is in the format of a three-character string in the function. For example plot(x,y, ‘bo-’) will plot the x, y coordinates and mark each coordinate with a blue circle with a blue line connecting each coordinate. If you look at the graph on the first page, that is the code used to plot the average line. The blue circles are beneath the red plot circles but are connected with the blue line.
reshape
reshape(x,m,n) returns the m-by-n matrix whose elements are taken columnwise from x. For example:
> data = [1;2;3;4;5;6;7;8]// Note: If you separate array elements by a semicolon (;) it will list data = // them in separate rows, if you use a comma (,) it will list them as
1 // one row.
2
3
4
5
6
7
8
> data = reshape(data, 2, 4)
data =
1357
2468
Note: If data does not have m*n elements, it will return an error.
title
Titles your plot. Syntax is title(‘stringTitle’)
transpose operator (‘)
While this operator is a powerful one and has two different forms, for the sake of the simple numbers being used in this assignment, you only need to know that it will turn a row vector into a column vector and vice versa. So:
data =
1 3 5 7
2 4 6 8
> data'
data =
1
2
3
4
5
6
7
8
xlabel/ylabel
Labels the axes for your graph. Syntax is xlabel(‘stringName’) or ylabel(‘stringName’).
readColData.m
function [labels,x,y] = readColData(fname,ncols,nhead,nlrows)
% readColData reads data from a file containing data in columns
% that have text titles, and possibly other header text
%
% Synopsis:
% [labels,x,y] = readColData(fname)
% [labels,x,y] = readColData(fname,ncols)
% [labels,x,y] = readColData(fname,ncols,nhead)
% [labels,x,y] = readColData(fname,ncols,nhead,nlrows)
%
% Input:
% fname = name of the file containing the data (required)
% ncols = number of columns in the data file. Default = 2. A value
% of ncols is required only if nlrows is also specified.
% nhead = number of lines of header information at the very top of
% the file. Header text is read and discarded. Default = 0.
% A value of nhead is required only if nlrows is also specified.
% nlrows = number of rows of labels. Default = 1
%
% Output:
% labels = matrix of labels. Each row of lables is a different
% label from the columns of data. The number of columns
% in the labels matrix equals the length of the longest
% column heading in the data file. More than one row of
% labels is allowed. In this case the second row of column
% headings begins in row ncol+1 of labels. The third row
% column headings begins in row 2*ncol+1 of labels, etc.
%
% NOTE: Individual column headings must not contain blanks
%
% x = column vector of x values
% y = matrix of y values. y has length(x) rows and ncols columns
%
% process optional arguments
if nargin < 4
nlrows = 1; % default
if nargin < 3
nhead = 0; % default
if nargin < 2
ncols = 2; % default
end
end
end
% open file for input, include error handling
fin = fopen(fname,'r');
if fin < 0
error(['Could not open ',fname,' for input']);
end
% Preliminary reading of titles to determine number of columns
% needed in the labels matrix. This allows for an arbitrary number
% of column titles with unequal (string) lengths. We cannot simply
% append to the labels matrix as new labels are read because the first
% label might not be the longest. The number of columns in the labels
% matrix (= maxlen) needs to be set properly from the start.
% Read and discard header text on line at a time
for i=1:nhead, buffer = fgetl(fin); end
maxlen = 0;
for i=1:nlrows
buffer = fgetl(fin); % get next line as a string
for j=1:ncols
[next,buffer] = strtok(buffer); % parse next column label
maxlen = max(maxlen,length(next)); % find the longest so far
end
end
% Set the number of columns in the labels matrix equal to the length
% of the longest column title. A complete preallocation (including
% rows) of the label matrix is not possible since there is no string
% equivalent of the ones() or zeros() command. The blank() command
% only creates a string row vector not a matrix.
labels = blanks(maxlen);
frewind(fin); % rewind in preparation for actual reading of labels and data
% Read and discard header text on line at a time
for i=1:nhead, buffer = fgetl(fin); end
% Read titles for keeps this time
for i=1:nlrows
buffer = fgetl(fin); % get next line as a string
for j=1:ncols
[next,buffer] = strtok(buffer); % parse next column label
n = j + (i-1)*ncols; % pointer into the label array for next label
labels(n,1:length(next)) = next; % append to the labels matrix
end
end
% Read in the x-y data. Use the vetorized fscanf function to load all
% numerical values into one vector. Then reshape this vector into a
% matrix before copying it into the x and y matrices for return.
data = fscanf(fin,'%f'); % Load the numerical values into one long vector
nd = length(data); % total number of data points
nr = nd/ncols; % number of rows; check (next statement) to make sure
if nr ~= round(nd/ncols)
fprintf(1,'\ndata: nrow = %f\tncol = %d\n',nr,ncols);
fprintf(1,'number of data points = %d does not equal nrow*ncol\n',nd);
error('data is not rectangular')
end
data = reshape(data,ncols,nr)'; % notice the transpose operator
x = data(:,1);
y = data(:,2:ncols);
% end of readColData.m
% Script file plotReceiverPos.m
function [x,y] = plotReceiverPos(fname, pname, ntry, nlog, ncols, nhead)
% USAGE:
% plotReceiverPos('realPosData.txt', 'cz_palate_refhead.mat', 5, 4)
%
% If no palate data is available,
% plotReceiverPos(fname, 0, ntry, nlog, ncols, nhead)
% i.e. Set pname to 0
%
% fname = file name
% pname = palate file name
% nlog = number of points that were logged – this is the number of receivers
% ntry = number of tries/samples taken from each receiver, i.e. token was
% said 5 times)
% ncols = number of columns in toyfile
% nhead = number of header lines
%
%Sets values if the user does not set them
if nargin <6
nhead = 0;
if nargin <5
ncols = 2;
if nargin < 4
nlog = 1;
if nargin <3
ntry = 1;
if nargin <2
pname = 0;
end
end
end
end
end
% open file for input, include error handling
fin = fopen(fname,'r'); %gives a 'read' file permission
if fin < 0
error(['Could not open ',fname,' for input']);
end
% Read in the x-y data. Use the vetorized fscanf function to load all
% numerical values into one vector. Then reshape this vector into a
% matrix before copying it into the x and y matrices for return.
data = fscanf(fin,'%f'); % Load the numerical values into one long vector
nd = length(data); % total number of data points
nr = nd/ncols; % number of rows
if nr ~= round(nd/ncols)
fprintf(1,'\ndata: nrow = %f\tncol = %d\n',nr,ncols);
fprintf(1,'number of data points = %d does not equal nrow*ncol\n',nd);
error('data is not rectangular')
end
data = reshape(data,ncols,nr)'; % notice the transpose operator
x = data(:,1);
y = data(:,2:ncols);
rightPlace = 0;
for i = 1:nlog
for j = 1:ntry
a(j) = x(j+rightPlace);
end
a = mean(a);
averageX(i) = a;
rightPlace = rightPlace + ntry;
end
rightPlace = 0;
for k = 1:nlog
for l = 1:ntry
b(l) = y(l+rightPlace);
end
b = mean(b);
averageY(k) = b;
rightPlace = rightPlace + ntry;
end
if pname == 0
plot(averageX,averageY,'bo-', x, y, 'ro');
else
load (pname);
pal = data(:,[2 3])*10;
palx = pal(:,1);
paly = pal(:,2);
plot(averageX,averageY,'bo-', x, y, 'ro', palx, paly, 'g-', 'LineWidth', 2);
end
% This loads the .mat file containing the palate data and gets rid of the z
% axis. The x and y axis which remain are assigned to palx and paly for the
% plotting function.
axis([0 60 -20 20])
xlabel('mm'), ylabel('mm');
title('Receiver Plots');
% This plots all the points and displays it. Ideally, the axis should be
% determined by the scope of your data but for a mock script it works fine.