Compare commits

47 Commits

Author SHA1 Message Date
ba455bb934 Rename config.h to config_platform.h 2024-12-13 19:57:05 +01:00
a65955248a Add mdlp as dependency 2024-12-13 10:28:27 +01:00
84930b0537 Remove lib/mdlp folder 2024-12-13 10:11:45 +01:00
10c65f44a0 Add mdlp library dependency 2024-12-13 09:55:37 +01:00
6d112f01e7 Remove external library dependency 2024-12-13 09:49:46 +01:00
401296293b Add header to b_main time 2024-12-11 23:18:20 +01:00
9566ae4cf6 Fix gridsearch discretize_algo mistake 2024-12-11 12:45:16 +01:00
55187ee521 Add time to experiment seed 2024-12-11 10:05:24 +01:00
68ea06d129 Fix fimdlp library includes 2024-11-20 21:19:35 +01:00
6c1d1d0d32 Remove mdlp files 2024-11-20 21:14:42 +01:00
b0853d169b Remove mdlp submodule 2024-11-20 21:14:19 +01:00
26f8e07774 Remove Python 3.11 only requirement 2024-11-20 20:21:39 +01:00
315dfb104f Add train test time to report console 2024-10-25 09:53:31 +02:00
381f226d53 Fix pm code in tex bestresults 2024-10-15 10:32:28 +02:00
ea13835701 Add Markdown best results output 2024-10-07 18:08:42 +02:00
d75468cf78 Replace Nº with # in output labels 2024-09-28 22:55:11 +02:00
c58bd9d60d add score name to best results excel file name 2024-09-28 18:58:49 +02:00
148a3b831a Add missing \ to results.tex 2024-09-03 12:57:22 +02:00
69063badbb Fix status error in holm.tex 2024-09-03 12:54:09 +02:00
6ae2b2182a Complete Tex output with Holm test 2024-09-03 12:43:50 +02:00
4dbd76df55 Continue TeX output 2024-09-02 20:30:47 +02:00
4545f76667 Begin adding TeX output to b_best -m any command 2024-09-02 18:14:53 +02:00
8372987dae Update sample to last library version 2024-08-31 12:41:11 +02:00
d72943c749 Fix hyperparams mistake 2024-08-07 10:52:04 +02:00
800246acd2 Accept nested hyperparameters in b_main 2024-08-04 17:19:31 +02:00
0ea967dd9d Support b_main with best hyperparameters 2024-08-02 19:10:25 +02:00
97abec8b69 Fix hide result error 2024-08-02 12:02:11 +02:00
17c9522e77 Add support to old format results 2024-07-25 17:06:31 +02:00
45af550cf9 Change time showed in report 2024-07-24 18:40:59 +02:00
5d5f49777e Fix wrong columns message 2024-07-16 11:30:28 +02:00
540a8ea06d Refactor update rows 2024-07-16 10:33:44 +02:00
1924c4392b Adapt screen to resized window 2024-07-16 10:25:15 +02:00
f2556a30af Add screen width control in b_manage 2024-07-15 18:06:39 +02:00
2f2ed00ca1 Add roc-auc-ovr as score to b_main 2024-07-14 12:48:33 +02:00
28f6a0d7a7 RocAuc refactor to speed up binary classif. problems 2024-07-13 16:54:34 +02:00
028522f180 Add AUC to reportConsole 2024-07-12 17:41:23 +02:00
84adf13a79 Add AUC computing in Experiment and store in result 2024-07-12 17:23:03 +02:00
26dfe6d056 Add Graphs to results
Add bin5..bin10 q & u discretizers algos
Fix trouble in computing states
Update mdlp to 2.0.0
2024-07-11 11:23:20 +02:00
3acc34e4c6 Fix title mistake in b_main 2024-06-17 19:07:15 +02:00
8f92b74260 Change Constant smooth type 2024-06-14 10:16:32 +02:00
3d900f8c81 Update models versions 2024-06-13 12:30:31 +02:00
e628d80f4c Experiment working with smoothing and disc-algo 2024-06-11 13:52:26 +02:00
0f06f8971e Change default smooth type in Experiment 2024-06-10 15:50:54 +02:00
f800772149 Add new hyperparameters validation in b_main 2024-06-10 10:16:07 +02:00
b8a8ddaf8c Add smooth strategy to hyperparameter in b_main
Add smooth strategy to reports
2024-06-09 20:46:14 +02:00
90555489ff Add discretiz_algo to b_main as hyperparameter 2024-06-09 11:35:50 +02:00
080f3cee34 Add discretization algo to reports 2024-06-09 01:11:56 +02:00
67 changed files with 1196 additions and 354 deletions

7
.gitmodules vendored
View File

@@ -10,13 +10,12 @@
[submodule "lib/libxlsxwriter"]
path = lib/libxlsxwriter
url = https://github.com/jmcnamara/libxlsxwriter.git
[submodule "lib/mdlp"]
path = lib/mdlp
url = https://github.com/rmontanana/mdlp
update = merge
[submodule "lib/folding"]
path = lib/folding
url = https://github.com/rmontanana/folding
[submodule "lib/Files"]
path = lib/Files
url = https://github.com/rmontanana/ArffFiles
[submodule "lib/mdlp"]
path = lib/mdlp
url = https://github.com/rmontanana/mdlp

View File

