From d0cb45bce2b3060f46a07cd5ffabf4a37573a537 Mon Sep 17 00:00:00 2001 From: Gijs Date: Thu, 30 Jun 2022 18:15:24 +0200 Subject: [PATCH] Initial commit --- receiver/Tree_0.h | 92 +++++++++++ receiver/Tree_1.h | 67 ++++++++ receiver/Tree_2.h | 92 +++++++++++ receiver/Tree_3.h | 52 ++++++ receiver/Tree_4.h | 97 +++++++++++ receiver/Tree_of_unease.h | 57 +++++++ receiver/Tree_of_unease_0.h | 62 +++++++ receiver/Tree_of_unease_1.h | 57 +++++++ receiver/Tree_of_unease_2.h | 52 ++++++ receiver/Tree_of_unease_3.h | 37 +++++ receiver/Tree_of_unease_4.h | 67 ++++++++ receiver/receiver.ino | 297 ++++++++++++++++++++++++++++++++++ sender/observations.h | 154 ++++++++++++++++++ sender/sender.ino | 97 +++++++++++ tree-thoughts/Readme.md | 7 + tree-thoughts/data.csv | 28 ++++ tree-thoughts/encode_model.py | 159 ++++++++++++++++++ tree-thoughts/parse-unease.py | 44 +++++ 18 files changed, 1518 insertions(+) create mode 100644 receiver/Tree_0.h create mode 100644 receiver/Tree_1.h create mode 100644 receiver/Tree_2.h create mode 100644 receiver/Tree_3.h create mode 100644 receiver/Tree_4.h create mode 100644 receiver/Tree_of_unease.h create mode 100644 receiver/Tree_of_unease_0.h create mode 100644 receiver/Tree_of_unease_1.h create mode 100644 receiver/Tree_of_unease_2.h create mode 100644 receiver/Tree_of_unease_3.h create mode 100644 receiver/Tree_of_unease_4.h create mode 100644 receiver/receiver.ino create mode 100644 sender/observations.h create mode 100644 sender/sender.ino create mode 100644 tree-thoughts/Readme.md create mode 100644 tree-thoughts/data.csv create mode 100644 tree-thoughts/encode_model.py create mode 100644 tree-thoughts/parse-unease.py diff --git a/receiver/Tree_0.h b/receiver/Tree_0.h new file mode 100644 index 0000000..b74fa2b --- /dev/null +++ b/receiver/Tree_0.h @@ -0,0 +1,92 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class DecisionTree + { + public: + const char* predict(float (*getObservationValue)(int), bool (*consider)(float, int, float)) + { + if ((consider)((getObservationValue)(3), 3, 1.0)) { + if ((consider)((getObservationValue)(3), 3, 0.2)) { + if ((consider)((getObservationValue)(3), 3, 0.1)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((consider)((getObservationValue)(2), 2, 4.8)) { + if ((consider)((getObservationValue)(2), 2, 4.6)) { + if ((consider)((getObservationValue)(2), 2, 3.9)) { + if ((consider)((getObservationValue)(3), 3, 1.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + if ((consider)((getObservationValue)(1), 1, 2.7)) { + if ((consider)((getObservationValue)(2), 2, 5.0)) { + if ((consider)((getObservationValue)(3), 3, 1.5)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Virginica"; + } + } + else { + if ((consider)((getObservationValue)(2), 2, 5.1)) { + if ((consider)((getObservationValue)(3), 3, 1.8)) { + if ((consider)((getObservationValue)(2), 2, 5.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + if ((consider)((getObservationValue)(0), 0, 6.0)) { + if ((consider)((getObservationValue)(1), 1, 3.2)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Virginica"; + } + } + } + else { + return "Iris Virginica"; + } + } + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_1.h b/receiver/Tree_1.h new file mode 100644 index 0000000..efd124a --- /dev/null +++ b/receiver/Tree_1.h @@ -0,0 +1,67 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class DecisionTree + { + public: + const char* predict(float (*getObservationValue)(int), bool (*consider)(float, int, float)) + { + if ((consider)((getObservationValue)(2), 2, 3.3)) { + if ((consider)((getObservationValue)(2), 2, 1.4)) { + if ((consider)((getObservationValue)(3), 3, 0.1)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((consider)((getObservationValue)(2), 2, 4.8)) { + if ((consider)((getObservationValue)(3), 3, 1.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + if ((consider)((getObservationValue)(2), 2, 4.9)) { + if ((consider)((getObservationValue)(0), 0, 6.2)) { + return "Iris Versicolour"; + } + else { + return "Iris Virginica"; + } + } + else { + if ((consider)((getObservationValue)(3), 3, 1.8)) { + if ((consider)((getObservationValue)(3), 3, 1.7)) { + if ((consider)((getObservationValue)(0), 0, 6.3)) { + return "Iris Virginica"; + } + else { + return "Iris Virginica"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Virginica"; + } + } + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_2.h b/receiver/Tree_2.h new file mode 100644 index 0000000..8d4a9e6 --- /dev/null +++ b/receiver/Tree_2.h @@ -0,0 +1,92 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class DecisionTree + { + public: + const char* predict(float (*getObservationValue)(int), bool (*consider)(float, int, float)) + { + if ((consider)((getObservationValue)(3), 3, 1.0)) { + if ((consider)((getObservationValue)(1), 1, 3.0)) { + if ((consider)((getObservationValue)(0), 0, 4.4)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((consider)((getObservationValue)(3), 3, 1.8)) { + if ((consider)((getObservationValue)(3), 3, 1.5)) { + if ((consider)((getObservationValue)(3), 3, 1.4)) { + if ((consider)((getObservationValue)(1), 1, 2.5)) { + if ((consider)((getObservationValue)(3), 3, 1.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + if ((consider)((getObservationValue)(1), 1, 3.0)) { + if ((consider)((getObservationValue)(0), 0, 6.2)) { + return "Iris Virginica"; + } + else { + if ((consider)((getObservationValue)(0), 0, 6.3)) { + return "Iris Versicolour"; + } + else { + if ((consider)((getObservationValue)(2), 2, 5.1)) { + if ((consider)((getObservationValue)(3), 3, 1.5)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Virginica"; + } + } + } + } + else { + if ((consider)((getObservationValue)(2), 2, 5.8)) { + if ((consider)((getObservationValue)(2), 2, 4.5)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Virginica"; + } + } + } + } + else { + return "Iris Virginica"; + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_3.h b/receiver/Tree_3.h new file mode 100644 index 0000000..9ffe8ce --- /dev/null +++ b/receiver/Tree_3.h @@ -0,0 +1,52 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class DecisionTree + { + public: + const char* predict(float (*getObservationValue)(int), bool (*consider)(float, int, float)) + { + if ((consider)((getObservationValue)(2), 2, 3.5)) { + if ((consider)((getObservationValue)(1), 1, 4.0)) { + if ((consider)((getObservationValue)(1), 1, 3.0)) { + if ((consider)((getObservationValue)(2), 2, 1.4)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((consider)((getObservationValue)(3), 3, 1.8)) { + if ((consider)((getObservationValue)(0), 0, 5.7)) { + if ((consider)((getObservationValue)(0), 0, 5.2)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Virginica"; + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_4.h b/receiver/Tree_4.h new file mode 100644 index 0000000..5b13c02 --- /dev/null +++ b/receiver/Tree_4.h @@ -0,0 +1,97 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class DecisionTree + { + public: + const char* predict(float (*getObservationValue)(int), bool (*consider)(float, int, float)) + { + if ((consider)((getObservationValue)(2), 2, 3.3)) { + if ((consider)((getObservationValue)(0), 0, 5.1)) { + if ((consider)((getObservationValue)(3), 3, 0.2)) { + if ((consider)((getObservationValue)(3), 3, 0.1)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((consider)((getObservationValue)(3), 3, 1.8)) { + if ((consider)((getObservationValue)(3), 3, 1.6)) { + if ((consider)((getObservationValue)(0), 0, 6.1)) { + if ((consider)((getObservationValue)(2), 2, 4.2)) { + if ((consider)((getObservationValue)(0), 0, 5.8)) { + if ((consider)((getObservationValue)(1), 1, 3.0)) { + if ((consider)((getObservationValue)(0), 0, 5.6)) { + if ((consider)((getObservationValue)(3), 3, 1.3)) { + if ((consider)((getObservationValue)(0), 0, 5.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + if ((consider)((getObservationValue)(1), 1, 3.3)) { + return "Iris Virginica"; + } + else { + return "Iris Versicolour"; + } + } + } + else { + if ((consider)((getObservationValue)(2), 2, 4.9)) { + if ((consider)((getObservationValue)(0), 0, 6.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Virginica"; + } + } + else { + return "Iris Virginica"; + } + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_of_unease.h b/receiver/Tree_of_unease.h new file mode 100644 index 0000000..4327cbe --- /dev/null +++ b/receiver/Tree_of_unease.h @@ -0,0 +1,57 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class TreeOfUnease + { + public: + const char* traverse(float (*getObservationValue)(int), bool (*considerWithUnease)(const char*, float, int, float)) + { + if ((considerWithUnease)("Message", (getObservationValue)(3), 3, 1.0)) { + if ((considerWithUnease)("Message", (getObservationValue)(1), 1, 3.1)) { + if ((considerWithUnease)("Message", (getObservationValue)(2), 2, 1.3)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((considerWithUnease)("Message", (getObservationValue)(3), 3, 1.8)) { + if ((considerWithUnease)("Message", (getObservationValue)(2), 2, 5.6)) { + if ((considerWithUnease)("Message", (getObservationValue)(3), 3, 1.5)) { + if ((considerWithUnease)("Message", (getObservationValue)(3), 3, 1.3)) { + if ((considerWithUnease)("Message", (getObservationValue)(1), 1, 2.2)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Virginica"; + } + } + else { + return "Iris Virginica"; + } + } + } + private: + }; + } +} diff --git a/receiver/Tree_of_unease_0.h b/receiver/Tree_of_unease_0.h new file mode 100644 index 0000000..dd65d3f --- /dev/null +++ b/receiver/Tree_of_unease_0.h @@ -0,0 +1,62 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class TreeOfUnease + { + public: + const char* traverse(float (*getObservationValue)(int), bool (*considerWithUnease)(const char*, float, int, float)) + { + if ((considerWithUnease)("Meise herbarium",(getObservationValue)(3), 3, 1.0)) { + if ((considerWithUnease)("About their « living herbarium »",(getObservationValue)(3), 3, 0.2)) { + if ((considerWithUnease)("Some of plants there come from Leopold II his garden",(getObservationValue)(3), 3, 0.1)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((considerWithUnease)("About the creation of its herbarium",(getObservationValue)(2), 2, 4.8)) { + if ((considerWithUnease)("The basis of the Meise Collection is an herbarium of the plants from Brazil",(getObservationValue)(2), 2, 4.6)) { + if ((considerWithUnease)("Leopold 2 bought it to assess whether to make Brazil a colony. But he chose Congo instead",(getObservationValue)(2), 2, 3.9)) { + return "?"; + } + else { + return "Iris Versicolour"; + } + } + else { + return "Iris Versicolour"; + } + } + else { + if ((considerWithUnease)("Most of Meise specimens were acquired when you did not need permission from local communities to collect plants",(getObservationValue)(1), 1, 2.7)) { + if ((considerWithUnease)("This seems to change with frameworks such as the Nagoya Protocol and the convention on biological diversity",(getObservationValue)(2), 2, 5.0)) { + return "?"; + } + else { + return "Iris Virginica"; + } + } + else { + if ((considerWithUnease)("And there does not seem to be project to give them back to the countries they were collected from",(getObservationValue)(2), 2, 5.1)) { + return "?"; + } + else { + return "Iris Virginica"; + } + } + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_of_unease_1.h b/receiver/Tree_of_unease_1.h new file mode 100644 index 0000000..e808e66 --- /dev/null +++ b/receiver/Tree_of_unease_1.h @@ -0,0 +1,57 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class TreeOfUnease + { + public: + const char* traverse(float (*getObservationValue)(int), bool (*considerWithUnease)(const char*, float, int, float)) + { + if ((considerWithUnease)("Meise Data",(getObservationValue)(2), 2, 3.3)) { + if ((considerWithUnease)("About their digital portal",(getObservationValue)(2), 2, 1.4)) { + if ((considerWithUnease)("The fields used to describe the specimens seem restrictive",(getObservationValue)(3), 3, 0.1)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((considerWithUnease)("About the content of the dataset",(getObservationValue)(2), 2, 4.8)) { + if ((considerWithUnease)("One of Meise’s expertise is coffee and they gather a lot of data about it",(getObservationValue)(3), 3, 1.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Versicolour"; + } + } + else { + if ((considerWithUnease)("The repartition of the samples is quite telling",(getObservationValue)(2), 2, 4.9)) { + if ((considerWithUnease)("1,5 % of type specimen were collected in Belgium, 22 % in RDC, 18 % in Brazil",(getObservationValue)(0), 0, 6.2)) { + return "Iris Versicolour"; + } + else { + return "Iris Virginica"; + } + } + else { + if ((considerWithUnease)("and mirrors the history of Botany",(getObservationValue)(3), 3, 1.8)) { + return "?"; + } + else { + return "Iris Virginica"; + } + } + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_of_unease_2.h b/receiver/Tree_of_unease_2.h new file mode 100644 index 0000000..7944e20 --- /dev/null +++ b/receiver/Tree_of_unease_2.h @@ -0,0 +1,52 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class TreeOfUnease + { + public: + const char* traverse(float (*getObservationValue)(int), bool (*considerWithUnease)(const char*, float, int, float)) + { + if ((considerWithUnease)("Classification",(getObservationValue)(3), 3, 1.0)) { + if ((considerWithUnease)("About the need for models",(getObservationValue)(1), 1, 3.0)) { + if ((considerWithUnease)("What is simple is always wrong. What is not is unusable. Paul Valéry 1942",(getObservationValue)(0), 0, 4.4)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((considerWithUnease)("About modeling from observations",(getObservationValue)(3), 3, 1.8)) { + if ((considerWithUnease)("it seems that we are reducing plants to a few numbered characteristics",(getObservationValue)(3), 3, 1.5)) { + if ((considerWithUnease)("Why not apply the same to humans ?",(getObservationValue)(3), 3, 1.4)) { + return "?"; + } + else { + return "Iris Versicolour"; + } + } + else { + if ((considerWithUnease)("Is the practice of categorisation bad in itself ?",(getObservationValue)(1), 1, 3.0)) { + return "?"; + } + else { + return "?"; + } + } + } + else { + return "Iris Virginica"; + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_of_unease_3.h b/receiver/Tree_of_unease_3.h new file mode 100644 index 0000000..7224d31 --- /dev/null +++ b/receiver/Tree_of_unease_3.h @@ -0,0 +1,37 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class TreeOfUnease + { + public: + const char* traverse(float (*getObservationValue)(int), bool (*considerWithUnease)(const char*, float, int, float)) + { + if ((considerWithUnease)("Iris Dataset",(getObservationValue)(2), 2, 3.5)) { + if ((considerWithUnease)("About the iris Dataset",(getObservationValue)(1), 1, 4.0)) { + if ((considerWithUnease)("Its origin are troubling to say the least",(getObservationValue)(1), 1, 3.0)) { + if ((considerWithUnease)("It was first published in the Annals of Eugenics… Is it wrong to display it ?",(getObservationValue)(2), 2, 1.4)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + return "?"; + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/Tree_of_unease_4.h b/receiver/Tree_of_unease_4.h new file mode 100644 index 0000000..335a6ca --- /dev/null +++ b/receiver/Tree_of_unease_4.h @@ -0,0 +1,67 @@ +#pragma once +#include +namespace PublishingHouse +{ + namespace RandomForest + { + class TreeOfUnease + { + public: + const char* traverse(float (*getObservationValue)(int), bool (*considerWithUnease)(const char*, float, int, float)) + { + if ((considerWithUnease)("Meise herbarium",(getObservationValue)(2), 2, 3.3)) { + if ((considerWithUnease)("About their « living herbarium »",(getObservationValue)(0), 0, 5.1)) { + if ((considerWithUnease)("Some of plants there come from Leopold II his garden",(getObservationValue)(3), 3, 0.2)) { + if ((considerWithUnease)("They don’t specify it, but you can infer it from their signs",(getObservationValue)(3), 3, 0.1)) { + return "Iris Setosa"; + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + return "Iris Setosa"; + } + } + else { + if ((considerWithUnease)("About the creation of its herbarium",(getObservationValue)(3), 3, 1.8)) { + if ((considerWithUnease)("The basis of the Meise Collection is an herbarium of the plants from Brazil",(getObservationValue)(3), 3, 1.6)) { + if ((considerWithUnease)("Leopold 2 bought it to assess whether to make Brazil a colony. But he chose Congo instead",(getObservationValue)(0), 0, 6.1)) { + return "?"; + } + else { + return "Iris Versicolour"; + } + } + else { + if ((considerWithUnease)("JSB Pohl contributed to it with 1479 specimens. He also brought back a pair of indigenous people.",(getObservationValue)(1), 1, 3.3)) { + return "Iris Virginica"; + } + else { + return "Iris Versicolour"; + } + } + } + else { + if ((considerWithUnease)("Most of Meise specimens were acquired when you did not need permission from local communities to collect plants",(getObservationValue)(2), 2, 4.9)) { + if ((considerWithUnease)("This seems to change with frameworks such as the Nagoya Protocol and the convention on biological diversity",(getObservationValue)(0), 0, 6.0)) { + return "Iris Versicolour"; + } + else { + return "Iris Virginica"; + } + } + else { + return "Iris Virginica"; + } + } + } + } + private: + }; + } +} \ No newline at end of file diff --git a/receiver/receiver.ino b/receiver/receiver.ino new file mode 100644 index 0000000..ba3c8ff --- /dev/null +++ b/receiver/receiver.ino @@ -0,0 +1,297 @@ +/********* + For display + GxEPD_Example : test example for e-Paper displays from Waveshare and from Dalian Good Display Inc. +// +// Created by Jean-Marc Zingg based on demo code from Good Display, +// available on http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html +// +// Supporting Arduino Forum Topics: +// Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0 +// Good Dispay ePaper for Arduino : https://forum.arduino.cc/index.php?topic=436411.0 + +// mapping suggestion for ESP32, e.g. LOLIN32, see .../variants/.../pins_arduino.h for your board +// NOTE: there are variants with different pins for SPI ! CHECK SPI PINS OF YOUR BOARD +// BUSY -> 4, RST -> 16, DC -> 17, CS -> SS(5), CLK -> SCK(18), DIN -> MOSI(23), GND -> GND, 3.3V -> 3.3V +*********/ + + +/********* + For communication + Rui Santos + Complete project details at https://RandomNerdTutorials.com/esp-now-one-to-many-esp32-esp8266/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files. + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. +*********/ + + +// include library, include base class, make path known +#include //->display +#include +#include +//#include "unease.h" +#include "Tree_0.h" +#include "Tree_of_unease_0.h" +//#include "Tree_1.h" +//#include "Tree_of_unease_1.h" +//#include "Tree_2.h" +//#include "Tree_of_unease_2.h" +//#include "Tree_3.h" +//#include "Tree_of_unease_3.h" +//#include "Tree_4.h" +//#include "Tree_of_unease_4.h" + +PublishingHouse :: RandomForest :: TreeOfUnease treeOfUnease; +PublishingHouse :: RandomForest :: DecisionTree tree; + + + +//const int randSeed = 6730/; +const int featureCount = 4; +const int treeNumber = 0; +int considerationStep = 0; +String considerationTraces = String(); +const int verticalOffset = 19; +float lastObservation[featureCount] = { 0.0, 0.0, 0.0, 0.0 }; +const char *answer; +const int answerPrefixLength = 3; +const char *answerPrefix[] = { + "It might be ", + "I think it is ", + "I would say it is " +}; + +/////////////////// +///DISPLAY/// +////////////////// + + +// select the display class to use, only one + +#include // 1.54" b/w + +// FreeFonts from Adafruit_GFX +//#include / +//#include // +#include +#include +//#include / +//#include / + + +#include +#include + + +#if defined(ESP32) + +// for SPI pin definitions see e.g.: +// C:\Users\xxx\Documents\Arduino\hardware\espressif\esp32\variants\lolin32\pins_arduino.h + +GxIO_Class io(SPI, /*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16); // arbitrary selection of 17, 16 +GxEPD_Class display(io, /*RST=*/ 16, /*BUSY=*/ 4); // arbitrary selection of (16), 4 + +#else + +// for SPI pin definitions see e.g.: +// C:\Users\xxx\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.21\variants\standard\pins_arduino.h + +GxIO_Class io(SPI, /*CS=*/ SS, /*DC=*/ 8, /*RST=*/ 9); // arbitrary selection of 8, 9 selected for default of GxEPD_Class +GxEPD_Class display(io, /*RST=*/ 9, /*BUSY=*/ 7); // default selection of (9), 7 + +#endif + +float getObservationValue (int key) +{ + return lastObservation[key]; +} + +/////////////////// +///COMMUNICATION/// +////////////////// +//Structure example to receive data +//Must match the sender structure +// This is the main loop of the program. It is ran whenever the board +// receives an observation. +void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) +{ + Serial.println("received data"); + clearDisplay(); + display.println("Received an observation:"); + memcpy(&lastObservation, incomingData, sizeof(lastObservation)); + for(int i=0; i < featureCount; i++) { + //lastObservation[i] = *incomingData[i]; + Serial.println(lastObservation[i]); + display.println(lastObservation[i]); + } + display.update(); + delay(10 * 1000); + classify(); +} + + +void clearDisplay (){ + display.fillScreen(GxEPD_WHITE); + display.setTextColor(GxEPD_BLACK); + display.setFont(&FreeSans12pt7b); + display.setCursor(0, verticalOffset); +// display.update(); +} + +void mightPrintUnease () { +// Serial.println(random(2));/ +// Serial.println(random(2));/ + if (random(3) < 2) { + clearDisplay(); + display.fillScreen(GxEPD_BLACK); + display.setTextColor(GxEPD_WHITE); + display.setFont(&FreeSansOblique12pt7b); + Serial.println("Printing an unease."); +// display.println(tracesOfUnease[random(t/raceCount)]); + display.update(); + display.fillScreen(GxEPD_WHITE); + display.setTextColor(GxEPD_BLACK); + display.setFont(&FreeSans12pt7b); + delay(15 * 1000); + } +} + + +// Consider a given value, feature number and threshold. +// If the value is lower than the threshold, answer yes +// otherwise answer no. +// +// Once every three calls a trace of unease will be printed +bool consider(float value, int feature, float threshold) +{ + //mightPrintUnease(); + clearDisplay(); + Serial.println("Printing consideration"); + clearDisplay(); + Serial.println(considerationTraces); + display.println(considerationTraces); + // display.print("Step "); + // display.print(considerationStep); + display.print(".\nConsidering fea-\nture "); + display.print(feature); + display.print(", is it smaller\nthan "); + display.print(threshold); + display.print("?\n"); + display.update(); + delay(10 * 1000); + considerationStep = considerationStep + 1; + if (value < threshold) { + + display.println("Yes. Going left."); + considerationTraces.concat(considerationStep); + considerationTraces.concat(": L "); + display.update(); + delay(10 * 1000); + return true; + } + else { + display.println("No. Going right."); + considerationTraces.concat(considerationStep); + considerationTraces.concat(": R "); + display.update(); + delay(10 * 1000); + return false; + } +} + + +// Print the forwarded unease and consider the given value, +// feature number and threshold. If the value is lower than the threshold, +// return true otherwise false. +bool considerWithUnease(const char *unease, float value, int feature, float threshold) +{ + Serial.println("Printing unease"); + clearDisplay(); + Serial.println(unease); + display.println(unease); // Test whether it's * ? + display.update(); + delay(20 * 1000); + + if (value < threshold) { + return true; + } + else { + return false; + } +} + +void classify () +{ + considerationStep = 0; + Serial.println("Starting classification"); + if (random(3) == 0) { + treeOfUnease.traverse(&getObservationValue, &considerWithUnease); + } + else { + answer=tree.predict(&getObservationValue, &consider); + clearDisplay(); +// display.print("I say: "); + display.print(answerPrefix[random(answerPrefixLength)]); + display.print(answer); + display.update(); + } +} + +void setup() +{ + randomSeed(analogRead(26)); + // Setup serial + Serial.begin(115200); + Serial.println(); + Serial.println("setup"); + display.init(115200); // enable diagnostic output on Serial + Serial.println("Starting...\n"); + + // Setup WiFi + // Set device as a Wi-Fi Station + WiFi.mode(WIFI_STA); + // print our mac address + Serial.println(WiFi.macAddress()); + // Init ESP-NOW + if (esp_now_init() != ESP_OK) { + Serial.println("Error initializing ESP-NOW"); + for(;;) { delay(1); } // do not initialize wait forever + } + Serial.println("initialized ESP-NOW"); + + // First output to the world! + clearDisplay(); + display.setRotation(2); +// display.println("Hello!"); + display.print("Tree "); + display.print(treeNumber); + display.print("\n"); + display.println("You can reach me at"); + display.println(WiFi.macAddress()); + display.update(); + + delay(1000); + + // Register for a callback function that will be called when data is received + esp_now_register_recv_cb(OnDataRecv); + +} + +void loop() +{ + delay(500); +// clearDisplay(); +// if (dataReceived%2==0){ +// Serial.println(); +//// dataReceived_s= "whispers in my ears"; +//// predicted= " an oak "; +// } +// else{ +// Serial.println(clf.predict(irisSample, println, clear, delay)); +//// dataReceived_s= "murmurations"; +//// predicted= "an Iris"; +// } +} diff --git a/sender/observations.h b/sender/observations.h new file mode 100644 index 0000000..5015ba7 --- /dev/null +++ b/sender/observations.h @@ -0,0 +1,154 @@ +const int observationCount = 150; +const int observationFeatureCount = 4; +float observations[observationCount][observationFeatureCount] = { + {5.1, 3.5, 1.4, 0.2}, + {4.9, 3.0, 1.4, 0.2}, + {4.7, 3.2, 1.3, 0.2}, + {4.6, 3.1, 1.5, 0.2}, + {5.0, 3.6, 1.4, 0.2}, + {5.4, 3.9, 1.7, 0.4}, + {4.6, 3.4, 1.4, 0.3}, + {5.0, 3.4, 1.5, 0.2}, + {4.4, 2.9, 1.4, 0.2}, + {4.9, 3.1, 1.5, 0.1}, + {5.4, 3.7, 1.5, 0.2}, + {4.8, 3.4, 1.6, 0.2}, + {4.8, 3.0, 1.4, 0.1}, + {4.3, 3.0, 1.1, 0.1}, + {5.8, 4.0, 1.2, 0.2}, + {5.7, 4.4, 1.5, 0.4}, + {5.4, 3.9, 1.3, 0.4}, + {5.1, 3.5, 1.4, 0.3}, + {5.7, 3.8, 1.7, 0.3}, + {5.1, 3.8, 1.5, 0.3}, + {5.4, 3.4, 1.7, 0.2}, + {5.1, 3.7, 1.5, 0.4}, + {4.6, 3.6, 1.0, 0.2}, + {5.1, 3.3, 1.7, 0.5}, + {4.8, 3.4, 1.9, 0.2}, + {5.0, 3.0, 1.6, 0.2}, + {5.0, 3.4, 1.6, 0.4}, + {5.2, 3.5, 1.5, 0.2}, + {5.2, 3.4, 1.4, 0.2}, + {4.7, 3.2, 1.6, 0.2}, + {4.8, 3.1, 1.6, 0.2}, + {5.4, 3.4, 1.5, 0.4}, + {5.2, 4.1, 1.5, 0.1}, + {5.5, 4.2, 1.4, 0.2}, + {4.9, 3.1, 1.5, 0.1}, + {5.0, 3.2, 1.2, 0.2}, + {5.5, 3.5, 1.3, 0.2}, + {4.9, 3.1, 1.5, 0.1}, + {4.4, 3.0, 1.3, 0.2}, + {5.1, 3.4, 1.5, 0.2}, + {5.0, 3.5, 1.3, 0.3}, + {4.5, 2.3, 1.3, 0.3}, + {4.4, 3.2, 1.3, 0.2}, + {5.0, 3.5, 1.6, 0.6}, + {5.1, 3.8, 1.9, 0.4}, + {4.8, 3.0, 1.4, 0.3}, + {5.1, 3.8, 1.6, 0.2}, + {4.6, 3.2, 1.4, 0.2}, + {5.3, 3.7, 1.5, 0.2}, + {5.0, 3.3, 1.4, 0.2}, + {7.0, 3.2, 4.7, 1.4}, + {6.4, 3.2, 4.5, 1.5}, + {6.9, 3.1, 4.9, 1.5}, + {5.5, 2.3, 4.0, 1.3}, + {6.5, 2.8, 4.6, 1.5}, + {5.7, 2.8, 4.5, 1.3}, + {6.3, 3.3, 4.7, 1.6}, + {4.9, 2.4, 3.3, 1.0}, + {6.6, 2.9, 4.6, 1.3}, + {5.2, 2.7, 3.9, 1.4}, + {5.0, 2.0, 3.5, 1.0}, + {5.9, 3.0, 4.2, 1.5}, + {6.0, 2.2, 4.0, 1.0}, + {6.1, 2.9, 4.7, 1.4}, + {5.6, 2.9, 3.6, 1.3}, + {6.7, 3.1, 4.4, 1.4}, + {5.6, 3.0, 4.5, 1.5}, + {5.8, 2.7, 4.1, 1.0}, + {6.2, 2.2, 4.5, 1.5}, + {5.6, 2.5, 3.9, 1.1}, + {5.9, 3.2, 4.8, 1.8}, + {6.1, 2.8, 4.0, 1.3}, + {6.3, 2.5, 4.9, 1.5}, + {6.1, 2.8, 4.7, 1.2}, + {6.4, 2.9, 4.3, 1.3}, + {6.6, 3.0, 4.4, 1.4}, + {6.8, 2.8, 4.8, 1.4}, + {6.7, 3.0, 5.0, 1.7}, + {6.0, 2.9, 4.5, 1.5}, + {5.7, 2.6, 3.5, 1.0}, + {5.5, 2.4, 3.8, 1.1}, + {5.5, 2.4, 3.7, 1.0}, + {5.8, 2.7, 3.9, 1.2}, + {6.0, 2.7, 5.1, 1.6}, + {5.4, 3.0, 4.5, 1.5}, + {6.0, 3.4, 4.5, 1.6}, + {6.7, 3.1, 4.7, 1.5}, + {6.3, 2.3, 4.4, 1.3}, + {5.6, 3.0, 4.1, 1.3}, + {5.5, 2.5, 4.0, 1.3}, + {5.5, 2.6, 4.4, 1.2}, + {6.1, 3.0, 4.6, 1.4}, + {5.8, 2.6, 4.0, 1.2}, + {5.0, 2.3, 3.3, 1.0}, + {5.6, 2.7, 4.2, 1.3}, + {5.7, 3.0, 4.2, 1.2}, + {5.7, 2.9, 4.2, 1.3}, + {6.2, 2.9, 4.3, 1.3}, + {5.1, 2.5, 3.0, 1.1}, + {5.7, 2.8, 4.1, 1.3}, + {6.3, 3.3, 6.0, 2.5}, + {5.8, 2.7, 5.1, 1.9}, + {7.1, 3.0, 5.9, 2.1}, + {6.3, 2.9, 5.6, 1.8}, + {6.5, 3.0, 5.8, 2.2}, + {7.6, 3.0, 6.6, 2.1}, + {4.9, 2.5, 4.5, 1.7}, + {7.3, 2.9, 6.3, 1.8}, + {6.7, 2.5, 5.8, 1.8}, + {7.2, 3.6, 6.1, 2.5}, + {6.5, 3.2, 5.1, 2.0}, + {6.4, 2.7, 5.3, 1.9}, + {6.8, 3.0, 5.5, 2.1}, + {5.7, 2.5, 5.0, 2.0}, + {5.8, 2.8, 5.1, 2.4}, + {6.4, 3.2, 5.3, 2.3}, + {6.5, 3.0, 5.5, 1.8}, + {7.7, 3.8, 6.7, 2.2}, + {7.7, 2.6, 6.9, 2.3}, + {6.0, 2.2, 5.0, 1.5}, + {6.9, 3.2, 5.7, 2.3}, + {5.6, 2.8, 4.9, 2.0}, + {7.7, 2.8, 6.7, 2.0}, + {6.3, 2.7, 4.9, 1.8}, + {6.7, 3.3, 5.7, 2.1}, + {7.2, 3.2, 6.0, 1.8}, + {6.2, 2.8, 4.8, 1.8}, + {6.1, 3.0, 4.9, 1.8}, + {6.4, 2.8, 5.6, 2.1}, + {7.2, 3.0, 5.8, 1.6}, + {7.4, 2.8, 6.1, 1.9}, + {7.9, 3.8, 6.4, 2.0}, + {6.4, 2.8, 5.6, 2.2}, + {6.3, 2.8, 5.1, 1.5}, + {6.1, 2.6, 5.6, 1.4}, + {7.7, 3.0, 6.1, 2.3}, + {6.3, 3.4, 5.6, 2.4}, + {6.4, 3.1, 5.5, 1.8}, + {6.0, 3.0, 4.8, 1.8}, + {6.9, 3.1, 5.4, 2.1}, + {6.7, 3.1, 5.6, 2.4}, + {6.9, 3.1, 5.1, 2.3}, + {5.8, 2.7, 5.1, 1.9}, + {6.8, 3.2, 5.9, 2.3}, + {6.7, 3.3, 5.7, 2.5}, + {6.7, 3.0, 5.2, 2.3}, + {6.3, 2.5, 5.0, 1.9}, + {6.5, 3.0, 5.2, 2.0}, + {6.2, 3.4, 5.4, 2.3}, + {5.9, 3.0, 5.1, 1.8} +}; \ No newline at end of file diff --git a/sender/sender.ino b/sender/sender.ino new file mode 100644 index 0000000..83edcba --- /dev/null +++ b/sender/sender.ino @@ -0,0 +1,97 @@ +#include +#include +#include "WiFi.h" +#include "observations.h" +#include +#include + +const int timeBetweenBroadcasts = 5 * 60 * 1000; +uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { + Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success 1" : "Delivery Fail 1"); + if (status ==0){ + Serial.println("Delivery Success 2"); + } + else{ + Serial.println("Delivery fail 2"); + } + + Serial.println(); +} + +void setup() +{ + // Seed random number generator + randomSeed(analogRead(26)); + // 2700451 + // 5827809 + + Serial.begin(115200); + + Serial.println("Starting...\n"); + + // Set device as a Wi-Fi Station + WiFi.mode(WIFI_STA); + + Serial.println(WiFi.macAddress()); + + // Init ESP-NOW + if (esp_now_init() != ESP_OK) { + Serial.println("Error initializing ESP-NOW"); + for(;;) { delay(1); } // do not initialize wait forever + } + + Serial.println("initialized ESP-NOW"); + + + // Once ESPNow is successfully Init, we will register for Send CB to + // get the status of Trasnmitted packet + esp_now_register_send_cb(OnDataSent); + + // Register peer + esp_now_peer_info_t peerInfo; + memset(&peerInfo, 0, sizeof(peerInfo)); + memcpy(peerInfo.peer_addr, broadcastAddress, 6); + peerInfo.channel = 0; + peerInfo.encrypt = false; + + // Add peer + if (esp_now_add_peer(&peerInfo) != ESP_OK){ + Serial.println("Failed to add peer"); + for(;;) { delay(1); } // do not initialize wait forever + } +} + +float pickedObservation[observationFeatureCount]; +const int datasize=observationFeatureCount*sizeof(float); +unsigned int pickedIndex; + +void loop() +{ + pickedIndex = random(observationCount); + + Serial.print("Picked index "); + Serial.print(pickedIndex); + Serial.println(); + + // Fix. Less ugly way of copying? + for (int i=0; i < observationFeatureCount; i++) { + pickedObservation[i] = observations[pickedIndex][i]; + Serial.println(pickedObservation[i]); + } + //pickedObservation[4] = observations[pickedIndex]; + + // Send message via ESP-NOW (size of an int is 4 bytes on ESP32) + esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) pickedObservation, datasize); + + if (result == ESP_OK) { + Serial.println("Sent observation with success"); + } + else { + Serial.println("Error sending the data"); + } + + Serial.println(WiFi.macAddress()); + delay(timeBetweenBroadcasts); +} diff --git a/tree-thoughts/Readme.md b/tree-thoughts/Readme.md new file mode 100644 index 0000000..4914b2d --- /dev/null +++ b/tree-thoughts/Readme.md @@ -0,0 +1,7 @@ +# A Council of trees + +Code is meant to run on ESP-32 boards. + +The sender broadcasts an observation, over WiFi. + +On receiving the data the receivers classify trees using a decision tree model. diff --git a/tree-thoughts/data.csv b/tree-thoughts/data.csv new file mode 100644 index 0000000..cf97250 --- /dev/null +++ b/tree-thoughts/data.csv @@ -0,0 +1,28 @@ +I have a thought about the Meise herbarium...,...about their « living herbarium »...,...some of plants there come from Leopold II his garden:,"...they don’t specify it, but you can infer it from their signs" +,,,...(how) Should they be exhibited ? +,,...some of the plants are engineered or kept just there...,…the sansevira was even long thought to be extinct in the wild +,,,..or cultivated to flourish the desks of Belgian civil servants +,...about the creation of its herbarium...,...the basis of the Meise Collection is an herbarium of the plants from Brazil...,...Leopold II bought it to assess Brazil as a colony. But he chose Congo instead +,,,...JSB Pohl contributed to it with 1479 specimens. He also brought back a pair of indigenous people. +,,...most specimens were acquired when permission from local communities wasn’t required…,...this seems to change with frameworks such as the Nagoya Protocol and the convention on biological diversity +,,,…and there does not seem to be project to give them back to the countries they were collected from +But on the data of Meise…,...and their digital portal...,...the fields used to describe the specimens seem restrictive,"...there is no field for indigenous names, indigenous use or possible help that the collecter had when collecting the plant" +,,,...is a plant collected or extracted ? What makes the difference ? +,,...an interesting term is a « type specimen »…,"...the ‘type specimen‘, is the first descrition a specimen for western science. Meise has 63,652 type specimens." +,,,...a type specimen cannot be living. The only way to make a type specimen of an endling is to kill it. +,...and the content of the dataset…,...one of Meise’s expertise is coffee and they gather a lot of data about it…,...yet Coffee does not grow in Belgium +,,,...an expertise they inherited from colonisation and that they can only sustain through their relationships with former colonies +,,...the repartition of the samples is quite telling...,"...1,5 % of type specimen were collected in Belgium, 22 % in RDC, 18 % in Brazil" +,,,...and mirrors the history of Botany +I feel unease on classification...,...about the need for models…,...what is simple is always wrong. What is not is unusable. Paul Valéry 1942…,...it is the core problem of abstract reasoning +,,,...but usable for who? And for what? +,,"...all models are wrong, but some are useful...",...but are they really ? +,,,"...it’s la carte et le territoire again, isn’t it ?" +,...about modeling from observations…,...it seems that we are reducing plants to a few numbered characteristics…,...why not apply the same to humans ? +,,,...is the practice of categorisation bad in itself ? +,,"...a model is an abstraction, considering only a limited set of measurable properties…",...that’s a lot to consider +,,,...we tend to forget that there is more to reality than this. +There is something with the Iris Dataset,...its origin are troubling to say the least…,...it was first published in the Annals of Eugenics… Is it wrong to display it ?, +,,...its creator was a fervent Eugenist… Should we still use it ?, +,...species in this dataset are lineraly separable.. ,...which make classification algorithms reach perfect accuracy…,...this also explains why it is so popular in Machine Learning education. +,,,...but the data in the world is seldom as simply separable and correlated as this. diff --git a/tree-thoughts/encode_model.py b/tree-thoughts/encode_model.py new file mode 100644 index 0000000..b352c73 --- /dev/null +++ b/tree-thoughts/encode_model.py @@ -0,0 +1,159 @@ +indent_prefix = 2 * ' ' + + +def indent_line(line): + return indent_prefix + line + + +def encode_branch_with_unease(branch): + if isinstance(branch, dict): + if 'label' in branch: + return encode_tree_with_unease(branch) + else: + return [ indent_line('return "?";') ] + elif isinstance(branch, (int, float)): + return [ indent_line('return {};'.format(branch)) ] + else: + return [ indent_line('return "{}";'.format(branch)) ] + +# if value at index is smaller than threshold +# enter left, else right + + +def encode_tree_with_unease(tree): + lines = [ + "if ((considerWithUnease)(\"{unease}\",(getObservationValue)({index}), {index}, {threshold})) {{".format(index=tree['index'], threshold=tree['value'], unease=tree['label']), + *encode_branch_with_unease(tree['left']), + "}", + "else {", + *encode_branch_with_unease(tree['right']), + "}" + ] + + return map(indent_line, lines) + + +def encode_branch(branch): + if isinstance(branch, dict): + return encode_tree(branch) + elif isinstance(branch, (int, float)): + return [ indent_line('return {};'.format(branch)) ] + else: + return [ indent_line('return "{}";'.format(branch)) ] + +# if value at index is smaller than threshold +# enter left, else right + + +def encode_tree(tree): + lines = [ + "if ((consider)((getObservationValue)({index}), {index}, {threshold})) {{".format(index=tree['index'], threshold=tree['value']), + *encode_branch(tree['left']), + "}", + "else {", + *encode_branch(tree['right']), + "}" + ] + + return map(indent_line, lines) + + +def make_classifier (tree): + lines = [ + "#pragma once", + "#include ", + "namespace PublishingHouse", + "{", + *map(indent_line, [ + "namespace RandomForest", + "{", + *map(indent_line, [ + "class DecisionTree", + "{", + "public:", + *map(indent_line, [ + "const char* predict(float (*getObservationValue)(int), bool (*consider)(float, int, float))", + "{", + *encode_tree(tree), + "}", + ]), + "private:", + "};" + ]), + "}" + ]), + "}", + ] + + return('\n'.join(lines)) + + +def make_classifier_unease (tree): + lines = [ + "#pragma once", + "#include ", + "namespace PublishingHouse", + "{", + *map(indent_line, [ + "namespace RandomForest", + "{", + *map(indent_line, [ + "class TreeOfUnease", + "{", + "public:", + *map(indent_line, [ + "const char* traverse(float (*getObservationValue)(int), bool (*considerWithUnease)(const char*, float, int, float))", + "{", + *encode_tree_with_unease(tree), + "}", + ]), + "private:", + "};" + ]), + "}" + ]), + "}", + ] + + return('\n'.join(lines)) + + +if __name__ == '__main__': + import json + import os.path + import glob + basepath = os.path.dirname(os.path.realpath(__file__)) + globpath = os.path.realpath(os.path.join(basepath, 'random_forest_model_*-trees.json')) + globpath = os.path.realpath(os.path.join(basepath, '*.json')) + + models = glob.glob(globpath) + + # Search for exported models + print("Found:") + for k, modelpath in enumerate(models): + print("[{}] {}".format(k, modelpath)) + + model_key = int(input("Which model?\n")) + + modelpath = models[model_key] + + # Open model + with open(modelpath, 'r') as file_in: + # Parse the forest + forest = json.load(file_in) + modelname, _ = os.path.splitext(os.path.basename(modelpath)) + classifiernamepattern = 'Tree_{{}}.h'.format(modelname) + uneasenamepattern = 'Tree_of_unease_{{}}.h'.format(modelname) + + # Walk through the forest and visualize the trees + for idx, tree in enumerate(forest): + print('Transforming tree {} of {}'.format(idx, len(forest))) + + with open(os.path.join(basepath, classifiernamepattern.format(idx)), 'w') as h: + h.write(make_classifier(tree)) + + with open(os.path.join(basepath, uneasenamepattern.format(idx)), 'w') as h: + h.write(make_classifier_unease(tree)) + + + print('Classifiers placed in: {}'.format(basepath)) \ No newline at end of file diff --git a/tree-thoughts/parse-unease.py b/tree-thoughts/parse-unease.py new file mode 100644 index 0000000..dc4fa82 --- /dev/null +++ b/tree-thoughts/parse-unease.py @@ -0,0 +1,44 @@ +""" + Parses the spreadsheet into a tree structure +""" + +import csv +import os.path +import json + +columns = 4 +position = [ 0 for _ in range(columns) ] +tree = [] + +def parse_column_chunk (column, start): + label = data[start][column] + end = start + children = [] + + while end + 1 < len(data) and (data[end + 1][column] == label or data[end + 1][column] == ''): + end += 1 + + if column + 1 < columns: + child_end = start + + while child_end <= end and child_end < len(data): + _, child_end, child = parse_column_chunk(column + 1, child_end) + child_end += 1 + children.append(child) + + return (start, end, { 'label': label, 'children': children}) + +basepath = os.path.abspath(os.path.dirname(__file__)) + +with open(os.path.join(basepath, 'data.csv'), 'r') as csvfile: + reader = csv.reader(csvfile) + data = [row for row in reader] + + start = 0 + + while start < len(data): + _, end, node = parse_column_chunk(0, start) + start = end + 1 + tree.append(node) + + json.dump(tree, open(os.path.join(basepath, 'unease.json'), 'w'), ensure_ascii=False) \ No newline at end of file