Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
data_splitter_randrand.hpp
Go to the documentation of this file.
1#ifndef FSTDATASPLITTERRANDRAND_H
2#define FSTDATASPLITTERRANDRAND_H
3 /*
27=========================================================================
28Copyright and license:
29 * FST4 software (with the exception of any bundled or externally linked
30 third-party components) is Copyright (C) 2000-present Institute of
31 Information Theory and Automation of the Czech Academy of Sciences (UTIA).
32 * FST4 is free software: you can redistribute it and/or modify it under
33 the terms of the GNU Affero General Public License as published by the
34 Free Software Foundation, version 3. The full text of the license is in
35 the file LICENSE in the distribution root and at
36 https://www.gnu.org/licenses/agpl-3.0.html.
37 * FST4 is distributed in the hope that it will be useful, but WITHOUT ANY
38 WARRANTY; without even the implied warranty of MERCHANTABILITY or
39 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
40 License for more details.
41 * Closed-source use without the AGPL's copyleft obligations requires a
42 commercial license from UTIA; in case of interest write to
43 utia@utia.cas.cz. LICENSING.md in the distribution root explains both
44 options, the contributor license agreement that every contribution
45 requires, and the contacts (technical questions and pull requests:
46 somol@utia.cas.cz, with "FST" in the subject line).
47========================================================================== */
48
49#include <memory>
50#include <cassert>
51#include <iostream>
52#include <cstdlib>
53#include <ctime>
54#include "fst_rand.hpp" // FST-owned PRNG: identical random sequences on every platform
55#include <random> // per-instance engine for the randomize==true path
56#include <vector>
57#include "error.hpp"
58#include "global.hpp"
59#include "data_splitter.hpp"
60
61/*============== Template parameter type naming conventions ==============
62--------- Numeric types: -------------------------------------------------
63DATATYPE - data sample values - usually real numbers (but may be integers
64 in text processing etc.)
65REALTYPE - must be real numbers - for representing intermediate results of
66 calculations like mean, covariance etc.
67IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
68 extent depends on numbers of samples in data
69DIMTYPE - index values for enumeration of features (dimensions), or classes (not
70 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
71 but be aware of expressions like _classes*_features*_features !
72 in linearized representations of feature matrices for all classes
73BINTYPE - feature selection marker type - represents ca. <10 different feature
74 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
75RETURNTYPE - criterion value: real value, but may be extended in future to support
76 multiple values
77--------- Class types: ---------------------------------------------------
78SUBSET - class of class type Subset
79CLASSIFIER - class implementing interface defined in abstract class Classifier
80EVALUATOR - class implementing interface defined in abstract class Sequential_Step
81DISTANCE - class implementing interface defined in abstract class Distance
82DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
83INTERVALCONTAINER - class of class type Data_Intervaller
84CONTAINER - STL container of class type Data_Interval
85========================================================================== */
86
87namespace FST {
88
89// NOTE: generates non-overlapping TCC::train and TCC::test lists of randomly chosen samples
90// NOTE: repeated traversal through split loops does not give equal output !
91// (i.e., generated random sample subsets are not remembered, thus two consecutive makeFirstSplit() calls give different output)
92
106template<class INTERVALCONTAINER, typename IDXTYPE, typename BINTYPE>
107class Data_Splitter_RandomRandom : public Clonable_Impl<Data_Splitter_RandomRandom<INTERVALCONTAINER,IDXTYPE,BINTYPE>,Data_Splitter<INTERVALCONTAINER,IDXTYPE>,CLONE_INHERIT,CLONE_INHERIT,CLONE_DEEP> {
108public:
111 Data_Splitter_RandomRandom(const IDXTYPE _splits, const IDXTYPE _perctrain, const IDXTYPE _perctest, const bool randomize=false) : parent_impl() ,_n_max(0), perctrain(_perctrain), perctest(_perctest), splits(_splits), _randomize(randomize) {assert(splits>0); assert(perctrain+perctest<=100); current_split=0; notify("Data_Splitter_RandomRandom constructor.");}
115 Data_Splitter_RandomRandom(const Data_Splitter_RandomRandom& dsp) : parent_impl(dsp) ,_n_max(0), perctrain(dsp.perctrain), perctest(dsp.perctest), splits(dsp.splits), _randomize(dsp._randomize) {current_split=dsp.current_split; notify("Data_Splitter_RandomRandom copy constructor.");}
116 virtual ~Data_Splitter_RandomRandom() {notify("Data_Splitter_RandomRandom destructor.");}
117 virtual IDXTYPE getNoOfSplits() const override {assert(splits>0); return splits;}
118
119 virtual std::ostream& print(std::ostream& os) const override {os << "Data_Splitter_RandomRandom(splits=" << splits << ", %train=" << perctrain << ", %test=" << perctest << ")"; return os;}
120protected:
123 virtual void makeRandomSplit(const IDXTYPE n, const std::shared_ptr<INTERVALCONTAINER> list_train, const std::shared_ptr<INTERVALCONTAINER> list_test);
124
125 // Template-method hooks: makeFirstSplit()/makeNextSplit() skeletons live in Data_Splitter.
126 // (splitter_name() stays "Data_Splitter_RandomRandom" in the derived randfix splitter too,
127 // which inherits these methods and their error messages)
128 virtual const char* splitter_name() const override {return "Data_Splitter_RandomRandom";}
129 virtual bool prepareFirstSplit() override {
130 // Each instance seeds its own engine here, rather than sharing one process-wide generator:
131 // a clone handed to a worker thread must not reproduce the parent's draws, and seeding from
132 // std::random_device avoids the collisions a clock-based seed gives to instances created
133 // within the same tick. The default (randomize==false) path does not come here at all --
134 // it draws from the library PRNG, whose sequence is what makes a run reproducible.
135 if(_randomize) _rng.seed(std::random_device{}());
136 current_split=1; // (set before the per-class loop; nothing reads it until the next prepare)
137 return true;
138 }
139 virtual bool prepareNextSplit() override {
140 if(current_split<splits) {++current_split; return true;}
142 return false;
143 }
144 virtual void splitClassFirst(typename TCC::ClassIterator c, const unsigned int) override {makeRandomSplit((*c)->n,(*c)->train,(*c)->test);}
145 virtual void splitClassNext(typename TCC::ClassIterator c, const unsigned int) override {makeRandomSplit((*c)->n,(*c)->train,(*c)->test);}
146
151 void fill_randomly(const IDXTYPE n, const BINTYPE id_empty, const BINTYPE id_fill, const IDXTYPE count, const IDXTYPE minidx, const IDXTYPE maxidx);
153 void fill(const IDXTYPE n, const BINTYPE id_empty, const BINTYPE id_fill, const IDXTYPE minidx, const IDXTYPE maxidx);
155 void translate(const IDXTYPE n, const BINTYPE id_fill, const std::shared_ptr<INTERVALCONTAINER> lst);
156
157 IDXTYPE _n_max;
158 std::unique_ptr<BINTYPE[]> _data;
159
160 const IDXTYPE perctrain;
161 const IDXTYPE perctest;
162 const IDXTYPE splits;
163 const bool _randomize;
164 std::mt19937 _rng;
166};
167
168template<class INTERVALCONTAINER, typename IDXTYPE, typename BINTYPE>
169void Data_Splitter_RandomRandom<INTERVALCONTAINER,IDXTYPE,BINTYPE>::fill_randomly(const IDXTYPE n, const BINTYPE id_empty, const BINTYPE id_fill, const IDXTYPE count, const IDXTYPE minidx, const IDXTYPE maxidx)
170{
171 assert(_data);
172 assert(_n_max>=n);
173 assert(maxidx>=minidx);
174 assert(maxidx<n);
175 assert(count<=maxidx-minidx+1);
176 // NOTE: assumes at least count id_empty slots exist in _data in interval <minidx,maxidx>
177#ifdef DEBUG
178 IDXTYPE dbg_cnt=0;
179 for(IDXTYPE dbg_i=minidx;dbg_i<=maxidx;dbg_i++) if(_data[dbg_i]==id_empty) ++dbg_cnt; // the interval is closed, <minidx,maxidx>
180 assert(count<=dbg_cnt);
181#endif
182 IDXTYPE piv;
183 for(IDXTYPE i=0;i<count;i++) // randomly choose samples
184 {
185 if(_randomize) piv=minidx+(IDXTYPE)std::uniform_int_distribution<long>(0,(long)(maxidx-minidx))(_rng); // unbiased over the whole range
186 else piv=minidx+(IDXTYPE)fst_rand_below((unsigned long)(maxidx-minidx+1)); // the reproducible library stream (slightly biased modulo; two composed draws once the range exceeds 32768 indexes, so every sample stays reachable)
187 assert(minidx<=piv && piv<=maxidx);
188 while(_data[piv]!=id_empty) {piv++; if(piv>maxidx) piv=minidx;}
189 _data[piv]=id_fill;
190 }
191}
192
193template<class INTERVALCONTAINER, typename IDXTYPE, typename BINTYPE>
194void Data_Splitter_RandomRandom<INTERVALCONTAINER,IDXTYPE,BINTYPE>::fill(const IDXTYPE n, const BINTYPE id_empty, const BINTYPE id_fill, const IDXTYPE minidx, const IDXTYPE maxidx)
195{
196 assert(_data);
197 assert(_n_max>=n);
198 assert(maxidx>=minidx);
199 assert(maxidx<n);
200 for(IDXTYPE i=minidx;i<=maxidx;i++) if(_data[i]==id_empty) _data[i]=id_fill;
201}
202
203template<class INTERVALCONTAINER, typename IDXTYPE, typename BINTYPE>
204void Data_Splitter_RandomRandom<INTERVALCONTAINER,IDXTYPE,BINTYPE>::translate(const IDXTYPE n, const BINTYPE id_fill, const std::shared_ptr<INTERVALCONTAINER> lst)
205{
206 assert(lst);
207 lst->clear();
208 IDXTYPE piv=0, count=0;
209 while(piv<n && _data[piv]!=id_fill) piv++;
210 while(piv<n)
211 {
212 count=1;
213 while(piv+count<n && _data[piv+count]==id_fill) ++count;
214 Data_Interval<IDXTYPE> tin={piv,count};
215 lst->push_back(tin);
216 piv+=count;
217 while(piv<n && _data[piv]!=id_fill) piv++;
218 }
219}
220
221template<class INTERVALCONTAINER, typename IDXTYPE, typename BINTYPE>
222void Data_Splitter_RandomRandom<INTERVALCONTAINER,IDXTYPE,BINTYPE>::makeRandomSplit(const IDXTYPE n, const std::shared_ptr<INTERVALCONTAINER> list_train, const std::shared_ptr<INTERVALCONTAINER> list_test)
223{
224 try {
225 assert(list_train);
226 assert(list_test);
227 assert(n>0);
228 // NOTE: a zero percentage is not rejected here; it yields a degenerate split with an empty
229 // train or test part. Validating the percentages is the caller's job.
230 assert(perctrain+perctest<=100);
231 if(!_data || _n_max<n) {_data.reset(new BINTYPE[n]); _n_max=n;}
232 const BINTYPE id_empty = 0;
233 const BINTYPE id_train = 1;
234 const BINTYPE id_test = 2;
235 const IDXTYPE trsiz=(n * perctrain)/100;
236 const IDXTYPE tesiz=(n * perctest)/100;
237 assert(trsiz+tesiz<=n);
238 IDXTYPE i;
239 for(i=0;i<n;i++) _data[i]=id_empty;
240
241 // now fill randomly perctrain% for training and perctest% for testing
242 // NOTE: acceleration - whichever of a part and its complement is smaller is the one actually
243 // drawn, so the number of random draws is min{perctrain,100-perctrain} and
244 // min{perctest,100-perctest} rather than perctrain and perctest; the other side is
245 // produced by pre-filling _data. And when perctrain+perctest==100 the test part is
246 // simply everything the train draw left over, so no second draw is needed at all.
247 // NOTE: the SEQUENCE of fst_rand() calls this produces is part of what a fixed seed
248 // reproduces, so changing the branch structure changes the splits a given seed yields.
249 if(perctrain<=50) {
250 fill_randomly(n,id_empty,id_train,trsiz/*count*/,0/*minidx*/,n-1/*maxidx*/); // randomly choose train samples
251 } else {
252 fill(n,id_empty,id_train,0/*minidx*/,n-1/*maxidx*/);
253 fill_randomly(n,id_train,id_empty,n-trsiz/*count*/,0/*minidx*/,n-1/*maxidx*/); // randomly choose non-train samples
254 }
255 if(perctrain+perctest==100) {
256 fill(n,id_empty,id_test,0/*minidx*/,n-1/*maxidx*/);
257 } else if(perctest<=(100-perctrain)/2) {
258 fill_randomly(n,id_empty,id_test,tesiz/*count*/,0/*minidx*/,n-1/*maxidx*/); // randomly choose test samples
259 } else {
260 fill(n,id_empty,id_test,0/*minidx*/,n-1/*maxidx*/);
261 fill_randomly(n,id_test,id_empty,n-trsiz-tesiz/*count*/,0/*minidx*/,n-1/*maxidx*/); // randomly choose non-test samples
262 }
263
264 // now transform the binary representation to TCC::train and TCC::test lists
265 translate(n,id_train,list_train);
266 translate(n,id_test,list_test);
267
268 } catch(...) {
269 throw fst_error("Data_Splitter_RandomRandom::makeRandomSplit() error.");
270 }
271}
272
273} // namespace
274#endif // FSTDATASPLITTERRANDRAND_H ///:~
CRTP mixin implementing the Clonable protocol for a concrete class.
Definition clonable.hpp:184
Implements train/test data splitting: use randomly chosen x% of data samples for training and another...
Definition data_splitter_randrand.hpp:107
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the splitter and its configuration; base default writes not...
Definition data_splitter_randrand.hpp:119
Data_Splitter_RandomRandom(const Data_Splitter_RandomRandom &dsp)
Copy constructor. It deliberately does NOT inherit RNG state: in the randomize==true mode each copy s...
Definition data_splitter_randrand.hpp:115
std::unique_ptr< BINTYPE[]> _data
Definition data_splitter_randrand.hpp:158
const IDXTYPE perctest
Definition data_splitter_randrand.hpp:161
const IDXTYPE splits
Definition data_splitter_randrand.hpp:162
std::mt19937 _rng
Definition data_splitter_randrand.hpp:164
Data_Splitter< INTERVALLER, IDXTYPE > TCC
Definition data_splitter_randrand.hpp:121
virtual void splitClassFirst(typename TCC::ClassIterator c, const unsigned int) override
write first-split train/test intervals of class cidx (c is that class's entry in csplit)
Definition data_splitter_randrand.hpp:144
virtual bool prepareNextSplit() override
advance split-sequence state; false = no more splits
Definition data_splitter_randrand.hpp:139
IDXTYPE current_split
Definition data_splitter_randrand.hpp:165
Clonable_Impl< Data_Splitter_RandomRandom, Data_Splitter< INTERVALLER, IDXTYPE >, CLONE_INHERIT, CLONE_INHERIT, CLONE_DEEP > parent_impl
Definition data_splitter_randrand.hpp:109
virtual void splitClassNext(typename TCC::ClassIterator c, const unsigned int) override
write next-split train/test intervals of class cidx (a single-split splitter never gets here,...
Definition data_splitter_randrand.hpp:145
IDXTYPE _n_max
Definition data_splitter_randrand.hpp:157
void translate(const IDXTYPE n, const BINTYPE id_fill, const std::shared_ptr< INTERVALCONTAINER > lst)
Converts the binary _data labeling (id_fill vs. everything else) over indices [0,n) into lst's list o...
Definition data_splitter_randrand.hpp:204
const bool _randomize
Definition data_splitter_randrand.hpp:163
virtual bool prepareFirstSplit() override
set up split-sequence state; false = no split can be made
Definition data_splitter_randrand.hpp:129
virtual void makeRandomSplit(const IDXTYPE n, const std::shared_ptr< INTERVALCONTAINER > list_train, const std::shared_ptr< INTERVALCONTAINER > list_test)
Generates one class's non-overlapping random train/test split of n samples into list_train / list_tes...
Definition data_splitter_randrand.hpp:222
Data_Splitter_RandomRandom(const IDXTYPE _splits, const IDXTYPE _perctrain, const IDXTYPE _perctest, const bool randomize=false)
_splits splits, each drawing _perctrain % of each class for training and _perctest % for testing (non...
Definition data_splitter_randrand.hpp:111
void fill_randomly(const IDXTYPE n, const BINTYPE id_empty, const BINTYPE id_fill, const IDXTYPE count, const IDXTYPE minidx, const IDXTYPE maxidx)
Randomly relabels count of the still-id_empty slots of _data (within [minidx,maxidx]) to id_fill; dra...
Definition data_splitter_randrand.hpp:169
virtual IDXTYPE getNoOfSplits() const override
Definition data_splitter_randrand.hpp:117
virtual const char * splitter_name() const override
class name, used in error messages
Definition data_splitter_randrand.hpp:128
void fill(const IDXTYPE n, const BINTYPE id_empty, const BINTYPE id_fill, const IDXTYPE minidx, const IDXTYPE maxidx)
Relabels every still-id_empty slot of _data (within [minidx,maxidx]) to id_fill, unconditionally (no ...
Definition data_splitter_randrand.hpp:194
const IDXTYPE perctrain
Definition data_splitter_randrand.hpp:160
Abstract class, defines interface for data splitting implementations (for use in data accessors).
Definition data_splitter.hpp:124
std::vector< PClassSplitter >::const_iterator ClassIterator
iterator over csplit, as passed to the per-class splitClassFirst()/splitClassNext() hooks
Definition data_splitter.hpp:174
FST4 specific error passing class.
Definition error.hpp:61
Defines interface for data splitting implementations (for use in data accessors).
FST4 specific error passing class.
FST4-owned pseudo-random number generator (UCRT-compatible LCG), replacing platform-specific std::ran...
Global definitions.
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
@ CLONE_DEEP
new Derived(*this), then set_cloned()
Definition clonable.hpp:149
@ CLONE_INHERIT
do not add behavior; defer to the Base class body (typically a family-level throwing default)
Definition clonable.hpp:147
unsigned long fst_rand_below(const unsigned long n)
Returns a pseudo-random index in 0..n-1 (n >= 1); use instead of fst_rand()n.
Definition fst_rand.hpp:112
void notify(const std::string msg)
Prints msg followed by a newline via syncout::print(); a no-op unless the preprocessor macro DEBUG is...
Definition global.hpp:112
Support structure to hold data interval (referring into data held by data accessor).
Definition data_intervaller.hpp:88
Copyright © 2000–present ÚTIA, Institute of Information Theory and Automation of the Czech Academy of Sciences · Contacts · This site sets no cookies and uses no analytics.