@@ -21,7 +21,7 @@ set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast")
set(CMAKE_CXX_FLAGS_DEBUG " ${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -O0 -g")
# Options
@@ -46,7 +46,7 @@ if(Boost_FOUND)
endif()
# Python
find_package(Python3 3.11...3.11.9 COMPONENTS Interpreter Development REQUIRED)
find_package(Python3 3.11 COMPONENTS Interpreter Development REQUIRED)
message("Python3_LIBRARIES=${Python3_LIBRARIES}")
# CMakes modules
@@ -90,7 +90,7 @@ cmake_path(SET TEST_DATA_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests/data")
configure_file(src/common/SourceData.h.in "${CMAKE_BINARY_DIR}/configured_files/include/SourceData.h")
add_subdirectory(config)
add_subdirectory(src)
add_subdirectory(sample)
# add_subdirectory(sample)
file(GLOB Platform_SOURCES CONFIGURE_DEPENDS ${Platform_SOURCE_DIR}/src/*.cpp)
# Testing

View File

@@ -6,7 +6,6 @@ f_release = build_release
f_debug = build_debug
app_targets = b_best b_list b_main b_manage b_grid
test_targets = unit_tests_platform
n_procs = -j 16
define ClearTests
@for t in $(test_targets); do \
@@ -56,10 +55,10 @@ dependency: ## Create a dependency graph diagram of the project (build/dependenc
cd $(f_debug) && cmake .. --graphviz=dependency.dot && dot -Tpng dependency.dot -o dependency.png
buildd: ## Build the debug targets
cmake --build $(f_debug) -t $(app_targets) PlatformSample $(n_procs)
cmake --build $(f_debug) -t $(app_targets) PlatformSample --parallel
buildr: ## Build the release targets
cmake --build $(f_release) -t $(app_targets) $(n_procs)
cmake --build $(f_release) -t $(app_targets) --parallel
clean: ## Clean the tests info
@echo ">>> Cleaning Debug Platform tests...";
@@ -87,7 +86,7 @@ opt = ""
test: ## Run tests (opt="-s") to verbose output the tests, (opt="-c='Test Maximum Spanning Tree'") to run only that section
@echo ">>> Running Platform tests...";
@$(MAKE) clean
@cmake --build $(f_debug) -t $(test_targets) $(n_procs)
@cmake --build $(f_debug) -t $(test_targets) --parallel
@for t in $(test_targets); do \
if [ -f $(f_debug)/tests/$$t ]; then \
cd $(f_debug)/tests ; \

View File

@@ -20,11 +20,18 @@ In Linux sometimes the library libstdc++ is mistaken from the miniconda installa
libstdc++.so.6: version `GLIBCXX_3.4.32' not found (required by b_xxxx)
```
The solution is to erase the libstdc++ library from the miniconda installation:
The solution is to erase the libstdc++ library from the miniconda installation and no further compilation is needed.
### MPI
In Linux just install openmpi & openmpi-devel packages. Only if cmake can't find openmpi installation (like in Oracle Linux) set the following variable:
In Linux just install openmpi & openmpi-devel packages.
```bash
source /etc/profile.d/modules.sh
module load mpi/openmpi-x86_64
```
If cmake can't find openmpi installation (like in Oracle Linux) set the following variable:
```bash
export MPI_HOME="/usr/lib64/openmpi"

View File

@@ -1,4 +1,4 @@
configure_file(
"config.h.in"
"${CMAKE_BINARY_DIR}/configured_files/include/config.h" ESCAPE_QUOTES
"${CMAKE_BINARY_DIR}/configured_files/include/config_platform.h" ESCAPE_QUOTES
)

View File

@@ -1,8 +1,3 @@
[submodule "lib/mdlp"]
path = lib/mdlp
url = https://github.com/rmontanana/mdlp
main = main
update = merge
[submodule "lib/catch2"]
path = lib/catch2
main = v2.x

View File

@@ -3,7 +3,7 @@ include_directories(
${Platform_SOURCE_DIR}/src/main
${Python3_INCLUDE_DIRS}
${Platform_SOURCE_DIR}/lib/Files
${Platform_SOURCE_DIR}/lib/mdlp
${Platform_SOURCE_DIR}/lib/mdlp/src
${Platform_SOURCE_DIR}/lib/argparse/include
${Platform_SOURCE_DIR}/lib/folding
${Platform_SOURCE_DIR}/lib/json/include
@@ -12,4 +12,4 @@ include_directories(
${Bayesnet_INCLUDE_DIRS}
)
add_executable(PlatformSample sample.cpp ${Platform_SOURCE_DIR}/src/main/Models.cpp)
target_link_libraries(PlatformSample "${PyClassifiers}" "${BayesNet}" mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy)
target_link_libraries(PlatformSample "${PyClassifiers}" "${BayesNet}" fimdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy)

View File

@@ -6,12 +6,12 @@
#include <argparse/argparse.hpp>
#include <nlohmann/json.hpp>
#include <ArffFiles.hpp>
#include <CPPFImdlp.h>
#include <fimdlp/CPPFImdlp.h>
#include <folding.hpp>
#include <bayesnet/utils/BayesMetrics.h>
#include "Models.h"
#include "modelRegister.h"
#include "config.h"
#include "config_platform.h"
const std::string PATH = { platform_data_path.begin(), platform_data_path.end() };
@@ -161,7 +161,8 @@ int main(int argc, char** argv)
}
states[className] = std::vector<int>(maxes[className]);
auto clf = platform::Models::instance()->create(model_name);
clf->fit(Xd, y, features, className, states);
bayesnet::Smoothing_t smoothing = bayesnet::Smoothing_t::ORIGINAL;
clf->fit(Xd, y, features, className, states, smoothing);
if (dump_cpt) {
std::cout << "--- CPT Tables ---" << std::endl;
clf->dump_cpt();
@@ -210,14 +211,14 @@ int main(int argc, char** argv)
torch::Tensor ytraint = yt.index({ ttrain });
torch::Tensor Xtestt = torch::index_select(Xt, 1, ttest);
torch::Tensor ytestt = yt.index({ ttest });
clf->fit(Xtraint, ytraint, features, className, states);
clf->fit(Xtraint, ytraint, features, className, states, smoothing);
auto temp = clf->predict(Xtraint);
score_train = clf->score(Xtraint, ytraint);
score_test = clf->score(Xtestt, ytestt);
} else {
auto [Xtrain, ytrain] = extract_indices(train, Xd, y);
auto [Xtest, ytest] = extract_indices(test, Xd, y);
clf->fit(Xtrain, ytrain, features, className, states);
clf->fit(Xtrain, ytrain, features, className, states, smoothing);
std::cout << "Nodes: " << clf->getNumberOfNodes() << std::endl;
nodes += clf->getNumberOfNodes();
score_train = clf->score(Xtrain, ytrain);

View File

@@ -2,7 +2,7 @@ include_directories(
## Libs
${Platform_SOURCE_DIR}/lib/Files
${Platform_SOURCE_DIR}/lib/folding
${Platform_SOURCE_DIR}/lib/mdlp
${Platform_SOURCE_DIR}/lib/mdlp/src
${Platform_SOURCE_DIR}/lib/argparse/include
${Platform_SOURCE_DIR}/lib/json/include
${Platform_SOURCE_DIR}/lib/libxlsxwriter/include
@@ -20,13 +20,13 @@ include_directories(
# b_best
add_executable(
b_best commands/b_best.cpp best/Statistics.cpp
best/BestResultsExcel.cpp best/BestResults.cpp
best/BestResultsExcel.cpp best/BestResultsTex.cpp best/BestResultsMd.cpp best/BestResults.cpp
common/Datasets.cpp common/Dataset.cpp common/Discretization.cpp
main/Models.cpp main/Scores.cpp
reports/ReportExcel.cpp reports/ReportBase.cpp reports/ExcelFile.cpp
results/Result.cpp
)
target_link_libraries(b_best Boost::boost "${PyClassifiers}" "${BayesNet}" mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy "${XLSXWRITER_LIB}")
target_link_libraries(b_best Boost::boost "${PyClassifiers}" "${BayesNet}" fimdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy "${XLSXWRITER_LIB}")
# b_grid
set(grid_sources GridSearch.cpp GridData.cpp)
@@ -35,7 +35,7 @@ add_executable(b_grid commands/b_grid.cpp ${grid_sources}
common/Datasets.cpp common/Dataset.cpp common/Discretization.cpp
main/HyperParameters.cpp main/Models.cpp
)
target_link_libraries(b_grid ${MPI_CXX_LIBRARIES} "${PyClassifiers}" "${BayesNet}" mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy)
target_link_libraries(b_grid ${MPI_CXX_LIBRARIES} "${PyClassifiers}" "${BayesNet}" fimdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy)
# b_list
add_executable(b_list commands/b_list.cpp
@@ -44,7 +44,7 @@ add_executable(b_list commands/b_list.cpp
reports/ReportExcel.cpp reports/ExcelFile.cpp reports/ReportBase.cpp reports/DatasetsExcel.cpp reports/DatasetsConsole.cpp reports/ReportsPaged.cpp
results/Result.cpp results/ResultsDatasetExcel.cpp results/ResultsDataset.cpp results/ResultsDatasetConsole.cpp
)
target_link_libraries(b_list "${PyClassifiers}" "${BayesNet}" mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy "${XLSXWRITER_LIB}")
target_link_libraries(b_list "${PyClassifiers}" "${BayesNet}" fimdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy "${XLSXWRITER_LIB}")
# b_main
set(main_sources Experiment.cpp Models.cpp HyperParameters.cpp Scores.cpp)
@@ -54,10 +54,10 @@ add_executable(b_main commands/b_main.cpp ${main_sources}
reports/ReportConsole.cpp reports/ReportBase.cpp
results/Result.cpp
)
target_link_libraries(b_main "${PyClassifiers}" "${BayesNet}" mdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy)
target_link_libraries(b_main "${PyClassifiers}" "${BayesNet}" fimdlp ${Python3_LIBRARIES} "${TORCH_LIBRARIES}" ${LIBTORCH_PYTHON} Boost::python Boost::numpy)
# b_manage
set(manage_sources ManageScreen.cpp CommandParser.cpp ResultsManager.cpp)
set(manage_sources ManageScreen.cpp OptionsMenu.cpp ResultsManager.cpp)
list(TRANSFORM manage_sources PREPEND manage/)
add_executable(
b_manage commands/b_manage.cpp ${manage_sources}
@@ -66,4 +66,4 @@ add_executable(
results/Result.cpp results/ResultsDataset.cpp results/ResultsDatasetConsole.cpp
main/Scores.cpp
)
target_link_libraries(b_manage "${TORCH_LIBRARIES}" "${XLSXWRITER_LIB}" mdlp "${BayesNet}")
target_link_libraries(b_manage "${TORCH_LIBRARIES}" "${XLSXWRITER_LIB}" fimdlp "${BayesNet}")

View File

@@ -6,8 +6,12 @@
#include <algorithm>
#include "common/Colors.h"
#include "common/CLocale.h"
#include "common/Paths.h"
#include "common/Utils.h" // compute_std
#include "results/Result.h"
#include "BestResultsExcel.h"
#include "BestResultsTex.h"
#include "BestResultsMd.h"
#include "best/Statistics.h"
#include "BestResults.h"
@@ -51,7 +55,7 @@ namespace platform {
}
}
if (update) {
bests[datasetName] = { item.at("score").get<double>(), item.at("hyperparameters"), file };
bests[datasetName] = { item.at("score").get<double>(), item.at("hyperparameters"), file, item.at("score_std").get<double>() };
}
}
}
@@ -59,16 +63,12 @@ namespace platform {
std::cerr << Colors::MAGENTA() << "No results found for model " << model << " and score " << score << Colors::RESET() << std::endl;
exit(1);
}
std::string bestFileName = path + bestResultFile();
std::string bestFileName = path + Paths::bestResultsFile(score, model);
std::ofstream file(bestFileName);
file << bests;
file.close();
return bestFileName;
}
std::string BestResults::bestResultFile()
{
return "best_results_" + score + "_" + model + ".json";
}
std::pair<std::string, std::string> getModelScore(std::string name)
{
// results_accuracy_BoostAODE_MacBookpro16_2023-09-06_12:27:00_1.json
@@ -150,7 +150,7 @@ namespace platform {
}
void BestResults::listFile()
{
std::string bestFileName = path + bestResultFile();
std::string bestFileName = path + Paths::bestResultsFile(score, model);
if (FILE* fileTest = fopen(bestFileName.c_str(), "r")) {
fclose(fileTest);
} else {
@@ -196,7 +196,7 @@ namespace platform {
auto maxDate = std::filesystem::file_time_type::max();
for (const auto& model : models) {
this->model = model;
std::string bestFileName = path + bestResultFile();
std::string bestFileName = path + Paths::bestResultsFile(score, model);
if (FILE* fileTest = fopen(bestFileName.c_str(), "r")) {
fclose(fileTest);
} else {
@@ -213,13 +213,20 @@ namespace platform {
table["dateTable"] = ftime_to_string(maxDate);
return table;
}
void BestResults::printTableResults(std::vector<std::string> models, json table)
void BestResults::printTableResults(std::vector<std::string> models, json table, bool tex)
{
std::stringstream oss;
oss << Colors::GREEN() << "Best results for " << score << " as of " << table.at("dateTable").get<std::string>() << std::endl;
std::cout << oss.str();
std::cout << std::string(oss.str().size() - 8, '-') << std::endl;
std::cout << Colors::GREEN() << " # " << std::setw(maxDatasetName + 1) << std::left << std::string("Dataset");
auto bestResultsTex = BestResultsTex();
auto bestResultsMd = BestResultsMd();
if (tex) {
bestResultsTex.results_header(models, table.at("dateTable").get<std::string>());
bestResultsMd.results_header(models, table.at("dateTable").get<std::string>());
}
for (const auto& model : models) {
std::cout << std::setw(maxModelName) << std::left << model << " ";
}
@@ -230,12 +237,13 @@ namespace platform {
}
std::cout << std::endl;
auto i = 0;
std::map<std::string, double> totals;
std::map<std::string, std::vector<double>> totals;
int nDatasets = table.begin().value().size();
for (const auto& model : models) {
totals[model] = 0.0;
}
auto datasets = getDatasets(table.begin().value());
if (tex) {
bestResultsTex.results_body(datasets, table);
bestResultsMd.results_body(datasets, table);
}
for (auto const& dataset_ : datasets) {
auto color = (i % 2) ? Colors::BLUE() : Colors::CYAN();
std::cout << color << std::setw(3) << std::fixed << std::right << i++ << " ";
@@ -270,7 +278,7 @@ namespace platform {
if (value == -1) {
std::cout << Colors::YELLOW() << std::setw(maxModelName) << std::right << "N/A" << " ";
} else {
totals[model] += value;
totals[model].push_back(value);
std::cout << efectiveColor << std::setw(maxModelName) << std::setprecision(maxModelName - 2) << std::fixed << value << " ";
}
}
@@ -281,19 +289,26 @@ namespace platform {
std::cout << std::string(maxModelName, '=') << " ";
}
std::cout << std::endl;
std::cout << Colors::GREEN() << " Totals" << std::string(maxDatasetName - 6, '.') << " ";
std::cout << Colors::GREEN() << " Average" << std::string(maxDatasetName - 7, '.') << " ";
double max_value = 0.0;
std::string best_model = "";
for (const auto& total : totals) {
if (total.second > max_value) {
max_value = total.second;
auto actual = std::reduce(total.second.begin(), total.second.end());
if (actual > max_value) {
max_value = actual;
best_model = total.first;
}
}
if (tex) {
bestResultsTex.results_footer(totals, best_model);
bestResultsMd.results_footer(totals, best_model);
}
for (const auto& model : models) {
std::string efectiveColor = Colors::GREEN();
if (totals[model] == max_value) {
efectiveColor = Colors::RED();
}
std::cout << efectiveColor << std::right << std::setw(maxModelName) << std::setprecision(maxModelName - 4) << std::fixed << totals[model] << " ";
std::string efectiveColor = model == best_model ? Colors::RED() : Colors::GREEN();
double value = std::reduce(totals[model].begin(), totals[model].end()) / nDatasets;
double std_value = compute_std(totals[model], value);
std::cout << efectiveColor << std::right << std::setw(maxModelName) << std::setprecision(maxModelName - 4) << std::fixed << value << " ";
}
std::cout << std::endl;
}
@@ -306,26 +321,34 @@ namespace platform {
json table = buildTableResults(models);
std::vector<std::string> datasets = getDatasets(table.begin().value());
BestResultsExcel excel_report(score, datasets);
excel_report.reportSingle(model, path + bestResultFile());
messageExcelFile(excel_report.getFileName());
excel_report.reportSingle(model, path + Paths::bestResultsFile(score, model));
messageOutputFile("Excel", excel_report.getFileName());
}
}
void BestResults::reportAll(bool excel)
void BestResults::reportAll(bool excel, bool tex)
{
auto models = getModels();
// Build the table of results
json table = buildTableResults(models);
std::vector<std::string> datasets = getDatasets(table.begin().value());
// Print the table of results
printTableResults(models, table);
printTableResults(models, table, tex);
// Compute the Friedman test
std::map<std::string, std::map<std::string, float>> ranksModels;
if (friedman) {
Statistics stats(models, datasets, table, significance);
auto result = stats.friedmanTest();
stats.postHocHolmTest(result);
stats.postHocHolmTest(result, tex);
ranksModels = stats.getRanks();
}
if (tex) {
messageOutputFile("TeX", Paths::tex() + Paths::tex_output());
messageOutputFile("MarkDown", Paths::tex() + Paths::md_output());
if (friedman) {
messageOutputFile("TeX", Paths::tex() + Paths::tex_post_hoc());
messageOutputFile("MarkDown", Paths::tex() + Paths::md_post_hoc());
}
}
if (excel) {
BestResultsExcel excel(score, datasets);
excel.reportAll(models, table, ranksModels, friedman, significance);
@@ -346,13 +369,14 @@ namespace platform {
}
}
model = models.at(idx);
excel.reportSingle(model, path + bestResultFile());
excel.reportSingle(model, path + Paths::bestResultsFile(score, model));
}
messageExcelFile(excel.getFileName());
messageOutputFile("Excel", excel.getFileName());
}
}
void BestResults::messageExcelFile(const std::string& fileName)
void BestResults::messageOutputFile(const std::string& title, const std::string& fileName)
{
std::cout << Colors::YELLOW() << "** Excel file generated: " << fileName << Colors::RESET() << std::endl;
std::cout << Colors::YELLOW() << "** " << std::setw(5) << std::left << title
<< " file generated: " << fileName << Colors::RESET() << std::endl;
}
}

View File

@@ -13,16 +13,15 @@ namespace platform {
}
std::string build();
void reportSingle(bool excel);
void reportAll(bool excel);
void reportAll(bool excel, bool tex);
void buildAll();
private:
std::vector<std::string> getModels();
std::vector<std::string> getDatasets(json table);
std::vector<std::string> loadResultFiles();
void messageExcelFile(const std::string& fileName);
void messageOutputFile(const std::string& title, const std::string& fileName);
json buildTableResults(std::vector<std::string> models);
void printTableResults(std::vector<std::string> models, json table);
std::string bestResultFile();
void printTableResults(std::vector<std::string> models, json table, bool tex);
json loadFile(const std::string& fileName);
void listFile();
std::string path;

View File

@@ -32,7 +32,7 @@ namespace platform {
}
BestResultsExcel::BestResultsExcel(const std::string& score, const std::vector<std::string>& datasets) : score(score), datasets(datasets)
{
file_name = "BestResults.xlsx";
file_name = Paths::bestResultsExcel(score);
workbook = workbook_new(getFileName().c_str());
setProperties("Best Results");
int maxDatasetName = (*max_element(datasets.begin(), datasets.end(), [](const std::string& a, const std::string& b) { return a.size() < b.size(); })).size();
@@ -68,7 +68,7 @@ namespace platform {
// Body header
row = 3;
int col = 1;
writeString(row, 0, "", "bodyHeader");
writeString(row, 0, "#", "bodyHeader");
writeString(row, 1, "Dataset", "bodyHeader");
writeString(row, 2, "Score", "bodyHeader");
writeString(row, 3, "File", "bodyHeader");
@@ -184,7 +184,7 @@ namespace platform {
// Body header
row = 3;
int col = 1;
writeString(row, 0, "", "bodyHeader");
writeString(row, 0, "#", "bodyHeader");
writeString(row, 1, "Dataset", "bodyHeader");
for (const auto& model : models) {
writeString(row, ++col, model.c_str(), "bodyHeader");

103
src/best/BestResultsMd.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include <iostream>
#include "BestResultsMd.h"
#include "common/Utils.h" // compute_std
namespace platform {
using json = nlohmann::ordered_json;
void BestResultsMd::openMdFile(const std::string& name)
{
handler.open(name);
if (!handler.is_open()) {
std::cerr << "Error opening file " << name << std::endl;
exit(1);
}
}
void BestResultsMd::results_header(const std::vector<std::string>& models, const std::string& date)
{
this->models = models;
auto file_name = Paths::tex() + Paths::md_output();
openMdFile(file_name);
handler << "<!-- This file has been generated by the platform program" << std::endl;
handler << " Date: " << date.c_str() << std::endl;
handler << "" << std::endl;
handler << " Table of results" << std::endl;
handler << "-->" << std::endl;
handler << "| # | Dataset |";
for (const auto& model : models) {
handler << " " << model.c_str() << " |";
}
handler << std::endl;
handler << "|--: | :--- |";
for (const auto& model : models) {
handler << " :---: |";
}
handler << std::endl;
}
void BestResultsMd::results_body(const std::vector<std::string>& datasets, json& table)
{
int i = 0;
for (auto const& dataset : datasets) {
// Find out max value for this dataset
double max_value = 0;
// Find out the max value for this dataset
for (const auto& model : models) {
double value;
try {
value = table[model].at(dataset).at(0).get<double>();
}
catch (nlohmann::json_abi_v3_11_3::detail::out_of_range err) {
value = -1.0;
}
if (value > max_value) {
max_value = value;
}
}
handler << "| " << ++i << " | " << dataset.c_str() << " | ";
for (const auto& model : models) {
double value = table[model].at(dataset).at(0).get<double>();
double std_value = table[model].at(dataset).at(3).get<double>();
const char* bold = value == max_value ? "**" : "";
handler << bold << std::setprecision(4) << std::fixed << value << "±" << std::setprecision(3) << std_value << bold << " | ";
}
handler << std::endl;
}
}
void BestResultsMd::results_footer(const std::map<std::string, std::vector<double>>& totals, const std::string& best_model)
{
handler << "| | **Average Score** | ";
int nDatasets = totals.begin()->second.size();
for (const auto& model : models) {
double value = std::reduce(totals.at(model).begin(), totals.at(model).end()) / nDatasets;
double std_value = compute_std(totals.at(model), value);
const char* bold = model == best_model ? "**" : "";
handler << bold << std::setprecision(4) << std::fixed << value << "±" << std::setprecision(3) << std::fixed << std_value << bold << " | ";
}
handler.close();
}
void BestResultsMd::holm_test(struct HolmResult& holmResult, const std::string& date)
{
auto file_name = Paths::tex() + Paths::md_post_hoc();
openMdFile(file_name);
handler << "<!-- This file has been generated by the platform program" << std::endl;
handler << " Date: " << date.c_str() << std::endl;
handler << std::endl;
handler << " Post-hoc handler test" << std::endl;
handler << "-->" << std::endl;
handler << "Post-hoc Holm test: H<sub>0</sub>: There is no significant differences between the control model and the other models." << std::endl << std::endl;
handler << "| classifier | pvalue | rank | win | tie | loss | H<sub>0</sub> |" << std::endl;
handler << "| :-- | --: | --: | --:| --: | --: | :--: |" << std::endl;
for (auto const& line : holmResult.holmLines) {
auto textStatus = !line.reject ? "**" : " ";
if (line.model == holmResult.model) {
handler << "| " << line.model << " | - | " << std::fixed << std::setprecision(2) << line.rank << " | - | - | - |" << std::endl;
} else {
handler << "| " << line.model << " | " << textStatus << std::scientific << std::setprecision(4) << line.pvalue << textStatus << " |";
handler << std::fixed << std::setprecision(2) << line.rank << " | " << line.wtl.win << " | " << line.wtl.tie << " | " << line.wtl.loss << " |";
handler << (line.reject ? "rejected" : "**accepted**") << " |" << std::endl;
}
}
handler << std::endl;
handler.close();
}
}

24
src/best/BestResultsMd.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef BEST_RESULTS_MD_H
#define BEST_RESULTS_MD_H
#include <map>
#include <vector>
#include <nlohmann/json.hpp>
#include "common/Paths.h"
#include "Statistics.h"
namespace platform {
using json = nlohmann::ordered_json;
class BestResultsMd {
public:
BestResultsMd() = default;
~BestResultsMd() = default;
void results_header(const std::vector<std::string>& models, const std::string& date);
void results_body(const std::vector<std::string>& datasets, json& table);
void results_footer(const std::map<std::string, std::vector<double>>& totals, const std::string& best_model);
void holm_test(struct HolmResult& holmResult, const std::string& date);
private:
void openMdFile(const std::string& name);
std::ofstream handler;
std::vector<std::string> models;
};
}
#endif

117
src/best/BestResultsTex.cpp Normal file
View File

@@ -0,0 +1,117 @@
#include <iostream>
#include "BestResultsTex.h"
#include "common/Utils.h" // compute_std
namespace platform {
using json = nlohmann::ordered_json;
void BestResultsTex::openTexFile(const std::string& name)
{
handler.open(name);
if (!handler.is_open()) {
std::cerr << "Error opening file " << name << std::endl;
exit(1);
}
}
void BestResultsTex::results_header(const std::vector<std::string>& models, const std::string& date)
{
this->models = models;
auto file_name = Paths::tex() + Paths::tex_output();
openTexFile(file_name);
handler << "%% This file has been generated by the platform program" << std::endl;
handler << "%% Date: " << date.c_str() << std::endl;
handler << "%%" << std::endl;
handler << "%% Table of results" << std::endl;
handler << "%%" << std::endl;
handler << "\\begin{table}[htbp] " << std::endl;
handler << "\\centering " << std::endl;
handler << "\\tiny " << std::endl;
handler << "\\renewcommand{\\arraystretch }{1.2} " << std::endl;
handler << "\\renewcommand{\\tabcolsep }{0.07cm} " << std::endl;
handler << "\\caption{Accuracy results(mean $\\pm$ std) for all the algorithms and datasets} " << std::endl;
handler << "\\label{tab:results_accuracy}" << std::endl;
handler << "\\begin{tabular} {{r" << std::string(models.size(), 'c').c_str() << "}}" << std::endl;
handler << "\\hline " << std::endl;
handler << "" << std::endl;
for (const auto& model : models) {
handler << "& " << model.c_str();
}
handler << "\\\\" << std::endl;
handler << "\\hline" << std::endl;
}
void BestResultsTex::results_body(const std::vector<std::string>& datasets, json& table)
{
int i = 0;
for (auto const& dataset : datasets) {
// Find out max value for this dataset
double max_value = 0;
// Find out the max value for this dataset
for (const auto& model : models) {
double value;
try {
value = table[model].at(dataset).at(0).get<double>();
}
catch (nlohmann::json_abi_v3_11_3::detail::out_of_range err) {
value = -1.0;
}
if (value > max_value) {
max_value = value;
}
}
handler << ++i << " ";
for (const auto& model : models) {
double value = table[model].at(dataset).at(0).get<double>();
double std_value = table[model].at(dataset).at(3).get<double>();
const char* bold = value == max_value ? "\\bfseries" : "";
handler << "& " << bold << std::setprecision(4) << std::fixed << value << "$\\pm$" << std::setprecision(3) << std_value;
}
handler << "\\\\" << std::endl;
}
}
void BestResultsTex::results_footer(const std::map<std::string, std::vector<double>>& totals, const std::string& best_model)
{
handler << "\\hline" << std::endl;
handler << "Average ";
int nDatasets = totals.begin()->second.size();
for (const auto& model : models) {
double value = std::reduce(totals.at(model).begin(), totals.at(model).end()) / nDatasets;
double std_value = compute_std(totals.at(model), value);
const char* bold = model == best_model ? "\\bfseries" : "";
handler << "& " << bold << std::setprecision(4) << std::fixed << value << "$\\pm$" << std::setprecision(3) << std::fixed << std_value;
}
handler << "\\\\" << std::endl;
handler << "\\hline " << std::endl;
handler << "\\end{tabular}" << std::endl;
handler << "\\end{table}" << std::endl;
handler.close();
}
void BestResultsTex::holm_test(struct HolmResult& holmResult, const std::string& date)
{
auto file_name = Paths::tex() + Paths::tex_post_hoc();
openTexFile(file_name);
handler << "%% This file has been generated by the platform program" << std::endl;
handler << "%% Date: " << date.c_str() << std::endl;
handler << "%%" << std::endl;
handler << "%% Post-hoc handler test" << std::endl;
handler << "%%" << std::endl;
handler << "\\begin{table}[htbp]" << std::endl;
handler << "\\centering" << std::endl;
handler << "\\caption{Results of the post-hoc test for the mean accuracy of the algorithms.}\\label{tab:tests}" << std::endl;
handler << "\\begin{tabular}{lrrrrr}" << std::endl;
handler << "\\hline" << std::endl;
handler << "classifier & pvalue & rank & win & tie & loss\\\\" << std::endl;
handler << "\\hline" << std::endl;
for (auto const& line : holmResult.holmLines) {
auto textStatus = !line.reject ? "\\bf " : " ";
if (line.model == holmResult.model) {
handler << line.model << " & - & " << std::fixed << std::setprecision(2) << line.rank << " & - & - & - \\\\" << std::endl;
} else {
handler << line.model << " & " << textStatus << std::scientific << std::setprecision(4) << line.pvalue << " & ";
handler << std::fixed << std::setprecision(2) << line.rank << " & " << line.wtl.win << " & " << line.wtl.tie << " & " << line.wtl.loss << "\\\\" << std::endl;
}
}
handler << "\\hline " << std::endl;
handler << "\\end{tabular}" << std::endl;
handler << "\\end{table}" << std::endl;
handler.close();
}
}

24
src/best/BestResultsTex.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef BEST_RESULTS_TEX_H
#define BEST_RESULTS_TEX_H
#include <map>
#include <vector>
#include <nlohmann/json.hpp>
#include "common/Paths.h"
#include "Statistics.h"
namespace platform {
using json = nlohmann::ordered_json;
class BestResultsTex {
public:
BestResultsTex() = default;
~BestResultsTex() = default;
void results_header(const std::vector<std::string>& models, const std::string& date);
void results_body(const std::vector<std::string>& datasets, json& table);
void results_footer(const std::map<std::string, std::vector<double>>& totals, const std::string& best_model);
void holm_test(struct HolmResult& holmResult, const std::string& date);
private:
void openTexFile(const std::string& name);
std::ofstream handler;
std::vector<std::string> models;
};
}
#endif

View File

@@ -4,6 +4,8 @@
#include "common/Colors.h"
#include "common/Symbols.h"
#include "common/CLocale.h"
#include "BestResultsTex.h"
#include "BestResultsMd.h"
#include "Statistics.h"
@@ -113,7 +115,7 @@ namespace platform {
}
}
void Statistics::postHocHolmTest(bool friedmanResult)
void Statistics::postHocHolmTest(bool friedmanResult, bool tex)
{
if (!fitted) {
fit();
@@ -195,6 +197,12 @@ namespace platform {
if (output) {
std::cout << oss.str();
}
if (tex) {
BestResultsTex bestResultsTex;
BestResultsMd bestResultsMd;
bestResultsTex.holm_test(holmResult, get_date() + " " + get_time());
bestResultsMd.holm_test(holmResult, get_date() + " " + get_time());
}
}
bool Statistics::friedmanTest()
{

View File

@@ -34,7 +34,7 @@ namespace platform {
public:
Statistics(const std::vector<std::string>& models, const std::vector<std::string>& datasets, const json& data, double significance = 0.05, bool output = true);
bool friedmanTest();
void postHocHolmTest(bool friedmanResult);
void postHocHolmTest(bool friedmanResult, bool tex=false);
FriedmanResult& getFriedmanResult();
HolmResult& getHolmResult();
std::map<std::string, std::map<std::string, float>>& getRanks();

View File

@@ -5,7 +5,7 @@
#include "common/Paths.h"
#include "common/Colors.h"
#include "best/BestResults.h"
#include "config.h"
#include "config_platform.h"
void manageArguments(argparse::ArgumentParser& program)
{
@@ -16,6 +16,7 @@ void manageArguments(argparse::ArgumentParser& program)
program.add_argument("-s", "--score").default_value("accuracy").help("Filter results of the score name supplied");
program.add_argument("--friedman").help("Friedman test").default_value(false).implicit_value(true);
program.add_argument("--excel").help("Output to excel").default_value(false).implicit_value(true);
program.add_argument("--tex").help("Output result table to TeX file").default_value(false).implicit_value(true);
program.add_argument("--level").help("significance level").default_value(0.05).scan<'g', double>().action([](const std::string& value) {
try {
auto k = std::stod(value);
@@ -37,7 +38,7 @@ int main(int argc, char** argv)
argparse::ArgumentParser program("b_best", { platform_project_version.begin(), platform_project_version.end() });
manageArguments(program);
std::string model, dataset, score;
bool build, report, friedman, excel;
bool build, report, friedman, excel, tex;
double level;
try {
program.parse_args(argc, argv);
@@ -46,6 +47,7 @@ int main(int argc, char** argv)
score = program.get<std::string>("score");
friedman = program.get<bool>("friedman");
excel = program.get<bool>("excel");
tex = program.get<bool>("tex");
level = program.get<double>("level");
if (model == "" || score == "") {
throw std::runtime_error("Model and score name must be supplied");
@@ -65,7 +67,7 @@ int main(int argc, char** argv)
auto results = platform::BestResults(platform::Paths::results(), score, model, dataset, friedman, level);
if (model == "any") {
results.buildAll();
results.reportAll(excel);
results.reportAll(excel, tex);
} else {
std::string fileName = results.build();
std::cout << Colors::GREEN() << fileName << " created!" << Colors::RESET() << std::endl;

View File

@@ -11,7 +11,7 @@
#include "common/Colors.h"
#include "common/DotEnv.h"
#include "grid/GridSearch.h"
#include "config.h"
#include "config_platform.h"
using json = nlohmann::ordered_json;
const int MAXL = 133;
@@ -93,8 +93,10 @@ void list_dump(std::string& model)
if (item.first.size() > max_dataset) {
max_dataset = item.first.size();
}
if (item.second.dump().size() > max_hyper) {
max_hyper = item.second.dump().size();
for (auto const& [key, value] : item.second.items()) {
if (value.dump().size() > max_hyper) {
max_hyper = value.dump().size();
}
}
}
std::cout << Colors::GREEN() << left << " # " << left << setw(max_dataset) << "Dataset" << " #Com. "
@@ -106,7 +108,12 @@ void list_dump(std::string& model)
std::cout << color;
auto num_combinations = data.getNumCombinations(item.first);
std::cout << setw(3) << fixed << right << ++index << left << " " << setw(max_dataset) << item.first
<< " " << setw(5) << right << num_combinations << " " << setw(max_hyper) << left << item.second.dump() << std::endl;
<< " " << setw(5) << right << num_combinations << " ";
std::string prefix = "";
for (auto const& [key, value] : item.second.items()) {
std::cout << prefix << setw(max_hyper) << std::left << value.dump() << std::endl;
prefix = string(11 + max_dataset, ' ');
}
}
std::cout << Colors::RESET() << std::endl;
}

View File

@@ -13,7 +13,7 @@
#include "results/ResultsDatasetConsole.h"
#include "results/ResultsDataset.h"
#include "results/ResultsDatasetExcel.h"
#include "config.h"
#include "config_platform.h"
void list_datasets(argparse::ArgumentParser& program)

View File

@@ -7,11 +7,12 @@
#include "common/Paths.h"
#include "main/Models.h"
#include "main/modelRegister.h"
#include "config.h"
#include "config_platform.h"
using json = nlohmann::ordered_json;
void manageArguments(argparse::ArgumentParser& program)
{
auto env = platform::DotEnv();
@@ -35,6 +36,7 @@ void manageArguments(argparse::ArgumentParser& program)
program.add_argument("--hyperparameters").default_value("{}").help("Hyperparameters passed to the model in Experiment");
program.add_argument("--hyper-file").default_value("").help("Hyperparameters file name." \
"Mutually exclusive with hyperparameters. This file should contain hyperparameters for each dataset in json format.");
program.add_argument("--hyper-best").default_value(false).help("Use best results of the model as source of hyperparameters").implicit_value(true);
program.add_argument("-m", "--model")
.help("Model to use: " + platform::Models::instance()->toString())
.action([](const std::string& value) {
@@ -47,7 +49,23 @@ void manageArguments(argparse::ArgumentParser& program)
);
program.add_argument("--title").default_value("").help("Experiment title");
program.add_argument("--discretize").help("Discretize input dataset").default_value((bool)stoi(env.get("discretize"))).implicit_value(true);
auto valid_choices = env.valid_tokens("discretize_algo");
auto& disc_arg = program.add_argument("--discretize-algo").help("Algorithm to use in discretization. Valid values: " + env.valid_values("discretize_algo")).default_value(env.get("discretize_algo"));
for (auto choice : valid_choices) {
disc_arg.choices(choice);
}
valid_choices = env.valid_tokens("smooth_strat");
auto& smooth_arg = program.add_argument("--smooth-strat").help("Smooth strategy used in Bayes Network node initialization. Valid values: " + env.valid_values("smooth_strat")).default_value(env.get("smooth_strat"));
for (auto choice : valid_choices) {
smooth_arg.choices(choice);
}
auto& score_arg = program.add_argument("-s", "--score").help("Score to use. Valid values: " + env.valid_values("score")).default_value(env.get("score"));
valid_choices = env.valid_tokens("score");
for (auto choice : valid_choices) {
score_arg.choices(choice);
}
program.add_argument("--generate-fold-files").help("generate fold information in datasets_experiment folder").default_value(false).implicit_value(true);
program.add_argument("--graph").help("generate graphviz dot files with the model").default_value(false).implicit_value(true);
program.add_argument("--no-train-score").help("Don't compute train score").default_value(false).implicit_value(true);
program.add_argument("--quiet").help("Don't display detailed progress").default_value(false).implicit_value(true);
program.add_argument("--save").help("Save result (always save if no dataset is supplied)").default_value(false).implicit_value(true);
@@ -67,16 +85,16 @@ void manageArguments(argparse::ArgumentParser& program)
throw std::runtime_error("Number of folds must be an integer");
}});
auto seed_values = env.getSeeds();
program.add_argument("-s", "--seeds").nargs(1, 10).help("Random seeds. Set to -1 to have pseudo random").scan<'i', int>().default_value(seed_values);
program.add_argument("--seeds").nargs(1, 10).help("Random seeds. Set to -1 to have pseudo random").scan<'i', int>().default_value(seed_values);
}
int main(int argc, char** argv)
{
argparse::ArgumentParser program("b_main", { platform_project_version.begin(), platform_project_version.end() });
manageArguments(program);
std::string file_name, model_name, title, hyperparameters_file, datasets_file;
std::string file_name, model_name, title, hyperparameters_file, datasets_file, discretize_algo, smooth_strat, score;
json hyperparameters_json;
bool discretize_dataset, stratified, saveResults, quiet, no_train_score, generate_fold_files;
bool discretize_dataset, stratified, saveResults, quiet, no_train_score, generate_fold_files, graph, hyper_best;
std::vector<int> seeds;
std::vector<std::string> file_names;
std::vector<std::string> filesToTest;
@@ -88,21 +106,33 @@ int main(int argc, char** argv)
datasets_file = program.get<std::string>("datasets-file");
model_name = program.get<std::string>("model");
discretize_dataset = program.get<bool>("discretize");
discretize_algo = program.get<std::string>("discretize-algo");
smooth_strat = program.get<std::string>("smooth-strat");
stratified = program.get<bool>("stratified");
quiet = program.get<bool>("quiet");
graph = program.get<bool>("graph");
n_folds = program.get<int>("folds");
score = program.get<std::string>("score");
seeds = program.get<std::vector<int>>("seeds");
auto hyperparameters = program.get<std::string>("hyperparameters");
hyperparameters_json = json::parse(hyperparameters);
hyperparameters_file = program.get<std::string>("hyper-file");
no_train_score = program.get<bool>("no-train-score");
hyper_best = program.get<bool>("hyper-best");
generate_fold_files = program.get<bool>("generate-fold-files");
if (hyper_best) {
// Build the best results file_name
hyperparameters_file = platform::Paths::results() + platform::Paths::bestResultsFile(score, model_name);
// ignore this parameter
hyperparameters = "{}";
} else {
if (hyperparameters_file != "" && hyperparameters != "{}") {
throw runtime_error("hyperparameters and hyper_file are mutually exclusive");
}
}
title = program.get<std::string>("title");
if (title == "" && file_name == "") {
throw runtime_error("title is mandatory if dataset is not provided");
if (title == "" && file_name == "all") {
throw runtime_error("title is mandatory if all datasets are to be tested");
}
saveResults = program.get<bool>("save");
}
@@ -167,7 +197,7 @@ int main(int argc, char** argv)
platform::HyperParameters test_hyperparams;
if (hyperparameters_file != "") {
test_hyperparams = platform::HyperParameters(datasets.getNames(), hyperparameters_file);
test_hyperparams = platform::HyperParameters(datasets.getNames(), hyperparameters_file, hyper_best);
} else {
test_hyperparams = platform::HyperParameters(datasets.getNames(), hyperparameters_json);
}
@@ -177,18 +207,17 @@ int main(int argc, char** argv)
*/
auto env = platform::DotEnv();
auto experiment = platform::Experiment();
std::string discretiz_algo = env.get("discretiz_algo");
experiment.setTitle(title).setLanguage("c++").setLanguageVersion("13.2.1");
experiment.setDiscretizationAlgorithm(discretiz_algo);
experiment.setTitle(title).setLanguage("c++").setLanguageVersion("gcc 14.1.1");
experiment.setDiscretizationAlgorithm(discretize_algo).setSmoothSrategy(smooth_strat);
experiment.setDiscretized(discretize_dataset).setModel(model_name).setPlatform(env.get("platform"));
experiment.setStratified(stratified).setNFolds(n_folds).setScoreName("accuracy");
experiment.setStratified(stratified).setNFolds(n_folds).setScoreName(score);
experiment.setHyperparameters(test_hyperparams);
for (auto seed : seeds) {
experiment.addRandomSeed(seed);
}
platform::Timer timer;
timer.start();
experiment.go(filesToTest, quiet, no_train_score, generate_fold_files);
experiment.go(filesToTest, quiet, no_train_score, generate_fold_files, graph);
experiment.setDuration(timer.getDuration());
if (!quiet) {
// Classification report if only one dataset is tested
@@ -197,6 +226,9 @@ int main(int argc, char** argv)
if (saveResults) {
experiment.saveResult();
}
if (graph) {
experiment.saveGraph();
}
std::cout << "Done!" << std::endl;
return 0;
}

View File

@@ -4,7 +4,10 @@
#include <unistd.h>
#include <argparse/argparse.hpp>
#include "manage/ManageScreen.h"
#include "config.h"
#include <signal.h>
#include "config_platform.h"
platform::ManageScreen* manager = nullptr;
void manageArguments(argparse::ArgumentParser& program, int argc, char** argv)
{
@@ -42,6 +45,11 @@ std::pair<int, int> numRowsCols()
return { ts.ws_row, ts.ws_col };
#endif /* TIOCGSIZE */
}
void handleResize(int sig)
{
auto [rows, cols] = numRowsCols();
manager->updateSize(rows, cols);
}
int main(int argc, char** argv)
{
@@ -50,13 +58,15 @@ int main(int argc, char** argv)
std::string model = program.get<std::string>("model");
std::string score = program.get<std::string>("score");
std::string platform = program.get<std::string>("platform");
auto complete = program.get<bool>("complete");
auto partial = program.get<bool>("partial");
auto compare = program.get<bool>("compare");
auto [rows, cols] = numRowsCols();
bool complete = program.get<bool>("complete");
bool partial = program.get<bool>("partial");
bool compare = program.get<bool>("compare");
if (complete)
partial = false;
auto manager = platform::ManageScreen(rows, cols, model, score, platform, complete, partial, compare);
manager.doMenu();
signal(SIGWINCH, handleResize);
auto [rows, cols] = numRowsCols();
manager = new platform::ManageScreen(rows, cols, model, score, platform, complete, partial, compare);
manager->doMenu();
delete manager;
return 0;
}

View File

@@ -131,8 +131,7 @@ namespace platform {
for (int i = 0; i < features.size(); ++i) {
auto [max_value, idx] = torch::max(X_train.index({ i, "..." }), 0);
states[features[i]] = std::vector<int>(max_value.item<int>() + 1);
auto item = states.at(features[i]);
iota(begin(item), end(item), 0);
iota(begin(states.at(features[i])), end(states.at(features[i])), 0);
}
auto [max_value, idx] = torch::max(y_train, 0);
states[className] = std::vector<int>(max_value.item<int>() + 1);

View File

@@ -5,6 +5,14 @@
namespace platform {
using json = nlohmann::ordered_json;
const std::string message_dataset_not_loaded = "dataset not loaded.";
Datasets::Datasets(bool discretize, std::string sfileType, std::string discretizer_algorithm) :
discretize(discretize), sfileType(sfileType), discretizer_algorithm(discretizer_algorithm)
{
if ((discretizer_algorithm == "none" || discretizer_algorithm == "") && discretize) {
throw std::runtime_error("Can't discretize without discretization algorithm");
}
load();
}
void Datasets::load()
{
auto sd = SourceData(sfileType);

View File

@@ -4,14 +4,7 @@
namespace platform {
class Datasets {
public:
explicit Datasets(bool discretize, std::string sfileType, std::string discretizer_algorithm = "none") :
discretize(discretize), sfileType(sfileType), discretizer_algorithm(discretizer_algorithm)
{
if (discretizer_algorithm == "none" && discretize) {
throw std::runtime_error("Can't discretize without discretization algorithm");
}
load();
};
explicit Datasets(bool discretize, std::string sfileType, std::string discretizer_algorithm = "none");
std::vector<std::string> getNames();
bool isDataset(const std::string& name) const;
Dataset& getDataset(const std::string& name) const { return *datasets.at(name); }

View File

@@ -5,9 +5,9 @@
#include <string>
#include <functional>
#include <vector>
#include <Discretizer.h>
#include <BinDisc.h>
#include <CPPFImdlp.h>
#include <fimdlp/Discretizer.h>
#include <fimdlp/BinDisc.h>
#include <fimdlp/CPPFImdlp.h>
namespace platform {
class Discretization {
public:

View File

@@ -11,4 +11,28 @@ static platform::RegistrarDiscretization registrarBU4("bin4u",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(4, mdlp::strategy_t::UNIFORM);});
static platform::RegistrarDiscretization registrarBQ4("bin4q",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(4, mdlp::strategy_t::QUANTILE);});
static platform::RegistrarDiscretization registrarBU5("bin5u",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(5, mdlp::strategy_t::UNIFORM);});
static platform::RegistrarDiscretization registrarBQ5("bin5q",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(5, mdlp::strategy_t::QUANTILE);});
static platform::RegistrarDiscretization registrarBU6("bin6u",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(6, mdlp::strategy_t::UNIFORM);});
static platform::RegistrarDiscretization registrarBQ6("bin6q",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(6, mdlp::strategy_t::QUANTILE);});
static platform::RegistrarDiscretization registrarBU7("bin7u",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(7, mdlp::strategy_t::UNIFORM);});
static platform::RegistrarDiscretization registrarBQ7("bin7q",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(7, mdlp::strategy_t::QUANTILE);});
static platform::RegistrarDiscretization registrarBU8("bin8u",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(8, mdlp::strategy_t::UNIFORM);});
static platform::RegistrarDiscretization registrarBQ8("bin8q",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(8, mdlp::strategy_t::QUANTILE);});
static platform::RegistrarDiscretization registrarBU9("bin9u",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(9, mdlp::strategy_t::UNIFORM);});
static platform::RegistrarDiscretization registrarBQ9("bin9q",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(9, mdlp::strategy_t::QUANTILE);});
static platform::RegistrarDiscretization registrarBU10("bin10u",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(10, mdlp::strategy_t::UNIFORM);});
static platform::RegistrarDiscretization registrarBQ10("bin10q",
[](void) -> mdlp::Discretizer* { return new mdlp::BinDisc(10, mdlp::strategy_t::QUANTILE);});
#endif

