easyresearch 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- easyresearch-0.1.0/LICENSE +21 -0
- easyresearch-0.1.0/PKG-INFO +198 -0
- easyresearch-0.1.0/README.md +161 -0
- easyresearch-0.1.0/pyproject.toml +49 -0
- easyresearch-0.1.0/setup.cfg +4 -0
- easyresearch-0.1.0/src/easyresearch/__init__.py +28 -0
- easyresearch-0.1.0/src/easyresearch/__main__.py +3 -0
- easyresearch-0.1.0/src/easyresearch/cli.py +165 -0
- easyresearch-0.1.0/src/easyresearch/common/__init__.py +1 -0
- easyresearch-0.1.0/src/easyresearch/common/hassanat.py +70 -0
- easyresearch-0.1.0/src/easyresearch/common/output.py +156 -0
- easyresearch-0.1.0/src/easyresearch/common/references.py +235 -0
- easyresearch-0.1.0/src/easyresearch/regression/__init__.py +12 -0
- easyresearch-0.1.0/src/easyresearch/regression/analysis.py +371 -0
- easyresearch-0.1.0/src/easyresearch/regression/data.py +114 -0
- easyresearch-0.1.0/src/easyresearch/regression/evaluation.py +298 -0
- easyresearch-0.1.0/src/easyresearch/regression/figures.py +190 -0
- easyresearch-0.1.0/src/easyresearch/regression/models.py +124 -0
- easyresearch-0.1.0/src/easyresearch/regression/report.py +316 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/__init__.py +4 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/classify.py +430 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/data.py +224 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/deep.py +208 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/evaluation.py +242 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/figures.py +209 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/forecast.py +302 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/models.py +257 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/report.py +268 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/signal_data.py +146 -0
- easyresearch-0.1.0/src/easyresearch/timeseries/signal_models.py +379 -0
- easyresearch-0.1.0/src/easyresearch.egg-info/PKG-INFO +198 -0
- easyresearch-0.1.0/src/easyresearch.egg-info/SOURCES.txt +39 -0
- easyresearch-0.1.0/src/easyresearch.egg-info/dependency_links.txt +1 -0
- easyresearch-0.1.0/src/easyresearch.egg-info/entry_points.txt +2 -0
- easyresearch-0.1.0/src/easyresearch.egg-info/requires.txt +16 -0
- easyresearch-0.1.0/src/easyresearch.egg-info/top_level.txt +1 -0
- easyresearch-0.1.0/tests/test_evaluation_integrity.py +226 -0
- easyresearch-0.1.0/tests/test_models_save.py +64 -0
- easyresearch-0.1.0/tests/test_research_references.py +54 -0
- easyresearch-0.1.0/tests/test_signals_and_regression.py +125 -0
- easyresearch-0.1.0/tests/test_timeseries.py +160 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ahmad Hassanat
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: easyresearch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Machine learning without programming: regression, time-series forecasting and signal classification cores, with honest evaluation and plain-language reports.
|
|
5
|
+
Author-email: Ahmad Hassanat <ahmad.hassanat@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ahmadhassant/easyresearch
|
|
8
|
+
Project-URL: Issues, https://github.com/ahmadhassant/easyresearch/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/ahmadhassant/easyresearch/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: machine learning,regression,forecasting,time series,no-code
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: easyclassifier<0.9,>=0.8.2
|
|
23
|
+
Requires-Dist: pandas>=1.5
|
|
24
|
+
Requires-Dist: numpy>=1.23
|
|
25
|
+
Requires-Dist: scikit-learn>=1.2
|
|
26
|
+
Requires-Dist: matplotlib>=3.6
|
|
27
|
+
Requires-Dist: joblib>=1.1
|
|
28
|
+
Requires-Dist: openpyxl>=3.0.10
|
|
29
|
+
Requires-Dist: statsmodels>=0.14
|
|
30
|
+
Requires-Dist: torch>=2.2
|
|
31
|
+
Provides-Extra: full
|
|
32
|
+
Requires-Dist: xgboost>=2; extra == "full"
|
|
33
|
+
Requires-Dist: lightgbm>=4; extra == "full"
|
|
34
|
+
Provides-Extra: test
|
|
35
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# EasyResearch
|
|
39
|
+
|
|
40
|
+
[](https://github.com/ahmadhassant/easyresearch/actions/workflows/tests.yml)
|
|
41
|
+
[](https://github.com/ahmadhassant/easyresearch/blob/main/LICENSE)
|
|
42
|
+
|
|
43
|
+
Machine learning without programming: analysis cores for researchers who do
|
|
44
|
+
not code. They follow the rules of
|
|
45
|
+
[EasyClassifier](https://github.com/ahmadhassant/easyclassifier):
|
|
46
|
+
|
|
47
|
+
* every step that learns from data uses training data only;
|
|
48
|
+
* models use their authors' default settings with fixed random seeds (no
|
|
49
|
+
tuning), so results are reproducible;
|
|
50
|
+
* when several models are compared, the selected one gets a separate,
|
|
51
|
+
honest final score;
|
|
52
|
+
* every run writes a plain-language report with a Methods paragraph,
|
|
53
|
+
figures, tables, predictions, the trained model, citations and a log.
|
|
54
|
+
|
|
55
|
+
The cores have no user interaction (settings in, results out), so a terminal
|
|
56
|
+
wizard, the desktop application or a web server can all drive them.
|
|
57
|
+
|
|
58
|
+
This repository holds:
|
|
59
|
+
|
|
60
|
+
| Folder | What it is |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `src/easyresearch`, `tests` | the analysis cores and their tests (`pip install easyresearch`) |
|
|
63
|
+
| `desktop` | **EasyResearch Desktop**, a Windows application that runs classification, regression, forecasting and signal classification without code ([README](https://github.com/ahmadhassant/easyresearch/blob/main/desktop/README.md)) |
|
|
64
|
+
| `benchmarks` | correctness and honesty benchmarks, with all results |
|
|
65
|
+
| `docs` | development guide, release plan, publishing steps |
|
|
66
|
+
| `papers` | the EasyResearch paper |
|
|
67
|
+
|
|
68
|
+
| Core | Task | Status |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| Classification | predict a category from a table | EasyClassifier 0.8.2 |
|
|
71
|
+
| `easyresearch.regression` | predict a number from a table | ready |
|
|
72
|
+
| `easyresearch.timeseries` – forecasting | forecast one series | ready |
|
|
73
|
+
| `easyresearch.timeseries` – signals | classify recordings (ECG, EEG, sensors) | ready (one channel) |
|
|
74
|
+
|
|
75
|
+
## Install and run
|
|
76
|
+
|
|
77
|
+
**Windows, without Python:** download `EasyResearchDesktop-0.1.0-setup.exe`
|
|
78
|
+
from the [Releases page](https://github.com/ahmadhassant/easyresearch/releases)
|
|
79
|
+
and run it. It installs EasyResearch Desktop with its own Python and every
|
|
80
|
+
package it needs; no administrator rights are required. Windows may show
|
|
81
|
+
"Windows protected your PC" because the program is not code-signed: choose
|
|
82
|
+
**More info**, then **Run anyway**.
|
|
83
|
+
|
|
84
|
+
**Python (any system):**
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install easyresearch # installs EasyClassifier, PyTorch and statsmodels
|
|
88
|
+
easyresearch forecast --demo
|
|
89
|
+
easyresearch forecast sales.csv --value Sales --horizon 12
|
|
90
|
+
easyresearch regress houses.xlsx --target Price
|
|
91
|
+
easyresearch signals ECG200_TRAIN.tsv
|
|
92
|
+
easyresearch models forecast # list models; * = default fast set
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Add `--models all`, or a list such as `--models ets,lstm,nbeats`, to choose
|
|
96
|
+
models. Results go to a new folder in `./Results` (or `--out`). Deep models
|
|
97
|
+
use a GPU automatically when PyTorch finds one.
|
|
98
|
+
|
|
99
|
+
Figures: `--figures residuals,comparison` draws only the named figures (the
|
|
100
|
+
default is the task's usual set), `--figure-theme` chooses colour-blind safe
|
|
101
|
+
(default), greyscale or high-contrast colours, and `--figure-format png+pdf`
|
|
102
|
+
or `png+svg` adds vector files for journals.
|
|
103
|
+
|
|
104
|
+
## Forecasting
|
|
105
|
+
|
|
106
|
+
* **Data:** a CSV/Excel table with a date column (detected automatically)
|
|
107
|
+
and the value to forecast. Hourly, daily, business-daily, weekly, monthly,
|
|
108
|
+
quarterly and yearly series are recognised; the season length follows
|
|
109
|
+
(24, 7, 5, 52, 12, 4, none). Repeated time points are averaged; missing
|
|
110
|
+
points are filled with the **last known value** (never a later one).
|
|
111
|
+
Without a date column, rows are taken in order with no season.
|
|
112
|
+
* **Models:** reference rules (naive, seasonal naive) are always included.
|
|
113
|
+
Statistical: exponential smoothing (damped trend, seasonal) and Theta.
|
|
114
|
+
Machine learning on the last *L* values: Ridge, Random Forest, Gradient
|
|
115
|
+
Boosting, KNN with the Hassanat distance. Deep (PyTorch): MLP, LSTM, GRU,
|
|
116
|
+
TCN, N-BEATS, Transformer. Default set: ETS, Theta, Ridge, Random Forest
|
|
117
|
+
(deep models are optional because each takes minutes).
|
|
118
|
+
* **Honest evaluation:** never shuffled. Models are compared at up to five
|
|
119
|
+
rolling origins before a final period (the last ~20%); the selected model
|
|
120
|
+
is then scored in the final period, which played no part in choosing it,
|
|
121
|
+
retrained before each origin. The automatic horizon is the usual one for
|
|
122
|
+
the time unit, shortened (with a note) when the series is too short to
|
|
123
|
+
evaluate it honestly; a series too short for any horizon is refused with
|
|
124
|
+
the number of values it needs.
|
|
125
|
+
* **Measures:** MASE (used to choose, by default), MAE, RMSE, sMAPE and
|
|
126
|
+
**MHSP** (mean Hassanat similarity percentage) – any of MASE, RMSE, MAE or
|
|
127
|
+
MHSP can choose the model (`--metric mhsp`).
|
|
128
|
+
* **Outputs:** the future forecast with an 80% band (its coverage in the
|
|
129
|
+
final period is checked and reported), final-period forecasts, comparison
|
|
130
|
+
table, five figures, report, trained model, `settings.json` (what was
|
|
131
|
+
asked and what was used) and `versions.txt` (the software used).
|
|
132
|
+
|
|
133
|
+
### MHSP
|
|
134
|
+
|
|
135
|
+
For each prediction, the Hassanat similarity between actual *a* and
|
|
136
|
+
forecast *f* is (1 + min(a, f)) / (1 + max(a, f)), with the signed form
|
|
137
|
+
(1 + min + |min|) / (1 + max + |min|) when the smaller value is negative.
|
|
138
|
+
MHSP is 100 × the mean of these similarities (100% = perfect), computed on
|
|
139
|
+
the values as they are. It is also reported by the regression core. Reference:
|
|
140
|
+
Hassanat, A. B., et al. (2024). A novel outlier-robust accuracy measure for
|
|
141
|
+
machine learning regression using a non-convex distance metric. *Mathematics*,
|
|
142
|
+
12(22), 3623. https://doi.org/10.3390/math12223623
|
|
143
|
+
|
|
144
|
+
## Signal classification (ECG, EEG, sensors)
|
|
145
|
+
|
|
146
|
+
* **Data:** one recording per row – a CSV/Excel table with a label column and
|
|
147
|
+
the signal values in the other numeric columns, or UCR/UEA archive files
|
|
148
|
+
(`.tsv`/`.txt`, label first). Gaps inside a recording are filled from its
|
|
149
|
+
own neighbouring values.
|
|
150
|
+
* **Models:** KNN with the Hassanat distance (each recording scaled to 0–1),
|
|
151
|
+
ROCKET (10,000 random convolution kernels), signal features + Random
|
|
152
|
+
Forest, and deep FCN, ResNet, InceptionTime and LSTM.
|
|
153
|
+
* **Evaluation:** EasyClassifier's own – stratified cross-validation to
|
|
154
|
+
compare; nested cross-validation for the final score (up to 2,000
|
|
155
|
+
recordings, without deep networks) or an untouched stratified 20% test set
|
|
156
|
+
(larger data, or when deep networks are compared, because they take
|
|
157
|
+
minutes each on a normal computer).
|
|
158
|
+
* The demo (`--demo`) uses **synthetic** heartbeats for trying the tool; it
|
|
159
|
+
is not patient data.
|
|
160
|
+
* Not yet: several channels per recording (multichannel EEG) and long
|
|
161
|
+
recordings to be cut into windows.
|
|
162
|
+
|
|
163
|
+
## Regression
|
|
164
|
+
|
|
165
|
+
See the EasyResearch Desktop README for details: linear and ridge regression,
|
|
166
|
+
decision tree, random forest, gradient boosting, SVR, KNN with the Hassanat
|
|
167
|
+
distance and a neural network; R², RMSE, MAE, median absolute error, MAPE
|
|
168
|
+
and MHSP against the reference of always predicting the average.
|
|
169
|
+
XGBoost and LightGBM are added when installed (`pip install -e ".[full]"`).
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
pip install -e ".[test]"
|
|
175
|
+
python -m pytest tests
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
From the repository root, `python tools/run_all_tests.py` runs every suite
|
|
179
|
+
(these cores and the desktop adapters); see `docs/DEVELOPMENT.md`.
|
|
180
|
+
`benchmarks/easyresearch_benchmarks.py` measures accuracy, run time, memory
|
|
181
|
+
and agreement with reference implementations, and
|
|
182
|
+
`benchmarks/easyresearch_honesty.py` measures whether the reported scores are
|
|
183
|
+
honest, against data that is never touched (results in
|
|
184
|
+
`benchmarks/results/easyresearch_honesty`).
|
|
185
|
+
|
|
186
|
+
The tests include a check that no forecasting model – statistical, machine
|
|
187
|
+
learning or deep – changes its forecast when every value after the forecast
|
|
188
|
+
origin is changed.
|
|
189
|
+
|
|
190
|
+
## Citing
|
|
191
|
+
|
|
192
|
+
If you use EasyResearch in published work, please cite it (GitHub's "Cite
|
|
193
|
+
this repository" button uses `CITATION.cff`); every run also writes the exact
|
|
194
|
+
references for the methods it used to `citations.txt`.
|
|
195
|
+
|
|
196
|
+
## Licence
|
|
197
|
+
|
|
198
|
+
MIT. Ahmad Hassanat.
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# EasyResearch
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ahmadhassant/easyresearch/actions/workflows/tests.yml)
|
|
4
|
+
[](https://github.com/ahmadhassant/easyresearch/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
Machine learning without programming: analysis cores for researchers who do
|
|
7
|
+
not code. They follow the rules of
|
|
8
|
+
[EasyClassifier](https://github.com/ahmadhassant/easyclassifier):
|
|
9
|
+
|
|
10
|
+
* every step that learns from data uses training data only;
|
|
11
|
+
* models use their authors' default settings with fixed random seeds (no
|
|
12
|
+
tuning), so results are reproducible;
|
|
13
|
+
* when several models are compared, the selected one gets a separate,
|
|
14
|
+
honest final score;
|
|
15
|
+
* every run writes a plain-language report with a Methods paragraph,
|
|
16
|
+
figures, tables, predictions, the trained model, citations and a log.
|
|
17
|
+
|
|
18
|
+
The cores have no user interaction (settings in, results out), so a terminal
|
|
19
|
+
wizard, the desktop application or a web server can all drive them.
|
|
20
|
+
|
|
21
|
+
This repository holds:
|
|
22
|
+
|
|
23
|
+
| Folder | What it is |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `src/easyresearch`, `tests` | the analysis cores and their tests (`pip install easyresearch`) |
|
|
26
|
+
| `desktop` | **EasyResearch Desktop**, a Windows application that runs classification, regression, forecasting and signal classification without code ([README](https://github.com/ahmadhassant/easyresearch/blob/main/desktop/README.md)) |
|
|
27
|
+
| `benchmarks` | correctness and honesty benchmarks, with all results |
|
|
28
|
+
| `docs` | development guide, release plan, publishing steps |
|
|
29
|
+
| `papers` | the EasyResearch paper |
|
|
30
|
+
|
|
31
|
+
| Core | Task | Status |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| Classification | predict a category from a table | EasyClassifier 0.8.2 |
|
|
34
|
+
| `easyresearch.regression` | predict a number from a table | ready |
|
|
35
|
+
| `easyresearch.timeseries` – forecasting | forecast one series | ready |
|
|
36
|
+
| `easyresearch.timeseries` – signals | classify recordings (ECG, EEG, sensors) | ready (one channel) |
|
|
37
|
+
|
|
38
|
+
## Install and run
|
|
39
|
+
|
|
40
|
+
**Windows, without Python:** download `EasyResearchDesktop-0.1.0-setup.exe`
|
|
41
|
+
from the [Releases page](https://github.com/ahmadhassant/easyresearch/releases)
|
|
42
|
+
and run it. It installs EasyResearch Desktop with its own Python and every
|
|
43
|
+
package it needs; no administrator rights are required. Windows may show
|
|
44
|
+
"Windows protected your PC" because the program is not code-signed: choose
|
|
45
|
+
**More info**, then **Run anyway**.
|
|
46
|
+
|
|
47
|
+
**Python (any system):**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install easyresearch # installs EasyClassifier, PyTorch and statsmodels
|
|
51
|
+
easyresearch forecast --demo
|
|
52
|
+
easyresearch forecast sales.csv --value Sales --horizon 12
|
|
53
|
+
easyresearch regress houses.xlsx --target Price
|
|
54
|
+
easyresearch signals ECG200_TRAIN.tsv
|
|
55
|
+
easyresearch models forecast # list models; * = default fast set
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Add `--models all`, or a list such as `--models ets,lstm,nbeats`, to choose
|
|
59
|
+
models. Results go to a new folder in `./Results` (or `--out`). Deep models
|
|
60
|
+
use a GPU automatically when PyTorch finds one.
|
|
61
|
+
|
|
62
|
+
Figures: `--figures residuals,comparison` draws only the named figures (the
|
|
63
|
+
default is the task's usual set), `--figure-theme` chooses colour-blind safe
|
|
64
|
+
(default), greyscale or high-contrast colours, and `--figure-format png+pdf`
|
|
65
|
+
or `png+svg` adds vector files for journals.
|
|
66
|
+
|
|
67
|
+
## Forecasting
|
|
68
|
+
|
|
69
|
+
* **Data:** a CSV/Excel table with a date column (detected automatically)
|
|
70
|
+
and the value to forecast. Hourly, daily, business-daily, weekly, monthly,
|
|
71
|
+
quarterly and yearly series are recognised; the season length follows
|
|
72
|
+
(24, 7, 5, 52, 12, 4, none). Repeated time points are averaged; missing
|
|
73
|
+
points are filled with the **last known value** (never a later one).
|
|
74
|
+
Without a date column, rows are taken in order with no season.
|
|
75
|
+
* **Models:** reference rules (naive, seasonal naive) are always included.
|
|
76
|
+
Statistical: exponential smoothing (damped trend, seasonal) and Theta.
|
|
77
|
+
Machine learning on the last *L* values: Ridge, Random Forest, Gradient
|
|
78
|
+
Boosting, KNN with the Hassanat distance. Deep (PyTorch): MLP, LSTM, GRU,
|
|
79
|
+
TCN, N-BEATS, Transformer. Default set: ETS, Theta, Ridge, Random Forest
|
|
80
|
+
(deep models are optional because each takes minutes).
|
|
81
|
+
* **Honest evaluation:** never shuffled. Models are compared at up to five
|
|
82
|
+
rolling origins before a final period (the last ~20%); the selected model
|
|
83
|
+
is then scored in the final period, which played no part in choosing it,
|
|
84
|
+
retrained before each origin. The automatic horizon is the usual one for
|
|
85
|
+
the time unit, shortened (with a note) when the series is too short to
|
|
86
|
+
evaluate it honestly; a series too short for any horizon is refused with
|
|
87
|
+
the number of values it needs.
|
|
88
|
+
* **Measures:** MASE (used to choose, by default), MAE, RMSE, sMAPE and
|
|
89
|
+
**MHSP** (mean Hassanat similarity percentage) – any of MASE, RMSE, MAE or
|
|
90
|
+
MHSP can choose the model (`--metric mhsp`).
|
|
91
|
+
* **Outputs:** the future forecast with an 80% band (its coverage in the
|
|
92
|
+
final period is checked and reported), final-period forecasts, comparison
|
|
93
|
+
table, five figures, report, trained model, `settings.json` (what was
|
|
94
|
+
asked and what was used) and `versions.txt` (the software used).
|
|
95
|
+
|
|
96
|
+
### MHSP
|
|
97
|
+
|
|
98
|
+
For each prediction, the Hassanat similarity between actual *a* and
|
|
99
|
+
forecast *f* is (1 + min(a, f)) / (1 + max(a, f)), with the signed form
|
|
100
|
+
(1 + min + |min|) / (1 + max + |min|) when the smaller value is negative.
|
|
101
|
+
MHSP is 100 × the mean of these similarities (100% = perfect), computed on
|
|
102
|
+
the values as they are. It is also reported by the regression core. Reference:
|
|
103
|
+
Hassanat, A. B., et al. (2024). A novel outlier-robust accuracy measure for
|
|
104
|
+
machine learning regression using a non-convex distance metric. *Mathematics*,
|
|
105
|
+
12(22), 3623. https://doi.org/10.3390/math12223623
|
|
106
|
+
|
|
107
|
+
## Signal classification (ECG, EEG, sensors)
|
|
108
|
+
|
|
109
|
+
* **Data:** one recording per row – a CSV/Excel table with a label column and
|
|
110
|
+
the signal values in the other numeric columns, or UCR/UEA archive files
|
|
111
|
+
(`.tsv`/`.txt`, label first). Gaps inside a recording are filled from its
|
|
112
|
+
own neighbouring values.
|
|
113
|
+
* **Models:** KNN with the Hassanat distance (each recording scaled to 0–1),
|
|
114
|
+
ROCKET (10,000 random convolution kernels), signal features + Random
|
|
115
|
+
Forest, and deep FCN, ResNet, InceptionTime and LSTM.
|
|
116
|
+
* **Evaluation:** EasyClassifier's own – stratified cross-validation to
|
|
117
|
+
compare; nested cross-validation for the final score (up to 2,000
|
|
118
|
+
recordings, without deep networks) or an untouched stratified 20% test set
|
|
119
|
+
(larger data, or when deep networks are compared, because they take
|
|
120
|
+
minutes each on a normal computer).
|
|
121
|
+
* The demo (`--demo`) uses **synthetic** heartbeats for trying the tool; it
|
|
122
|
+
is not patient data.
|
|
123
|
+
* Not yet: several channels per recording (multichannel EEG) and long
|
|
124
|
+
recordings to be cut into windows.
|
|
125
|
+
|
|
126
|
+
## Regression
|
|
127
|
+
|
|
128
|
+
See the EasyResearch Desktop README for details: linear and ridge regression,
|
|
129
|
+
decision tree, random forest, gradient boosting, SVR, KNN with the Hassanat
|
|
130
|
+
distance and a neural network; R², RMSE, MAE, median absolute error, MAPE
|
|
131
|
+
and MHSP against the reference of always predicting the average.
|
|
132
|
+
XGBoost and LightGBM are added when installed (`pip install -e ".[full]"`).
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pip install -e ".[test]"
|
|
138
|
+
python -m pytest tests
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
From the repository root, `python tools/run_all_tests.py` runs every suite
|
|
142
|
+
(these cores and the desktop adapters); see `docs/DEVELOPMENT.md`.
|
|
143
|
+
`benchmarks/easyresearch_benchmarks.py` measures accuracy, run time, memory
|
|
144
|
+
and agreement with reference implementations, and
|
|
145
|
+
`benchmarks/easyresearch_honesty.py` measures whether the reported scores are
|
|
146
|
+
honest, against data that is never touched (results in
|
|
147
|
+
`benchmarks/results/easyresearch_honesty`).
|
|
148
|
+
|
|
149
|
+
The tests include a check that no forecasting model – statistical, machine
|
|
150
|
+
learning or deep – changes its forecast when every value after the forecast
|
|
151
|
+
origin is changed.
|
|
152
|
+
|
|
153
|
+
## Citing
|
|
154
|
+
|
|
155
|
+
If you use EasyResearch in published work, please cite it (GitHub's "Cite
|
|
156
|
+
this repository" button uses `CITATION.cff`); every run also writes the exact
|
|
157
|
+
references for the methods it used to `citations.txt`.
|
|
158
|
+
|
|
159
|
+
## Licence
|
|
160
|
+
|
|
161
|
+
MIT. Ahmad Hassanat.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "easyresearch"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Machine learning without programming: regression, time-series forecasting and signal classification cores, with honest evaluation and plain-language reports."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Ahmad Hassanat", email = "ahmad.hassanat@gmail.com" }]
|
|
13
|
+
keywords = ["machine learning", "regression", "forecasting", "time series", "no-code"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"easyclassifier>=0.8.2,<0.9",
|
|
26
|
+
"pandas>=1.5",
|
|
27
|
+
"numpy>=1.23",
|
|
28
|
+
"scikit-learn>=1.2",
|
|
29
|
+
"matplotlib>=3.6",
|
|
30
|
+
"joblib>=1.1",
|
|
31
|
+
"openpyxl>=3.0.10",
|
|
32
|
+
"statsmodels>=0.14",
|
|
33
|
+
"torch>=2.2",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
full = ["xgboost>=2", "lightgbm>=4"]
|
|
38
|
+
test = ["pytest>=7"]
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://github.com/ahmadhassant/easyresearch"
|
|
42
|
+
Issues = "https://github.com/ahmadhassant/easyresearch/issues"
|
|
43
|
+
Changelog = "https://github.com/ahmadhassant/easyresearch/blob/main/CHANGELOG.md"
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
easyresearch = "easyresearch.cli:main"
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""EasyResearch - machine learning without programming.
|
|
2
|
+
|
|
3
|
+
Analysis cores that share EasyClassifier's rules (leakage-free preparation,
|
|
4
|
+
default parameters with fixed seeds, honest final scores, plain-language
|
|
5
|
+
reports):
|
|
6
|
+
|
|
7
|
+
* ``easyresearch.regression`` - predict a number from a table
|
|
8
|
+
* ``easyresearch.timeseries`` - forecast a series; classify recordings
|
|
9
|
+
(ECG, EEG, sensor signals)
|
|
10
|
+
* classification is provided by the EasyClassifier package (0.8.2)
|
|
11
|
+
|
|
12
|
+
The cores have no user interaction: settings go in, results and files come
|
|
13
|
+
out, so a terminal wizard, the desktop application or a web server can drive
|
|
14
|
+
them in the same way.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
__author__ = "Ahmad Hassanat"
|
|
19
|
+
|
|
20
|
+
CITATION = (
|
|
21
|
+
"Hassanat, A. B. A. (2026). EasyResearch: Machine Learning without "
|
|
22
|
+
"Programming (Version {version}) [Software]. "
|
|
23
|
+
"https://github.com/ahmadhassant/easyresearch".format(version=__version__)
|
|
24
|
+
)
|
|
25
|
+
EASYCLASSIFIER_CITATION = (
|
|
26
|
+
"Hassanat, A. B. A. (2026). EasyClassifier: Machine Learning without "
|
|
27
|
+
"Programming (Version 0.8.2) [Software]. Zenodo. "
|
|
28
|
+
"https://doi.org/10.5281/zenodo.23122901")
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Command line for the EasyResearch cores (no programming needed beyond one
|
|
2
|
+
command). Examples::
|
|
3
|
+
|
|
4
|
+
easyresearch forecast sales.csv --value Sales --horizon 12
|
|
5
|
+
easyresearch forecast --demo
|
|
6
|
+
easyresearch regress houses.xlsx --target Price
|
|
7
|
+
easyresearch signals ECG200_TRAIN.tsv --label label
|
|
8
|
+
easyresearch models forecast # list the available models
|
|
9
|
+
easyresearch regress --demo --figures residuals,comparison --figure-format png+pdf
|
|
10
|
+
|
|
11
|
+
Results are written to a new folder inside ./Results (or --out).
|
|
12
|
+
A terminal wizard and the desktop/web interfaces will drive the same cores.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from . import __version__
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _models(arg, registry):
|
|
26
|
+
if not arg:
|
|
27
|
+
return None
|
|
28
|
+
if arg == "all":
|
|
29
|
+
return [k for k, s in registry.items() if getattr(s, "available", True)]
|
|
30
|
+
return [m.strip() for m in arg.split(",") if m.strip()]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _figure_settings(st, a):
|
|
34
|
+
"""Figure colours, file formats and the figures to draw (default: the
|
|
35
|
+
task's usual set)."""
|
|
36
|
+
st.figure_theme, st.figure_format = a.figure_theme, a.figure_format
|
|
37
|
+
if a.figures:
|
|
38
|
+
st.figure_keys = [k.strip() for k in a.figures.split(",") if k.strip()]
|
|
39
|
+
return st
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main(argv=None) -> int:
|
|
43
|
+
from easyclassifier.figures import DEFAULT_THEME, FORMATS, THEMES
|
|
44
|
+
p = argparse.ArgumentParser(prog="easyresearch",
|
|
45
|
+
description="Machine learning without "
|
|
46
|
+
"programming - analysis cores.")
|
|
47
|
+
p.add_argument("--version", action="version",
|
|
48
|
+
version=f"EasyResearch {__version__}")
|
|
49
|
+
sub = p.add_subparsers(dest="task", required=True)
|
|
50
|
+
|
|
51
|
+
for name, help_ in (("forecast", "forecast one time series"),
|
|
52
|
+
("regress", "predict a number from a table"),
|
|
53
|
+
("signals", "classify recordings (ECG, EEG, ...)")):
|
|
54
|
+
s = sub.add_parser(name, help=help_)
|
|
55
|
+
s.add_argument("data", nargs="?", help="CSV, Excel or UCR .tsv file")
|
|
56
|
+
s.add_argument("--demo", action="store_true",
|
|
57
|
+
help="use the built-in example data")
|
|
58
|
+
s.add_argument("--models", help="comma-separated model ids, or 'all'"
|
|
59
|
+
" (default: the fast set)")
|
|
60
|
+
s.add_argument("--out", default=".", help="folder for Results/")
|
|
61
|
+
s.add_argument("--no-figures", action="store_true")
|
|
62
|
+
s.add_argument("--figures", help="comma-separated figure names "
|
|
63
|
+
"(default: the usual set; see the report's list)")
|
|
64
|
+
s.add_argument("--figure-theme", default=DEFAULT_THEME,
|
|
65
|
+
choices=list(THEMES), help="figure colours")
|
|
66
|
+
s.add_argument("--figure-format", default="png", choices=list(FORMATS),
|
|
67
|
+
help="figure files: png, png+pdf or png+svg")
|
|
68
|
+
if name == "forecast":
|
|
69
|
+
s.add_argument("--value", help="column to forecast")
|
|
70
|
+
s.add_argument("--time", help="date/time column (auto-detected)")
|
|
71
|
+
s.add_argument("--horizon", default="auto",
|
|
72
|
+
help="steps ahead (default: automatic)")
|
|
73
|
+
s.add_argument("--metric", default="mase",
|
|
74
|
+
choices=["mase", "rmse", "mae", "mhsp"],
|
|
75
|
+
help="measure used to choose the model")
|
|
76
|
+
elif name == "regress":
|
|
77
|
+
s.add_argument("--target", help="numeric column to predict")
|
|
78
|
+
else:
|
|
79
|
+
s.add_argument("--label", help="column holding the class")
|
|
80
|
+
lm = sub.add_parser("models", help="list the models of a task")
|
|
81
|
+
lm.add_argument("which", choices=["forecast", "regress", "signals"])
|
|
82
|
+
a = p.parse_args(argv)
|
|
83
|
+
|
|
84
|
+
if a.task == "models":
|
|
85
|
+
if a.which == "forecast":
|
|
86
|
+
from .timeseries.models import build_registry
|
|
87
|
+
elif a.which == "regress":
|
|
88
|
+
from .regression.models import build_registry
|
|
89
|
+
else:
|
|
90
|
+
from .timeseries.signal_models import build_registry
|
|
91
|
+
for k, s in build_registry().items():
|
|
92
|
+
mark = "*" if getattr(s, "default_selected", False) else " "
|
|
93
|
+
print(f" {mark} {k:20s} {s.name}")
|
|
94
|
+
print("\n * = in the default (fast) set")
|
|
95
|
+
return 0
|
|
96
|
+
|
|
97
|
+
if not a.demo and not a.data:
|
|
98
|
+
p.error("give a data file or --demo")
|
|
99
|
+
out = os.path.abspath(a.out)
|
|
100
|
+
try:
|
|
101
|
+
if a.task == "forecast":
|
|
102
|
+
from .timeseries import ForecastSettings, run_forecast
|
|
103
|
+
from .timeseries import data as td
|
|
104
|
+
from .timeseries.models import build_registry
|
|
105
|
+
df = td.load_demo() if a.demo else _load(a.data)
|
|
106
|
+
value = a.value or ("CO2_ppm" if a.demo else td.suggest_value(df))
|
|
107
|
+
st = ForecastSettings(time_column=a.time, figures=not a.no_figures,
|
|
108
|
+
selection_metric=a.metric,
|
|
109
|
+
horizon=a.horizon if a.horizon == "auto"
|
|
110
|
+
else int(a.horizon))
|
|
111
|
+
st.models = _models(a.models, build_registry()) or st.models
|
|
112
|
+
_figure_settings(st, a)
|
|
113
|
+
r = run_forecast(df, value, st, out,
|
|
114
|
+
td.DEMO_NAME if a.demo else Path(a.data).name,
|
|
115
|
+
"demo_co2" if a.demo else Path(a.data).stem,
|
|
116
|
+
td.DEMO_CITATION if a.demo else "")
|
|
117
|
+
head = f"Selected: {r['best'].name}"
|
|
118
|
+
elif a.task == "regress":
|
|
119
|
+
from .regression import Settings, run_analysis
|
|
120
|
+
from .regression import data as rd
|
|
121
|
+
from .regression.models import build_registry
|
|
122
|
+
df = rd.load_demo() if a.demo else _load(a.data)
|
|
123
|
+
target = a.target or rd.suggest_target(df)
|
|
124
|
+
reg = build_registry()
|
|
125
|
+
models = _models(a.models, reg) or [
|
|
126
|
+
k for k, s in reg.items() if s.default_selected]
|
|
127
|
+
st = _figure_settings(Settings(models=models,
|
|
128
|
+
figures=not a.no_figures), a)
|
|
129
|
+
r = run_analysis(df, target, st, out, rd.DEMO_NAME if a.demo else
|
|
130
|
+
Path(a.data).name, "demo_diabetes" if a.demo
|
|
131
|
+
else Path(a.data).stem,
|
|
132
|
+
rd.DEMO_CITATION if a.demo else "")
|
|
133
|
+
head = f"Selected: {r['best'].name}"
|
|
134
|
+
else:
|
|
135
|
+
from .timeseries import signal_data as sd
|
|
136
|
+
from .timeseries.classify import (SignalSettings,
|
|
137
|
+
run_signal_classification)
|
|
138
|
+
from .timeseries.signal_models import build_registry
|
|
139
|
+
df = sd.load_demo() if a.demo else sd.load_table(a.data)
|
|
140
|
+
label = a.label or sd.suggest_label(df)
|
|
141
|
+
st = SignalSettings(figures=not a.no_figures)
|
|
142
|
+
st.models = _models(a.models, build_registry()) or st.models
|
|
143
|
+
_figure_settings(st, a)
|
|
144
|
+
r = run_signal_classification(
|
|
145
|
+
df, label, st, out, sd.DEMO_NAME if a.demo else
|
|
146
|
+
Path(a.data).name, "demo_heartbeats" if a.demo
|
|
147
|
+
else Path(a.data).stem)
|
|
148
|
+
head = f"Selected: {r['best'].classifier_name}"
|
|
149
|
+
except (ValueError, RuntimeError, FileNotFoundError) as exc:
|
|
150
|
+
print(f"\nCould not run the analysis: {exc}", file=sys.stderr)
|
|
151
|
+
return 1
|
|
152
|
+
print("\n" + head)
|
|
153
|
+
for line in r["summary"]:
|
|
154
|
+
print(" " + line)
|
|
155
|
+
print(f"\nAll results: {r['out']}")
|
|
156
|
+
return 0
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _load(path):
|
|
160
|
+
from easyclassifier.dataset import clean_path, load_csv
|
|
161
|
+
return load_csv(clean_path(path))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Pieces shared by all EasyResearch cores."""
|