function [curvestructarray, relevant_data]  = meansemfun(datasource, xcolindex, ycolindex, paramcolindex)
% function curvestructarray =  meansemfun(datasource, xcolindex, ycolindex,
% paramcolindex)
% Meansemfun plots y vs x with errorbars, starting with a disordered list
% of data. The data should be in the form of a matrix where each row
% represents an observation, with columns for the dependent variable (y)
% and for an independent (x axis) variable. Optionally it can also have a
% column for a second independent variable, that is used for distinguishing
% among different curves to be plotted. There can be any number of rows,
% with x and curve-parameter values recurring in any order and with any
% frequency. The x values can be different for the different curves.
%
% If no arguments are supplied, the user is asked to specify a file source
% for the data, by entering the name of a text file that contains the data
% matrix. Arguments 2:4 are the column indices for x, y, and (optionally)
% the curve parameter; if these are missing, user input is requested.
% Meansemfun plots the mean and +-1 x the s.e.m. of y as a function of x
% for each value of the curve parameter and returns curvestructarray, an
% array of structures, one for each curve. Fields are X; mean of Y;
% standard error of the mean of Y; and number of observations, each as a
% column vectors ordered in ascending order of X; and the curve parameter
% value, if applicable. Also returned are the relevant data, suitably
% sorted.
% EXAMPLE:
%
% If datalist is a matrix in the workspace (primary usage):
% >>meansemfun(datalist,1,2) creates an errorbar plot.
% Y values are taken from datalist column 2
% X values are taken from datalist column 1
%
% >>meansemfun(datalist,1,2,4) creates a plot containing one errorbar curve
% for each of the Z (column 4) values that appear in column 4 of datalist.
% Each curve is based on all the data for the corresponding Z value.
% Y axis shows mean and standard error of Y values from datalist column 2;
% X axis shows the corresponding X values from datalist column 1;
% legend shows value(s) of Z, (datalist column 4) that apply to each curve.
%
% To load the data from an ascii file in the current directory:
% >>meansemfun('mydatalist.dat') loads a data matrix from that file.
% You are then prompted for the column indices for x, y, and optionally,
% if there are many curves to plot, z. Or, you can use
% >>meansemfun('mydatalist.dat', 1,2,5)
% if you already know that x is column 1, and y and z are columns 2 and 5.
% >>meansemfun
% will prompt the user for an ascii data file name and column indices.
%
% mycurvestructs = meansemfun... returns an array of structures, one for
% each value of Z. Each structure has these fields:
%     x     % vector of all x values that occur for this z value 
%     ymean  % mean of y for corresponding z and x
%     ysem
%     nmeasures % the number of replications (data rows) for given z and x.
%     z  % (the additional independent variable value for that curve)
%     plotsym  % the plot symbol, color and line style for that curve
% Here mycurvestructs(1) has information for the lowest Z curve, etc.
% Plot symbols cycle through 4 symbols and colors, with continuous lines,
% and then the same sequence with dashed lines, repeating if > 8 curves.

% don macleod, ed vul, 1993, 2008 
% July 2008: less clunky code, color graphics

if nargin==2 || nargin>4
    error('meansemfun(datasource, xcolindex, ycolindex, paramcolindex) requires 0,1,3 or 4 arguments');
end
%% Ask user for data source and relevant column indices as needed:
if nargin==0 % have to ask for data file as source
% File assumed to be in current directory; consider uigetfile()
    datasourcename = input('ascii file to analyze, including any extension: ','s');
    load(datasourcename); % creates a data matrix variable, named with the filename prefix
    if ~isempty(datasourcename==46)  % found an extension (46 = ascii('.'))
        datasourcename = datasourcename(1:find(datasourcename==46)-1); % trim off the extension
    end
    originaldata = eval(datasourcename); % datasourcename string has name of loaded data matrix variable
else   % data matrix is 1st argument
    originaldata = datasource;
end
if isempty(originaldata)
    error('No data in specified source');
end
if nargin<4
    paramcolindex = [];
end
if nargin<=1
    xcolindex = input('Column index (>=1) for x: ');
    ycolindex = input('Column index for y: ');
    paramcolindex = input(...
        'Column index for z, to create multiple curves, one for each z value (hit ''enter'' if none)? ');
end
if isempty(paramcolindex)                       % want just a single curve
    relevant_data = [originaldata(:,[xcolindex ycolindex]) ones(size(originaldata,1),1) ];  % [X Y Z] as 3 columns, Z is dummy
else
    relevant_data = originaldata(:,[xcolindex ycolindex paramcolindex]);
end
%% sort data, get means and standard errors for y per z value per x value
zs = sort(unique(relevant_data(:,3)));
xs = cell(size(zs));
figure();
% cell array containing strings for plot colors and symbols:
plotsym = {'ro--' 'go--' 'bo--' 'ko--' 'rs--' 'gs--' 'bs--'  'ks--' 'rd--' 'gd--' 'bd--' 'kd--'  };  
for zi = 1:length(zs)
    legs{zi} = ['Z = ' num2str(zs(zi))];
    xydata = relevant_data((relevant_data(:,3)==zs(zi)),1:2); 
    xs{zi}  = sort(unique(xydata(:,1)));
    ymean = [];
    ysem = [];
    ny = [];
    for xi = 1:length(xs{zi})
        ydata = xydata((xydata(:,1)==xs{zi}(xi)),2);
        ymean(xi) = mean(ydata);
        ysem(xi) = std(ydata)./sqrt(numel(ydata));
        ny(xi) = length(ydata);
    end
%% create a structure for this curve, add errorbar plot to the current axes
    curvestructarray(zi) = ...
        struct('x', xs{zi}, 'ymean', ymean, 'ysem', ysem, 'nmeasures', ny, 'z', zs(zi), 'plotsym', plotsym{mod(zi-1, length(plotsym))+1}  );
    errorbar(curvestructarray(zi).x, curvestructarray(zi).ymean, curvestructarray(zi).ysem,...
        curvestructarray(zi).plotsym ,'MarkerEdgeColor',curvestructarray(zi).plotsym(1), 'MarkerFaceColor', curvestructarray(zi).plotsym (1),'MarkerSize',10);
    hold on
end
if ~isempty(paramcolindex)  % want a legend (a Z column was specified)
    legend(legs);
end
if exist('datasourcename','var')    % Case of file input
    title(['reduced data from file ', datasourcename]);
end
xlabel(['column ',num2str(xcolindex),' value']);
ylabel(['column ',num2str(ycolindex),' value']);
shg % display graph
grid
