- 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.
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