View File

@@ -19,29 +19,47 @@ namespace platform {
{
valid =
{
{"source_data", {"Arff", "Tanveer", "Surcov"}},
{"experiment", {"discretiz", "odte", "covid"}},
{"fit_features", {"0", "1"}},
{"discretize", {"0", "1"}},
{"ignore_nan", {"0", "1"}},
{"stratified", {"0", "1"}},
{"score", {"accuracy"}},
{"framework", {"bulma", "bootstrap"}},
{"margin", {"0.1", "0.2", "0.3"}},
{"n_folds", {"5", "10"}},
{"discretiz_algo", {"mdlp", "bin3u", "bin3q", "bin4u", "bin4q"}},
{"platform", {"any"}},
{"model", {"any"}},
{"seeds", {"any"}},
{"nodes", {"any"}},
{"leaves", {"any"}},
{"depth", {"any"}},
{"discretize", {"0", "1"}},
{"discretize_algo", {"mdlp", "bin3u", "bin3q", "bin4u", "bin4q", "bin5q", "bin5u", "bin6q", "bin6u", "bin7q", "bin7u", "bin8q", "bin8u", "bin9q", "bin9u", "bin10q", "bin10u"}},
{"experiment", {"discretiz", "odte", "covid", "Test"}},
{"fit_features", {"0", "1"}},
{"framework", {"bulma", "bootstrap"}},
{"ignore_nan", {"0", "1"}},
{"leaves", {"any"}},
{"margin", {"0.1", "0.2", "0.3"}},
{"model", {"any"}},
{"n_folds", {"5", "10"}},
{"nodes", {"any"}},
{"platform", {"any"}},
{"stratified", {"0", "1"}},
{"score", {"accuracy", "roc-auc-ovr"}},
{"seeds", {"any"}},
{"smooth_strat", {"ORIGINAL", "LAPLACE", "CESTNIK"}},
{"source_data", {"Arff", "Tanveer", "Surcov", "Test"}},
};
if (create) {
// For testing purposes
std::ofstream file(".env");
file << "experiment=Test" << std::endl;
file << "source_data=Test" << std::endl;
file << "margin=0.1" << std::endl;
file << "score=accuracy" << std::endl;
file << "platform=um790Linux" << std::endl;
file << "n_folds=5" << std::endl;
file << "discretize_algo=mdlp" << std::endl;
file << "smooth_strat=ORIGINAL" << std::endl;
file << "stratified=0" << std::endl;
file << "model=TAN" << std::endl;
file << "seeds=[271]" << std::endl;
file << "discretize=0" << std::endl;
file << "ignore_nan=0" << std::endl;
file << "nodes=Nodes" << std::endl;
file << "leaves=Edges" << std::endl;
file << "depth=States" << std::endl;
file << "fit_features=0" << std::endl;
file << "framework=bulma" << std::endl;
file << "margin=0.1" << std::endl;
file.close();
}
std::ifstream file(".env");
@@ -80,22 +98,40 @@ namespace platform {
exit(1);
}
}
std::vector<std::string> valid_tokens(const std::string& key)
{
if (valid.find(key) == valid.end()) {
return {};
}
return valid.at(key);
}
std::string valid_values(const std::string& key)
{
std::string valid_values = "{", sep = "";
if (valid.find(key) == valid.end()) {
return "{}";
}
for (const auto& value : valid.at(key)) {
valid_values += sep + value;
sep = ", ";
}
return valid_values + "}";
}
void parseEnv()
{
for (auto& [key, values] : valid) {
if (env.find(key) == env.end()) {
std::string valid_values = "", sep = "";
for (const auto& value : values) {
valid_values += sep + value;
sep = ", ";
}
std::cerr << "Key not found in .env: " << key << ", valid values: " << valid_values << std::endl;
std::cerr << "Key not found in .env: " << key << ", valid values: " << valid_values(key) << std::endl;
exit(1);
}
}
}
std::string get(const std::string& key)
{
if (env.find(key) == env.end()) {
std::cerr << "Key not found in .env: " << key << std::endl;
exit(1);
}
return env.at(key);
}
std::vector<int> getSeeds()

View File

@@ -6,10 +6,19 @@
namespace platform {
class Paths {
public:
static std::string results() { return "results/"; }
static std::string hiddenResults() { return "hidden_results/"; }
static std::string excel() { return "excel/"; }
static std::string grid() { return "grid/"; }
static std::string createIfNotExists(const std::string& folder)
{
if (!std::filesystem::exists(folder)) {
std::filesystem::create_directory(folder);
}
return folder;
}
static std::string results() { return createIfNotExists("results/"); }
static std::string hiddenResults() { return createIfNotExists("hidden_results/"); }
static std::string excel() { return createIfNotExists("excel/"); }
static std::string grid() { return createIfNotExists("grid/"); }
static std::string graphs() { return createIfNotExists("graphs/"); }
static std::string tex() { return createIfNotExists("tex/"); }
static std::string datasets()
{
auto env = platform::DotEnv();
@@ -31,6 +40,14 @@ namespace platform {
throw std::runtime_error("Could not create directory " + path);
}
}
static std::string bestResultsFile(const std::string& score, const std::string& model)
{
return "best_results_" + score + "_" + model + ".json";
}
static std::string bestResultsExcel(const std::string& score)
{
return "BestResults_" + score + ".xlsx";
}
static std::string excelResults() { return "some_results.xlsx"; }
static std::string grid_input(const std::string& model)
{
@@ -40,6 +57,22 @@ namespace platform {
{
return grid() + "grid_" + model + "_output.json";
}
static std::string tex_output()
{
return "results.tex";
}
static std::string md_output()
{
return "results.md";
}
static std::string tex_post_hoc()
{
return "post_hoc.tex";
}
static std::string md_post_hoc()
{
return "post_hoc.md";
}
};
}
#endif

View File

@@ -12,6 +12,7 @@ namespace platform {
inline static const std::string downward_arrow{ "\u27B4" };
inline static const std::string up_arrow{ "\u2B06" };
inline static const std::string down_arrow{ "\u2B07" };
inline static const std::string ellipsis{ "\u2026" };
inline static const std::string equal_best{ check_mark };
inline static const std::string better_best{ black_star };
inline static const std::string notebook{ "\U0001F5C8" };

View File

@@ -4,7 +4,17 @@
#include <string>
#include <vector>
#include <algorithm>
#include <torch/torch.h>
namespace platform {
template <typename T>
std::vector<T> tensorToVector(const torch::Tensor& tensor)
{
torch::Tensor contig_tensor = tensor.contiguous();
auto num_elements = contig_tensor.numel();
const T* tensor_data = contig_tensor.data_ptr<T>();
std::vector<T> result(tensor_data, tensor_data + num_elements);
return result;
}
static std::string trim(const std::string& str)
{
std::string result = str;
@@ -26,5 +36,35 @@ namespace platform {
}
return result;
}
inline double compute_std(std::vector<double> values, double mean)
{
// Compute standard devation of the values
double sum = 0.0;
for (const auto& value : values) {
sum += std::pow(value - mean, 2);
}
double variance = sum / values.size();
return std::sqrt(variance);
}
inline std::string get_date()
{
time_t rawtime;
tm* timeinfo;
time(&rawtime);
timeinfo = std::localtime(&rawtime);
std::ostringstream oss;
oss << std::put_time(timeinfo, "%Y-%m-%d");
return oss.str();
}
inline std::string get_time()
{
time_t rawtime;
tm* timeinfo;
time(&rawtime);
timeinfo = std::localtime(&rawtime);
std::ostringstream oss;
oss << std::put_time(timeinfo, "%H:%M:%S");
return oss.str();
}
}
#endif

View File

@@ -5,29 +5,11 @@
#include "main/Models.h"
#include "common/Paths.h"
#include "common/Colors.h"
#include "common/Utils.h"
#include "GridSearch.h"
namespace platform {
std::string get_date()
{
time_t rawtime;
tm* timeinfo;
time(&rawtime);
timeinfo = std::localtime(&rawtime);
std::ostringstream oss;
oss << std::put_time(timeinfo, "%Y-%m-%d");
return oss.str();
}
std::string get_time()
{
time_t rawtime;
tm* timeinfo;
time(&rawtime);
timeinfo = std::localtime(&rawtime);
std::ostringstream oss;
oss << std::put_time(timeinfo, "%H:%M:%S");
return oss.str();
}
std::string get_color_rank(int rank)
{
auto colors = { Colors::WHITE(), Colors::RED(), Colors::GREEN(), Colors::BLUE(), Colors::MAGENTA(), Colors::CYAN() };
@@ -103,11 +85,11 @@ namespace platform {
std::mt19937 g{ 271 }; // Use fixed seed to obtain the same shuffle
std::shuffle(tasks.begin(), tasks.end(), g);
std::cout << get_color_rank(rank) << "* Number of tasks: " << tasks.size() << std::endl;
std::cout << "|";
std::cout << separator;
for (int i = 0; i < tasks.size(); ++i) {
std::cout << (i + 1) % 10;
}
std::cout << "|" << std::endl << "|" << std::flush;
std::cout << separator << std::endl << separator << std::flush;
return tasks;
}
void process_task_mpi_consumer(struct ConfigGrid& config, struct ConfigMPI& config_mpi, json& tasks, int n_task, Datasets& datasets, Task_Result* result)
@@ -126,6 +108,7 @@ namespace platform {
// Generate the hyperparamters combinations
auto& dataset = datasets.getDataset(dataset_name);
auto combinations = grid.getGrid(dataset_name);
dataset.load();
auto [X, y] = dataset.getTensors();
auto features = dataset.getFeatures();
auto className = dataset.getClassName();
@@ -142,6 +125,7 @@ namespace platform {
auto states = dataset.getStates(); // Get the states of the features Once they are discretized
double best_fold_score = 0.0;
int best_idx_combination = -1;
bayesnet::Smoothing_t smoothing = bayesnet::Smoothing_t::NONE;
json best_fold_hyper;
for (int idx_combination = 0; idx_combination < combinations.size(); ++idx_combination) {
auto hyperparam_line = combinations[idx_combination];
@@ -167,7 +151,7 @@ namespace platform {
hyperparameters.check(valid, dataset_name);
clf->setHyperparameters(hyperparameters.get(dataset_name));
// Train model
clf->fit(X_nested_train, y_nested_train, features, className, states);
clf->fit(X_nested_train, y_nested_train, features, className, states, smoothing);
// Test model
score += clf->score(X_nested_test, y_nested_test);
}
@@ -186,7 +170,7 @@ namespace platform {
auto valid = clf->getValidHyperparameters();
hyperparameters.check(valid, dataset_name);
clf->setHyperparameters(best_fold_hyper);
clf->fit(X_train, y_train, features, className, states);
clf->fit(X_train, y_train, features, className, states, smoothing);
best_fold_score = clf->score(X_test, y_test);
// Return the result
result->idx_dataset = task["idx_dataset"].get<int>();
@@ -370,14 +354,15 @@ namespace platform {
tasks = json::parse(msg);
delete[] msg;
auto env = platform::DotEnv();
auto datasets = Datasets(config.discretize, Paths::datasets(), env.get("discretiz_algo"));
auto datasets = Datasets(config.discretize, Paths::datasets(), env.get("discretize_algo"));
if (config_mpi.rank == config_mpi.manager) {
//
// 2a. Producer delivers the tasks to the consumers
//
auto datasets_names = filterDatasets(datasets);
json all_results = producer(datasets_names, tasks, config_mpi, MPI_Result);
std::cout << get_color_rank(config_mpi.rank) << "|" << std::endl;
std::cout << get_color_rank(config_mpi.rank) << separator << std::endl;
//
// 3. Manager select the bests sccores for each dataset
//

View File

@@ -55,6 +55,7 @@ namespace platform {
struct ConfigGrid config;
json build_tasks_mpi(int rank);
Timer timer; // used to measure the time of the whole process
const std::string separator = "|";
};
} /* namespace platform */
#endif

View File

@@ -24,7 +24,24 @@ namespace platform {
{
std::cout << result.getJson().dump(4) << std::endl;
}
void Experiment::go(std::vector<std::string> filesToProcess, bool quiet, bool no_train_score, bool generate_fold_files)
void Experiment::saveGraph()
{
std::cout << "Saving graphs..." << std::endl;
auto data = result.getJson();
for (const auto& item : data["results"]) {
auto graphs = item["graph"];
int i = 0;
for (const auto& graph : graphs) {
i++;
auto fileName = Paths::graphs() + result.getFilename() + "_graph_" + item["dataset"].get<std::string>() + "_" + std::to_string(i) + ".dot";
auto file = std::ofstream(fileName);
file << graph.get<std::string>();
file.close();
std::cout << "Graph saved in " << fileName << std::endl;
}
}
}
void Experiment::go(std::vector<std::string> filesToProcess, bool quiet, bool no_train_score, bool generate_fold_files, bool graph)
{
for (auto fileName : filesToProcess) {
if (fileName.size() > max_name)
@@ -41,14 +58,14 @@ namespace platform {
std::cout << " ( " << Colors::GREEN() << "b" << Colors::RESET() << " ) Scoring train dataset" << std::endl;
std::cout << " ( " << Colors::GREEN() << "c" << Colors::RESET() << " ) Scoring test dataset" << std::endl << std::endl;
std::cout << Colors::YELLOW() << "Note: fold number in this color means fitting had issues such as not using all features in BoostAODE classifier" << std::endl << std::endl;
std::cout << Colors::GREEN() << left << " # " << setw(max_name) << "Dataset" << " #Samp #Feat Seed Status" << std::endl;
std::cout << " --- " << string(max_name, '-') << " ----- ----- ---- " << string(4 + 3 * nfolds, '-') << Colors::RESET() << std::endl;
std::cout << Colors::GREEN() << left << " # " << setw(max_name) << "Dataset" << " #Samp #Feat Seed Status" << string(3 * nfolds - 2, ' ') << " Time" << std::endl;
std::cout << " --- " << string(max_name, '-') << " ----- ----- ---- " << string(4 + 3 * nfolds, '-') << " ----------" << Colors::RESET() << std::endl;
}
int num = 0;
for (auto fileName : filesToProcess) {
if (!quiet)
std::cout << " " << setw(3) << right << num++ << " " << setw(max_name) << left << fileName << right << flush;
cross_validation(fileName, quiet, no_train_score, generate_fold_files);
cross_validation(fileName, quiet, no_train_score, generate_fold_files, graph);
if (!quiet)
std::cout << std::endl;
}
@@ -68,10 +85,17 @@ namespace platform {
return Colors::RESET();
}
}
score_t Experiment::parse_score() const
{
if (result.getScoreName() == "accuracy")
return score_t::ACCURACY;
if (result.getScoreName() == "roc-auc-ovr")
return score_t::ROC_AUC_OVR;
throw std::runtime_error("Unknown score: " + result.getScoreName());
}
void showProgress(int fold, const std::string& color, const std::string& phase)
{
std::string prefix = phase == "a" ? "" : "\b\b\b\b";
std::string prefix = phase == "-" ? "" : "\b\b\b\b";
std::cout << prefix << color << fold << Colors::RESET() << "(" << color << phase << Colors::RESET() << ")" << flush;
}
@@ -113,7 +137,7 @@ namespace platform {
file << output.dump(4);
file.close();
}
void Experiment::cross_validation(const std::string& fileName, bool quiet, bool no_train_score, bool generate_fold_files)
void Experiment::cross_validation(const std::string& fileName, bool quiet, bool no_train_score, bool generate_fold_files, bool graph)
{
//
// Load dataset and prepare data
@@ -141,8 +165,8 @@ namespace platform {
// Initialize results std::vectors
//
int nResults = nfolds * static_cast<int>(randomSeeds.size());
auto accuracy_test = torch::zeros({ nResults }, torch::kFloat64);
auto accuracy_train = torch::zeros({ nResults }, torch::kFloat64);
auto score_test = torch::zeros({ nResults }, torch::kFloat64);
auto score_train = torch::zeros({ nResults }, torch::kFloat64);
auto train_time = torch::zeros({ nResults }, torch::kFloat64);
auto test_time = torch::zeros({ nResults }, torch::kFloat64);
auto nodes = torch::zeros({ nResults }, torch::kFloat64);
@@ -151,13 +175,16 @@ namespace platform {
json confusion_matrices = json::array();
json confusion_matrices_train = json::array();
std::vector<std::string> notes;
Timer train_timer, test_timer;
std::vector<std::string> graphs;
Timer train_timer, test_timer, seed_timer;
int item = 0;
bool first_seed = true;
//
// Loop over random seeds
//
auto score = parse_score();
for (auto seed : randomSeeds) {
seed_timer.start();
if (!quiet) {
string prefix = " ";
if (!first_seed) {
@@ -176,6 +203,8 @@ namespace platform {
//
for (int nfold = 0; nfold < nfolds; nfold++) {
auto clf = Models::instance()->create(result.getModel());
if (!quiet)
showProgress(nfold + 1, getColor(clf->getStatus()), "-");
setModelVersion(clf->getVersion());
auto valid = clf->getValidHyperparameters();
hyperparameters.check(valid, fileName);
@@ -194,15 +223,7 @@ namespace platform {
//
// Train model
//
std::cout << "X_Train.dtype: " << X_train.dtype() << "\n";
std::cout << "y_Train.dtype: " << y_train.dtype() << "\n";
std::cout << "X_Test.dtype: " << X_test.dtype() << "\n";
std::cout << "y_Test.dtype: " << y_test.dtype() << "\n";
for (int i = 0; i < features.size(); i++) {
std::cout << "Feature: " << features[i] << " states: " << states[features[i]].size() << "\n";
}
std::cout << "className: " << className << " states: " << states[className].size() << "\n";
clf->fit(X_train, y_train, features, className, states);
clf->fit(X_train, y_train, features, className, states, smooth_type);
if (!quiet)
showProgress(nfold + 1, getColor(clf->getStatus()), "b");
auto clf_notes = clf->getNotes();
@@ -212,14 +233,14 @@ namespace platform {
edges[item] = clf->getNumberOfEdges();
num_states[item] = clf->getNumberOfStates();
train_time[item] = train_timer.getDuration();
double accuracy_train_value = 0.0;
double score_train_value = 0.0;
//
// Score train
//
if (!no_train_score) {
auto y_predict = clf->predict(X_train);
Scores scores(y_train, y_predict, num_classes, labels);
accuracy_train_value = scores.accuracy();
auto y_proba_train = clf->predict_proba(X_train);
Scores scores(y_train, y_proba_train, num_classes, labels);
score_train_value = score == score_t::ACCURACY ? scores.accuracy() : scores.auc();
confusion_matrices_train.push_back(scores.get_confusion_matrix_json(true));
}
//
@@ -228,33 +249,44 @@ namespace platform {
if (!quiet)
showProgress(nfold + 1, getColor(clf->getStatus()), "c");
test_timer.start();
auto y_predict = clf->predict(X_test);
Scores scores(y_test, y_predict, num_classes, labels);
auto accuracy_test_value = scores.accuracy();
// auto y_predict = clf->predict(X_test);
auto y_proba_test = clf->predict_proba(X_test);
Scores scores(y_test, y_proba_test, num_classes, labels);
auto score_test_value = score == score_t::ACCURACY ? scores.accuracy() : scores.auc();
test_time[item] = test_timer.getDuration();
accuracy_train[item] = accuracy_train_value;
accuracy_test[item] = accuracy_test_value;
score_train[item] = score_train_value;
score_test[item] = score_test_value;
confusion_matrices.push_back(scores.get_confusion_matrix_json(true));
if (!quiet)
std::cout << "\b\b\b, " << flush;
//
// Store results and times in std::vector
//
partial_result.addScoreTrain(accuracy_train_value);
partial_result.addScoreTest(accuracy_test_value);
partial_result.addScoreTrain(score_train_value);
partial_result.addScoreTest(score_test_value);
partial_result.addTimeTrain(train_time[item].item<double>());
partial_result.addTimeTest(test_time[item].item<double>());
item++;
if (graph) {
std::string result = "";
for (const auto& line : clf->graph()) {
result += line + "\n";
}
graphs.push_back(result);
}
}
if (!quiet) {
seed_timer.stop();
std::cout << "end. [" << seed_timer.getDurationString() << "]" << std::endl;
}
if (!quiet)
std::cout << "end. " << flush;
delete fold;
}
//
// Store result totals in Result
//
partial_result.setScoreTest(torch::mean(accuracy_test).item<double>()).setScoreTrain(torch::mean(accuracy_train).item<double>());
partial_result.setScoreTestStd(torch::std(accuracy_test).item<double>()).setScoreTrainStd(torch::std(accuracy_train).item<double>());
partial_result.setGraph(graphs);
partial_result.setScoreTest(torch::mean(score_test).item<double>()).setScoreTrain(torch::mean(score_train).item<double>());
partial_result.setScoreTestStd(torch::std(score_test).item<double>()).setScoreTrainStd(torch::std(score_train).item<double>());
partial_result.setTrainTime(torch::mean(train_time).item<double>()).setTestTime(torch::mean(test_time).item<double>());
partial_result.setTestTimeStd(torch::std(test_time).item<double>()).setTrainTimeStd(torch::std(train_time).item<double>());
partial_result.setNodes(torch::mean(nodes).item<double>()).setLeaves(torch::mean(edges).item<double>()).setDepth(torch::mean(num_states).item<double>());

View File

@@ -7,10 +7,11 @@
#include "bayesnet/BaseClassifier.h"
#include "HyperParameters.h"
#include "results/Result.h"
#include "bayesnet/network/Network.h"
namespace platform {
using json = nlohmann::ordered_json;
enum class score_t { NONE, ACCURACY, ROC_AUC_OVR };
class Experiment {
public:
Experiment() = default;
@@ -24,6 +25,21 @@ namespace platform {
{
this->discretization_algo = discretization_algo; this->result.setDiscretizationAlgorithm(discretization_algo); return *this;
}
Experiment& setSmoothSrategy(const std::string& smooth_strategy)
{
this->smooth_strategy = smooth_strategy; this->result.setSmoothStrategy(smooth_strategy);
if (smooth_strategy == "ORIGINAL")
smooth_type = bayesnet::Smoothing_t::ORIGINAL;
else if (smooth_strategy == "LAPLACE")
smooth_type = bayesnet::Smoothing_t::LAPLACE;
else if (smooth_strategy == "CESTNIK")
smooth_type = bayesnet::Smoothing_t::CESTNIK;
else {
std::cerr << "Experiment: Unknown smoothing strategy: " << smooth_strategy << std::endl;
exit(1);
}
return *this;
}
Experiment& setLanguageVersion(const std::string& language_version) { this->result.setLanguageVersion(language_version); return *this; }
Experiment& setDiscretized(bool discretized) { this->discretized = discretized; result.setDiscretized(discretized); return *this; }
Experiment& setStratified(bool stratified) { this->stratified = stratified; result.setStratified(stratified); return *this; }
@@ -32,17 +48,21 @@ namespace platform {
Experiment& addRandomSeed(int randomSeed) { randomSeeds.push_back(randomSeed); result.addSeed(randomSeed); return *this; }
Experiment& setDuration(float duration) { this->result.setDuration(duration); return *this; }
Experiment& setHyperparameters(const HyperParameters& hyperparameters_) { this->hyperparameters = hyperparameters_; return *this; }
void cross_validation(const std::string& fileName, bool quiet, bool no_train_score, bool generate_fold_files);
void go(std::vector<std::string> filesToProcess, bool quiet, bool no_train_score, bool generate_fold_files);
void cross_validation(const std::string& fileName, bool quiet, bool no_train_score, bool generate_fold_files, bool graph);
void go(std::vector<std::string> filesToProcess, bool quiet, bool no_train_score, bool generate_fold_files, bool graph);
void saveResult();
void show();
void saveGraph();
void report(bool classification_report = false);
private:
score_t parse_score() const;
Result result;
bool discretized{ false }, stratified{ false };
std::vector<PartialResult> results;
std::vector<int> randomSeeds;
std::string discretization_algo;
std::string smooth_strategy;
bayesnet::Smoothing_t smooth_type{ bayesnet::Smoothing_t::NONE };
HyperParameters hyperparameters;
int nfolds{ 0 };
int max_name{ 7 }; // max length of dataset name for formatting (default 7)

View File

@@ -10,16 +10,9 @@ namespace platform {
for (const auto& item : datasets) {
hyperparameters[item] = hyperparameters_;
}
normalize_nested(datasets);
}
// https://www.techiedelight.com/implode-a-vector-of-strings-into-a-comma-separated-string-in-cpp/
std::string join(std::vector<std::string> const& strings, std::string delim)
{
std::stringstream ss;
std::copy(strings.begin(), strings.end(),
std::ostream_iterator<std::string>(ss, delim.c_str()));
return ss.str();
}
HyperParameters::HyperParameters(const std::vector<std::string>& datasets, const std::string& hyperparameters_file)
HyperParameters::HyperParameters(const std::vector<std::string>& datasets, const std::string& hyperparameters_file, bool best)
{
// Check if file exists
std::ifstream file(hyperparameters_file);
@@ -28,7 +21,14 @@ namespace platform {
}
// Check if file is a json
json file_hyperparameters = json::parse(file);
auto input_hyperparameters = file_hyperparameters["results"];
json input_hyperparameters;
if (best) {
for (const auto& [key, value] : file_hyperparameters.items()) {
input_hyperparameters[key]["hyperparameters"] = value[1];
}
} else {
input_hyperparameters = file_hyperparameters["results"];
}
// Check if hyperparameters are valid
for (const auto& dataset : datasets) {
if (!input_hyperparameters.contains(dataset)) {
@@ -38,6 +38,24 @@ namespace platform {
}
hyperparameters[dataset] = input_hyperparameters[dataset]["hyperparameters"].get<json>();
}
normalize_nested(datasets);
}
void HyperParameters::normalize_nested(const std::vector<std::string>& datasets)
{
// for (const auto& dataset : datasets) {
// if (hyperparameters[dataset].contains("be_hyperparams")) {
// // Odte has base estimator hyperparameters set this way
// hyperparameters[dataset]["be_hyperparams"] = hyperparameters[dataset]["be_hyperparams"].dump();
// }
// }
}
// https://www.techiedelight.com/implode-a-vector-of-strings-into-a-comma-separated-string-in-cpp/
std::string join(std::vector<std::string> const& strings, std::string delim)
{
std::stringstream ss;
std::copy(strings.begin(), strings.end(),
std::ostream_iterator<std::string>(ss, delim.c_str()));
return ss.str();
}
void HyperParameters::check(const std::vector<std::string>& valid, const std::string& fileName)
{

View File

@@ -10,14 +10,18 @@ namespace platform {
class HyperParameters {
public:
HyperParameters() = default;
// Constructor to use command line hyperparameters
explicit HyperParameters(const std::vector<std::string>& datasets, const json& hyperparameters_);
explicit HyperParameters(const std::vector<std::string>& datasets, const std::string& hyperparameters_file);
// Constructor to use hyperparameters file generated by grid or by best results
explicit HyperParameters(const std::vector<std::string>& datasets, const std::string& hyperparameters_file, bool best = false);
~HyperParameters() = default;
bool notEmpty(const std::string& key) const { return !hyperparameters.at(key).empty(); }
void check(const std::vector<std::string>& valid, const std::string& fileName);
json get(const std::string& fileName);
private:
void normalize_nested(const std::vector<std::string>& datasets);
std::map<std::string, json> hyperparameters;
bool best = false; // Used to separate grid/best hyperparameters as the format of those files are different
};
} /* namespace platform */
#endif

View File

@@ -15,6 +15,7 @@ namespace platform {
data["times_train"] = json::array();
data["times_test"] = json::array();
data["notes"] = json::array();
data["graph"] = json::array();
data["train_time"] = 0.0;
data["train_time_std"] = 0.0;
data["test_time"] = 0.0;
@@ -27,6 +28,12 @@ namespace platform {
data["notes"].insert(data["notes"].end(), notes_.begin(), notes_.end());
return *this;
}
PartialResult& setGraph(const std::vector<std::string>& graph)
{
json graph_ = graph;
data["graph"].insert(data["graph"].end(), graph_.begin(), graph_.end());
return *this;
}
PartialResult& setConfusionMatrices(const json& confusion_matrices) { data["confusion_matrices"] = confusion_matrices; return *this; }
PartialResult& setConfusionMatricesTrain(const json& confusion_matrices) { data["confusion_matrices_train"] = confusion_matrices; return *this; }
PartialResult& setHyperparameters(const json& hyperparameters) { data["hyperparameters"] = hyperparameters; return *this; }

67
src/main/RocAuc.cpp Normal file
View File

@@ -0,0 +1,67 @@
#include <sstream>
#include <algorithm>
#include <numeric>
#include <utility>
#include "RocAuc.h"
namespace platform {
double RocAuc::compute(const torch::Tensor& y_proba, const torch::Tensor& labels)
{
size_t nClasses = y_proba.size(1);
// In binary classification problem there's no need to calculate the average of the AUCs
if (nClasses == 2)
nClasses = 1;
size_t nSamples = y_proba.size(0);
y_test = tensorToVector(labels);
std::vector<double> aucScores(nClasses, 0.0);
for (size_t classIdx = 0; classIdx < nClasses; ++classIdx) {
scoresAndLabels.clear();
for (size_t i = 0; i < nSamples; ++i) {
scoresAndLabels.emplace_back(y_proba[i][classIdx].item<float>(), y_test[i] == classIdx ? 1 : 0);
}
aucScores[classIdx] = compute_common(nSamples, classIdx);
}
return std::accumulate(aucScores.begin(), aucScores.end(), 0.0) / nClasses;
}
double RocAuc::compute(const std::vector<std::vector<double>>& y_proba, const std::vector<int>& labels)
{
y_test = labels;
size_t nClasses = y_proba[0].size();
// In binary classification problem there's no need to calculate the average of the AUCs
if (nClasses == 2)
nClasses = 1;
size_t nSamples = y_proba.size();
std::vector<double> aucScores(nClasses, 0.0);
for (size_t classIdx = 0; classIdx < nClasses; ++classIdx) {
scoresAndLabels.clear();
for (size_t i = 0; i < nSamples; ++i) {
scoresAndLabels.emplace_back(y_proba[i][classIdx], labels[i] == classIdx ? 1 : 0);
}
aucScores[classIdx] = compute_common(nSamples, classIdx);
}
return std::accumulate(aucScores.begin(), aucScores.end(), 0.0) / nClasses;
}
double RocAuc::compute_common(size_t nSamples, size_t classIdx)
{
std::sort(scoresAndLabels.begin(), scoresAndLabels.end(), std::greater<>());
std::vector<double> tpr, fpr;
double tp = 0, fp = 0;
double totalPos = std::count(y_test.begin(), y_test.end(), classIdx);
double totalNeg = nSamples - totalPos;
for (const auto& [score, label] : scoresAndLabels) {
if (label == 1) {
tp += 1;
} else {
fp += 1;
}
tpr.push_back(tp / totalPos);
fpr.push_back(fp / totalNeg);
}
double auc = 0.0;
for (size_t i = 1; i < tpr.size(); ++i) {
auc += 0.5 * (fpr[i] - fpr[i - 1]) * (tpr[i] + tpr[i - 1]);
}
return auc;
}
}

21
src/main/RocAuc.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef ROCAUC_H
#define ROCAUC_H
#include <torch/torch.h>
#include <vector>
#include <string>
#include <nlohmann/json.hpp>
namespace platform {
using json = nlohmann::ordered_json;
class RocAuc {
public:
RocAuc() = default;
double compute(const std::vector<std::vector<double>>& y_proba, const std::vector<int>& y_test);
double compute(const torch::Tensor& y_proba, const torch::Tensor& y_test);
private:
double compute_common(size_t nSamples, size_t classIdx);
std::vector<std::pair<double, int>> scoresAndLabels;
std::vector<int> y_test;
};
}
#endif

View File

@@ -1,13 +1,15 @@
#include <sstream>
#include "Scores.h"
#include "common/Utils.h" // tensorToVector
#include "common/Colors.h"
namespace platform {
Scores::Scores(torch::Tensor& y_test, torch::Tensor& y_pred, int num_classes, std::vector<std::string> labels) : num_classes(num_classes), labels(labels)
Scores::Scores(torch::Tensor& y_test, torch::Tensor& y_proba, int num_classes, std::vector<std::string> labels) : num_classes(num_classes), labels(labels), y_test(y_test), y_proba(y_proba)
{
if (labels.size() == 0) {
init_default_labels();
}
total = y_test.size(0);
auto y_pred = y_proba.argmax(1);
accuracy_value = (y_pred == y_test).sum().item<float>() / total;
init_confusion_matrix();
for (int i = 0; i < total; i++) {
@@ -40,6 +42,48 @@ namespace platform {
}
compute_accuracy_value();
}
float Scores::auc()
{
size_t nSamples = y_test.numel();
if (nSamples == 0) return 0;
// In binary classification problem there's no need to calculate the average of the AUCs
auto nClasses = num_classes;
if (num_classes == 2)
nClasses = 1;
auto y_testv = tensorToVector<int>(y_test);
std::vector<double> aucScores(nClasses, 0.0);
std::vector<std::pair<double, int>> scoresAndLabels;
for (size_t classIdx = 0; classIdx < nClasses; ++classIdx) {
if (classIdx >= y_proba.size(1)) {
std::cerr << "AUC warning - class index out of range" << std::endl;
return 0;
}
scoresAndLabels.clear();
for (size_t i = 0; i < nSamples; ++i) {
scoresAndLabels.emplace_back(y_proba[i][classIdx].item<float>(), y_testv[i] == classIdx ? 1 : 0);
}
std::sort(scoresAndLabels.begin(), scoresAndLabels.end(), std::greater<>());
std::vector<double> tpr, fpr;
double tp = 0, fp = 0;
double totalPos = std::count(y_testv.begin(), y_testv.end(), classIdx);
double totalNeg = nSamples - totalPos;
for (const auto& [score, label] : scoresAndLabels) {
if (label == 1) {
tp += 1;
} else {
fp += 1;
}
tpr.push_back(tp / totalPos);
fpr.push_back(fp / totalNeg);
}
double auc = 0.0;
for (size_t i = 1; i < tpr.size(); ++i) {
auc += 0.5 * (fpr[i] - fpr[i - 1]) * (tpr[i] + tpr[i - 1]);
}
aucScores[classIdx] = auc;
}
return std::accumulate(aucScores.begin(), aucScores.end(), 0.0) / nClasses;
}
Scores Scores::create_aggregate(const json& data, const std::string key)
{
auto scores = Scores(data[key][0]);

View File

@@ -9,10 +9,11 @@ namespace platform {
using json = nlohmann::ordered_json;
class Scores {
public:
Scores(torch::Tensor& y_test, torch::Tensor& y_pred, int num_classes, std::vector<std::string> labels = {});
Scores(torch::Tensor& y_test, torch::Tensor& y_proba, int num_classes, std::vector<std::string> labels = {});
explicit Scores(const json& confusion_matrix_);
static Scores create_aggregate(const json& data, const std::string key);
float accuracy();
float auc();
float f1_score(int num_class);
float f1_weighted();
float f1_macro();
@@ -34,6 +35,9 @@ namespace platform {
int total;
std::vector<std::string> labels;
torch::Tensor confusion_matrix; // Rows ar actual, columns are predicted
torch::Tensor null_t; // Covenient null tensor needed when confusion_matrix constructor is used
torch::Tensor& y_test = null_t; // for ROC AUC
torch::Tensor& y_proba = null_t; // for ROC AUC
int label_len = 16;
int dlen = 9;
int ndec = 7;

View File

@@ -1,21 +0,0 @@
#ifndef COMMAND_PARSER_H
#define COMMAND_PARSER_H
#include <string>
#include <vector>
#include <tuple>
namespace platform {
class CommandParser {
public:
CommandParser() = default;
std::tuple<char, int, bool> parse(const std::string& color, const std::vector<std::tuple<std::string, char, bool>>& options, const char defaultCommand, const int minIndex, const int maxIndex);
char getCommand() const { return command; };
int getIndex() const { return index; };
std::string getErrorMessage() const { return errorMessage; };
private:
std::string errorMessage;
char command;
int index;
};
} /* namespace platform */
#endif

View File

@@ -3,29 +3,33 @@
#include <string>
#include <algorithm>
#include "folding.hpp"
#include "common/Colors.h"
#include "common/CLocale.h"
#include "common/Paths.h"
#include "CommandParser.h"
#include "OptionsMenu.h"
#include "ManageScreen.h"
#include "reports/DatasetsConsole.h"
#include "reports/ReportConsole.h"
#include "reports/ReportExcel.h"
#include "reports/ReportExcelCompared.h"
#include <bayesnet/classifiers/TAN.h>
#include "CPPFImdlp.h"
#include <fimdlp/CPPFImdlp.h>
namespace platform {
const std::string STATUS_OK = "Ok.";
const std::string STATUS_COLOR = Colors::GREEN();
ManageScreen::ManageScreen(int rows, int cols, const std::string& model, const std::string& score, const std::string& platform, bool complete, bool partial, bool compare) :
rows{ rows }, cols{ cols }, complete{ complete }, partial{ partial }, compare{ compare }, didExcel(false), results(ResultsManager(model, score, platform, complete, partial))
{
results.load();
openExcel = false;
workbook = NULL;
this->rows = std::max(0, rows - 6); // 6 is the number of lines used by the menu & header
cols = std::max(cols, 140);
maxModel = results.maxModelSize();
maxTitle = results.maxTitleSize();
header_lengths = { 3, 10, maxModel, 11, 10, 12, 2, 3, 7, maxTitle };
header_labels = { " #", "Date", "Model", "Score Name", "Score", "Platform", "SD", "C/P", "Time", "Title" };
sort_fields = { "Date", "Model", "Score", "Time" };
updateSize(rows, cols);
// Initializes the paginator for each output type (experiments, datasets, result)
for (int i = 0; i < static_cast<int>(OutputType::Count); i++) {
paginator.push_back(Paginator(this->rows, results.size()));
@@ -36,12 +40,41 @@ namespace platform {
subIndex = -1;
output_type = OutputType::EXPERIMENTS;
}
void ManageScreen::computeSizes()
{
int minTitle = 10;
// set 10 chars as minimum for Title
auto header_title = header_lengths[header_lengths.size() - 1];
min_columns = std::accumulate(header_lengths.begin(), header_lengths.end(), 0) + header_lengths.size() - header_title + minTitle;
maxTitle = minTitle + cols - min_columns;
header_lengths[header_lengths.size() - 1] = maxTitle;
cols = std::min(cols, min_columns + maxTitle);
for (auto& paginator_ : paginator) {
paginator_.setPageSize(rows);
}
}
bool ManageScreen::checkWrongColumns()
{
if (min_columns > cols) {
std::cerr << Colors::MAGENTA() << "Make screen bigger to fit the results! " + std::to_string(min_columns - cols) + " columns needed! " << std::endl;
return true;
}
return false;
}
void ManageScreen::updateSize(int rows_, int cols_)
{
rows = std::max(6, rows_ - 6); // 6 is the number of lines used by the menu & header
cols = cols_;
computeSizes();
}
void ManageScreen::doMenu()
{
if (results.empty()) {
std::cout << Colors::MAGENTA() << "No results found!" << Colors::RESET() << std::endl;
std::cerr << Colors::MAGENTA() << "No results found!" << Colors::RESET() << std::endl;
return;
}
if (checkWrongColumns())
return;
results.sortResults(sort_field, sort_type);
list(STATUS_OK, STATUS_COLOR);
menu();
@@ -115,7 +148,6 @@ namespace platform {
}
void ManageScreen::list_result(const std::string& status_message, const std::string& status_color)
{
auto data = results.at(index).getJson();
ReportConsole report(data, compare);
auto header_text = report.getHeader();
@@ -140,11 +172,9 @@ namespace platform {
// Status Area
//
footer(status_message, status_color);
}
void ManageScreen::list_detail(const std::string& status_message, const std::string& status_color)
{
auto data = results.at(index).getJson();
ReportConsole report(data, compare, subIndex);
auto header_text = report.getHeader();
@@ -169,7 +199,6 @@ namespace platform {
// Status Area
//
footer(status_message, status_color);
}
void ManageScreen::list_datasets(const std::string& status_message, const std::string& status_color)
{
@@ -193,7 +222,6 @@ namespace platform {
// Status Area
//
footer(status_message, status_color);
}
void ManageScreen::list_experiments(const std::string& status_message, const std::string& status_color)
{
@@ -201,17 +229,9 @@ namespace platform {
// header
//
header();
//
// Field names
//
int maxModel = results.maxModelSize();
int maxTitle = results.maxTitleSize();
std::vector<int> header_lengths = { 3, 10, maxModel, 11, 10, 12, 2, 3, 7, maxTitle };
std::cout << Colors::RESET();
std::string arrow_dn = Symbols::down_arrow + " ";
std::string arrow_up = Symbols::up_arrow + " ";
std::vector<std::string> header_labels = { " #", "Date", "Model", "Score Name", "Score", "Platform", "SD", "C/P", "Time", "Title" };
std::vector<std::string> sort_fields = { "Date", "Model", "Score", "Time" };
for (int i = 0; i < header_labels.size(); i++) {
std::string suffix = "", color = Colors::GREEN();
int diff = 0;
@@ -230,11 +250,15 @@ namespace platform {
//
// Results
//
if (results.empty()) {
std::cout << "No results found!" << std::endl;
return;
}
auto [index_from, index_to] = paginator[static_cast<int>(output_type)].getOffset();
for (int i = index_from; i <= index_to; i++) {
auto color = (i % 2) ? Colors::BLUE() : Colors::CYAN();
std::cout << color << std::setw(3) << std::fixed << std::right << i << " ";
std::cout << results.at(i).to_string(maxModel) << std::endl;
std::cout << results.at(i).to_string(maxModel, maxTitle) << std::endl;
}
//
// Status Area
@@ -289,16 +313,28 @@ namespace platform {
}
std::pair<std::string, std::string> ManageScreen::sortList()
{
std::cout << Colors::YELLOW() << "Choose sorting field (date='d', score='s', time='t', model='m', ascending='+', descending='-'): ";
std::vector<std::string> fields = { "Date", "Model", "Score", "Time" };
std::vector<std::tuple<std::string, char, bool>> sortOptions = {
{"date", 'd', false},
{"score", 's', false},
{"time", 't', false},
{"model", 'm', false},
{"ascending+", '+', false},
{"descending-", '-', false}
};
auto sortMenu = OptionsMenu(sortOptions, Colors::YELLOW(), Colors::RED(), cols);
std::string invalid_option = "Invalid sorting option";
std::string line;
char option;
getline(std::cin, line);
if (line.size() == 0 || line.size() > 1) {
bool parserError = true; // force the first iteration
while (parserError) {
if (checkWrongColumns())
return { Colors::RED(), "Invalid column size" };
auto [min_index, max_index] = paginator[static_cast<int>(output_type)].getOffset();
std::tie(option, index, parserError) = sortMenu.parse(' ', 0, 0);
sortMenu.updateColumns(cols);
if (parserError) {
return { Colors::RED(), invalid_option };
}
option = line[0];
}
switch (option) {
case 'd':
sort_field = SortField::DATE;
@@ -322,7 +358,7 @@ namespace platform {
return { Colors::RED(), invalid_option };
}
results.sortResults(sort_field, sort_type);
return { Colors::GREEN(), "Sorted by " + fields[static_cast<int>(sort_field)] + " " + (sort_type == SortType::ASC ? "ascending" : "descending") };
return { Colors::GREEN(), "Sorted by " + sort_fields[static_cast<int>(sort_field)] + " " + (sort_type == SortType::ASC ? "ascending" : "descending") };
}
void ManageScreen::menu()
{
@@ -333,17 +369,17 @@ namespace platform {
std::vector<std::tuple<std::string, char, bool>> mainOptions = {
{"quit", 'q', false},
{"list", 'l', false},
{"delete", 'D', true},
{"Delete", 'D', true},
{"datasets", 'd', false},
{"hide", 'h', true},
{"sort", 's', false},
{"report", 'r', true},
{"excel", 'e', true},
{"title", 't', true},
{"set A", 'a', true},
{"set B", 'b', true},
{"set A", 'A', true},
{"set B", 'B', true},
{"compare A~B", 'c', false},
{"Page", 'p', true},
{"page", 'p', true},
{"Page+", '+', false },
{"Page-", '-', false}
};
@@ -352,25 +388,33 @@ namespace platform {
{"quit", 'q', false},
{"report", 'r', true},
{"list", 'l', false},
{"excel", 'e', false},
{"excel", 'e', true},
{"back", 'b', false},
{"Page", 'p', true},
{"page", 'p', true},
{"Page+", '+', false},
{"Page-", '-', false}
};
auto parser = CommandParser();
while (!finished) {
auto main_menu = OptionsMenu(mainOptions, Colors::IGREEN(), Colors::YELLOW(), cols);
auto list_menu = OptionsMenu(listOptions, Colors::IBLUE(), Colors::YELLOW(), cols);
OptionsMenu& menu = output_type == OutputType::EXPERIMENTS ? main_menu : list_menu;
bool parserError = true; // force the first iteration
while (parserError) {
int index_menu;
auto [min_index, max_index] = paginator[static_cast<int>(output_type)].getOffset();
std::tie(option, index_menu, parserError) = menu.parse('r', min_index, max_index);
if (output_type == OutputType::EXPERIMENTS) {
std::tie(option, index, parserError) = parser.parse(Colors::IGREEN(), mainOptions, 'r', min_index, max_index);
index = index_menu;
} else {
std::tie(option, subIndex, parserError) = parser.parse(Colors::IBLUE(), listOptions, 'r', min_index, max_index);
subIndex = index_menu;
}
if (min_columns > cols) {
std::cerr << "Make screen bigger to fit the results! " + std::to_string(min_columns - cols) + " columns needed! " << std::endl;
return;
}
menu.updateColumns(cols);
if (parserError) {
list(parser.getErrorMessage(), Colors::RED());
list(menu.getErrorMessage(), Colors::RED());
}
}
switch (option) {
@@ -405,7 +449,7 @@ namespace platform {
case 'q':
finished = true;
break;
case 'a':
case 'A':
if (index == index_B) {
list("A and B cannot be the same!", Colors::RED());
break;
@@ -413,7 +457,7 @@ namespace platform {
index_A = index;
list("A set to " + std::to_string(index), Colors::GREEN());
break;
case 'b': // set_b or back to list
case 'B': // set_b or back to list
if (output_type == OutputType::EXPERIMENTS) {
if (index == index_A) {
list("A and B cannot be the same!", Colors::RED());
@@ -465,6 +509,7 @@ namespace platform {
std::cout << "Hiding " << filename << std::endl;
results.hideResult(index, Paths::hiddenResults());
status_message = filename + " hidden! (moved to " + Paths::hiddenResults() + ")";
paginator[static_cast<int>(OutputType::EXPERIMENTS)].setTotal(results.size());
list(status_message, Colors::YELLOW());
}
break;

View File

@@ -2,6 +2,7 @@
#define MANAGE_SCREEN_H
#include <xlsxwriter.h>
#include "ResultsManager.h"
#include "common/Colors.h"
#include "Paginator.hpp"
namespace platform {
@@ -17,6 +18,7 @@ namespace platform {
ManageScreen(int rows, int cols, const std::string& model, const std::string& score, const std::string& platform, bool complete, bool partial, bool compare);
~ManageScreen() = default;
void doMenu();
void updateSize(int rows, int cols);
private:
void list(const std::string& status, const std::string& color);
void list_experiments(const std::string& status, const std::string& color);
@@ -28,12 +30,15 @@ namespace platform {
std::string report_compared();
std::pair<std::string, std::string> sortList();
std::string getVersions();
void computeSizes();
bool checkWrongColumns();
void menu();
void header();
void footer(const std::string& status, const std::string& color);
OutputType output_type;
int rows;
int cols;
int min_columns;
int index;
int subIndex;
int index_A, index_B; // used for comparison of experiments
@@ -43,6 +48,10 @@ namespace platform {
bool complete;
bool partial;
bool compare;
int maxModel, maxTitle;
std::vector<std::string> header_labels;
std::vector<int> header_lengths;
std::vector<std::string> sort_fields;
SortField sort_field = SortField::DATE;
SortType sort_type = SortType::DESC;
std::vector<Paginator> paginator;

View File

@@ -1,30 +1,46 @@
#include "CommandParser.h"
#include "OptionsMenu.h"
#include <iostream>
#include <sstream>
#include <algorithm>
#include "common/Colors.h"
#include "common/Utils.h"
namespace platform {
std::tuple<char, int, bool> CommandParser::parse(const std::string& color, const std::vector<std::tuple<std::string, char, bool>>& options, const char defaultCommand, const int minIndex, const int maxIndex)
std::string OptionsMenu::to_string()
{
bool first = true;
std::string result = color_normal + "Options: (";
size_t size = 10; // Size of "Options: ("
for (auto& option : options) {
if (!first) {
result += ", ";
size += 2;
}
std::string title = std::get<0>(option);
auto pos = title.find(std::get<1>(option));
result += color_normal + title.substr(0, pos) + color_bold + title.substr(pos, 1) + color_normal + title.substr(pos + 1);
size += title.size();
first = false;
}
if (size + 3 > cols) { // 3 is the size of the "): " at the end
result = "";
first = true;
for (auto& option : options) {
if (!first) {
result += color_normal + ", ";
}
result += color_bold + std::get<1>(option);
first = false;
}
}
result += "): ";
return result;
}
std::tuple<char, int, bool> OptionsMenu::parse(char defaultCommand, int minIndex, int maxIndex)
{
bool finished = false;
while (!finished) {
std::stringstream oss;
std::cout << to_string();
std::string line;
oss << color << "Options (";
bool first = true;
for (auto& option : options) {
if (first) {
first = false;
} else {
oss << ", ";
}
oss << std::get<char>(option) << "=" << std::get<std::string>(option);
}
oss << "): ";
std::cout << oss.str();
getline(std::cin, line);
line = trim(line);
if (line.size() == 0) {

26
src/manage/OptionsMenu.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef OPTIONS_MENU_H
#define OPTIONS_MENU_H
#include <string>
#include <vector>
#include <tuple>
namespace platform {
class OptionsMenu {
public:
OptionsMenu(std::vector<std::tuple<std::string, char, bool>>& options, std::string color_normal, std::string color_bold, int cols) : options(options), color_normal(color_normal), color_bold(color_bold), cols(cols) {}
std::string to_string();
std::tuple<char, int, bool> parse(char defaultCommand, int minIndex, int maxIndex);
char getCommand() const { return command; };
int getIndex() const { return index; };
std::string getErrorMessage() const { return errorMessage; };
void updateColumns(int cols) { this->cols = cols; }
private:
std::vector<std::tuple<std::string, char, bool>>& options;
std::string color_normal, color_bold;
int cols;
std::string errorMessage;
char command;
int index;
};
} /* namespace platform */
#endif

View File

@@ -21,7 +21,7 @@ namespace platform {
// Body header
row = 2;
int col = 0;
for (const auto& name : { "", "Dataset", "Samples", "Features", "#Numer.", "Classes", "Balance" }) {
for (const auto& name : { "#", "Dataset", "Samples", "Features", "#Numer.", "Classes", "Balance" }) {
writeString(row, col++, name, "bodyHeader");
}
// Body

View File

@@ -2,6 +2,7 @@
#include <locale>
#include "best/BestScore.h"
#include "common/CLocale.h"
#include "common/Timer.h"
#include "ReportConsole.h"
#include "main/Scores.h"
@@ -23,12 +24,30 @@ namespace platform {
+ " random seeds. " + data["date"].get<std::string>() + " " + data["time"].get<std::string>()
);
sheader << headerLine(data["title"].get<std::string>());
std::string discretize_algo = data.find("discretization_algorithm") != data.end() ? data["discretization_algorithm"].get<std::string>() : "ORIGINAL";
std::string algorithm = data["discretized"].get<bool>() ? " (" + discretize_algo + ")" : "";
std::string smooth = data.find("smooth_strategy") != data.end() ? data["smooth_strategy"].get<std::string>() : "ORIGINAL";
std::string stratified;
try {
stratified = data["stratified"].get<bool>() ? "True" : "False";
}
catch (nlohmann::json::type_error) {
stratified = data["stratified"].get<int>() == 1 ? "True" : "False";
}
std::string discretized;
try {
discretized = data["discretized"].get<bool>() ? "True" : "False";
}
catch (nlohmann::json::type_error) {
discretized = data["discretized"].get<int>() == 1 ? "True" : "False";
}
sheader << headerLine(
"Random seeds: " + fromVector("seeds") + " Discretized: " + (data["discretized"].get<bool>() ? "True" : "False")
+ " Stratified: " + (data["stratified"].get<bool>() ? "True" : "False")
"Random seeds: " + fromVector("seeds") + " Discretized: " + discretized + " " + algorithm
+ " Stratified: " + stratified + " Smooth Strategy: " + smooth
);
oss << "Execution took " << std::setprecision(2) << std::fixed << data["duration"].get<float>()
<< " seconds, " << data["duration"].get<float>() / 3600 << " hours, on " << data["platform"].get<std::string>();
Timer timer;
oss << "Execution took " << timer.translate2String(data["duration"].get<float>())
<< " on " << data["platform"].get<std::string>() << " Language: " << data["language"].get<std::string>();
sheader << headerLine(oss.str());
sheader << headerLine("Score is " + data["score_name"].get<std::string>());
sheader << std::string(MAXL, '*') << std::endl;
@@ -121,12 +140,35 @@ namespace platform {
}
}
}
line.str("");
if (lastResult.find("score_train") == lastResult.end()) {
line << headerLine("Train score: -");
} else {
line << headerLine("Train score: " + std::to_string(lastResult["score_train"].get<double>()));
}
vbody.push_back(line.str()); sbody << line.str();
line.str(""); line << headerLine(fVector("Train scores: ", lastResult["scores_train"], 14, 12));
vbody.push_back(line.str()); sbody << line.str();
line.str(""); line << headerLine("Test score: " + std::to_string(lastResult["score"].get<double>()));
vbody.push_back(line.str()); sbody << line.str();
line.str(""); line << headerLine(fVector("Test scores: ", lastResult["scores_test"], 14, 12));
vbody.push_back(line.str()); sbody << line.str();
line.str("");
if (lastResult.find("train_time") == lastResult.end()) {
line << headerLine("Train time: -");
} else {
line << headerLine("Train time: " + std::to_string(lastResult["train_time"].get<double>()));
}
vbody.push_back(line.str()); sbody << line.str();
line.str(""); line << headerLine(fVector("Train times: ", lastResult["times_train"], 10, 3));
vbody.push_back(line.str()); sbody << line.str();
line.str("");
if (lastResult.find("test_time") == lastResult.end()) {
line << headerLine("Test time: -");
} else {
line << headerLine("Test time: " + std::to_string(lastResult["test_time"].get<double>()));
}
vbody.push_back(line.str()); sbody << line.str();
line.str(""); line << headerLine(fVector("Test times: ", lastResult["times_test"], 10, 3));
vbody.push_back(line.str()); sbody << line.str();

View File

@@ -49,7 +49,10 @@ namespace platform {
worksheet_merge_range(worksheet, 0, 0, 0, 12, message.c_str(), styles["headerFirst"]);
worksheet_merge_range(worksheet, 1, 0, 1, 12, data["title"].get<std::string>().c_str(), styles["headerRest"]);
worksheet_merge_range(worksheet, 2, 0, 3, 0, ("Score is " + data["score_name"].get<std::string>()).c_str(), styles["headerRest"]);
worksheet_merge_range(worksheet, 2, 1, 3, 3, "Execution time", styles["headerRest"]);
writeString(2, 1, "Smooth", "headerRest");
std::string smooth = data.find("smooth_strategy") != data.end() ? data["smooth_strategy"].get<std::string>() : "ORIGINAL";
writeString(3, 1, smooth, "headerSmall");
worksheet_merge_range(worksheet, 2, 2, 3, 3, "Execution time", styles["headerRest"]);
oss << std::setprecision(2) << std::fixed << data["duration"].get<float>() << " s";
worksheet_merge_range(worksheet, 2, 4, 2, 5, oss.str().c_str(), styles["headerRest"]);
oss.str("");
@@ -65,7 +68,9 @@ namespace platform {
worksheet_merge_range(worksheet, 3, 10, 3, 11, oss.str().c_str(), styles["headerSmall"]);
oss.str("");
oss.clear();
oss << "Discretized: " << (data["discretized"].get<bool>() ? "True" : "False");
std::string discretize_algo = data.find("discretization_algorithm") != data.end() ? data["discretization_algorithm"].get<std::string>() : "mdlp";
std::string algorithm = data["discretized"].get<bool>() ? " (" + discretize_algo + ")" : "";
oss << "Discretized: " << (data["discretized"].get<bool>() ? "True" : "False") << algorithm;
worksheet_write_string(worksheet, 3, 12, oss.str().c_str(), styles["headerSmall"]);
}
void ReportExcel::header_notes(int row)

View File

@@ -6,6 +6,7 @@
#include "common/DotEnv.h"
#include "common/CLocale.h"
#include "common/Paths.h"
#include "common/Symbols.h"
#include "Result.h"
namespace platform {
@@ -71,20 +72,39 @@ namespace platform {
std::string Result::getFilename() const
{
std::ostringstream oss;
oss << "results_" << data.at("score_name").get<std::string>() << "_" << data.at("model").get<std::string>() << "_"
<< data.at("platform").get<std::string>() << "_" << data["date"].get<std::string>() << "_"
<< data["time"].get<std::string>() << "_" << (data["stratified"] ? "1" : "0") << ".json";
std::string stratified;
try {
stratified = data["stratified"].get<bool>() ? "1" : "0";
}
catch (nlohmann::json_abi_v3_11_3::detail::type_error) {
stratified = data["stratified"].get<int>() == 1 ? "1" : "0";
}
oss << "results_"
<< data.at("score_name").get<std::string>() << "_"
<< data.at("model").get<std::string>() << "_"
<< data.at("platform").get<std::string>() << "_"
<< data["date"].get<std::string>() << "_"
<< data["time"].get<std::string>() << "_"
<< stratified << ".json";
return oss.str();
}
std::string Result::to_string(int maxModel) const
std::string Result::to_string(int maxModel, int maxTitle) const
{
auto tmp = ConfigLocale();
std::stringstream oss;
std::string s = data["stratified"].get<bool>() ? "S" : "";
std::string d = data["discretized"].get<bool>() ? "D" : "";
std::string sd = s + d;
std::string s, d;
try {
s = data["stratified"].get<bool>() ? "S" : " ";
}
catch (nlohmann::json_abi_v3_11_3::detail::type_error) {
s = data["stratified"].get<int>() == 1 ? "S" : " ";
}
try {
d = data["discretized"].get<bool>() ? "D" : " ";
}
catch (nlohmann::json_abi_v3_11_3::detail::type_error) {
d = data["discretized"].get<int>() == 1 ? "D" : " ";
}
auto duration = data["duration"].get<double>();
double durationShow = duration > 3600 ? duration / 3600 : duration > 60 ? duration / 60 : duration;
std::string durationUnit = duration > 3600 ? "h" : duration > 60 ? "m" : "s";
@@ -93,11 +113,15 @@ namespace platform {
oss << std::setw(11) << std::left << data["score_name"].get<std::string>() << " ";
oss << std::right << std::setw(10) << std::setprecision(7) << std::fixed << score << " ";
oss << std::left << std::setw(12) << data["platform"].get<std::string>() << " ";
oss << std::left << std::setw(2) << sd << " ";
oss << s << d << " ";
auto completeString = isComplete() ? "C" : "P";
oss << std::setw(1) << " " << completeString << " ";
oss << std::setw(5) << std::right << std::setprecision(2) << std::fixed << durationShow << " " << durationUnit << " ";
oss << std::setw(50) << std::left << data["title"].get<std::string>() << " ";
auto title = data["title"].get<std::string>();
if (title.size() > maxTitle) {
title = title.substr(0, maxTitle - 1) + Symbols::ellipsis;
}
oss << std::setw(maxTitle) << std::left << title;
return oss.str();
}
}

View File

@@ -18,7 +18,7 @@ namespace platform {
void save();
// Getters
json getJson();
std::string to_string(int maxModel) const;
std::string to_string(int maxModel, int maxTitle) const;
std::string getFilename() const;
std::string getDate() const { return data["date"].get<std::string>(); };
std::string getTime() const { return data["time"].get<std::string>(); };
@@ -28,10 +28,12 @@ namespace platform {
std::string getModel() const { return data["model"].get<std::string>(); };
std::string getPlatform() const { return data["platform"].get<std::string>(); };
std::string getScoreName() const { return data["score_name"].get<std::string>(); };
bool isComplete() const { return complete; };
json getData() const { return data; }
// Setters
void setTitle(const std::string& title) { data["title"] = title; };
void setSmoothStrategy(const std::string& smooth_strategy) { data["smooth_strategy"] = smooth_strategy; };
void setDiscretizationAlgorithm(const std::string& discretization_algo) { data["discretization_algorithm"] = discretization_algo; };
void setLanguage(const std::string& language) { data["language"] = language; };
void setLanguageVersion(const std::string& language_version) { data["language_version"] = language_version; };
@@ -45,7 +47,6 @@ namespace platform {
void setStratified(bool stratified) { data["stratified"] = stratified; };
void setNFolds(int nfolds) { data["folds"] = nfolds; };
void setPlatform(const std::string& platform_name) { data["platform"] = platform_name; };
private:
json data;
bool complete;

View File

@@ -20,7 +20,7 @@ namespace platform {
// Body header
row = 2;
int col = 0;
for (const auto& name : { "", "Model", "Date", "Time", "Score", "Hyperparameters" }) {
for (const auto& name : { "#", "Model", "Date", "Time", "Score", "Hyperparameters" }) {
writeString(row, col++, name, "bodyHeader");
}
// Body

View File

@@ -3,7 +3,7 @@ if(ENABLE_TESTING)
include_directories(
${Platform_SOURCE_DIR}/src
${Platform_SOURCE_DIR}/lib/argparse/include
${Platform_SOURCE_DIR}/lib/mdlp
${Platform_SOURCE_DIR}/lib/mdlp/src
${Platform_SOURCE_DIR}/lib/Files
${Platform_SOURCE_DIR}/lib/json/include
${Platform_SOURCE_DIR}/lib/folding
@@ -13,10 +13,10 @@ if(ENABLE_TESTING)
)
set(TEST_SOURCES_PLATFORM
TestUtils.cpp TestPlatform.cpp TestResult.cpp TestScores.cpp
${Platform_SOURCE_DIR}/src/common/Datasets.cpp ${Platform_SOURCE_DIR}/src/common/Dataset.cpp
${Platform_SOURCE_DIR}/src/common/Datasets.cpp ${Platform_SOURCE_DIR}/src/common/Dataset.cpp ${Platform_SOURCE_DIR}/src/common/Discretization.cpp
${Platform_SOURCE_DIR}/src/main/Scores.cpp
)
add_executable(${TEST_PLATFORM} ${TEST_SOURCES_PLATFORM})
target_link_libraries(${TEST_PLATFORM} PUBLIC "${TORCH_LIBRARIES}" ArffFiles mdlp Catch2::Catch2WithMain BayesNet)
target_link_libraries(${TEST_PLATFORM} PUBLIC "${TORCH_LIBRARIES}" mdlp Catch2::Catch2WithMain BayesNet)
add_test(NAME ${TEST_PLATFORM} COMMAND ${TEST_PLATFORM})
endif(ENABLE_TESTING)

View File

@@ -7,14 +7,15 @@
#include <string>
#include "TestUtils.h"
#include "folding.hpp"
#include <ArffFiles.hpp>
#include <bayesnet/classifiers/TAN.h>
#include "config.h"
#include "config_platform.h"
TEST_CASE("Test Platform version", "[Platform]")
{
std::string version = { platform_project_version.begin(), platform_project_version.end() };
REQUIRE(version == "1.0.4");
REQUIRE(version == "1.1.0");
}
TEST_CASE("Test Folding library version", "[Folding]")
{
@@ -24,10 +25,15 @@ TEST_CASE("Test Folding library version", "[Folding]")
TEST_CASE("Test BayesNet version", "[BayesNet]")
{
std::string version = bayesnet::TAN().getVersion();
REQUIRE(version == "1.0.5.1");
REQUIRE(version == "1.0.6");
}
TEST_CASE("Test mdlp version", "[mdlp]")
{
std::string version = mdlp::CPPFImdlp::version();
REQUIRE(version == "1.1.2");
REQUIRE(version == "2.0.0");
}
TEST_CASE("Test Arff version", "[Arff]")
{
std::string version = ArffFiles().version();
REQUIRE(version == "1.1.0");
}

View File

@@ -7,19 +7,20 @@
#include "common/DotEnv.h"
#include "common/Datasets.h"
#include "common/Paths.h"
#include "config.h"
#include "config_platform.h"
TEST_CASE("ZeroR comparison in reports", "[Report]")
{
auto dotEnv = platform::DotEnv(true);
auto margin = 1e-2;
std::string dataset = "liver-disorders";
auto margin = 1e-4;
std::string dataset_name = "liver-disorders";
auto dt = platform::Datasets(false, platform::Paths::datasets());
dt.loadDataset(dataset);
std::vector<int> distribution = dt.getClassesCounts(dataset);
double nSamples = dt.getNSamples(dataset);
auto& dataset = dt.getDataset(dataset_name);
dataset.load();
std::vector<int> distribution = dataset.getClassesCounts();
double nSamples = dataset.getNSamples();
std::vector<int>::iterator maxValue = max_element(distribution.begin(), distribution.end());
double mark = *maxValue / nSamples * (1 + margin);
REQUIRE(mark == Catch::Approx(0.585507f).epsilon(1e-5));
REQUIRE(mark == Catch::Approx(0.57976811f).epsilon(margin));
}

View File

@@ -9,7 +9,7 @@
#include "common/Paths.h"
#include "common/Colors.h"
#include "main/Scores.h"
#include "config.h"
#include "config_platform.h"
using json = nlohmann::ordered_json;
auto epsilon = 1e-4;
@@ -128,7 +128,7 @@ TEST_CASE("Confusion Matrix JSON", "[Scores]")
REQUIRE(res_json_str["Car"][1] == 2);
REQUIRE(res_json_str["Car"][2] == 3);
}
TEST_CASE("Classification Report", "[Scores]") -
TEST_CASE("Classification Report", "[Scores]")
{
std::vector<int> y_test = { 0, 2, 2, 2, 2, 0, 1, 2, 0, 2 };
std::vector<int> y_pred = { 0, 1, 2, 2, 1, 1, 1, 0, 0, 2 };

View File

@@ -1,5 +1,5 @@
#include "TestUtils.h"
#include "config.h"
#include "config_platform.h"
class Paths {
public:

View File

@@ -5,8 +5,8 @@
#include <vector>
#include <map>
#include <tuple>
#include "ArffFiles.h"
#include "CPPFImdlp.h"
#include <ArffFiles.hpp>
#include <fimdlp/CPPFImdlp.h>
bool file_exists(const std::string& name);
std::pair<vector<mdlp::labels_t>, map<std::string, int>> discretize(std::vector<mdlp::samples_t>& X, mdlp::labels_t& y, std::vector<string> features);

View File

@@ -1,8 +1,8 @@
diabetes,class, all
ecoli,class, all
glass,Type, all
iris,class, all
kdd_JapaneseVowels,speaker, [2,3,4,5,6,7,8,9,10,11,12,13]
letter,class, all
liver-disorders,selector, all
mfeat-factors,class, all
diabetes;class;all
ecoli;class;all
glass;Type,all
iris;class;all
kdd_JapaneseVowels;speaker;[2,3,4,5,6,7,8,9,10,11,12,13]
letter;class;all
liver-disorders;selector;all
mfeat-factors;class;all