1#ifndef FSTDATAFILECSV_H
2#define FSTDATAFILECSV_H
63 struct Data_File_CSV_Options {
102 std::vector<std::vector<float> >
rows;
135 bool readLine( FILE *f, std::vector<char> &buff ) {
136 size_t len = 0; buff[0] = 0;
137 while (fgets(buff.data()+len, (
int)(buff.size()-len), f) !=
nullptr) {
138 len += strlen(buff.data()+len);
139 if ((len > 0 && buff[len-1] ==
'\n') || feof(f))
return true;
140 if (len >= buff.size()-2) buff.resize(2*buff.size());
146 static std::string
trimTok(
const std::string &s) {
147 size_t b = 0, e = s.size();
148 while (b < e && (s[b]==
' ' || s[b]==
'\t' || s[b]==
'\r')) b++;
149 while (e > b && (s[e-1]==
' ' || s[e-1]==
'\t' || s[e-1]==
'\r')) e--;
150 return s.substr(b, e-b);
155 std::vector<std::string>
splitLine(
const char *line)
const {
156 std::vector<std::string> out; std::string cur;
bool inq =
false;
157 for (
const char *p = line; *p; p++) {
159 if (inq) {
if (c==
'"') {
if (p[1]==
'"') {cur +=
'"'; p++;}
else inq =
false; }
else cur += c; }
160 else if (c==
'"') inq =
true;
161 else if (c==
delim) { out.push_back(
trimTok(cur)); cur.clear(); }
170 std::string u; u.reserve(tok.size());
171 for (
size_t i = 0; i < tok.size(); i++) u += (
char)toupper((
unsigned char)tok[i]);
172 return u==
"" || u==
"?" || u==
"NA" || u==
"N/A" || u==
"NAN" || u==
"NULL";
181 if (tok.empty())
return false;
183 if (
delim !=
',' && t.find(
'.') == std::string::npos)
184 for (
size_t i = 0; i < t.size(); i++)
if (t[i]==
',') t[i]=
'.';
185 const char *s = t.c_str();
char *end =
nullptr;
186 const double v = strtod(s, &end);
187 if (end == s)
return false;
188 while (*end==
' ' || *end==
'\t') end++;
189 if (*end)
return false;
190 out = (float)v;
return true;
208 if (
opts.delimiter==
"comma")
delim =
',';
209 else if (
opts.delimiter==
"semicolon")
delim =
';';
210 else if (
opts.delimiter==
"tab")
delim =
'\t';
213 int cnt[3] = {0,0,0};
bool inq =
false;
214 for (
const char *p = firstLine; *p; p++) {
215 if (*p==
'"') inq = !inq;
216 else if (!inq) {
if (*p==
'\t') cnt[0]++;
else if (*p==
';') cnt[1]++;
else if (*p==
',') cnt[2]++; }
218 int best = 0;
if (cnt[1] > cnt[best]) best = 1;
if (cnt[2] > cnt[best]) best = 2;
219 delim = (best==0) ?
'\t' : (best==1) ?
';' :
',';
224 columns = (
unsigned int)toks.size();
225 if (
columns < 2)
throw fst_error(
"Data_File_CSV: fewer than 2 columns found on the first line (need at least one feature column and the class column; check --csv-delimiter).",1);
232 const long ix = strtol(
opts.class_column.c_str(), &end, 10);
233 if (end !=
opts.class_column.c_str() && *end == 0) {
234 if (ix < 0 || ix >= (
long)
columns)
throw fst_error(
"Data_File_CSV: --csv-class index "+
opts.class_column+
" out of range (file has "+std::to_string((
long long)
columns)+
" columns, 0-based).",1);
236 }
else byName =
true;
240 if (byName &&
opts.header==
"no")
throw fst_error(
"Data_File_CSV: --csv-class names a header column but --csv-header is 'no'.",1);
245 for (
unsigned int ci = 0; ci <
columns; ci++) {
256 for (
unsigned int ci = 0; ci <
columns; ci++) {
258 for (
size_t i = 0; i < toks[ci].size(); i++) a += (
char)toupper((
unsigned char)toks[ci][i]);
259 for (
size_t i = 0; i <
opts.class_column.size(); i++) b += (
char)toupper((
unsigned char)
opts.class_column[i]);
263 std::cout <<
"Header columns:";
for (
unsigned int ci = 0; ci <
columns; ci++) std::cout <<
" '" <<
headerNames[ci].c_str() <<
"'"; std::cout << std::endl;
264 throw fst_error(
"Data_File_CSV: --csv-class column '"+
opts.class_column+
"' not found in the header (available names listed above).",1);
271 FILE *f = fopen(
_fileName.c_str(),
"rt");
272 if (f ==
nullptr)
throw fst_error(
"Data_File_CSV file read problem.",1);
273 std::vector<char> buff(8192);
276 long lineNo = 0, dataRowNo = 0;
279 char *line = buff.data();
280 if (first && (
unsigned char)line[0]==0xEF && (
unsigned char)line[1]==0xBB && (
unsigned char)line[2]==0xBF) line += 3;
281 size_t len = strlen(line);
282 while (len > 0 && (line[len-1]==
'\n' || line[len-1]==
'\r')) line[--len] = 0;
287 for (
size_t i = 0; i < len; i++) {
288 const char ch = line[i];
289 if (ch==
' ' || ch==
'\t')
continue;
290 if (!first && ch==
delim)
continue;
291 if (first && (ch==
',' || ch==
';'))
continue;
292 blank =
false;
break;
300 std::vector<std::string> toks =
splitLine(line);
306 std::vector<std::string> toks =
splitLine(line);
309 throw fst_error(
"Data_File_CSV: line "+std::to_string((
long long)lineNo)+
" has "+std::to_string((
long long)toks.size())+
" columns, expected "+std::to_string((
long long)
columns)+
" (check --csv-delimiter and quoting).",1);
319 for (
unsigned int ci = 0; ci <
columns; ci++)
if (
isMissing(toks[ci])) { missCol = (int)ci;
break; }
320 if (missCol >= 0 && !
opts.missing_to_active) {
321 std::cout <<
"Skipping data row " << dataRowNo <<
" (line " << lineNo <<
"): missing value in column " << missCol+1 << std::endl;
326 std::cout <<
"Skipping data row " << dataRowNo <<
" (line " << lineNo <<
"): missing class label in column " <<
classColumn+1 <<
" (--csv-missing-to maps feature values only)" << std::endl;
332 std::vector<float> vals; vals.reserve(
columns-1);
333 unsigned int rowMapped = 0;
334 for (
unsigned int ci = 0; ci <
columns; ci++) {
337 if (
opts.missing_to_active &&
isMissing(toks[ci])) { v =
opts.missing_to; rowMapped++; }
340 throw fst_error(
"Data_File_CSV: line "+std::to_string((
long long)lineNo)+
" column "+std::to_string((
long long)(ci+1))+
": feature value '"+toks[ci]+
"' is not numeric.",1);
349 for (
unsigned int k = 0; k <
classNames.size(); k++)
if (
classNames[k] == cls) {
id = (int)k;
break; }
352 rows.push_back(std::move(vals));
357 if (
rows.empty())
throw fst_error(
"Data_File_CSV: no data rows loaded"+std::string(
skippedRows ?
" (all rows had missing values)" :
"")+
".",1);
360 std::cout <<
"CSV file: " <<
_fileName.c_str()
361 <<
" (delimiter " << (
delim==
'\t' ?
"TAB" :
delim==
';' ?
"';'" :
"','")
362 <<
", header " << (
hasHeader ?
"yes" :
"no")
365 std::cout <<
" of " <<
columns <<
")" << std::endl;
366 std::cout <<
"Classes count: " <<
classNames.size() << std::endl;
367 for (
unsigned int k = 0; k <
classNames.size(); k++)
368 std::cout <<
" class " << k <<
" = '" <<
classNames[k].c_str() <<
"' (" <<
classSizes[k] <<
" records)" << std::endl;
369 std::cout <<
"Records count: " <<
rows.size() << std::endl;
371 std::cout <<
"Skipped " <<
skippedRows <<
" of " << dataRowNo <<
" data rows (missing values, reported above)" << std::endl;
373 std::cout <<
"Mapped " <<
mappedCells <<
" missing value(s) in " <<
mappedRows <<
" of " << dataRowNo <<
" data rows to code " <<
opts.missing_to <<
" (--csv-missing-to)";
376 if (
opts.missing_to_unpaired) std::cout <<
"; the code is left in the data - add --missing-value " <<
opts.missing_to <<
" to substitute per-feature training averages instead";
377 std::cout << std::endl;
379 std::cout <<
"Features count: " <<
columns-1 <<
" (feature indices follow the non-class columns in file order)" << std::endl;
387 std::vector<std::vector<float> > newRows; newRows.reserve(
rows.size());
388 std::vector<int> newIds; newIds.reserve(
classIds.size());
389 for (
unsigned int c = 0; c <
classNames.size(); c++)
390 for (
unsigned int i = 0; i <
rows.size(); i++)
391 if (
classIds[i] == (
int)c) { newRows.push_back(std::move(
rows[i])); newIds.push_back((
int)c); }
unsigned int skippedRows
number of data rows skipped due to a missing value (see the class description's missing-value paragra...
Definition data_file_CSV.hpp:106
void resolveClassAndHeader(const std::vector< std::string > &toks)
Resolves columns, classColumn and hasHeader from the first line's split tokens toks (also headerNames...
Definition data_file_CSV.hpp:223
unsigned int getClassSize(int clsId)
number of records in class clsId; throws fst_error if not loaded or clsId is out of range (rather tha...
Definition data_file_CSV.hpp:126
bool prepareFile()
Reads and parses the whole CSV/TSV file: BOM-strip + blank-line skip, dialect/class-column/header res...
Definition data_file_CSV.hpp:270
std::vector< int > classIds
each record's class id, parallel to rows
Definition data_file_CSV.hpp:103
std::vector< std::string > classNames
class label text, indexed by class id (assigned in first-appearance order)
Definition data_file_CSV.hpp:104
unsigned int getNoOfClasses()
number of classes (classNames's size); throws fst_error if not loaded
Definition data_file_CSV.hpp:123
std::string _fileName
input .csv/.tsv file name, as passed to the constructor
Definition data_file_CSV.hpp:93
std::vector< unsigned int > classSizes
per-class record counts, indexed by class id, parallel to classNames
Definition data_file_CSV.hpp:105
std::vector< std::string > splitLine(const char *line) const
Definition data_file_CSV.hpp:155
Data_File_CSV_Options opts
dialect/class-column/missing-value options, as passed to the constructor
Definition data_file_CSV.hpp:94
bool parseNumeric(const std::string &tok, float &out) const
Definition data_file_CSV.hpp:180
void resolveDialect(const char *firstLine)
Resolves delim from opts.delimiter, or auto-detects it from firstLine (candidates counted outside dou...
Definition data_file_CSV.hpp:207
unsigned int mappedCells
number of missing feature values replaced by Data_File_CSV_Options::missing_to
Definition data_file_CSV.hpp:107
unsigned int mappedRows
data rows carrying at least one replaced value
Definition data_file_CSV.hpp:108
Data_File_CSV(std::string fileName, const Data_File_CSV_Options &_opts)
Constructs an (unloaded) reader for fileName under options _opts; call prepareFile() to actually read...
Definition data_file_CSV.hpp:112
int classColumn
resolved 0-based file column of the class label, set by resolveClassAndHeader()
Definition data_file_CSV.hpp:98
static std::string trimTok(const std::string &s)
Strips leading/trailing spaces, tabs and CRs from s (a single already-split field token).
Definition data_file_CSV.hpp:146
std::string getClassName(int clsId)
Definition data_file_CSV.hpp:127
bool loaded
whether prepareFile() completed without error; getters that need loaded data throw fst_error while fa...
Definition data_file_CSV.hpp:109
std::vector< std::vector< float > > rows
loaded records; each row holds feature values only, in non-class file-column order
Definition data_file_CSV.hpp:102
bool readLine(FILE *f, std::vector< char > &buff)
Definition data_file_CSV.hpp:135
std::vector< std::string > headerNames
the header row's tokens, empty when hasHeader is false
Definition data_file_CSV.hpp:100
void sortRecordsByClass()
Regroups rows/classIds into class-id order (stable within each class), mirroring Data_File_ARFF::sort...
Definition data_file_CSV.hpp:384
char delim
resolved delimiter character (comma/semicolon/tab), set by resolveDialect()
Definition data_file_CSV.hpp:96
unsigned int getNoOfFeatures()
data dimensionality (columns minus the class column); throws fst_error if not loaded
Definition data_file_CSV.hpp:124
float getFeatureValue(long record, int feature)
the (already-parsed) value of feature feature in record record
Definition data_file_CSV.hpp:131
unsigned int getTotalSize()
number of loaded records (rows's size); throws fst_error if not loaded
Definition data_file_CSV.hpp:125
bool hasHeader
resolved header presence, set by resolveClassAndHeader()
Definition data_file_CSV.hpp:97
bool isMissing(const std::string &tok) const
Whether tok (case-insensitively) is one of the recognized missing-value markers: empty,...
Definition data_file_CSV.hpp:169
unsigned int columns
total file columns (features + class), set by resolveClassAndHeader()
Definition data_file_CSV.hpp:99
FST4 specific error passing class.
Definition error.hpp:61
FST4 specific error passing class.
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
CSV loader options.
Definition data_file_CSV.hpp:63
std::string delimiter
field separator: "auto" (detect from the first line), "comma", "semicolon" or "tab"
Definition data_file_CSV.hpp:65
float missing_to
the numeric code that replaces a missing feature value when missing_to_active. The mapping is purely ...
Definition data_file_CSV.hpp:68
bool missing_to_unpaired
set by the caller when no missing-value substitution is configured; the load summary then names the p...
Definition data_file_CSV.hpp:69
std::string header
whether the first line is a header row: "auto" (detect), "yes" or "no"
Definition data_file_CSV.hpp:66
std::string class_column
which column carries the class label: "" = the last column; a decimal integer = that 0-based file col...
Definition data_file_CSV.hpp:64
bool missing_to_active
when true, missing FEATURE tokens are replaced by missing_to and the row is kept instead of skipped; ...
Definition data_file_CSV.hpp:67