ochreml 0.1.1__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.
- ochreml-0.1.1/.github/workflows/release.yml +109 -0
- ochreml-0.1.1/.gitignore +6 -0
- ochreml-0.1.1/Cargo.lock +132 -0
- ochreml-0.1.1/Cargo.toml +18 -0
- ochreml-0.1.1/PKG-INFO +148 -0
- ochreml-0.1.1/README.md +132 -0
- ochreml-0.1.1/demo.py +116 -0
- ochreml-0.1.1/generate_notebook.py +329 -0
- ochreml-0.1.1/ochreml_demo_kaggle.ipynb +372 -0
- ochreml-0.1.1/pyproject.toml +26 -0
- ochreml-0.1.1/python/ochreml/__init__.py +279 -0
- ochreml-0.1.1/python/ochreml/_utils.py +118 -0
- ochreml-0.1.1/src/decision_tree.rs +532 -0
- ochreml-0.1.1/src/lib.rs +159 -0
- ochreml-0.1.1/src/linear_regression.rs +137 -0
- ochreml-0.1.1/src/matrix.rs +279 -0
- ochreml-0.1.1/tests/test_ochreml.py +187 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Publish to PyPI (*≧ω≦*)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
linux:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
matrix:
|
|
18
|
+
target: [x86_64, aarch64]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.11'
|
|
24
|
+
- name: Build wheels
|
|
25
|
+
uses: PyO3/maturin-action@v1
|
|
26
|
+
with:
|
|
27
|
+
target: ${{ matrix.target }}
|
|
28
|
+
args: --release --out dist --find-interpreter
|
|
29
|
+
manylinux: auto
|
|
30
|
+
- name: Upload wheels
|
|
31
|
+
uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: wheels-linux-${{ matrix.target }}
|
|
34
|
+
path: dist
|
|
35
|
+
|
|
36
|
+
windows:
|
|
37
|
+
runs-on: windows-latest
|
|
38
|
+
strategy:
|
|
39
|
+
matrix:
|
|
40
|
+
target: [x64]
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: actions/setup-python@v5
|
|
44
|
+
with:
|
|
45
|
+
python-version: '3.11'
|
|
46
|
+
- name: Build wheels
|
|
47
|
+
uses: PyO3/maturin-action@v1
|
|
48
|
+
with:
|
|
49
|
+
target: ${{ matrix.target }}
|
|
50
|
+
args: --release --out dist --find-interpreter
|
|
51
|
+
- name: Upload wheels
|
|
52
|
+
uses: actions/upload-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: wheels-windows-${{ matrix.target }}
|
|
55
|
+
path: dist
|
|
56
|
+
|
|
57
|
+
macos:
|
|
58
|
+
runs-on: macos-latest
|
|
59
|
+
strategy:
|
|
60
|
+
matrix:
|
|
61
|
+
target: [x86_64, aarch64]
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
- uses: actions/setup-python@v5
|
|
65
|
+
with:
|
|
66
|
+
python-version: '3.11'
|
|
67
|
+
- name: Build wheels
|
|
68
|
+
uses: PyO3/maturin-action@v1
|
|
69
|
+
with:
|
|
70
|
+
target: ${{ matrix.target }}
|
|
71
|
+
args: --release --out dist --find-interpreter
|
|
72
|
+
- name: Upload wheels
|
|
73
|
+
uses: actions/upload-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: wheels-macos-${{ matrix.target }}
|
|
76
|
+
path: dist
|
|
77
|
+
|
|
78
|
+
sdist:
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v4
|
|
82
|
+
- name: Build sdist
|
|
83
|
+
uses: PyO3/maturin-action@v1
|
|
84
|
+
with:
|
|
85
|
+
command: sdist
|
|
86
|
+
args: --out dist
|
|
87
|
+
- name: Upload sdist
|
|
88
|
+
uses: actions/upload-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
name: wheels-sdist
|
|
91
|
+
path: dist
|
|
92
|
+
|
|
93
|
+
release:
|
|
94
|
+
name: Publish to PyPI
|
|
95
|
+
runs-on: ubuntu-latest
|
|
96
|
+
needs: [linux, windows, macos, sdist]
|
|
97
|
+
environment:
|
|
98
|
+
name: pypi
|
|
99
|
+
url: https://pypi.org/p/ochreml
|
|
100
|
+
permissions:
|
|
101
|
+
id-token: write
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/download-artifact@v4
|
|
104
|
+
with:
|
|
105
|
+
pattern: wheels-*
|
|
106
|
+
merge-multiple: true
|
|
107
|
+
path: dist
|
|
108
|
+
- name: Publish to PyPI
|
|
109
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
ochreml-0.1.1/.gitignore
ADDED
ochreml-0.1.1/Cargo.lock
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "heck"
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "libc"
|
|
13
|
+
version = "0.2.190"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "ochreml"
|
|
19
|
+
version = "0.1.1"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"pyo3",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[[package]]
|
|
25
|
+
name = "once_cell"
|
|
26
|
+
version = "1.21.4"
|
|
27
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
28
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
29
|
+
|
|
30
|
+
[[package]]
|
|
31
|
+
name = "portable-atomic"
|
|
32
|
+
version = "1.15.0"
|
|
33
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
34
|
+
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
|
35
|
+
|
|
36
|
+
[[package]]
|
|
37
|
+
name = "proc-macro2"
|
|
38
|
+
version = "1.0.107"
|
|
39
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
40
|
+
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
|
41
|
+
dependencies = [
|
|
42
|
+
"unicode-ident",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[[package]]
|
|
46
|
+
name = "pyo3"
|
|
47
|
+
version = "0.29.3"
|
|
48
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
49
|
+
checksum = "700d18fa267b73b9b521fd7e13580e2f446916f176cacee1ab63fcc8191f1655"
|
|
50
|
+
dependencies = [
|
|
51
|
+
"libc",
|
|
52
|
+
"once_cell",
|
|
53
|
+
"portable-atomic",
|
|
54
|
+
"pyo3-build-config",
|
|
55
|
+
"pyo3-ffi",
|
|
56
|
+
"pyo3-macros",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "pyo3-build-config"
|
|
61
|
+
version = "0.29.3"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "7b3fc0c4d08f6bb10e71fe39dfb9e2f59c6eb6854e22ec8092f50c69a4499adb"
|
|
64
|
+
dependencies = [
|
|
65
|
+
"target-lexicon",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[[package]]
|
|
69
|
+
name = "pyo3-ffi"
|
|
70
|
+
version = "0.29.3"
|
|
71
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
72
|
+
checksum = "dfc0b8e19df29aad7086cf977bb0c2a2f143e30567eb113e9cf72b62ca698330"
|
|
73
|
+
dependencies = [
|
|
74
|
+
"libc",
|
|
75
|
+
"pyo3-build-config",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[[package]]
|
|
79
|
+
name = "pyo3-macros"
|
|
80
|
+
version = "0.29.3"
|
|
81
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
82
|
+
checksum = "6100e8a4b5eba53afaa5ed078364851a0b2499c553a44c31026b929049b49dc6"
|
|
83
|
+
dependencies = [
|
|
84
|
+
"proc-macro2",
|
|
85
|
+
"pyo3-macros-backend",
|
|
86
|
+
"quote",
|
|
87
|
+
"syn",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[[package]]
|
|
91
|
+
name = "pyo3-macros-backend"
|
|
92
|
+
version = "0.29.3"
|
|
93
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
94
|
+
checksum = "6143877a16e82b5a727b7127ff4cd86858a24a28d745d72f43e6f227c7b1bdb3"
|
|
95
|
+
dependencies = [
|
|
96
|
+
"heck",
|
|
97
|
+
"proc-macro2",
|
|
98
|
+
"quote",
|
|
99
|
+
"syn",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
[[package]]
|
|
103
|
+
name = "quote"
|
|
104
|
+
version = "1.0.47"
|
|
105
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
106
|
+
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
|
107
|
+
dependencies = [
|
|
108
|
+
"proc-macro2",
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
[[package]]
|
|
112
|
+
name = "syn"
|
|
113
|
+
version = "2.0.119"
|
|
114
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
115
|
+
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
|
116
|
+
dependencies = [
|
|
117
|
+
"proc-macro2",
|
|
118
|
+
"quote",
|
|
119
|
+
"unicode-ident",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
[[package]]
|
|
123
|
+
name = "target-lexicon"
|
|
124
|
+
version = "0.13.5"
|
|
125
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
126
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
127
|
+
|
|
128
|
+
[[package]]
|
|
129
|
+
name = "unicode-ident"
|
|
130
|
+
version = "1.0.26"
|
|
131
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
132
|
+
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
ochreml-0.1.1/Cargo.toml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "ochreml"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
description = "Classical Machine Learning Library in Rust with PyO3 Bindings (*^▽^*)"
|
|
6
|
+
license = "MIT OR Apache-2.0"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
|
|
9
|
+
[lib]
|
|
10
|
+
name = "_ochreml"
|
|
11
|
+
crate-type = ["cdylib", "rlib"]
|
|
12
|
+
|
|
13
|
+
[dependencies]
|
|
14
|
+
pyo3 = { version = "0.29.3", features = ["extension-module"] }
|
|
15
|
+
|
|
16
|
+
[profile.release]
|
|
17
|
+
lto = true
|
|
18
|
+
opt-level = 3
|
ochreml-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ochreml
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: Intended Audience :: Science/Research
|
|
7
|
+
Classifier: Programming Language :: Rust
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
10
|
+
Summary: Classical Machine Learning Library in Rust with PyO3 Bindings (*^▽^*)
|
|
11
|
+
Keywords: machine-learning,linear-regression,decision-tree,rust,pyo3
|
|
12
|
+
License: MIT OR Apache-2.0
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
15
|
+
|
|
16
|
+
# OchreML (*≧ω≦*)
|
|
17
|
+
|
|
18
|
+
**Classical Machine Learning Library in Rust with PyO3 Bindings**
|
|
19
|
+
|
|
20
|
+
Welcome to **OchreML**! (^▽^)
|
|
21
|
+
OchreML is a classical Machine Learning library (Linear Regression & Decision Trees) combining Rust's blazingly fast computational performance with Python's intuitive, scikit-learn-compatible API.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Key Features (*^▽^*)
|
|
26
|
+
|
|
27
|
+
1. **Linear Regression (OLS / Normal Equation)**
|
|
28
|
+
- Exact analytical solution using the Normal Equation $(X^T X)^{-1} X^T y$.
|
|
29
|
+
- Automated Tikhonov (Ridge) regularization fallback when design matrices are ill-conditioned or singular.
|
|
30
|
+
- Configurable `fit_intercept` parameter for calculating bias.
|
|
31
|
+
|
|
32
|
+
2. **Decision Tree Classifier**
|
|
33
|
+
- CART classification trees supporting both **Gini Impurity** and **Entropy** criteria.
|
|
34
|
+
- Flexible hyperparameters: `max_depth`, `min_samples_split`, and `min_samples_leaf`.
|
|
35
|
+
|
|
36
|
+
3. **Decision Tree Regressor**
|
|
37
|
+
- Non-linear regression trees based on **Mean Squared Error (MSE / Variance Reduction)** minimization.
|
|
38
|
+
- Robust continuous target estimation.
|
|
39
|
+
|
|
40
|
+
4. **Multi-Format Data Interoperability** (o´∀`o)
|
|
41
|
+
- Seamlessly accepts and processes:
|
|
42
|
+
- **Python Nested Lists** (`list[list[float]]`)
|
|
43
|
+
- **NumPy ndarrays** (`numpy.ndarray`)
|
|
44
|
+
- **Pandas DataFrames & Series** (`pandas.DataFrame`, `pandas.Series`)
|
|
45
|
+
- **Polars DataFrames & Series** (`polars.DataFrame`, `polars.Series`)
|
|
46
|
+
|
|
47
|
+
5. **Scikit-Learn Compatible API** (^▽^)
|
|
48
|
+
- Standard estimator interface: `fit(X, y)`, `predict(X)`, and `score(X, y)`.
|
|
49
|
+
- Inspection attributes: `coef_`, `intercept_`, `classes_`, and `n_features_in_`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Build & Installation (o´∀`o)
|
|
54
|
+
|
|
55
|
+
Ensure Rust (cargo) and your Python virtual environment are activated:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Activate your virtual environment
|
|
59
|
+
source .venv/bin/activate
|
|
60
|
+
|
|
61
|
+
# Compile and install locally with Maturin
|
|
62
|
+
maturin develop --release
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Quickstart Examples (^▽^)
|
|
68
|
+
|
|
69
|
+
### 1. Linear Regression with NumPy & Pandas
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import numpy as np
|
|
73
|
+
import pandas as pd
|
|
74
|
+
from ochreml import LinearRegression
|
|
75
|
+
|
|
76
|
+
# Prepare dataset with Pandas DataFrame
|
|
77
|
+
df = pd.DataFrame({
|
|
78
|
+
"x1": [1.0, 2.0, 3.0, 4.0, 5.0],
|
|
79
|
+
"x2": [2.0, 1.0, 4.0, 3.0, 5.0],
|
|
80
|
+
})
|
|
81
|
+
# Target relationship: y = 2*x1 + 3*x2 + 5
|
|
82
|
+
y = np.array([13.0, 12.0, 23.0, 22.0, 30.0])
|
|
83
|
+
|
|
84
|
+
model = LinearRegression(fit_intercept=True)
|
|
85
|
+
model.fit(df, y)
|
|
86
|
+
|
|
87
|
+
print("Coefficients:", model.coef_) # approx [2.0, 3.0]
|
|
88
|
+
print("Intercept:", model.intercept_) # approx 5.0
|
|
89
|
+
print("R^2 Score:", model.score(df, y)) # 1.0
|
|
90
|
+
|
|
91
|
+
# Predict on new samples
|
|
92
|
+
X_new = pd.DataFrame({"x1": [6.0], "x2": [2.0]})
|
|
93
|
+
print("Prediction:", model.predict(X_new)) # [23.0]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. Decision Tree Classifier with Polars
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import polars as pl
|
|
100
|
+
from ochreml import DecisionTreeClassifier
|
|
101
|
+
|
|
102
|
+
# Prepare dataset with Polars
|
|
103
|
+
df_polars = pl.DataFrame({
|
|
104
|
+
"study_hours": [1.0, 2.0, 2.5, 5.0, 6.0, 7.5, 8.0, 9.0],
|
|
105
|
+
"attendance": [50.0, 60.0, 40.0, 80.0, 85.0, 90.0, 95.0, 100.0],
|
|
106
|
+
})
|
|
107
|
+
y_polars = pl.Series("passed", [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0])
|
|
108
|
+
|
|
109
|
+
clf = DecisionTreeClassifier(criterion="gini", max_depth=3)
|
|
110
|
+
clf.fit(df_polars, y_polars)
|
|
111
|
+
|
|
112
|
+
print("Classes discovered:", clf.classes_)
|
|
113
|
+
print("Training accuracy:", clf.score(df_polars, y_polars))
|
|
114
|
+
|
|
115
|
+
# Predict on new samples
|
|
116
|
+
test_data = pl.DataFrame({"study_hours": [1.5, 7.0], "attendance": [55.0, 88.0]})
|
|
117
|
+
print("Predictions:", clf.predict(test_data)) # [0.0, 1.0]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 3. Decision Tree Regressor with Standard Python Lists
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from ochreml import DecisionTreeRegressor
|
|
124
|
+
|
|
125
|
+
# Dataset using standard Python nested lists
|
|
126
|
+
X = [[100.0], [300.0], [500.0], [1500.0], [1800.0], [2200.0]]
|
|
127
|
+
y = [31.0, 30.0, 29.5, 17.0, 16.0, 15.0]
|
|
128
|
+
|
|
129
|
+
reg = DecisionTreeRegressor(criterion="squared_error", max_depth=2)
|
|
130
|
+
reg.fit(X, y)
|
|
131
|
+
|
|
132
|
+
print("R^2 Score:", reg.score(X, y))
|
|
133
|
+
print("Prediction [200.0m]:", reg.predict([[200.0]])) # [31.0]
|
|
134
|
+
print("Prediction [2000.0m]:", reg.predict([[2000.0]])) # [15.5]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Testing Suite (★ω★)
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Run internal Rust unit tests
|
|
143
|
+
cargo test
|
|
144
|
+
|
|
145
|
+
# Run Python integration tests (Lists, NumPy, Pandas, Polars)
|
|
146
|
+
pytest
|
|
147
|
+
```
|
|
148
|
+
|
ochreml-0.1.1/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# OchreML (*≧ω≦*)
|
|
2
|
+
|
|
3
|
+
**Classical Machine Learning Library in Rust with PyO3 Bindings**
|
|
4
|
+
|
|
5
|
+
Welcome to **OchreML**! (^▽^)
|
|
6
|
+
OchreML is a classical Machine Learning library (Linear Regression & Decision Trees) combining Rust's blazingly fast computational performance with Python's intuitive, scikit-learn-compatible API.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Key Features (*^▽^*)
|
|
11
|
+
|
|
12
|
+
1. **Linear Regression (OLS / Normal Equation)**
|
|
13
|
+
- Exact analytical solution using the Normal Equation $(X^T X)^{-1} X^T y$.
|
|
14
|
+
- Automated Tikhonov (Ridge) regularization fallback when design matrices are ill-conditioned or singular.
|
|
15
|
+
- Configurable `fit_intercept` parameter for calculating bias.
|
|
16
|
+
|
|
17
|
+
2. **Decision Tree Classifier**
|
|
18
|
+
- CART classification trees supporting both **Gini Impurity** and **Entropy** criteria.
|
|
19
|
+
- Flexible hyperparameters: `max_depth`, `min_samples_split`, and `min_samples_leaf`.
|
|
20
|
+
|
|
21
|
+
3. **Decision Tree Regressor**
|
|
22
|
+
- Non-linear regression trees based on **Mean Squared Error (MSE / Variance Reduction)** minimization.
|
|
23
|
+
- Robust continuous target estimation.
|
|
24
|
+
|
|
25
|
+
4. **Multi-Format Data Interoperability** (o´∀`o)
|
|
26
|
+
- Seamlessly accepts and processes:
|
|
27
|
+
- **Python Nested Lists** (`list[list[float]]`)
|
|
28
|
+
- **NumPy ndarrays** (`numpy.ndarray`)
|
|
29
|
+
- **Pandas DataFrames & Series** (`pandas.DataFrame`, `pandas.Series`)
|
|
30
|
+
- **Polars DataFrames & Series** (`polars.DataFrame`, `polars.Series`)
|
|
31
|
+
|
|
32
|
+
5. **Scikit-Learn Compatible API** (^▽^)
|
|
33
|
+
- Standard estimator interface: `fit(X, y)`, `predict(X)`, and `score(X, y)`.
|
|
34
|
+
- Inspection attributes: `coef_`, `intercept_`, `classes_`, and `n_features_in_`.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Build & Installation (o´∀`o)
|
|
39
|
+
|
|
40
|
+
Ensure Rust (cargo) and your Python virtual environment are activated:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Activate your virtual environment
|
|
44
|
+
source .venv/bin/activate
|
|
45
|
+
|
|
46
|
+
# Compile and install locally with Maturin
|
|
47
|
+
maturin develop --release
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quickstart Examples (^▽^)
|
|
53
|
+
|
|
54
|
+
### 1. Linear Regression with NumPy & Pandas
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import numpy as np
|
|
58
|
+
import pandas as pd
|
|
59
|
+
from ochreml import LinearRegression
|
|
60
|
+
|
|
61
|
+
# Prepare dataset with Pandas DataFrame
|
|
62
|
+
df = pd.DataFrame({
|
|
63
|
+
"x1": [1.0, 2.0, 3.0, 4.0, 5.0],
|
|
64
|
+
"x2": [2.0, 1.0, 4.0, 3.0, 5.0],
|
|
65
|
+
})
|
|
66
|
+
# Target relationship: y = 2*x1 + 3*x2 + 5
|
|
67
|
+
y = np.array([13.0, 12.0, 23.0, 22.0, 30.0])
|
|
68
|
+
|
|
69
|
+
model = LinearRegression(fit_intercept=True)
|
|
70
|
+
model.fit(df, y)
|
|
71
|
+
|
|
72
|
+
print("Coefficients:", model.coef_) # approx [2.0, 3.0]
|
|
73
|
+
print("Intercept:", model.intercept_) # approx 5.0
|
|
74
|
+
print("R^2 Score:", model.score(df, y)) # 1.0
|
|
75
|
+
|
|
76
|
+
# Predict on new samples
|
|
77
|
+
X_new = pd.DataFrame({"x1": [6.0], "x2": [2.0]})
|
|
78
|
+
print("Prediction:", model.predict(X_new)) # [23.0]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. Decision Tree Classifier with Polars
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import polars as pl
|
|
85
|
+
from ochreml import DecisionTreeClassifier
|
|
86
|
+
|
|
87
|
+
# Prepare dataset with Polars
|
|
88
|
+
df_polars = pl.DataFrame({
|
|
89
|
+
"study_hours": [1.0, 2.0, 2.5, 5.0, 6.0, 7.5, 8.0, 9.0],
|
|
90
|
+
"attendance": [50.0, 60.0, 40.0, 80.0, 85.0, 90.0, 95.0, 100.0],
|
|
91
|
+
})
|
|
92
|
+
y_polars = pl.Series("passed", [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0])
|
|
93
|
+
|
|
94
|
+
clf = DecisionTreeClassifier(criterion="gini", max_depth=3)
|
|
95
|
+
clf.fit(df_polars, y_polars)
|
|
96
|
+
|
|
97
|
+
print("Classes discovered:", clf.classes_)
|
|
98
|
+
print("Training accuracy:", clf.score(df_polars, y_polars))
|
|
99
|
+
|
|
100
|
+
# Predict on new samples
|
|
101
|
+
test_data = pl.DataFrame({"study_hours": [1.5, 7.0], "attendance": [55.0, 88.0]})
|
|
102
|
+
print("Predictions:", clf.predict(test_data)) # [0.0, 1.0]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 3. Decision Tree Regressor with Standard Python Lists
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from ochreml import DecisionTreeRegressor
|
|
109
|
+
|
|
110
|
+
# Dataset using standard Python nested lists
|
|
111
|
+
X = [[100.0], [300.0], [500.0], [1500.0], [1800.0], [2200.0]]
|
|
112
|
+
y = [31.0, 30.0, 29.5, 17.0, 16.0, 15.0]
|
|
113
|
+
|
|
114
|
+
reg = DecisionTreeRegressor(criterion="squared_error", max_depth=2)
|
|
115
|
+
reg.fit(X, y)
|
|
116
|
+
|
|
117
|
+
print("R^2 Score:", reg.score(X, y))
|
|
118
|
+
print("Prediction [200.0m]:", reg.predict([[200.0]])) # [31.0]
|
|
119
|
+
print("Prediction [2000.0m]:", reg.predict([[2000.0]])) # [15.5]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Testing Suite (★ω★)
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Run internal Rust unit tests
|
|
128
|
+
cargo test
|
|
129
|
+
|
|
130
|
+
# Run Python integration tests (Lists, NumPy, Pandas, Polars)
|
|
131
|
+
pytest
|
|
132
|
+
```
|
ochreml-0.1.1/demo.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Demo Penggunaan OchreML (^▽^)
|
|
2
|
+
# Integrasi Python List, NumPy ndarray, Pandas DataFrame, dan Polars DataFrame.
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import polars as pl
|
|
7
|
+
|
|
8
|
+
from ochreml import LinearRegression, DecisionTreeClassifier, DecisionTreeRegressor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def banner(title: str, kaomoji: str):
|
|
12
|
+
print("\n" + "=" * 65)
|
|
13
|
+
print(f" {kaomoji} {title}")
|
|
14
|
+
print("=" * 65)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def demo_linear_regression():
|
|
18
|
+
banner("1. LINEAR REGRESSION (Pandas & NumPy)", "(*^▽^*)")
|
|
19
|
+
|
|
20
|
+
# Fitur: [area_sqm, rooms]
|
|
21
|
+
df_features = pd.DataFrame({
|
|
22
|
+
"area_sqm": [36.0, 45.0, 60.0, 72.0, 90.0, 120.0],
|
|
23
|
+
"rooms": [1.0, 2.0, 2.0, 3.0, 3.0, 4.0],
|
|
24
|
+
})
|
|
25
|
+
# Target: 10 * area + 25 * rooms + 50
|
|
26
|
+
y = np.array([435.0, 550.0, 700.0, 845.0, 1025.0, 1350.0])
|
|
27
|
+
|
|
28
|
+
print("Training Features (Pandas DataFrame):")
|
|
29
|
+
print(df_features)
|
|
30
|
+
|
|
31
|
+
model = LinearRegression(fit_intercept=True)
|
|
32
|
+
print("\nTraining Linear Regression with Normal Equation...")
|
|
33
|
+
model.fit(df_features, y)
|
|
34
|
+
|
|
35
|
+
print("\nTraining Results (^▽^):")
|
|
36
|
+
print(f" - Coefficients (coef_): {model.coef_}")
|
|
37
|
+
print(f" - Intercept (intercept_): {model.intercept_:.2f}")
|
|
38
|
+
print(f" - R^2 Score: {model.score(df_features, y):.4f}")
|
|
39
|
+
|
|
40
|
+
# Prediksi menggunakan NumPy array
|
|
41
|
+
x_new = np.array([[50.0, 2.0], [100.0, 3.0]])
|
|
42
|
+
predictions = model.predict(x_new)
|
|
43
|
+
print("\nPredictions for new houses [50 sqm, 2 rooms] and [100 sqm, 3 rooms] (*≧ω≦*):")
|
|
44
|
+
for i, p in enumerate(predictions):
|
|
45
|
+
print(f" House {i + 1}: ${p:.2f}k")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def demo_decision_tree_classifier():
|
|
49
|
+
banner("2. DECISION TREE CLASSIFIER (Polars & Gini Impurity)", "(★ω★)")
|
|
50
|
+
|
|
51
|
+
# Data menggunakan Polars DataFrame dan Series
|
|
52
|
+
df_students = pl.DataFrame({
|
|
53
|
+
"study_hours": [1.0, 2.0, 2.5, 5.0, 6.0, 7.5, 8.0, 9.0],
|
|
54
|
+
"attendance_pct": [50.0, 60.0, 40.0, 80.0, 85.0, 90.0, 95.0, 100.0],
|
|
55
|
+
})
|
|
56
|
+
y_students = pl.Series("passed", [0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0])
|
|
57
|
+
|
|
58
|
+
print("Student Dataset (Polars DataFrame):")
|
|
59
|
+
print(df_students)
|
|
60
|
+
|
|
61
|
+
clf = DecisionTreeClassifier(criterion="gini", max_depth=3)
|
|
62
|
+
print("\nFitting Decision Tree Classifier...")
|
|
63
|
+
clf.fit(df_students, y_students)
|
|
64
|
+
|
|
65
|
+
print("\nEvaluation Results (^▽^):")
|
|
66
|
+
print(f" - Registered Classes: {clf.classes_}")
|
|
67
|
+
print(f" - Training Accuracy: {clf.score(df_students, y_students) * 100:.1f}%")
|
|
68
|
+
|
|
69
|
+
# Prediksi menggunakan nested list Python standar
|
|
70
|
+
test_data = [[1.5, 55.0], [7.0, 88.0]]
|
|
71
|
+
preds = clf.predict(test_data)
|
|
72
|
+
status = ["Failed (´-ω-`)", "Passed (*≧ω≦*)"]
|
|
73
|
+
print("\nPredictions for New Students (o´∀`o):")
|
|
74
|
+
for idx, p in enumerate(preds):
|
|
75
|
+
print(f" Student {idx + 1} ({test_data[idx]}): {status[int(p)]}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def demo_decision_tree_regressor():
|
|
79
|
+
banner("3. DECISION TREE REGRESSOR (Nested List & MSE)", "(o´∀`o)")
|
|
80
|
+
|
|
81
|
+
# Data menggunakan nested list Python standar
|
|
82
|
+
X = [[100.0], [300.0], [500.0], [1500.0], [1800.0], [2200.0]]
|
|
83
|
+
y = [31.0, 30.0, 29.5, 17.0, 16.0, 15.0]
|
|
84
|
+
|
|
85
|
+
print("Training Data (Python Nested List):")
|
|
86
|
+
for r_x, r_y in zip(X, y):
|
|
87
|
+
print(f" - Elevation: {r_x[0]} m -> Temperature: {r_y} C")
|
|
88
|
+
|
|
89
|
+
reg = DecisionTreeRegressor(criterion="squared_error", max_depth=2)
|
|
90
|
+
print("\nFitting Decision Tree Regressor...")
|
|
91
|
+
reg.fit(X, y)
|
|
92
|
+
|
|
93
|
+
print("\nEvaluation Results (★ω★):")
|
|
94
|
+
print(f" - R^2 Score: {reg.score(X, y):.4f}")
|
|
95
|
+
|
|
96
|
+
# Prediksi elevasi baru
|
|
97
|
+
test_elevations = [[200.0], [1000.0], [2000.0]]
|
|
98
|
+
temp_preds = reg.predict(test_elevations)
|
|
99
|
+
print("\nEstimated Temperatures for New Elevations (*^▽^*):")
|
|
100
|
+
for h, s in zip(test_elevations, temp_preds):
|
|
101
|
+
print(f" Elevation {h[0]} m -> Estimated Temp: {s:.2f} C")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
print("\n" + "#" * 65)
|
|
106
|
+
print(" (*≧ω≦*) OCHREML OFFICIAL DEMO: LINEAR REGRESSION & DECISION TREE (*≧ω≦*)")
|
|
107
|
+
print(" Classical Machine Learning in Rust with PyO3 Bindings")
|
|
108
|
+
print("#" * 65)
|
|
109
|
+
|
|
110
|
+
demo_linear_regression()
|
|
111
|
+
demo_decision_tree_classifier()
|
|
112
|
+
demo_decision_tree_regressor()
|
|
113
|
+
|
|
114
|
+
print("\n" + "=" * 65)
|
|
115
|
+
print(" All demonstrations completed successfully! (^▽^)")
|
|
116
|
+
print("=" * 65 + "\n")
|