Home > src > population.m

# population

## PURPOSE

- population object

## SYNOPSIS

This is a script file.

## DESCRIPTION

     - population object

     copyright 2009-2012 Blair Armstrong, Christine Watson, David Plaut

        This file is part of SOS

        SOS is free software: you can redistribute it and/or modify
        it for academic and non-commercial purposes
        under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.  For commercial or for-profit
        uses, please contact the authors (sos@cnbc.cmu.edu).

        SOS is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

## CROSS-REFERENCE INFORMATION

This function calls:

- dataFrame - dataframe object
- population - population object
- sample - sample object
- validFileName - helper function to validate that a string represents a valid file name
- validLogical - helper function to validate that a string represents a logical operator
- validStringOrNull - helper function to validate that a string represents a valid string or null
- verbosePrint - master print function that regulates cmd line output based on verbosity flags

This function is called by:

- population - population object
- sample - sample object

## SUBFUNCTIONS

- function obj = population(fileName,varargin)
- function obj = addSample(obj,sample)
- function item = popItem(obj,itemIndex)
- function obj = appendItem(obj,item)
- function p = populationInputParser()
- function p = parseConstructorArgs(fileName,varargin)

## SOURCE CODE

    0001 % - population object
    0002 %
    0003 % copyright 2009-2012 Blair Armstrong, Christine Watson, David Plaut
    0004 %
    0005 %    This file is part of SOS
    0006 %
    0007 %    SOS is free software: you can redistribute it and/or modify
    0008 %    it for academic and non-commercial purposes
    0009 %    under the terms of the GNU General Public License as published by
    0010 %    the Free Software Foundation, either version 3 of the License, or
    0011 %    (at your option) any later version.  For commercial or for-profit
    0012 %    uses, please contact the authors (sos@cnbc.cmu.edu).
    0013 %
    0014 %    SOS is distributed in the hope that it will be useful,
    0015 %    but WITHOUT ANY WARRANTY; without even the implied warranty of
    0016 %    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    0017 %    GNU General Public License for more details.
    0018
    0019 %    You should have received a copy of the GNU General Public License
    0020 %    along with SOS (see COPYING.txt).
    0021 %    If not, see <http://www.gnu.org/licenses/>.
    0022
    0023
    0024 classdef population < dataFrame
    0025     %% creates and manipulates population objects
    0026     %
    0027     %   This class provides support for functions and data storage that are
    0028     %   specific to population objects, from which samples are created.
    0029     %
    0030     %   Additional functionality is inherited from parent class <dataFrame>
    0031     %
    0032     %
    0033     %PROPERTIES
    0034     %   samples - Array of samples associated with the population
    0035     %   name - string name associated with the variable
    0036     %
    0037     % ** ALSO manages the global property 'popCount', which tracks the
    0038     % number of populations that have been created.
    0039     %
    0040     %METHODS
    0041     %   population(filename, ['isHeader',<logical>, 'isFormatting',<logical>, 'outFile',<string>]) -  constructor
    0042     %   addSample(sample) - associate a sample with the population
    0043     %   popItem(itemIndex) - pops and returns the item at {itemIndex}
    0044     %   insertItem(itemIndex,item) - inserts {item} at {itemIndex}
    0045     %
    0046     %METHODS (STATIC)
    0047     %   populationInputParser() - generates an input parser for the constructor
    0048     %
    0049     %METHODS (Static, Access=private)
    0050     % function p = parseConstructorArgs(fileName,varargin) - parses constructor args
    0051
    0052     %% PROPERTIES
    0053     properties
    0054         samples % Array of samples associated with the population
    0055         name % string name associated with the variable
    0056     end %# properties
    0057
    0058     %% METHODS
    0059     methods
    0060
    0061         %% population CONSTRUCTOR
    0062         function obj = population(fileName,varargin)
    0063         % Constructor - Creates a population object
    0064             %
    0065             % CALL:
    0066             % population(filename, ['isHeader',<logical>, 'isFormatting',<logical>, 'outFile',<string>])
    0067             %
    0068             % SYNOPSIS:
    0069             % Constructor - Creates a population object and returns it
    0070             %
    0071             % PARAMETERS:
    0072             %  REQUIRED:
    0073             %   fileName - src file for the population is required, which must
    0074             %       follow the SOS dataFrame format specifications.
    0075             %
    0076             %  OPTIONAL:
    0077             %   isHeader/logical - param/logical-value pair indicating if
    0078             %       the source file has a header.  Defaults to false.
    0079             %   isFormatting/logical -  param/logical-value indicating if
    0080             %       the source file has formating.  Defaults to false.
    0081             %   outFile - param/string-value pair indicating the name
    0082             %       (inc. path, if other than pwd is desired) of
    0083             %       of file to save the residual population in after
    0084             %       optimization has been completed.  Outfile is not
    0085             %       validated until write.  Defaults to 'null'
    0086             %   name/string - string name for the variable.
    0087             %
    0088             % EXAMPLE:
    0089             %   p1 = population('p1.txt',1,5);
    0090             %
    0091
    0092             verbosePrint([char(10) 'Creating and Configuring Population Object'], ...
    0093                 'population_constructor_startObjCreation');
    0094
    0095             p = population.parseConstructorArgs(fileName,varargin);
    0096
    0097
    0098              global popCount;
    0099             if(isempty(popCount))
    0100                 curCount = 1;
    0101             else
    0102                 curCount = popCount + 1;
    0103             end
    0104
    0105             %override the default outFile with the sample number if a user
    0106             %specified value was not specified.
    0107             if any(strcmp(p.UsingDefaults,'outFile'))
    0108                 outFile = ['pop',num2str(curCount),'.res.out.txt'];
    0109             else
    0110                 outFile = p.Results.outFile;
    0111             end
    0112
    0113             %check that the new outfile is a valid filename, warn if file
    0114             %exists
    0115             if ischar(outFile) == false || strcmp(outFile,'null')
    0116                 error('Outfile has not been set to a string != null.');
    0117             end
    0118
    0119             if (exist(outFile,'file'))
    0120                verbosePrint([char(10) 'WARNING: File {', outFile, '} already exists.  ', ...
    0121                    'If you attempt to write to this file, ',...
    0122                    'the existing one will be overridden' char(10)], ...
    0123                     'population_constructor_fileExists');
    0124             end
    0125
    0126             %similarly, add a default name based on the sample count if no
    0127             %name was supplied
    0128             if (any(strcmp(p.UsingDefaults,'name')))
    0129                 name = ['pop',num2str(curCount)]; %#ok<PROP>
    0130             else
    0131                 name = p.Results.name; %#ok<PROP>
    0132             end
    0133
    0134
    0135             obj = obj@dataFrame('fileName',p.Results.fileName,'isHeader',p.Results.isHeader, ...
    0136                 'isFormatting',p.Results.isFormatting,'outFile', ...
    0137                  outFile);
    0138
    0139             obj.name = name; %#ok<PROP>
    0140
    0141             verbosePrint(['Creation of Population Object Complete' char(10)], ...
    0142                 'population_constructor_endObjCreation');
    0143
    0144             popCount = curCount;
    0145
    0146         end % population constructor
    0147
    0148         %% addSample METHOD
    0149         function obj = addSample(obj,sample)
    0150             % Associates a sample with a population
    0151             %
    0152             %CALL:
    0153             % <populationObj>.addSample(sample)
    0154             %
    0155             %SYNOPSIS:
    0156             %associates a sample with the population.  Also updates the number
    0157             %and order of columns in the population and its samples so that
    0158             %all of these dataframes are in sync.
    0159             %
    0160             %PARAMETERS:
    0161             %   sample - a sample object
    0162             %
    0163             %EXAMPLE:
    0164             %   p1.addSample(obj,s1); % where s1 is a sample object
    0165
    0166             obj.samples = [obj.samples sample];
    0167
    0168             %ensure that that all the samples associated with the
    0169             %population have the same columns and order of columns as in
    0170             %the population.  If addSample is called in the intended way,
    0171             %i.e., from within the sample.setPop() method, the population
    0172             %will already be synchronized with the new sample, so this just
    0173             %pushes those changes to the other samples associated with the
    0174             %population
    0175
    0176             %First, make sure all the samples contain the same columns as
    0177             %the population
    0178             for i=1:length(obj.samples)
    0179                 updatedSample = dataFrame.aContainsb(obj.samples(i),obj);
    0180                 obj.samples(i) = updatedSample;
    0181             end
    0182
    0183             %Second, make sure all the columns are ordered in the same way
    0184             %across all the samples and the population.
    0185             for i=1:length(obj.samples)
    0186                 tmpData = obj.samples(i).data;
    0187
    0188                 %if the sample does have data, need to make sure it's
    0189                 %odered.
    0190
    0191                 %This section of the code could be make more efficient, as
    0192                 %it currently applies the same blind 'matching' algorithm
    0193                 %to all non-empty dataframes, even those that are already
    0194                 %sorted.  However, for present purposes, this method is a
    0195                 %simple, guaranteed way to make sure the columns line up.
    0196                 %Best to spend the time adding more features ;)
    0197                 if (isempty(tmpData) == 0)
    0198                     for j=1:length(obj.header)
    0199                        for k=1:length(obj.samples(i).header)
    0200                             if(strcmp(char(obj.header{j}), ...
    0201                                     char(obj.samples(i).header{k})))
    0202                                 sampleIndex = k;
    0203                             end;
    0204                        end
    0205
    0206                        tmpData(j) = obj.samples(i).data(sampleIndex);
    0207
    0208                     end
    0209                 end
    0210
    0211                 %update the sample dataframe and header information so it's
    0212                 %all synched with the population
    0213                 obj.samples(i).data = tmpData;
    0214                 obj.samples(i).header = obj.header;
    0215                 obj.samples(i).format = obj.format;
    0216
    0217             end
    0218         end %addSample
    0219
    0220         %% popItem(itemIndex) METHOD
    0221         function item = popItem(obj,itemIndex)
    0222             % pops and returns the item at {item index}
    0223             %
    0224             %CALL:
    0225             % item =  <populationObj>.popItem(itemIndex)
    0226             %
    0227             %SYNOPSIS:
    0228             % pops and returns the item at {itemIndex}
    0229             %
    0230             %PARAMETERS:
    0231             %   itemIndex - the row index of the to-be-popped item
    0232
    0233             p = inputParser;
    0234
    0235             p.addRequired('obj');
    0236             p.addRequired('itemIndex',@(itemIndex)validateattributes(itemIndex, {'numeric'}, ...
    0237                 {'scalar', 'integer', 'positive','>' 0}));
    0238             p.parse(obj,itemIndex);
    0239
    0240             if(isempty(obj.data{1}) == true)
    0241                 error(['ERROR: No data in dataframe object - cannot pop item.',...
    0242                        char(10),'       If you are filling samples, make sure the pop size >= to',...
    0243                        char(10),'       The number of items in all the samples drawing from it.']);
    0244             elseif itemIndex > length(obj.data{1})
    0245                 error('{itemIndex} exceeds row range of data array');
    0246             end
    0247
    0248             item = popItem@dataFrame(obj,itemIndex);
    0249         end
    0250
    0251         %% insertItem(item, itemIndex) METHOD
    0252         function obj = appendItem(obj,item)
    0253             % inserts {item} at {itemIndex}
    0254             %
    0255             % Warning!!!  Method does not confirm that the column structure
    0256             % of {item} (i.e., what columns of data are in what order)
    0257             % match that of the population's data.  This should be the case if
    0258             % SOS is manipulating the items since it will only insert items
    0259             % into the population that belong to the corresponding samples,
    0260             % which should be in sync.  However, manual invocation of this
    0261             % method does not have this guarantee.  Use carefully!
    0262             %
    0263             %CALL:
    0264             % <populationObj>.insertItem(itemIndex,item)
    0265             %
    0266             %SYOPSIS:
    0267             %inserts {item} at {itemIndex}
    0268             %
    0269             %PARAMETERS:
    0270             %item - an item (row) consistent with the population
    0271             %itemIndex - the index at which this item should be inserted
    0272
    0273             appendItem@dataFrame(obj,item);
    0274         end
    0275
    0276     end %methods
    0277
    0278     methods (Static)
    0279
    0280         %% p = populationInputParser() STATIC METHOD
    0281         function p = populationInputParser()
    0282         % generates an input parser with parameter/value pairs and validators for the constructor args.
    0283
    0284             p = inputParser;
    0285
    0286             %Define required and optional arguments, and specify how they
    0287             %are to be validated
    0288
    0289             p.addRequired('fileName',@(fileName)validFileName(fileName));
    0290             p.addParamValue('isHeader',false, ...
    0291                 @(isHeader)validLogical(isHeader));
    0292             p.addParamValue('isFormatting',false, ...
    0293                 @(isFormatting)validLogical(isFormatting));
    0294             p.addParamValue('name','noname', ...
    0295                 @(name)ischar(name));
    0296             %Note that outfile is not validated at this point; this will be
    0297             %done when it comes time to write to the file.  Motivation is
    0298             %that I don't want to write to create a file (and directory
    0299             %structure) unless the user ultimately wants to write
    0300             %something.
    0301             p.addParamValue('outFile','null',@(outFile)validStringOrNull(outFile));
    0302
    0303         end
    0304
    0305     end
    0306
    0307     methods (Static, Access=private)
    0308
    0309         %% p = parseConstructorArgs(fileName,varargin) STATIC PRIVATE METHOD
    0310         function p = parseConstructorArgs(fileName,varargin)
    0311             % parses arguments from population constructor
    0312             %
    0313             %CALL:
    0314             % p = population.parseConstructorArgs(fileName,varargin)
    0315             %
    0316             %SYOPSIS:
    0317             %parses the arguments from the population constructor.  Default
    0318             %values are substituted where appropriate.  Returns a struct
    0319             %with the parsed args
    0320             %
    0321             %PARAMETERS:
    0322             % SAME as population CONSTRUCTOR
    0323             %
    0324             %RETURNS:
    0325             % the parsed constructor arguments in struct format
    0326
    0327             %this cell gets recursively wrapped when it is passed, so
    0328             %unwrap one layer.
    0329             varargin = varargin{1};
    0330
    0331             p = population.populationInputParser();
    0332
    0333             p.parse(fileName,varargin{:});
    0334
    0335         end %parse Constructor Args
    0336
    0337     end % methods (Static)
    0338 end
    0339
    0340
    0341