Merge fastlib-armadillo/fastlib/data with fastlib-stl/fastlib/data. Runtime dataset_test fails, but it compiles ok.
This commit is contained in:
@@ -7,6 +7,10 @@ set(SOURCES
|
||||
crossvalidation.h
|
||||
dataset.h
|
||||
dataset.cc
|
||||
dataset_info.h
|
||||
dataset_info.cc
|
||||
dataset_feature.h
|
||||
dataset_feature.cc
|
||||
)
|
||||
|
||||
# add directory name to sources
|
||||
|
||||
@@ -38,12 +38,9 @@
|
||||
#ifndef DATA_CROSSVALIDATION
|
||||
#define DATA_CROSSVALIDATION
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <armadillo>
|
||||
|
||||
#include "dataset.h"
|
||||
//#include "dataset.h"
|
||||
|
||||
#include "../la/matrix.h"
|
||||
#include "../fx/fx.h"
|
||||
@@ -111,7 +108,7 @@
|
||||
* //
|
||||
* void InitTrain(const Dataset& dataset, int n_classes, datanode *module);
|
||||
* // For a test datum, returns the class label 0 <= label < n_classes
|
||||
* int Classify(const Vector& test_datum);
|
||||
* int Classify(const vec& test_datum);
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
@@ -139,7 +136,7 @@ class SimpleCrossValidator {
|
||||
/** Total number correct classified. */
|
||||
index_t n_correct_;
|
||||
/** Confusion matrix. */
|
||||
Matrix confusion_matrix_;
|
||||
arma::mat confusion_matrix_;
|
||||
|
||||
public:
|
||||
SimpleCrossValidator() {}
|
||||
@@ -198,7 +195,7 @@ class SimpleCrossValidator {
|
||||
* The element at row i column j is the number of training samples where
|
||||
* the actual classification is i but the predicted classification is j.
|
||||
*/
|
||||
const Matrix& confusion_matrix() const {
|
||||
const arma::mat& confusion_matrix() const {
|
||||
return confusion_matrix_;
|
||||
}
|
||||
|
||||
@@ -221,12 +218,12 @@ void SimpleCrossValidator<TClassifier>::SaveTrainTest_(
|
||||
std::ostringstream o;
|
||||
|
||||
if( !(o << i_fold) )
|
||||
abort(); //?
|
||||
abort(); // bizarre error, should we really check?
|
||||
train_name = "train_" + o.str() + ".csv";
|
||||
test_name = "test_" + o.str() + ".csv";
|
||||
|
||||
train.WriteCsv(train_name.c_str());
|
||||
test.WriteCsv(test_name.c_str());
|
||||
train.WriteCsv(train_name);
|
||||
test.WriteCsv(test_name);
|
||||
}
|
||||
|
||||
|
||||
@@ -258,8 +255,7 @@ void SimpleCrossValidator<TClassifier>::Init(
|
||||
|
||||
DEBUG_ONLY(n_correct_ = BIG_BAD_NUMBER);
|
||||
|
||||
confusion_matrix_.Init(n_classes_, n_classes_);
|
||||
confusion_matrix_.SetZero();
|
||||
confusion_matrix_.zeros(n_classes_, n_classes_);
|
||||
}
|
||||
|
||||
template<class TClassifier>
|
||||
@@ -286,7 +282,7 @@ void SimpleCrossValidator<TClassifier>::Run(bool randomized) {
|
||||
kfold_module_->key, i_fold, classifier_fx_name_);
|
||||
datanode *foldmodule = fx_submodule(classifier_module, "..");
|
||||
|
||||
data_->SplitTrainTest(n_folds_, i_fold, permutation, &train, &test);
|
||||
data_->SplitTrainTest(n_folds_, i_fold, permutation, train, test);
|
||||
|
||||
if (fx_param_bool(kfold_module_, "save", 0)) {
|
||||
SaveTrainTest_(i_fold, train, test);
|
||||
@@ -300,15 +296,12 @@ void SimpleCrossValidator<TClassifier>::Run(bool randomized) {
|
||||
fx_timer_start(foldmodule, "test");
|
||||
VERBOSE_MSG(1, "cross: Testing fold %d", i_fold);
|
||||
for (index_t i = 0; i < test.n_points(); i++) {
|
||||
Vector test_vector_with_label;
|
||||
Vector test_vector;
|
||||
|
||||
test.matrix().MakeColumnVector(i, &test_vector_with_label);
|
||||
test_vector_with_label.MakeSubvector(
|
||||
0, test.n_features()-1, &test_vector);
|
||||
arma::vec test_vector(test.n_features() - 1);
|
||||
for(int j = 0; j < test.n_features() - 1; j++)
|
||||
test_vector[j] = test.matrix()(i, j);
|
||||
|
||||
int label_predict = classifier.Classify(test_vector);
|
||||
double label_expect_dbl = test_vector_with_label[test.n_features()-1];
|
||||
double label_expect_dbl = test.matrix()(i, test.n_features() - 1);
|
||||
int label_expect = int(label_expect_dbl);
|
||||
|
||||
DEBUG_ASSERT(double(label_expect) == label_expect_dbl);
|
||||
@@ -321,7 +314,7 @@ void SimpleCrossValidator<TClassifier>::Run(bool randomized) {
|
||||
local_n_correct++;
|
||||
}
|
||||
|
||||
confusion_matrix_.ref(label_expect, label_predict) += 1;
|
||||
confusion_matrix_(label_expect, label_predict) += 1;
|
||||
}
|
||||
fx_timer_stop(foldmodule, "test");
|
||||
|
||||
@@ -405,7 +398,7 @@ class GeneralCrossValidator {
|
||||
/** Total number correct classified */
|
||||
index_t clsf_n_correct_;
|
||||
/** Confusion matrix */
|
||||
Matrix clsf_confusion_matrix_;
|
||||
arma::mat clsf_confusion_matrix_;
|
||||
|
||||
/** variables for type 1/2: regression, density estimation, etc. */
|
||||
/** mean squared error over all folds*/
|
||||
@@ -468,7 +461,7 @@ class GeneralCrossValidator {
|
||||
* The element at row i column j is the number of training samples where
|
||||
* the actual classification is i but the predicted classification is j.
|
||||
*/
|
||||
const Matrix& clsf_confusion_matrix() const {
|
||||
const arma::mat& clsf_confusion_matrix() const {
|
||||
return clsf_confusion_matrix_;
|
||||
}
|
||||
|
||||
@@ -489,9 +482,9 @@ class GeneralCrossValidator {
|
||||
// detemine the number of data samples for training and validation according to i_fold
|
||||
index_t n_cv_validation, i_validation, i_train;
|
||||
n_cv_validation = 0;
|
||||
for (index_t i_classes=0; i_classes<num_classes; i_classes++) {
|
||||
for (index_t i_classes = 0; i_classes < num_classes; i_classes++) {
|
||||
i_validation = 0;
|
||||
for (index_t j=0; j<cv_labels_ct[i_classes]; j++) {
|
||||
for (index_t j = 0; j < cv_labels_ct[i_classes]; j++) {
|
||||
if ((j - i_fold) % n_folds_ == 0) { // point for validation
|
||||
i_validation++;
|
||||
}
|
||||
@@ -501,28 +494,28 @@ class GeneralCrossValidator {
|
||||
index_t n_cv_train = num_data_points_ - n_cv_validation;
|
||||
train->InitBlank();
|
||||
train->info().InitContinuous(n_cv_features);
|
||||
train->matrix().Init(n_cv_features, n_cv_train);
|
||||
train->matrix().set_size(n_cv_features, n_cv_train);
|
||||
|
||||
validation->InitBlank();
|
||||
validation->info().InitContinuous(n_cv_features);
|
||||
validation->matrix().Init(n_cv_features, n_cv_validation);
|
||||
validation->matrix().set_size(n_cv_features, n_cv_validation);
|
||||
|
||||
// make training set and vaidation set by concatenation
|
||||
i_train = 0;
|
||||
i_validation = 0;
|
||||
for (index_t i_classes=0; i_classes<num_classes; i_classes++) {
|
||||
for (index_t j=0; j<cv_labels_ct[i_classes]; j++) {
|
||||
Vector source, dest;
|
||||
for (index_t i_classes = 0; i_classes < num_classes; i_classes++) {
|
||||
for (index_t j = 0; j < cv_labels_ct[i_classes]; j++) {
|
||||
double *dest;
|
||||
if ((j - i_fold) % n_folds_ != 0) { // add to training set
|
||||
train->matrix().MakeColumnVector(i_train, &dest);
|
||||
dest = train->matrix().colptr(i_train);
|
||||
i_train++;
|
||||
}
|
||||
else { // add to validation set
|
||||
validation->matrix().MakeColumnVector(i_validation, &dest);
|
||||
dest = validation->matrix().colptr(i_validation);
|
||||
i_validation++;
|
||||
}
|
||||
data_->matrix().MakeColumnVector(cv_labels_startpos[i_classes]+j, &source);
|
||||
dest.CopyValues(source);
|
||||
memcpy(dest, data_->matrix().colptr(cv_labels_startpos[i_classes] + j),
|
||||
sizeof(double) * data_->matrix().n_rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -570,11 +563,10 @@ void GeneralCrossValidator<TLearner>::Init(
|
||||
clsf_n_classes_ = data_->n_labels();
|
||||
clsf_n_correct_ = 0;
|
||||
// initialize confusion matrix
|
||||
clsf_confusion_matrix_.Init(clsf_n_classes_, clsf_n_classes_);
|
||||
clsf_confusion_matrix_.SetZero();
|
||||
clsf_confusion_matrix_.zeros(clsf_n_classes_, clsf_n_classes_);
|
||||
}
|
||||
else if (learner_typeid_ == 1 || learner_typeid_ == 2) {
|
||||
clsf_confusion_matrix_.Init(1,1);
|
||||
clsf_confusion_matrix_ = 0.0; /* 1x1 matrix */
|
||||
// initialize mean squared error over all folds
|
||||
msq_err_all_folds_ = 0.0;
|
||||
}
|
||||
@@ -619,11 +611,6 @@ void GeneralCrossValidator<TLearner>::Run(bool randomized) {
|
||||
for (index_t j=0; j<cv_labels_ct[i_classes]; j++)
|
||||
permutation[cv_labels_startpos[i_classes]+j] = cv_labels_index[ cv_labels_ct[i_classes-1]+sub_permutation[j] ];
|
||||
}
|
||||
// Do we actually need to free the memory used here?
|
||||
{
|
||||
std::vector<index_t> empty;
|
||||
sub_permutation.swap(empty);
|
||||
}
|
||||
}
|
||||
} // e.g. [10,13,5,17,0,6,7,,4,9,8,1,2,,...]
|
||||
else {
|
||||
@@ -659,14 +646,16 @@ void GeneralCrossValidator<TLearner>::Run(bool randomized) {
|
||||
VERBOSE_MSG(1, "cross: Validation fold %d", i_fold);
|
||||
|
||||
for (index_t i = 0; i < validation.n_points(); i++) {
|
||||
Vector validation_vector_with_label;
|
||||
Vector validation_vector;
|
||||
arma::vec validation_vector(validation.n_features() - 1);
|
||||
|
||||
memcpy(validation_vector.memptr(), validation.matrix().colptr(i),
|
||||
sizeof(double) * (validation_vector.n_elem));
|
||||
|
||||
validation.matrix().MakeColumnVector(i, &validation_vector_with_label);
|
||||
validation_vector_with_label.MakeSubvector(0, validation.n_features()-1, &validation_vector);
|
||||
// testing (classification)
|
||||
int label_predict = int(classifier.Predict(learner_typeid_, validation_vector));
|
||||
double label_expect_dbl = validation_vector_with_label[validation.n_features()-1];
|
||||
double label_expect_dbl = validation.matrix()(i, validation_vector.n_elem);
|
||||
int label_expect = int(label_expect_dbl);
|
||||
|
||||
DEBUG_ASSERT(double(label_expect) == label_expect_dbl);
|
||||
@@ -678,7 +667,7 @@ void GeneralCrossValidator<TLearner>::Run(bool randomized) {
|
||||
if (label_expect == label_predict) {
|
||||
local_n_correct++;
|
||||
}
|
||||
clsf_confusion_matrix_.ref(label_expect, label_predict) += 1;
|
||||
clsf_confusion_matrix_(label_expect, label_predict) += 1;
|
||||
}
|
||||
fx_timer_stop(foldmodule, "validation");
|
||||
|
||||
@@ -720,7 +709,7 @@ void GeneralCrossValidator<TLearner>::Run(bool randomized) {
|
||||
datanode *foldmodule = fx_submodule(learner_module, "..");
|
||||
|
||||
// Split general data sets according to i_fold
|
||||
data_->SplitTrainTest(n_folds_, i_fold, permutation, &train, &validation);
|
||||
data_->SplitTrainTest(n_folds_, i_fold, permutation, train, validation);
|
||||
|
||||
if (fx_param_bool(kfold_module_, "save", 0)) {
|
||||
SaveTrainValidationSet_(i_fold, train, validation);
|
||||
@@ -736,15 +725,14 @@ void GeneralCrossValidator<TLearner>::Run(bool randomized) {
|
||||
fx_timer_start(foldmodule, "validation");
|
||||
VERBOSE_MSG(1, "cross: Validation fold %d", i_fold);
|
||||
for (index_t i = 0; i < validation.n_points(); i++) {
|
||||
Vector validation_vector_with_label;
|
||||
Vector validation_vector;
|
||||
arma::vec validation_vector(validation.n_features() - 1);
|
||||
|
||||
memcpy(validation_vector.memptr(), validation.matrix().colptr(i),
|
||||
sizeof(double) * (validation_vector.n_elem));
|
||||
|
||||
validation.matrix().MakeColumnVector(i, &validation_vector_with_label);
|
||||
validation_vector_with_label.MakeSubvector(
|
||||
0, validation.n_features()-1, &validation_vector);
|
||||
// testing
|
||||
// testing
|
||||
double value_predict = learner.Predict(learner_typeid_, validation_vector);
|
||||
double value_true = validation_vector_with_label[validation.n_features()-1];
|
||||
double value_true = validation.matrix()(i, validation_vector.n_elem);
|
||||
double value_err = value_predict - value_true;
|
||||
|
||||
// Calculate squared error: sublevel
|
||||
@@ -769,5 +757,4 @@ void GeneralCrossValidator<TLearner>::Run(bool randomized) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,290 +47,26 @@
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
void DatasetFeature::Format(double value, std::string& result) const {
|
||||
if (unlikely(isnan(value))) {
|
||||
result = "?";
|
||||
return;
|
||||
}
|
||||
std::ostringstream o;
|
||||
switch (type_) {
|
||||
case CONTINUOUS:
|
||||
if (floor(value) != value) {
|
||||
// non-integer
|
||||
o.setf( std::ios::scientific );
|
||||
} else {
|
||||
// value is actually an integer
|
||||
o.precision(17);
|
||||
}
|
||||
break;
|
||||
case INTEGER:
|
||||
case NOMINAL:
|
||||
break;
|
||||
#ifdef DEBUG
|
||||
default: abort();
|
||||
#endif
|
||||
}
|
||||
if( !(o << value ) )
|
||||
abort();
|
||||
result = o.str();
|
||||
}
|
||||
|
||||
success_t DatasetFeature::Parse(const std::string& str, double *d) const {
|
||||
if (unlikely(str[0] == '?') && unlikely(str[1] == '\0')) {
|
||||
*d = DBL_NAN;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
switch (type_) {
|
||||
case CONTINUOUS: {
|
||||
// *d = strtod(str, &end);
|
||||
std::istringstream is(str);
|
||||
if( !(is >> *d) )
|
||||
return SUCCESS_FAIL;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
case INTEGER: {
|
||||
int i;
|
||||
std::istringstream is(str);
|
||||
if( !(is >> i) )
|
||||
return SUCCESS_FAIL;
|
||||
*d = i;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
case NOMINAL: {
|
||||
index_t i;
|
||||
for (i = 0; i < value_names_.size(); i++) {
|
||||
if (value_names_[i] == str) {
|
||||
*d = i;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
}
|
||||
*d = DBL_NAN;
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
default: abort();
|
||||
}
|
||||
}
|
||||
|
||||
// DatasetInfo ------------------------------------------------------
|
||||
|
||||
|
||||
void DatasetInfo::InitContinuous(index_t n_features,
|
||||
const char *name_in) {
|
||||
features_.reserve(n_features);
|
||||
|
||||
name_ = name_in;
|
||||
|
||||
for (index_t i = 0; i < n_features; i++) {
|
||||
std::ostringstream o;
|
||||
if(!(o << i))
|
||||
abort();
|
||||
DatasetFeature f;
|
||||
f.InitContinuous(o.str());
|
||||
features_.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
void DatasetInfo::Init(const char *name_in) {
|
||||
name_ = name_in;
|
||||
}
|
||||
|
||||
index_t DatasetInfo::SkipSpace_(std::string& s) {
|
||||
int i = 0;
|
||||
while (isspace(s[i])) {
|
||||
++i;
|
||||
}
|
||||
|
||||
if (unlikely(s[i] == '%') || unlikely(s[i] == '\0')) {
|
||||
return s.length();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
char *DatasetInfo::SkipNonspace_(char *s) {
|
||||
while (likely(*s != '\0')
|
||||
&& likely(*s != '%')
|
||||
&& likely(*s != ' ')
|
||||
&& likely(*s != '\t')) {
|
||||
s++;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void DatasetInfo::SkipBlanks_(TextLineReader *reader) {
|
||||
while (reader->MoreLines() && reader->Peek()[SkipSpace_(reader->Peek())] == '\0') {
|
||||
reader->Gobble();
|
||||
}
|
||||
}
|
||||
|
||||
success_t DatasetInfo::InitFromArff(TextLineReader *reader,
|
||||
const char *filename) {
|
||||
success_t result = SUCCESS_PASS;
|
||||
|
||||
Init(filename);
|
||||
|
||||
while (1) {
|
||||
SkipBlanks_(reader);
|
||||
|
||||
std::string *peeked = &reader->Peek();
|
||||
std::vector<std::string> portions;
|
||||
|
||||
tokenizeString(*peeked, ", \t", portions, 0, "%{", 3, true );
|
||||
|
||||
if (portions.size() == 0) {
|
||||
/* empty line */
|
||||
} else if (portions[0][0] != '@') {
|
||||
reader->Error("ARFF: Unexpected @command. Did you forget @data?");
|
||||
result = SUCCESS_FAIL;
|
||||
break;
|
||||
} else {
|
||||
// if (portions[0].EqualsNoCase("@relation")) {
|
||||
if(strcasecmp(portions[0].c_str(), "@relation") == 0) {
|
||||
if (portions.size() < 2) {
|
||||
reader->Error("ARFF: @relation requires name");
|
||||
result = SUCCESS_FAIL;
|
||||
} else {
|
||||
set_name(portions[1]);
|
||||
}
|
||||
// } else if (portions[0].EqualsNoCase("@attribute")) {
|
||||
} else if(strcasecmp(portions[0].c_str(), "@attribute") == 0) {
|
||||
if (portions.size() < 3) {
|
||||
reader->Error("ARFF: @attribute requires name and type.");
|
||||
result = SUCCESS_FAIL;
|
||||
} else {
|
||||
DatasetFeature feature;
|
||||
if (portions[2][0] == '{') { //}
|
||||
feature.InitNominal(portions[1]);
|
||||
// TODO: Doesn't support values with spaces {
|
||||
tokenizeString(portions[2], ", \t", feature.value_names(), 1, "}%", 0);
|
||||
features_.push_back(feature);
|
||||
} else {
|
||||
std::string type(portions[2]);
|
||||
//portions[2].Trim(" \t", &type);
|
||||
// if (type.EqualsNoCase("numeric")
|
||||
// || type.EqualsNoCase("real")) {
|
||||
if(strcasecmp(type.c_str(), "numeric") == 0
|
||||
|| strcasecmp(type.c_str(), "real") == 0) {
|
||||
feature.InitContinuous(portions[1]);
|
||||
features_.push_back(feature);
|
||||
// } else if (type.EqualsNoCase("integer")) {
|
||||
} else if(strcasecmp( type.c_str(), "integer") == 0) {
|
||||
feature.InitContinuous(portions[1]);
|
||||
features_.push_back(feature);
|
||||
} else {
|
||||
reader->Error(
|
||||
"ARFF: Only support 'numeric', 'real', and {nominal}.");
|
||||
result = SUCCESS_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// } else if (portions[0].EqualsNoCase("@data")) {
|
||||
} else if(strcasecmp(portions[0].c_str(), "@data") == 0) {
|
||||
/* Done! */
|
||||
reader->Gobble();
|
||||
break;
|
||||
} else {
|
||||
reader->Error("ARFF: Expected @relation, @attribute, or @data.");
|
||||
result = SUCCESS_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reader->Gobble();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
success_t DatasetInfo::InitFromCsv(TextLineReader *reader,
|
||||
const char *filename) {
|
||||
std::vector<std::string> headers;
|
||||
bool nonnumeric = false;
|
||||
|
||||
Init(filename);
|
||||
|
||||
tokenizeString(reader->Peek(), ", \t", headers);
|
||||
|
||||
if (headers.size() == 0) {
|
||||
reader->Error("Trying to parse empty file as CSV.");
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
// Try to auto-detect if there is a header row
|
||||
for (index_t i = 0; i < headers.size(); i++) {
|
||||
char *end;
|
||||
|
||||
(void) strtod(headers[i].c_str(), &end);
|
||||
|
||||
if (end == headers[i].c_str()) {
|
||||
nonnumeric = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nonnumeric) {
|
||||
for (index_t i = 0; i < headers.size(); i++) {
|
||||
DatasetFeature feature;
|
||||
feature.InitContinuous(headers[i]);
|
||||
features_.push_back(feature);
|
||||
}
|
||||
reader->Gobble();
|
||||
} else {
|
||||
for (index_t i = 0; i < headers.size(); i++) {
|
||||
DatasetFeature feature;
|
||||
std::ostringstream o;
|
||||
if(!(o << i))
|
||||
abort();
|
||||
feature.InitContinuous(o.str());
|
||||
features_.push_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
|
||||
success_t DatasetInfo::InitFromFile(TextLineReader *reader,
|
||||
const char *filename) {
|
||||
SkipBlanks_(reader);
|
||||
|
||||
// WARNING: Safe?
|
||||
char first_char = reader->Peek()[SkipSpace_(reader->Peek())];
|
||||
|
||||
if (first_char == '\0') {
|
||||
Init();
|
||||
reader->Error("Could not parse the first line.");
|
||||
return SUCCESS_FAIL;
|
||||
} else if (first_char == '@') {
|
||||
/* Okay, it's ARFF. */
|
||||
return InitFromArff(reader, filename);
|
||||
} else {
|
||||
/* It's CSV. We'll try to see if there are headers. */
|
||||
return InitFromCsv(reader, filename);
|
||||
}
|
||||
}
|
||||
|
||||
index_t Dataset::n_labels() const {
|
||||
index_t i = 0;
|
||||
index_t label_row_idx = matrix_.n_rows() - 1; // the last row is for labels
|
||||
index_t label_row_idx = matrix_.n_rows - 1; // the last row is for labels
|
||||
index_t n_labels = 0;
|
||||
|
||||
double current_label;
|
||||
|
||||
std::vector<double> labels_list;
|
||||
labels_list.push_back(matrix_.get(label_row_idx,0));
|
||||
labels_list.push_back(matrix_[label_row_idx,0]);
|
||||
n_labels++;
|
||||
|
||||
for (i = 1; i < matrix_.n_cols(); i++) {
|
||||
current_label = matrix_.get(label_row_idx,i);
|
||||
for(i = 1; i < matrix_.n_cols; i++) {
|
||||
current_label = matrix_(label_row_idx, i);
|
||||
index_t j = 0;
|
||||
for (j = 0; j < n_labels; j++) {
|
||||
if (current_label == labels_list[j]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == n_labels) { // new label
|
||||
if(j == n_labels) { // new label
|
||||
labels_list.push_back(current_label);
|
||||
n_labels++;
|
||||
}
|
||||
@@ -344,17 +80,16 @@ void Dataset::GetLabels(std::vector<double> &labels_list,
|
||||
std::vector<index_t> &labels_ct,
|
||||
std::vector<index_t> &labels_startpos) const {
|
||||
index_t i = 0;
|
||||
index_t label_row_idx = matrix_.n_rows() - 1; // the last row is for labels
|
||||
index_t n_points = matrix_.n_cols();
|
||||
index_t label_row_idx = matrix_.n_rows - 1; // the last row is for labels
|
||||
index_t n_points = matrix_.n_cols;
|
||||
index_t n_labels = 0;
|
||||
|
||||
double current_label;
|
||||
|
||||
// these Arraylists need initialization before-hand
|
||||
/* This faithfully replicates the effect of ArrayList.Renew().
|
||||
Is this necessary? If all we care about is initialization,
|
||||
it shouldn't be.
|
||||
*/
|
||||
// This faithfully replicates the effect of ArrayList.Renew().
|
||||
// Is this necessary? If all we care about is initialization,
|
||||
// it shouldn't be.
|
||||
{
|
||||
std::vector<double> y;
|
||||
std::vector<index_t> x[3];
|
||||
@@ -368,19 +103,19 @@ void Dataset::GetLabels(std::vector<double> &labels_list,
|
||||
|
||||
std::vector<index_t> labels_temp;
|
||||
labels_temp.reserve(n_points);
|
||||
labels_temp[0] = 0;
|
||||
labels_temp.push_back(0);
|
||||
|
||||
labels_list.push_back(matrix_.get(label_row_idx,0));
|
||||
labels_list.push_back(matrix_(label_row_idx,0));
|
||||
labels_ct.push_back(1);
|
||||
n_labels++;
|
||||
|
||||
for (i = 1; i < n_points; i++) {
|
||||
current_label = matrix_.get(label_row_idx, i);
|
||||
current_label = matrix_(label_row_idx, i);
|
||||
index_t j = 0;
|
||||
for (j = 0; j < n_labels; j++) {
|
||||
if (current_label == labels_list[j]) {
|
||||
labels_ct[j]++;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
labels_temp[i] = j;
|
||||
@@ -392,9 +127,8 @@ void Dataset::GetLabels(std::vector<double> &labels_list,
|
||||
}
|
||||
|
||||
labels_startpos.push_back(0);
|
||||
for(i = 1; i < n_labels; i++){
|
||||
labels_startpos.push_back( labels_startpos[i-1] + labels_ct[i-1] );
|
||||
}
|
||||
for(i = 1; i < n_labels; i++)
|
||||
labels_startpos.push_back(labels_startpos[i - 1] + labels_ct[i - 1]);
|
||||
|
||||
for(i = 0; i < n_points; i++) {
|
||||
labels_index[labels_startpos[labels_temp[i]]] = i;
|
||||
@@ -403,233 +137,34 @@ void Dataset::GetLabels(std::vector<double> &labels_list,
|
||||
|
||||
labels_startpos[0] = 0;
|
||||
for(i = 1; i < n_labels; i++)
|
||||
labels_startpos[i] = labels_startpos[i-1] + labels_ct[i-1];
|
||||
labels_startpos[i] = labels_startpos[i - 1] + labels_ct[i - 1];
|
||||
|
||||
labels_temp.clear();
|
||||
}
|
||||
|
||||
bool DatasetInfo::is_all_continuous() const {
|
||||
for (index_t i = 0; i < features_.size(); i++) {
|
||||
if (features_[i].type() != DatasetFeature::CONTINUOUS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
success_t DatasetInfo::ReadMatrix(TextLineReader *reader, Matrix *matrix) const {
|
||||
std::vector<double> linearized;
|
||||
index_t n_features = this->n_features();
|
||||
index_t n_points = 0;
|
||||
success_t retval = SUCCESS_PASS;
|
||||
bool is_done;
|
||||
|
||||
// read through our file to find out how long it is
|
||||
index_t cur_line = reader->line_num();
|
||||
while(reader->Gobble()) { }
|
||||
matrix->Init(n_features, reader->line_num() - cur_line + 1);
|
||||
|
||||
char *fname = strncpy(new char[strlen(reader->filename()) + 1],
|
||||
reader->filename(),
|
||||
strlen(reader->filename()) + 1);
|
||||
|
||||
reader->Close();
|
||||
reader->Open(fname);
|
||||
delete[] fname;
|
||||
|
||||
// make sure we are in the same place in our file, just in case it was passed
|
||||
// to us and we had already been some of the way through it (this is why we
|
||||
// saved the line number
|
||||
while(reader->line_num() < cur_line) {
|
||||
reader->Gobble();
|
||||
}
|
||||
|
||||
while((n_points < matrix->n_cols()) && !is_done && !FAILED(retval)) {
|
||||
retval = ReadPoint(reader, matrix->GetColumnPtr(n_points), &is_done);
|
||||
n_points++;
|
||||
}
|
||||
|
||||
if (!FAILED(retval)) {
|
||||
n_points--; // last increment was the failure, so subtract that
|
||||
DEBUG_ASSERT(n_points == matrix->n_rows());
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
success_t DatasetInfo::ReadPoint(TextLineReader *reader, double *point,
|
||||
bool *is_done) const {
|
||||
index_t n_features = this->n_features();
|
||||
std::string str;
|
||||
std::string::iterator pos;
|
||||
|
||||
*is_done = false;
|
||||
|
||||
for (;;) {
|
||||
if (!reader->MoreLines()) {
|
||||
*is_done = true;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
|
||||
str = reader->Peek();
|
||||
pos = str.begin();
|
||||
|
||||
while (*pos == ' ' || *pos == '\t' || *pos == ',') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (unlikely(*pos == '\0' || *pos == '%')) {
|
||||
reader->Gobble();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (index_t i = 0; i < n_features; i++) {
|
||||
std::string::iterator next;
|
||||
|
||||
while (*pos == ' ' || *pos == '\t' || *pos == ',') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (unlikely(*pos == '\0')) {
|
||||
for (std::string::iterator s = reader->Peek().begin(); s < pos; s++) { // UNDEFINED
|
||||
if (!*s) {
|
||||
*s = ',';
|
||||
}
|
||||
}
|
||||
reader->Error("I am expecting %"LI"d entries per row, "
|
||||
"but this line has only %"LI"d.",
|
||||
n_features, i);
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
next = pos;
|
||||
while (*next != '\0' && *next != ' ' && *next != '\t' && *next != ','
|
||||
&& *next != '%') {
|
||||
next++;
|
||||
}
|
||||
|
||||
if (*next != '\0') {
|
||||
char c = *next;
|
||||
*next = '\0';
|
||||
if (c != '%') {
|
||||
next++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len = str.end() - pos;
|
||||
size_t cpos = pos - str.begin();
|
||||
if (!PASSED(features_[i].Parse(str.substr(cpos,len), &point[i]))) {
|
||||
std::string::iterator end = reader->Peek().end();
|
||||
std::string tmp;
|
||||
tmp.assign(pos,str.end());
|
||||
for (std::string::iterator s = reader->Peek().begin();
|
||||
s < next && s < end; s++) {
|
||||
if (*s == '\0') {
|
||||
*s = ',';
|
||||
}
|
||||
}
|
||||
reader->Error("Invalid parse: [%s]", tmp.c_str());
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
pos = next;
|
||||
}
|
||||
|
||||
while (*pos == ' ' || *pos == '\t' || *pos == ',') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (*pos != '\0') {
|
||||
for (std::string::iterator s = reader->Peek().begin(); s < pos; s++) {
|
||||
if (*s == '\0') {
|
||||
*s = ',';
|
||||
}
|
||||
}
|
||||
reader->Error("Extra junk on line.");
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
reader->Gobble();
|
||||
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
|
||||
|
||||
void DatasetInfo::WriteArffHeader(TextWriter *writer) const {
|
||||
writer->Printf("@relation %s\n", name_.c_str());
|
||||
|
||||
for (index_t i = 0; i < features_.size(); i++) {
|
||||
const DatasetFeature *feature = &features_[i];
|
||||
writer->Printf("@attribute %s ", feature->name().c_str());
|
||||
if (feature->type() == DatasetFeature::NOMINAL) {
|
||||
writer->Printf("{");
|
||||
for (index_t v = 0; v < feature->n_values(); v++) {
|
||||
if (v != 0) {
|
||||
writer->Write(",");
|
||||
}
|
||||
writer->Write(feature->value_name(v).c_str());
|
||||
}
|
||||
writer->Printf("}");
|
||||
} else {
|
||||
writer->Write("real");
|
||||
}
|
||||
writer->Write("\n");
|
||||
}
|
||||
writer->Printf("@data\n");
|
||||
}
|
||||
|
||||
void DatasetInfo::WriteCsvHeader(const char *sep, TextWriter *writer) const {
|
||||
for (index_t i = 0; i < features_.size(); i++) {
|
||||
if (i != 0) {
|
||||
writer->Write(sep);
|
||||
}
|
||||
writer->Write(features_[i].name().c_str());
|
||||
}
|
||||
writer->Write("\n");
|
||||
}
|
||||
|
||||
void DatasetInfo::WriteMatrix(const Matrix& matrix, const char *sep,
|
||||
TextWriter *writer) const {
|
||||
for (index_t i = 0; i < matrix.n_cols(); i++) {
|
||||
for (index_t f = 0; f < features_.size(); f++) {
|
||||
if (f != 0) {
|
||||
writer->Write(sep);
|
||||
}
|
||||
std::string str;
|
||||
features_[f].Format(matrix.get(f, i), str);
|
||||
writer->Write(str.c_str());
|
||||
}
|
||||
writer->Write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Dataset ------------------------------------------------------------------
|
||||
|
||||
success_t Dataset::InitFromFile(const char *fname) {
|
||||
TextLineReader reader;
|
||||
|
||||
if (PASSED(reader.Open(fname))) {
|
||||
return InitFromFile(&reader, fname);
|
||||
return InitFromFile(reader, fname);
|
||||
} else {
|
||||
matrix_.Init(0, 0);
|
||||
matrix_ = 0.0; // 0x0 matrix
|
||||
info_.Init();
|
||||
NONFATAL("Could not open file '%s' for reading.", fname);
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
success_t Dataset::InitFromFile(TextLineReader *reader,
|
||||
success_t Dataset::InitFromFile(TextLineReader& reader,
|
||||
const char *filename) {
|
||||
success_t result;
|
||||
|
||||
result = info_.InitFromFile(reader, filename);
|
||||
if (PASSED(result)) {
|
||||
result = info_.ReadMatrix(reader, &matrix_);
|
||||
result = info_.ReadMatrix(reader, matrix_);
|
||||
} else {
|
||||
matrix_.Init(0, 0);
|
||||
matrix_ = 0.0; // 0x0 matrix
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -644,9 +179,9 @@ success_t Dataset::WriteCsv(const char *fname, bool header) const {
|
||||
return SUCCESS_FAIL;
|
||||
} else {
|
||||
if (header) {
|
||||
info_.WriteCsvHeader(",\t", &writer);
|
||||
info_.WriteCsvHeader(",\t", writer);
|
||||
}
|
||||
info_.WriteMatrix(matrix_, ",\t", &writer);
|
||||
info_.WriteMatrix(matrix_, ",\t", writer);
|
||||
return writer.Close();
|
||||
}
|
||||
}
|
||||
@@ -658,26 +193,30 @@ success_t Dataset::WriteArff(const char *fname) const {
|
||||
NONFATAL("Couldn't open '%s' for writing.", fname);
|
||||
return SUCCESS_FAIL;
|
||||
} else {
|
||||
info_.WriteArffHeader(&writer);
|
||||
info_.WriteMatrix(matrix_, ",", &writer);
|
||||
info_.WriteArffHeader(writer);
|
||||
info_.WriteMatrix(matrix_, ",", writer);
|
||||
return writer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
void Dataset::SplitTrainTest(int folds, int fold_number,
|
||||
const std::vector<index_t>& permutation,
|
||||
Dataset *train, Dataset *test) const {
|
||||
Dataset& train, Dataset& test) const {
|
||||
// determine number of points in test and training sets
|
||||
index_t n_test = (n_points() + folds - fold_number - 1) / folds;
|
||||
index_t n_train = n_points() - n_test;
|
||||
|
||||
train->InitBlank();
|
||||
train->info().InitCopy(info());
|
||||
// initialize blank training data set
|
||||
train.InitBlank();
|
||||
train.info().InitCopy(info());
|
||||
|
||||
test->InitBlank();
|
||||
test->info().InitCopy(info());
|
||||
// initialize blank testing data set
|
||||
test.InitBlank();
|
||||
test.info().InitCopy(info());
|
||||
|
||||
train->matrix().Init(n_features(), n_train);
|
||||
test->matrix().Init(n_features(), n_test);
|
||||
// set sizes of training and test datasets
|
||||
train.matrix().set_size(n_features(), n_train);
|
||||
test.matrix().set_size(n_features(), n_test);
|
||||
|
||||
index_t i_train = 0;
|
||||
index_t i_test = 0;
|
||||
@@ -687,32 +226,59 @@ void Dataset::SplitTrainTest(int folds, int fold_number,
|
||||
double *dest;
|
||||
|
||||
if (unlikely((i_orig - fold_number) % folds == 0)) {
|
||||
dest = test->matrix().GetColumnPtr(i_test);
|
||||
// put this column into the test set
|
||||
dest = test.matrix().colptr(i_test);
|
||||
i_test++;
|
||||
} else {
|
||||
dest = train->matrix().GetColumnPtr(i_train);
|
||||
// put this column into the training set
|
||||
dest = train.matrix().colptr(i_train);
|
||||
i_train++;
|
||||
}
|
||||
|
||||
mem::Copy(dest,
|
||||
this->matrix().GetColumnPtr(permutation[i_orig]),
|
||||
n_features());
|
||||
// copy the column over in memory
|
||||
memcpy(dest,
|
||||
this->matrix().colptr(permutation[i_orig]),
|
||||
sizeof(double) * n_features());
|
||||
}
|
||||
|
||||
DEBUG_ASSERT(i_train == train->n_points());
|
||||
DEBUG_ASSERT(i_test == test->n_points());
|
||||
DEBUG_ASSERT(i_train == train.n_points());
|
||||
DEBUG_ASSERT(i_test == test.n_points());
|
||||
}
|
||||
|
||||
success_t data::Load(const char *fname, Matrix *matrix) {
|
||||
Dataset dataset;
|
||||
success_t result = dataset.InitFromFile(fname);
|
||||
matrix->Own(&dataset.matrix());
|
||||
success_t data::Load(const char *fname, arma::mat& matrix) {
|
||||
TextLineReader reader;
|
||||
DatasetInfo info; // we will ignore this, but it reads our matrix
|
||||
success_t result;
|
||||
|
||||
// clear our matrix
|
||||
matrix.reset();
|
||||
|
||||
if (PASSED(reader.Open(fname))) {
|
||||
// read our file, since it has successfully opened
|
||||
result = info.InitFromFile(reader, fname);
|
||||
if (PASSED(result)) {
|
||||
result = info.ReadMatrix(reader, matrix);
|
||||
}
|
||||
} else {
|
||||
NONFATAL("Could not open file '%s' for reading.", fname);
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
success_t data::Save(const char *fname, const Matrix& matrix) {
|
||||
Dataset dataset;
|
||||
dataset.AliasMatrix(matrix);
|
||||
return dataset.WriteCsv(fname);
|
||||
}
|
||||
success_t data::Save(const char *fname, const arma::mat& matrix) {
|
||||
TextWriter writer;
|
||||
|
||||
// temporary info object that will help write our CSV
|
||||
DatasetInfo info;
|
||||
info.InitContinuous(matrix.n_rows);
|
||||
|
||||
if (!PASSED(writer.Open(fname))) {
|
||||
NONFATAL("Couldn't open '%s' for writing.", fname);
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
info.WriteMatrix(matrix, ",\t", writer);
|
||||
return writer.Close();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#ifndef DATA_DATASET_H
|
||||
#define DATA_DATASET_H
|
||||
|
||||
#include <armadillo>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -51,316 +52,12 @@
|
||||
#include "../file/textfile.h"
|
||||
#include "../col/tokenizer.h"
|
||||
|
||||
#include "dataset_feature.h"
|
||||
#include "dataset_info.h"
|
||||
|
||||
class TextLineReader;
|
||||
class TextWriter;
|
||||
|
||||
/**
|
||||
* Metadata about a particular dataset feature (attribute).
|
||||
*
|
||||
* Supports nominal, continuous, and integer values.
|
||||
*/
|
||||
class DatasetFeature {
|
||||
public:
|
||||
/**
|
||||
* Feature types supported.
|
||||
*/
|
||||
enum Type {
|
||||
/** Real-valued data. */
|
||||
CONTINUOUS,
|
||||
/** Integer valued data. */
|
||||
INTEGER,
|
||||
/** Discrete data, each of which has a "name". */
|
||||
NOMINAL
|
||||
};
|
||||
|
||||
private:
|
||||
/** Name of the feature. */
|
||||
std::string name_;
|
||||
/** Type of data this feature represents. */
|
||||
Type type_;
|
||||
/** If nominal, the names of each numbered value. */
|
||||
std::vector<std::string> value_names_;
|
||||
|
||||
/**
|
||||
* Initialization common to all features.
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitGeneral(const std::string& name_in) {
|
||||
name_ = name_in;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initialize to be a continuous feature.
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitContinuous(const std::string& name_in) {
|
||||
InitGeneral(name_in);
|
||||
type_ = CONTINUOUS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes to an integer type.
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitInteger(const std::string& name_in) {
|
||||
InitGeneral(name_in);
|
||||
type_ = INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes to a nominal type.
|
||||
*
|
||||
* The value_names list starts empty, so you need to add the name of
|
||||
* each feature to this. (The dataset reading functions will do this
|
||||
* for you).
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitNominal(const std::string& name_in) {
|
||||
InitGeneral(name_in);
|
||||
type_ = NOMINAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text version of the value based on the type.
|
||||
*
|
||||
* Continuous parameters are printed in floating point, and integers
|
||||
* are shown as integers. For nominal, the value_name(int(value)) is
|
||||
* shown. NaN (missing data) is always shown as '?'.
|
||||
*
|
||||
* @param value the value to format
|
||||
* @param result this will be initialized to the formatted text
|
||||
*/
|
||||
void Format(double value, std::string& result) const;
|
||||
|
||||
/**
|
||||
* Parses a string into the particular value.
|
||||
*
|
||||
* Integers and continuous are parsed using the normal functions.
|
||||
* For nominal, the entry
|
||||
*
|
||||
* If an invalid parse occurs, such as a mal-formatted number or
|
||||
* a nominal value not in the list, SUCCESS_FAIL will be returned.
|
||||
*
|
||||
* @param str the string to parse
|
||||
* @param d where to store the result
|
||||
*/
|
||||
success_t Parse(const std::string& str, double *d) const;
|
||||
|
||||
/**
|
||||
* Gets what the feature is named.
|
||||
*
|
||||
* @return the name of the feature; for point, "Age" or "X Position"
|
||||
*/
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the type of feature.
|
||||
*
|
||||
* @return whether this is DatasetFeature::CONTINUOUS, INTEGER, or NOMINAL
|
||||
*/
|
||||
Type type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of a particular nominal value, given its index.
|
||||
*
|
||||
* The first nominal value is 0, the second is 1, etc.
|
||||
*
|
||||
* @param value the number of the value
|
||||
*/
|
||||
const std::string& value_name(int value) const {
|
||||
DEBUG_ASSERT(type_ == NOMINAL);
|
||||
return value_names_[value];
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of nominal values.
|
||||
*
|
||||
* The values 0 to n_values() - 1 are valid.
|
||||
* This will return zero for CONTINUOUS and INTEGER types.
|
||||
*
|
||||
* @return the number of nominal values
|
||||
*/
|
||||
index_t n_values() const {
|
||||
return value_names_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of value names.
|
||||
*
|
||||
* Useful for creating a nominal feature yourself.
|
||||
*
|
||||
* @return a mutable array of value names
|
||||
*/
|
||||
std::vector<std::string>& value_names() {
|
||||
return value_names_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Information describing a dataset and its features.
|
||||
*/
|
||||
class DatasetInfo {
|
||||
private:
|
||||
std::string name_;
|
||||
std::vector<DatasetFeature> features_;
|
||||
|
||||
public:
|
||||
/** Gets a mutable list of all features. */
|
||||
std::vector<DatasetFeature>& features() {
|
||||
return features_;
|
||||
}
|
||||
|
||||
/** Gets information about a particular feature. */
|
||||
const DatasetFeature& feature(index_t attrib_num) const {
|
||||
return features_[attrib_num];
|
||||
}
|
||||
|
||||
/** Gets the number of features. */
|
||||
index_t n_features() const {
|
||||
return features_.size();
|
||||
}
|
||||
|
||||
/** Gets the title of the data set. */
|
||||
const char *name() const {
|
||||
return name_.c_str();
|
||||
}
|
||||
|
||||
/** Sets the title of the data set. */
|
||||
void set_name(const std::string& name_in) {
|
||||
name_ = name_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all parameters are continuous.
|
||||
*/
|
||||
bool is_all_continuous() const;
|
||||
|
||||
/**
|
||||
* Initialize an all-continuous dataset;
|
||||
*
|
||||
* @param n_features the number of continuous features
|
||||
* @param name_in the dataset title
|
||||
*/
|
||||
void InitContinuous(index_t n_features,
|
||||
const char *name_in = "dataset");
|
||||
|
||||
/**
|
||||
* Initialize a custom dataset.
|
||||
*
|
||||
* This assumes you will eventually use the features() to add features.
|
||||
*
|
||||
* @param name_in the dataset title
|
||||
*/
|
||||
void Init(const char *name_in = "dataset");
|
||||
|
||||
/***
|
||||
* Copy constructor, written manually to avoid use of OBJECT_TRAVERSAL functions.
|
||||
* TODO: Make this Init() crap go away.
|
||||
*/
|
||||
void InitCopy(const DatasetInfo &info) {
|
||||
name_ = info.name();
|
||||
features_ = info.features_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the header for an ARFF file.
|
||||
*/
|
||||
void WriteArffHeader(TextWriter *writer) const;
|
||||
|
||||
/**
|
||||
* Writes header for CSV file.
|
||||
*
|
||||
* @param sep the value separator (use ",\t" for CSV)
|
||||
* @param writer the text writer to write the header line to
|
||||
*/
|
||||
void WriteCsvHeader(const char *sep, TextWriter *writer) const;
|
||||
|
||||
/**
|
||||
* Writes the contents of a matrix to a file.
|
||||
*
|
||||
* @param matrix the matrix
|
||||
* @param sep the separator (use ",\t" for CSV)
|
||||
* @param writer the writer to write to
|
||||
*/
|
||||
void WriteMatrix(const Matrix& matrix, const char *sep,
|
||||
TextWriter *writer) const;
|
||||
|
||||
/**
|
||||
* Initialize explicitly from an ARFF file.
|
||||
*
|
||||
* ARFF LIMITATIONS: Values cannot have spaces or commas, even with quotes;
|
||||
* 'string' data type not supported (nominal is supported).
|
||||
*
|
||||
* You might just use InitFromFile, which will guess the type for you.
|
||||
*
|
||||
* This will read only the header information and leave the reader at the
|
||||
* first line of data.
|
||||
*/
|
||||
success_t InitFromArff(TextLineReader *reader,
|
||||
const char *filename = "dataset");
|
||||
|
||||
/**
|
||||
* Initialize from a CSV-like file with numbers only, inferring
|
||||
* automatically that if the first row has non-numeric characters, it is
|
||||
* a header.
|
||||
*
|
||||
* InitFromFile will automatically detect this.
|
||||
*/
|
||||
success_t InitFromCsv(TextLineReader *reader,
|
||||
const char *filename = "dataset");
|
||||
|
||||
/**
|
||||
* Initializes the header from a file, either CSV or ARFF.
|
||||
*
|
||||
* All header lines will be gobbled, so the reader's position will be
|
||||
* left at the first line of actual data.
|
||||
* You can then read the data with matrix.
|
||||
*/
|
||||
success_t InitFromFile(TextLineReader *reader,
|
||||
const char *filename = "dataset");
|
||||
/**
|
||||
* Populates a matrix from a file, given the internal data model.
|
||||
*
|
||||
* ARFF LIMITATIONS: Values cannot have spaces or commas, even with quotes;
|
||||
* 'string' data type not supported (nominal is supported).
|
||||
*
|
||||
* @param reader the reader to get lines from
|
||||
* @param matrix the matrix to store text into
|
||||
*/
|
||||
success_t ReadMatrix(TextLineReader *reader, Matrix *matrix) const;
|
||||
|
||||
/**
|
||||
* Reads a single vector.
|
||||
*
|
||||
* @param reader the line reader being used
|
||||
* @param point an array of length n_features()
|
||||
* @param is_done set to true if we have finished reading the file
|
||||
* successfully -- the value of is_done is undefined if the
|
||||
* function returns failure!
|
||||
* @return whether reading the line was successful
|
||||
*/
|
||||
success_t ReadPoint(TextLineReader *reader, double *point,
|
||||
bool *is_done) const;
|
||||
|
||||
private:
|
||||
index_t SkipSpace_(std::string& s);
|
||||
|
||||
char *SkipNonspace_(char *s);
|
||||
|
||||
void SkipBlanks_(TextLineReader *reader);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Most generic dataset type.
|
||||
*
|
||||
@@ -374,7 +71,7 @@ class DatasetInfo {
|
||||
*/
|
||||
class Dataset {
|
||||
private:
|
||||
Matrix matrix_;
|
||||
arma::mat matrix_;
|
||||
DatasetInfo info_;
|
||||
|
||||
public:
|
||||
@@ -407,9 +104,9 @@ class Dataset {
|
||||
* @return the number of features, or variables, in the dataset
|
||||
*/
|
||||
index_t n_features() const {
|
||||
return matrix_.n_rows();
|
||||
return matrix_.n_rows;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of points/instances in the dataset.
|
||||
*
|
||||
@@ -418,7 +115,7 @@ class Dataset {
|
||||
* @return the number of points in the dataset
|
||||
*/
|
||||
index_t n_points() const {
|
||||
return matrix_.n_cols();
|
||||
return matrix_.n_cols;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,13 +146,13 @@ class Dataset {
|
||||
* @param labels_ct numbers of point in each label class. e.g.
|
||||
* [7,5,8]
|
||||
* @param labels_startpos start positions of each label class in
|
||||
* labels_index. e.g. [0,7,12]
|
||||
* labels_index. e.g. [0,7,12]
|
||||
*/
|
||||
void GetLabels(std::vector<double> &labels_list,
|
||||
std::vector<index_t> &labels_index,
|
||||
std::vector<index_t> &labels_ct,
|
||||
std::vector<index_t> &labels_startpos) const;
|
||||
|
||||
std::vector<index_t> &labels_index,
|
||||
std::vector<index_t> &labels_ct,
|
||||
std::vector<index_t> &labels_startpos) const;
|
||||
|
||||
/**
|
||||
* Gets the numeric value of a particular feature and point.
|
||||
*
|
||||
@@ -463,9 +160,9 @@ class Dataset {
|
||||
* @param point the point index
|
||||
*/
|
||||
double get(index_t feature, index_t point) const {
|
||||
return matrix_.get(feature, point);
|
||||
return matrix_(feature, point);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the integer value of a particular feature and point.
|
||||
*/
|
||||
@@ -475,7 +172,7 @@ class Dataset {
|
||||
DEBUG_ASSERT(d == double(i));
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modifies a value in the dataset.
|
||||
*
|
||||
@@ -484,40 +181,40 @@ class Dataset {
|
||||
* @param d the numeric value to set it to
|
||||
*/
|
||||
void set(index_t feature, index_t point, double d) {
|
||||
matrix_.set(feature, point, d);
|
||||
matrix_(feature, point) = d;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the "raw" form of a particular point.
|
||||
*
|
||||
* @return a C-like array of the values of a particular point
|
||||
*/
|
||||
const double *point(index_t point) const {
|
||||
return matrix_.GetColumnPtr(point);
|
||||
const arma::rowvec point(index_t point) const {
|
||||
return matrix_.row(point);
|
||||
}
|
||||
/**
|
||||
* Gets the "raw" form of a particular point.
|
||||
*
|
||||
* @return a C-like array of the values of a particular point
|
||||
*/
|
||||
double *point(index_t point) {
|
||||
return matrix_.GetColumnPtr(point);
|
||||
arma::rowvec point(index_t point) {
|
||||
return matrix_.row(point);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the matrix that stores all the data.
|
||||
*/
|
||||
const Matrix& matrix() const {
|
||||
const arma::mat& matrix() const {
|
||||
return matrix_;
|
||||
}
|
||||
/**
|
||||
* Returns the matrix that stores all the data
|
||||
* (can be used for modification).
|
||||
*/
|
||||
Matrix& matrix() {
|
||||
arma::mat& matrix() {
|
||||
return matrix_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats as text a particular location of the data set.
|
||||
*
|
||||
@@ -526,9 +223,9 @@ class Dataset {
|
||||
* @param result string that will be initialized to the formatted text
|
||||
*/
|
||||
void Format(index_t feature, index_t point, std::string& result) const {
|
||||
info_.feature(feature).Format(get(feature, point), result);
|
||||
info_.feature(feature).Format(matrix_(feature, point), result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializer that omits the matrix and info - you must initialize
|
||||
* these yourself.
|
||||
@@ -536,9 +233,8 @@ class Dataset {
|
||||
* (Although this currently does nothing, this is to future-proof your code
|
||||
* against possible changes.)
|
||||
*/
|
||||
void InitBlank() {
|
||||
}
|
||||
|
||||
void InitBlank() { }
|
||||
|
||||
/**
|
||||
* Reads in an ARFF or CSV/WSV file.
|
||||
*
|
||||
@@ -548,7 +244,7 @@ class Dataset {
|
||||
* @param fname the name of an ARFF, CSV, or whitespace-separated
|
||||
*/
|
||||
success_t InitFromFile(const char *fname);
|
||||
|
||||
|
||||
/**
|
||||
* Reads in an ARFF or CSV/WSV file.
|
||||
*
|
||||
@@ -559,9 +255,9 @@ class Dataset {
|
||||
* @param filename a title given to this data set, doesn't necessarily
|
||||
* need to be anything significant
|
||||
*/
|
||||
success_t InitFromFile(TextLineReader *reader,
|
||||
success_t InitFromFile(TextLineReader& reader,
|
||||
const char *filename = "dataset");
|
||||
|
||||
|
||||
/**
|
||||
* Writes to a CSV file.
|
||||
*
|
||||
@@ -584,12 +280,12 @@ class Dataset {
|
||||
*
|
||||
* @param matrix_in data where rows are features, columns are points
|
||||
*/
|
||||
void CopyMatrix(const Matrix& matrix_in) {
|
||||
void CopyMatrix(const arma::mat& matrix_in) {
|
||||
InitBlank();
|
||||
matrix_.Copy(matrix_in);
|
||||
info_.InitContinuous(matrix_.n_rows());
|
||||
matrix_ = matrix_in;
|
||||
info_.InitContinuous(matrix_.n_rows);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes by becoming the owner of an existing matrix, assuming
|
||||
* all features are continuous.
|
||||
@@ -600,12 +296,12 @@ class Dataset {
|
||||
*
|
||||
* @param matrix_in data where rows are features, columns are points
|
||||
*/
|
||||
void OwnMatrix(Matrix* matrix_in) {
|
||||
InitBlank();
|
||||
matrix_.Own(matrix_in);
|
||||
info_.InitContinuous(matrix_.n_rows());
|
||||
}
|
||||
|
||||
// void OwnMatrix(Matrix* matrix_in) {
|
||||
// InitBlank();
|
||||
// matrix_.Own(matrix_in);
|
||||
// info_.InitContinuous(matrix_.n_rows());
|
||||
// }
|
||||
|
||||
/**
|
||||
* Initializes as an alias or mirror of an existing matrix, assuming
|
||||
* all features are continuous.
|
||||
@@ -616,15 +312,17 @@ class Dataset {
|
||||
*
|
||||
* @param matrix_in data where rows are features, columns are points
|
||||
*/
|
||||
void AliasMatrix(const Matrix& matrix_in) {
|
||||
InitBlank();
|
||||
matrix_.Alias(matrix_in);
|
||||
info_.InitContinuous(matrix_.n_rows());
|
||||
}
|
||||
|
||||
// void AliasMatrix(const arma::mat& matrix_in) {
|
||||
// InitBlank();
|
||||
// use the memory pointer of the other matrix directly; do not free it when
|
||||
// we are done
|
||||
// matrix_(matrix_in.memptr(), matrix_in.n_rows, matrix_in.n_cols, false);
|
||||
// info_.InitContinuous(matrix_.n_rows);
|
||||
// }
|
||||
|
||||
//--- Cross-validation features ---
|
||||
|
||||
/**
|
||||
/*
|
||||
* Creates a training and test dataset for k-fold cross validation.
|
||||
*
|
||||
* The test set will be approximately n_points() / folds, and the
|
||||
@@ -635,13 +333,13 @@ class Dataset {
|
||||
* @param folds the number of folds being used
|
||||
* @param fold_number the fold number, 0 to folds - 1
|
||||
* @param permutation the permutation to use, the same size as n_points()
|
||||
* (use math::MakeIdentiyPermutation or math::MakeRandomPermutation)
|
||||
* (use math::MakeIdentityPermutation or math::MakeRandomPermutation)
|
||||
* @param train the training set
|
||||
* @param test the test set
|
||||
*/
|
||||
void SplitTrainTest(int folds, int fold_number,
|
||||
const std::vector<index_t>& permutation,
|
||||
Dataset *train, Dataset *test) const;
|
||||
Dataset& train, Dataset& test) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -662,12 +360,13 @@ namespace data {
|
||||
* @param fname the file name to load
|
||||
* @param matrix a pointer to an uninitialized matrix to load
|
||||
*/
|
||||
success_t Load(const char *fname, Matrix *matrix);
|
||||
/**
|
||||
success_t Load(const char *fname, arma::mat& matrix);
|
||||
|
||||
/**
|
||||
* Loads a matrix from a file.
|
||||
* The Matrix is Statically Initialized from a memory mapped file
|
||||
* The matrix is statically initialized from a memory mapped file.
|
||||
*
|
||||
* This supports only CSV Datasets
|
||||
* This supports only CSV datasets.
|
||||
*
|
||||
* @code
|
||||
* Matrix A;
|
||||
@@ -678,43 +377,50 @@ namespace data {
|
||||
* @param matrix a pointer to an uninitialized matrix to load
|
||||
*/
|
||||
template<typename Precision>
|
||||
success_t LargeLoad(const char *fname, GenMatrix<Precision> *matrix) {
|
||||
success_t LargeLoad(const char *fname, arma::Mat<Precision>& matrix) {
|
||||
// open our file
|
||||
TextLineReader *reader = new TextLineReader();
|
||||
if (reader->Open(fname)==SUCCESS_FAIL) {
|
||||
if (reader->Open(fname) == SUCCESS_FAIL) {
|
||||
reader->Error("Couldn't open %s", fname);
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
index_t dimension=0;
|
||||
std::string line=reader->Peek();
|
||||
std::vector<std::string> result;
|
||||
// line.Split(",", &result);
|
||||
tokenizeString( line, ",", result );
|
||||
dimension=result.size();
|
||||
while (reader->Gobble()) {
|
||||
}
|
||||
matrix->StaticInit(dimension, reader->line_num());
|
||||
matrix->SetAll(0.0);
|
||||
|
||||
// find dimensionality
|
||||
index_t dimension = 0;
|
||||
std::string line = reader->Peek();
|
||||
std::vector<std::string> result;
|
||||
tokenizeString(line, ",", result);
|
||||
|
||||
dimension = result.size();
|
||||
|
||||
// count lines in file
|
||||
while(reader->Gobble()) { }
|
||||
|
||||
// resize matrix to correct size and set all to 0
|
||||
matrix.zeros(dimension, reader->line_num());
|
||||
|
||||
delete reader;
|
||||
|
||||
// second pass through file: fill matrix
|
||||
reader = new TextLineReader();
|
||||
reader->Open(fname);
|
||||
while (true) {
|
||||
std::string line=reader->Peek();
|
||||
|
||||
do {
|
||||
// parse this line
|
||||
std::string line = reader->Peek();
|
||||
std::vector<std::string> result;
|
||||
// line.Split(",", &result);
|
||||
tokenizeString( line, ",", result );
|
||||
for(index_t i=0; i<result.size(); i++) {
|
||||
|
||||
tokenizeString(line, ",", result);
|
||||
for(index_t i = 0; i < result.size(); i++) {
|
||||
Precision num;
|
||||
sscanf(result[i].c_str(), "%lf", &num);
|
||||
matrix->set(i, reader->line_num()-1, (Precision)num);
|
||||
matrix(i, reader->line_num() - 1) = (Precision) num;
|
||||
}
|
||||
if (reader->Gobble()==false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(reader->Gobble()); // break when we can't read more
|
||||
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Saves a matrix to a file.
|
||||
*
|
||||
@@ -729,7 +435,7 @@ namespace data {
|
||||
* @param fname the file name to load
|
||||
* @param matrix a pointer to an uninitialized matrix to load
|
||||
*/
|
||||
success_t Save(const char *fname, const Matrix& matrix);
|
||||
success_t Save(const char *fname, const arma::mat& matrix);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/* MLPACK 0.2
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Alexander Gray,
|
||||
* Garry Boyer,
|
||||
* Ryan Riegel,
|
||||
* Nikolaos Vasiloglou,
|
||||
* Dongryeol Lee,
|
||||
* Chip Mappus,
|
||||
* Nishant Mehta,
|
||||
* Hua Ouyang,
|
||||
* Parikshit Ram,
|
||||
* Long Tran,
|
||||
* Wee Chin Wong
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Georgia Institute of Technology
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* @file dataset_feature.cc
|
||||
*
|
||||
* Implementations for the DatasetFeature class.
|
||||
*
|
||||
* @bug These routines fail when trying to read files linewise that use the Mac
|
||||
* eol '\r'. Both Windows and Unix eol ("\r\n" and '\n') work. Use the
|
||||
* programs 'dos2unix' or 'tr' to convert the '\r's to '\n's.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../base/base.h"
|
||||
|
||||
#include "dataset_feature.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void DatasetFeature::Format(double value, string& result) const {
|
||||
if (unlikely(isnan(value))) {
|
||||
result = "?";
|
||||
return;
|
||||
}
|
||||
ostringstream o;
|
||||
switch (type_) {
|
||||
case CONTINUOUS:
|
||||
if (floor(value) != value) {
|
||||
// non-integer
|
||||
o.setf(ios::scientific);
|
||||
} else {
|
||||
// value is actually an integer
|
||||
o.precision(17);
|
||||
}
|
||||
break;
|
||||
case INTEGER:
|
||||
case NOMINAL:
|
||||
break;
|
||||
#ifdef DEBUG
|
||||
default: abort();
|
||||
#endif
|
||||
}
|
||||
if(!(o << value))
|
||||
abort();
|
||||
result = o.str();
|
||||
}
|
||||
|
||||
success_t DatasetFeature::Parse(const std::string& str, double& d) const {
|
||||
if (unlikely(str[0] == '?') && unlikely(str[1] == '\0')) {
|
||||
d = DBL_NAN;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
switch (type_) {
|
||||
case CONTINUOUS:
|
||||
{
|
||||
istringstream is(str);
|
||||
if(!(is >> d))
|
||||
return SUCCESS_FAIL;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
case INTEGER:
|
||||
{
|
||||
int i;
|
||||
istringstream is(str);
|
||||
if(!(is >> i))
|
||||
return SUCCESS_FAIL;
|
||||
d = i;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
case NOMINAL: {
|
||||
index_t i;
|
||||
for (i = 0; i < value_names_.size(); i++) {
|
||||
if (value_names_[i] == str) {
|
||||
d = i;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
}
|
||||
d = DBL_NAN;
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
default: abort();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/* MLPACK 0.2
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Alexander Gray,
|
||||
* Garry Boyer,
|
||||
* Ryan Riegel,
|
||||
* Nikolaos Vasiloglou,
|
||||
* Dongryeol Lee,
|
||||
* Chip Mappus,
|
||||
* Nishant Mehta,
|
||||
* Hua Ouyang,
|
||||
* Parikshit Ram,
|
||||
* Long Tran,
|
||||
* Wee Chin Wong
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Georgia Institute of Technology
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* @file dataset_feature.h
|
||||
*
|
||||
* The DatasetFeature class, used by the Dataset class.
|
||||
*
|
||||
* @bug These routines fail when trying to read files linewise that use the Mac
|
||||
* eol '\r'. Both Windows and Unix eol ("\r\n" and '\n') work. Use the
|
||||
* programs 'dos2unix' or 'tr' to convert the '\r's to '\n's.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DATA_DATASET_FEATURE_H
|
||||
#define DATA_DATASET_FEATURE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../la/matrix.h"
|
||||
#include "../math/discrete.h"
|
||||
#include "../file/textfile.h"
|
||||
|
||||
/**
|
||||
* Metadata about a particular dataset feature (attribute).
|
||||
*
|
||||
* Supports nominal, continuous, and integer values.
|
||||
*/
|
||||
class DatasetFeature {
|
||||
public:
|
||||
/**
|
||||
* Supported feature types.
|
||||
*/
|
||||
enum Type {
|
||||
CONTINUOUS, /** Real-valued data. */
|
||||
INTEGER, /** Integer valued data. */
|
||||
NOMINAL /** Discrete data, each of which has a "name". */
|
||||
};
|
||||
|
||||
private:
|
||||
std::string name_; /** Name of the feature. */
|
||||
Type type_; /** Type of data this feature represents. */
|
||||
std::vector<std::string> value_names_; /** If nominal, the names of each numbered value. */
|
||||
|
||||
/**
|
||||
* Initialization common to all features.
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitGeneral(const std::string& name_in) {
|
||||
name_ = name_in;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initialize to be a continuous feature.
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitContinuous(const std::string& name_in) {
|
||||
InitGeneral(name_in);
|
||||
type_ = CONTINUOUS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes to an integer type.
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitInteger(const std::string& name_in) {
|
||||
InitGeneral(name_in);
|
||||
type_ = INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes to a nominal type.
|
||||
*
|
||||
* The value_names list starts empty, so you need to add the name of
|
||||
* each feature to this. (The dataset reading functions will do this
|
||||
* for you).
|
||||
*
|
||||
* @param name_in the name of the feature
|
||||
*/
|
||||
void InitNominal(const std::string& name_in) {
|
||||
InitGeneral(name_in);
|
||||
type_ = NOMINAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text version of the value based on the type.
|
||||
*
|
||||
* Continuous parameters are printed in floating point, and integers
|
||||
* are shown as integers. For nominal, the value_name(int(value)) is
|
||||
* shown. NaN (missing data) is always shown as '?'.
|
||||
*
|
||||
* @param value the value to format
|
||||
* @param result this will be initialized to the formatted text
|
||||
*/
|
||||
void Format(double value, std::string& result) const;
|
||||
|
||||
/**
|
||||
* Parses a string into the particular value.
|
||||
*
|
||||
* Integers and continuous are parsed using the normal functions.
|
||||
* For nominal, the entry
|
||||
*
|
||||
* If an invalid parse occurs, such as a mal-formatted number or
|
||||
* a nominal value not in the list, SUCCESS_FAIL will be returned.
|
||||
*
|
||||
* @param str the string to parse
|
||||
* @param d where to store the result
|
||||
*/
|
||||
success_t Parse(const std::string& str, double& d) const;
|
||||
|
||||
/**
|
||||
* Gets what the feature is named.
|
||||
*
|
||||
* @return the name of the feature; for point, "Age" or "X Position"
|
||||
*/
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the type of feature.
|
||||
*
|
||||
* @return whether this is DatasetFeature::CONTINUOUS, INTEGER, or NOMINAL
|
||||
*/
|
||||
Type type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of a particular nominal value, given its index.
|
||||
*
|
||||
* The first nominal value is 0, the second is 1, etc.
|
||||
*
|
||||
* @param value the number of the value
|
||||
*/
|
||||
const std::string& value_name(int value) const {
|
||||
DEBUG_ASSERT(type_ == NOMINAL);
|
||||
return value_names_[value];
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of nominal values.
|
||||
*
|
||||
* The values 0 to n_values() - 1 are valid.
|
||||
* This will return zero for CONTINUOUS and INTEGER types.
|
||||
*
|
||||
* @return the number of nominal values
|
||||
*/
|
||||
index_t n_values() const {
|
||||
return value_names_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of value names.
|
||||
*
|
||||
* Useful for creating a nominal feature yourself.
|
||||
*
|
||||
* @return a mutable array of value names
|
||||
*/
|
||||
std::vector<std::string>& value_names() {
|
||||
return value_names_;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,429 @@
|
||||
/* MLPACK 0.2
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Alexander Gray,
|
||||
* Garry Boyer,
|
||||
* Ryan Riegel,
|
||||
* Nikolaos Vasiloglou,
|
||||
* Dongryeol Lee,
|
||||
* Chip Mappus,
|
||||
* Nishant Mehta,
|
||||
* Hua Ouyang,
|
||||
* Parikshit Ram,
|
||||
* Long Tran,
|
||||
* Wee Chin Wong
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Georgia Institute of Technology
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* @file dataset_info.cc
|
||||
*
|
||||
* Implementations for DatasetInfo.
|
||||
*
|
||||
* @bug These routines fail when trying to read files linewise that use the Mac
|
||||
* eol '\r'. Both Windows and Unix eol ("\r\n" and '\n') work. Use the
|
||||
* programs 'dos2unix' or 'tr' to convert the '\r's to '\n's.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../base/base.h"
|
||||
#include "../col/tokenizer.h"
|
||||
|
||||
#include "dataset_info.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void DatasetInfo::InitContinuous(index_t n_features, const std::string& name_in) {
|
||||
features_.reserve(n_features);
|
||||
|
||||
name_ = name_in;
|
||||
|
||||
for (index_t i = 0; i < n_features; i++) {
|
||||
ostringstream o;
|
||||
if(!(o << i))
|
||||
abort();
|
||||
DatasetFeature f;
|
||||
f.InitContinuous(o.str());
|
||||
features_.push_back(f);
|
||||
}
|
||||
}
|
||||
|
||||
void DatasetInfo::Init(const string& name_in) {
|
||||
name_ = name_in;
|
||||
}
|
||||
|
||||
index_t DatasetInfo::SkipSpace_(string& s) {
|
||||
int i = 0;
|
||||
while (isspace(s[i])) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (unlikely(s[i] == '%') || unlikely(s[i] == '\0')) {
|
||||
return s.length();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
char *DatasetInfo::SkipNonspace_(char *s) {
|
||||
while (likely(*s != '\0')
|
||||
&& likely(*s != '%')
|
||||
&& likely(*s != ' ')
|
||||
&& likely(*s != '\t')) {
|
||||
s++;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void DatasetInfo::SkipBlanks_(TextLineReader& reader) {
|
||||
while (reader.MoreLines() && reader.Peek()[SkipSpace_(reader.Peek())] == '\0') {
|
||||
reader.Gobble();
|
||||
}
|
||||
}
|
||||
|
||||
success_t DatasetInfo::InitFromArff(TextLineReader& reader, const string& filename) {
|
||||
success_t result = SUCCESS_PASS;
|
||||
|
||||
Init(filename);
|
||||
|
||||
while (1) {
|
||||
SkipBlanks_(reader);
|
||||
|
||||
string *peeked = &reader.Peek();
|
||||
vector<string> portions;
|
||||
|
||||
tokenizeString(*peeked, ", \t", portions, 0, "%{", 3, true);
|
||||
|
||||
if (portions.size() == 0) {
|
||||
/* empty line */
|
||||
} else if (portions[0][0] != '@') {
|
||||
reader.Error("ARFF: Unexpected @command. Did you forget @data?");
|
||||
result = SUCCESS_FAIL;
|
||||
break;
|
||||
} else {
|
||||
if (strcasecmp(portions[0].c_str(), "@relation") == 0) {
|
||||
if (portions.size() < 2) {
|
||||
reader.Error("ARFF: @relation requires name");
|
||||
result = SUCCESS_FAIL;
|
||||
} else {
|
||||
set_name(portions[1]);
|
||||
}
|
||||
} else if (strcasecmp(portions[0].c_str(), "@relation") == 0) {
|
||||
if (portions.size() < 3) {
|
||||
reader.Error("ARFF: @attribute requires name and type.");
|
||||
result = SUCCESS_FAIL;
|
||||
} else {
|
||||
DatasetFeature feature;
|
||||
if (portions[2][0] == '{') { //}
|
||||
feature.InitNominal(portions[1]);
|
||||
// TODO: Doesn't support values with spaces {
|
||||
tokenizeString(portions[2], ", \t", feature.value_names(), 1, "}%", 0);
|
||||
} else {
|
||||
string type(portions[2]);
|
||||
//portions[2].Trim(" \t", &type);
|
||||
if (strcasecmp(type.c_str(), "numeric") == 0
|
||||
|| strcasecmp(type.c_str(), "real") == 0) {
|
||||
feature.InitContinuous(portions[1]);
|
||||
features_.push_back(feature);
|
||||
} else if (strcasecmp(type.c_str(), "integer") == 0) {
|
||||
feature.InitInteger(portions[1]);
|
||||
features_.push_back(feature);
|
||||
} else {
|
||||
reader.Error(
|
||||
"ARFF: Only supports 'numeric', 'real', and {nominal}.");
|
||||
result = SUCCESS_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (strcasecmp(portions[0].c_str(), "@data") == 0) {
|
||||
/* Done! */
|
||||
reader.Gobble();
|
||||
break;
|
||||
} else {
|
||||
reader.Error("ARFF: Expected @relation, @attribute, or @data.");
|
||||
result = SUCCESS_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reader.Gobble();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
success_t DatasetInfo::InitFromCsv(TextLineReader& reader, const std::string& filename) {
|
||||
vector<string> headers;
|
||||
bool nonnumeric = false;
|
||||
|
||||
Init(filename);
|
||||
|
||||
tokenizeString(reader.Peek(), ", \t", headers);
|
||||
|
||||
if (headers.size() == 0) {
|
||||
reader.Error("Trying to parse empty file as CSV.");
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
// Try to auto-detect if there is a header row
|
||||
for (index_t i = 0; i < headers.size(); i++) {
|
||||
char *end;
|
||||
|
||||
(void) strtod(headers[i].c_str(), &end);
|
||||
|
||||
if (end == headers[i].c_str()) {
|
||||
nonnumeric = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nonnumeric) {
|
||||
for (index_t i = 0; i < headers.size(); i++) {
|
||||
DatasetFeature feature;
|
||||
feature.InitContinuous(headers[i]);
|
||||
features_.push_back(feature);
|
||||
}
|
||||
reader.Gobble();
|
||||
} else {
|
||||
for (index_t i = 0; i < headers.size(); i++) {
|
||||
DatasetFeature feature;
|
||||
ostringstream o;
|
||||
if(!(o << i))
|
||||
abort();
|
||||
feature.InitContinuous(o.str());
|
||||
features_.push_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
|
||||
success_t DatasetInfo::InitFromFile(TextLineReader& reader, const std::string& filename) {
|
||||
SkipBlanks_(reader);
|
||||
|
||||
// WARNING: Safe?
|
||||
char first_char = reader.Peek()[SkipSpace_(reader.Peek())];
|
||||
|
||||
if (!first_char) {
|
||||
Init();
|
||||
reader.Error("Could not parse the first line.");
|
||||
return SUCCESS_FAIL;
|
||||
} else if (first_char == '@') {
|
||||
/* Okay, it's ARFF. */
|
||||
return InitFromArff(reader, filename);
|
||||
} else {
|
||||
/* It's CSV. We'll try to see if there are headers. */
|
||||
return InitFromCsv(reader, filename);
|
||||
}
|
||||
}
|
||||
|
||||
success_t DatasetInfo::ReadMatrix(TextLineReader& reader, arma::mat &matrix) const {
|
||||
vector<double> linearized;
|
||||
index_t n_features = this->n_features();
|
||||
index_t n_points = 0;
|
||||
success_t retval = SUCCESS_PASS;
|
||||
bool is_done;
|
||||
|
||||
// read through our file to find out how long it is
|
||||
index_t cur_line = reader.line_num();
|
||||
while(reader.Gobble()) { }
|
||||
matrix.set_size(n_features, reader.line_num() - cur_line + 1);
|
||||
|
||||
string fname = reader.filename();
|
||||
reader.Close();
|
||||
reader.Open(fname.c_str());
|
||||
|
||||
// make sure we are in the same place in our file, just in case it was passed
|
||||
// to us and we had already been some of the way through it (this is why we
|
||||
// saved the line number
|
||||
while(reader.line_num() < cur_line) {
|
||||
reader.Gobble();
|
||||
}
|
||||
|
||||
while((n_points < matrix.n_cols) && !is_done && !FAILED(retval)) {
|
||||
retval = ReadPoint(reader, matrix.begin_col(n_points), is_done);
|
||||
n_points++;
|
||||
}
|
||||
|
||||
if (!FAILED(retval)) {
|
||||
n_points--; // last increment was the failure, so subtract that
|
||||
DEBUG_ASSERT(n_points == matrix.n_rows);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
success_t DatasetInfo::ReadPoint(TextLineReader& reader, arma::mat::col_iterator col,
|
||||
bool &is_done) const {
|
||||
index_t n_features = this->n_features();
|
||||
string str;
|
||||
string::iterator pos;
|
||||
|
||||
is_done = false;
|
||||
|
||||
for (;;) {
|
||||
if (!reader.MoreLines()) {
|
||||
is_done = true;
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
|
||||
// str = reader->Peek();
|
||||
pos = reader.Peek().begin();
|
||||
|
||||
while (*pos == ' ' || *pos == '\t' || *pos == ',') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (unlikely(*pos == '\0' || *pos == '%')) {
|
||||
reader.Gobble();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (index_t i = 0; i < n_features; i++) {
|
||||
string::iterator next;
|
||||
|
||||
while (*pos == ' ' || *pos == '\t' || *pos == ',') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (unlikely(*pos == '\0')) {
|
||||
for (string::iterator s = reader.Peek().begin(); s < pos; s++) { // UNDEFINED
|
||||
if (!*s) {
|
||||
*s = ',';
|
||||
}
|
||||
}
|
||||
reader.Error("I am expecting %"LI"d entries per row, "
|
||||
"but this line has only %"LI"d.",
|
||||
n_features, i);
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
next = pos;
|
||||
while (*next != '\0' && *next != ' ' && *next != '\t' && *next != ','
|
||||
&& *next != '%') {
|
||||
next++;
|
||||
}
|
||||
|
||||
if (*next != '\0') {
|
||||
char c = *next;
|
||||
*next = '\0';
|
||||
if (c != '%') {
|
||||
next++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len = reader.Peek().end() - pos;
|
||||
size_t cpos = pos - reader.Peek().begin();
|
||||
if (!PASSED(features_[i].Parse(reader.Peek().substr(cpos, len), col[i]))) {
|
||||
string::iterator end = reader.Peek().end();
|
||||
string tmp;
|
||||
tmp.assign(pos, reader.Peek().end());
|
||||
for (string::iterator s = reader.Peek().begin(); s < next && s < end; s++) {
|
||||
if (*s == '\0') {
|
||||
*s = ',';
|
||||
}
|
||||
}
|
||||
reader.Error("Invalid parse: [%s]", tmp.c_str());
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
// increment the value we are looking at
|
||||
pos = next;
|
||||
}
|
||||
|
||||
while (*pos == ' ' || *pos == '\t' || *pos == ',') {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (*pos != '\0') {
|
||||
for (string::iterator s = reader.Peek().begin(); s < pos; s++) {
|
||||
if (*s == '\0') {
|
||||
*s = ',';
|
||||
}
|
||||
}
|
||||
reader.Error("Extra junk on line.");
|
||||
return SUCCESS_FAIL;
|
||||
}
|
||||
|
||||
reader.Gobble();
|
||||
|
||||
return SUCCESS_PASS;
|
||||
}
|
||||
|
||||
bool DatasetInfo::is_all_continuous() const {
|
||||
for (index_t i = 0; i < features_.size(); i++) {
|
||||
if (features_[i].type() != DatasetFeature::CONTINUOUS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DatasetInfo::WriteArffHeader(TextWriter& writer) const {
|
||||
writer.Printf("@relation %s\n", name_.c_str());
|
||||
|
||||
for (index_t i = 0; i < features_.size(); i++) {
|
||||
const DatasetFeature *feature = &features_[i];
|
||||
writer.Printf("@attribute %s ", feature->name().c_str());
|
||||
if (feature->type() == DatasetFeature::NOMINAL) {
|
||||
writer.Printf("{");
|
||||
for (index_t v = 0; v < feature->n_values(); v++) {
|
||||
if (v != 0) {
|
||||
writer.Write(",");
|
||||
}
|
||||
writer.Write(feature->value_name(v).c_str());
|
||||
}
|
||||
writer.Printf("}");
|
||||
} else {
|
||||
writer.Write("real");
|
||||
}
|
||||
writer.Write("\n");
|
||||
}
|
||||
writer.Printf("@data\n");
|
||||
}
|
||||
|
||||
void DatasetInfo::WriteCsvHeader(const char *sep, TextWriter& writer) const {
|
||||
for (index_t i = 0; i < features_.size(); i++) {
|
||||
if (i != 0) {
|
||||
writer.Write(sep);
|
||||
}
|
||||
writer.Write(features_[i].name().c_str());
|
||||
}
|
||||
writer.Write("\n");
|
||||
}
|
||||
|
||||
void DatasetInfo::WriteMatrix(const arma::mat& matrix, const char *sep,
|
||||
TextWriter& writer) const {
|
||||
for (index_t i = 0; i < matrix.n_cols; i++) {
|
||||
for (index_t f = 0; f < features_.size(); f++) {
|
||||
if (f != 0) {
|
||||
writer.Write(sep);
|
||||
}
|
||||
string str;
|
||||
features_[f].Format(matrix(f, i), str);
|
||||
writer.Write(str.c_str());
|
||||
}
|
||||
writer.Write("\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/* MLPACK 0.2
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Alexander Gray,
|
||||
* Garry Boyer,
|
||||
* Ryan Riegel,
|
||||
* Nikolaos Vasiloglou,
|
||||
* Dongryeol Lee,
|
||||
* Chip Mappus,
|
||||
* Nishant Mehta,
|
||||
* Hua Ouyang,
|
||||
* Parikshit Ram,
|
||||
* Long Tran,
|
||||
* Wee Chin Wong
|
||||
*
|
||||
* Copyright (c) 2008, 2009 Georgia Institute of Technology
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
/**
|
||||
* @file dataset_info.h
|
||||
*
|
||||
* Declaration of DatasetInfo class, which is a helper class for Dataset.
|
||||
*
|
||||
* @bug These routines fail when trying to read files linewise that use the Mac
|
||||
* eol '\r'. Both Windows and Unix eol ("\r\n" and '\n') work. Use the
|
||||
* programs 'dos2unix' or 'tr' to convert the '\r's to '\n's.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DATA_DATASET_INFO_H
|
||||
#define DATA_DATASET_INFO_H
|
||||
|
||||
#include <armadillo>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../la/matrix.h"
|
||||
#include "../math/discrete.h"
|
||||
#include "../file/textfile.h"
|
||||
|
||||
#include "dataset_feature.h"
|
||||
|
||||
class TextLineReader;
|
||||
class TextWriter;
|
||||
|
||||
/**
|
||||
* Information describing a dataset and its features.
|
||||
*/
|
||||
class DatasetInfo {
|
||||
private:
|
||||
std::string name_;
|
||||
std::vector<DatasetFeature> features_;
|
||||
|
||||
public:
|
||||
/** Gets a mutable list of all features. */
|
||||
std::vector<DatasetFeature>& features() {
|
||||
return features_;
|
||||
}
|
||||
|
||||
/** Gets information about a particular feature. */
|
||||
const DatasetFeature& feature(index_t attrib_num) const {
|
||||
return features_[attrib_num];
|
||||
}
|
||||
|
||||
/** Gets the number of features. */
|
||||
index_t n_features() const {
|
||||
return features_.size();
|
||||
}
|
||||
|
||||
/** Gets the title of the data set. */
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
/** Sets the title of the data set. */
|
||||
void set_name(const std::string& name_in) {
|
||||
name_ = name_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all parameters are continuous.
|
||||
*/
|
||||
bool is_all_continuous() const;
|
||||
|
||||
/**
|
||||
* Initialize an all-continuous dataset;
|
||||
*
|
||||
* @param n_features the number of continuous features
|
||||
* @param name_in the dataset title
|
||||
*/
|
||||
void InitContinuous(index_t n_features,
|
||||
const std::string& name_in = "dataset");
|
||||
|
||||
/**
|
||||
* Initialize a custom dataset.
|
||||
*
|
||||
* This assumes you will eventually use the features() to add features.
|
||||
*
|
||||
* @param name_in the dataset title
|
||||
*/
|
||||
void Init(const std::string& name_in = "dataset");
|
||||
|
||||
/**
|
||||
* Copy constructor, written manually to avoid use of OBJECT_TRAVERSAL functions.
|
||||
* TODO: Make this Init() crap go away.
|
||||
*/
|
||||
void InitCopy(const DatasetInfo &info) {
|
||||
name_ = info.name();
|
||||
features_ = info.features_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the header for an ARFF file.
|
||||
*/
|
||||
void WriteArffHeader(TextWriter& writer) const;
|
||||
|
||||
/**
|
||||
* Writes header for CSV file.
|
||||
*
|
||||
* @param sep the value separator (use ",\t" for CSV)
|
||||
* @param writer the text writer to write the header line to
|
||||
*/
|
||||
void WriteCsvHeader(const char *sep, TextWriter& writer) const;
|
||||
|
||||
/**
|
||||
* Writes the contents of a matrix to a file.
|
||||
*
|
||||
* @param matrix the matrix
|
||||
* @param sep the separator (use ",\t" for CSV)
|
||||
* @param writer the writer to write to
|
||||
*/
|
||||
void WriteMatrix(const arma::mat& matrix, const char *sep,
|
||||
TextWriter& writer) const;
|
||||
|
||||
/**
|
||||
* Initialize explicitly from an ARFF file.
|
||||
*
|
||||
* ARFF LIMITATIONS: Values cannot have spaces or commas, even with quotes;
|
||||
* 'string' data type not supported (nominal is supported).
|
||||
*
|
||||
* You might just use InitFromFile, which will guess the type for you.
|
||||
*
|
||||
* This will read only the header information and leave the reader at the
|
||||
* first line of data.
|
||||
*/
|
||||
success_t InitFromArff(TextLineReader& reader,
|
||||
const std::string& filename = "dataset");
|
||||
|
||||
/**
|
||||
* Initialize from a CSV-like file with numbers only, inferring
|
||||
* automatically that if the first row has non-numeric characters, it is
|
||||
* a header.
|
||||
*
|
||||
* InitFromFile will automatically detect this.
|
||||
*/
|
||||
success_t InitFromCsv(TextLineReader& reader,
|
||||
const std::string& filename = "dataset");
|
||||
|
||||
/**
|
||||
* Initializes the header from a file, either CSV or ARFF.
|
||||
*
|
||||
* All header lines will be gobbled, so the reader's position will be
|
||||
* left at the first line of actual data.
|
||||
* You can then read the data with matrix.
|
||||
*/
|
||||
success_t InitFromFile(TextLineReader& reader,
|
||||
const std::string& filename = "dataset");
|
||||
|
||||
/**
|
||||
* Populates a matrix from a file, given the internal data model.
|
||||
*
|
||||
* ARFF LIMITATIONS: Values cannot have spaces or commas, even with quotes;
|
||||
* 'string' data type not supported (nominal is supported).
|
||||
*
|
||||
* @param reader the reader to get lines from
|
||||
* @param matrix the matrix to store text into
|
||||
*/
|
||||
success_t ReadMatrix(TextLineReader& reader, arma::mat& matrix) const;
|
||||
|
||||
/**
|
||||
* Reads a single vector.
|
||||
*
|
||||
* @param reader the line reader being used
|
||||
* @param col a mat::col_iterator referencing the beginning of the
|
||||
* column that we are reading into
|
||||
* @param is_done set to true if we have finished reading the file
|
||||
* successfully -- the value of is_done is undefined if the
|
||||
* function returns failure!
|
||||
* @return whether reading the line was successful
|
||||
*/
|
||||
success_t ReadPoint(TextLineReader& reader, arma::mat::col_iterator col,
|
||||
bool& is_done) const;
|
||||
|
||||
private:
|
||||
index_t SkipSpace_(std::string& s);
|
||||
char *SkipNonspace_(char *s);
|
||||
void SkipBlanks_(TextLineReader& reader);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -29,23 +29,26 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
#include <armadillo>
|
||||
|
||||
#include "dataset.h"
|
||||
//#include "dataset.h"
|
||||
|
||||
#include "../math/discrete.h"
|
||||
#include "../base/test.h"
|
||||
|
||||
using arma::mat;
|
||||
|
||||
TEST_SUITE_BEGIN(dataset)
|
||||
|
||||
void TestSplitTrainTest() {
|
||||
Dataset orig;
|
||||
|
||||
orig.InitBlank();
|
||||
orig.matrix().Init(1, 12);
|
||||
orig.matrix().set_size(1, 12);
|
||||
orig.info().InitContinuous(1);
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
orig.matrix().set(0, i, i);
|
||||
orig.matrix()(0, i) = i;
|
||||
}
|
||||
|
||||
std::vector<int> found;
|
||||
@@ -61,7 +64,7 @@ void TestSplitTrainTest() {
|
||||
permutation.push_back(i);
|
||||
|
||||
orig.SplitTrainTest(5, 1, permutation,
|
||||
&train, &test);
|
||||
train, test);
|
||||
|
||||
DEBUG_ASSERT(test.n_points() == 3);
|
||||
DEBUG_ASSERT(train.n_points() == 9);
|
||||
@@ -80,17 +83,17 @@ void TestSplitTrainTest() {
|
||||
DEBUG_ASSERT_MSG(train.get(0, 8) == 10, "%f", (train.get(0, 8)));
|
||||
}
|
||||
|
||||
void AssertSameMatrix(const Matrix& a, const Matrix& b) {
|
||||
index_t r = a.n_rows();
|
||||
index_t c = a.n_cols();
|
||||
void AssertSameMatrix(const mat& a, const mat& b) {
|
||||
index_t r = a.n_rows;
|
||||
index_t c = a.n_cols;
|
||||
|
||||
TEST_ASSERT(a.n_rows() == b.n_rows());
|
||||
TEST_ASSERT(a.n_cols() == b.n_cols());
|
||||
TEST_ASSERT(a.n_rows == b.n_rows);
|
||||
TEST_ASSERT(a.n_cols == b.n_cols);
|
||||
|
||||
for (index_t ri = 0; ri < r; ri++) {
|
||||
for (index_t ci = 0; ci < c; ci++) {
|
||||
DEBUG_ASSERT_MSG(a.get(ri, ci) == b.get(ri, ci), "(%d, %d): %f != %f",
|
||||
ri, ci, a.get(ri, ci), b.get(ri, ci));
|
||||
DEBUG_ASSERT_MSG(a(ri, ci) == b(ri, ci), "(%d, %d): %f != %f",
|
||||
ri, ci, a(ri, ci), b(ri, ci));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,8 +132,8 @@ void TestStoreLoad() {
|
||||
AssertSameMatrix(d1.matrix(), d2.matrix());
|
||||
AssertSameMatrix(d1.matrix(), d3.matrix());
|
||||
|
||||
DEBUG_ASSERT_MSG(strcmp(d1.info().name(), d2.info().name()) == 0,
|
||||
"%s != %s", d1.info().name(), d2.info().name());
|
||||
DEBUG_ASSERT_MSG(strcmp(d1.info().name().c_str(), d2.info().name().c_str()) == 0,
|
||||
"%s != %s", d1.info().name().c_str(), d2.info().name().c_str());
|
||||
for (index_t i = 0; i < d1.info().n_features(); i++) {
|
||||
DEBUG_ASSERT( d1.info().feature(i).name() == d2.info().feature(i).name() );
|
||||
DEBUG_ASSERT(d1.info().feature(i).type() == d2.info().feature(i).type());
|
||||
|
||||
Reference in New Issue
Block a user