sylearn 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.
@@ -0,0 +1,26 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+
8
+ # Virtual Environment
9
+ .venv/
10
+ venv/
11
+ ENV/
12
+ env/
13
+
14
+ # OS
15
+ .DS_Store
16
+ Thumbs.db
17
+
18
+ # IDE & Notebook checkpoints
19
+ .ipynb_checkpoints
20
+ .vscode/
21
+ .idea/
22
+
23
+ # Python packaging builds
24
+ dist/
25
+ build/
26
+ *.egg-info/
sylearn-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shashank
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.
sylearn-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.5
2
+ Name: sylearn
3
+ Version: 0.1.0
4
+ Summary: Linear regression, decision tree and random forest regressors written from scratch in NumPy.
5
+ Author: Shashank
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: decision tree,from scratch,linear regression,machine learning,numpy,random forest
9
+ Classifier: Intended Audience :: Education
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Requires-Python: >=3.9
14
+ Requires-Dist: numpy>=1.22
15
+ Description-Content-Type: text/markdown
16
+
17
+ # sylearn
18
+
19
+ Regression models written from scratch with only NumPy. No scikit-learn inside.
20
+
21
+ - `syLinearRegression`: ordinary least squares, solved with the pseudo-inverse
22
+ - `syDecisionTreeRegressor`: a regression tree that picks splits by mean squared error
23
+ - `syRandomForestRegressor`: bagging (bootstrap samples) over `syDecisionTreeRegressor`
24
+
25
+ They were built to learn how these models work inside. On an air-quality (AQI) dataset they reach the same R² as scikit-learn's models with the same settings.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install sylearn
31
+ ```
32
+
33
+ ## Use
34
+
35
+ ```python
36
+ import numpy as np
37
+ from sylearn import syLinearRegression, syDecisionTreeRegressor, syRandomForestRegressor
38
+
39
+ X = np.random.rand(200, 3)
40
+ y = 4 * X[:, 0] - 2 * X[:, 1] + np.random.normal(0, 0.1, 200)
41
+
42
+ lr = syLinearRegression()
43
+ weights, intercept = lr.fit(X, y) # fit returns the learned weights and intercept
44
+ print(lr.predict(X[:5]))
45
+
46
+ tree = syDecisionTreeRegressor(max_depth=5, min_samples_split=2).fit(X, y)
47
+ print(tree.predict(X[:5]))
48
+
49
+ np.random.seed(42) # the forest uses np.random, so seed it for repeatable results
50
+ forest = syRandomForestRegressor(n_trees=10, max_depth=5).fit(X, y)
51
+ print(forest.predict(X[:5]))
52
+ ```
53
+
54
+ `X` can be a NumPy array, a list of lists or a pandas DataFrame. `y` is a 1-D array.
55
+
56
+ ## Good to know
57
+
58
+ - The decision tree tries every possible split, so training gets slow on large datasets (tens of thousands of rows).
59
+ - The models do not handle missing values. Fill or drop NaN values first.
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,30 @@
1
+ # Packaging for the sylearn library (pip install sylearn).
2
+ # Only the sylearn/ folder is published. The AQI app (app.py, dashboard/, models/, Data/) is not.
3
+
4
+ [build-system]
5
+ requires = ["hatchling>=1.27"]
6
+ build-backend = "hatchling.build"
7
+
8
+ [project]
9
+ name = "sylearn"
10
+ version = "0.1.0"
11
+ description = "Linear regression, decision tree and random forest regressors written from scratch in NumPy."
12
+ readme = "sylearn/README.md"
13
+ license = "MIT"
14
+ license-files = ["LICENSE"]
15
+ requires-python = ">=3.9"
16
+ authors = [{ name = "Shashank" }]
17
+ dependencies = ["numpy>=1.22"]
18
+ keywords = ["machine learning", "from scratch", "linear regression", "decision tree", "random forest", "numpy"]
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3",
21
+ "Operating System :: OS Independent",
22
+ "Intended Audience :: Education",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ ]
25
+
26
+ [tool.hatch.build.targets.wheel]
27
+ packages = ["sylearn"]
28
+
29
+ [tool.hatch.build.targets.sdist]
30
+ include = ["sylearn/*.py", "sylearn/README.md", "LICENSE", "pyproject.toml"]
@@ -0,0 +1,47 @@
1
+ # sylearn
2
+
3
+ Regression models written from scratch with only NumPy. No scikit-learn inside.
4
+
5
+ - `syLinearRegression`: ordinary least squares, solved with the pseudo-inverse
6
+ - `syDecisionTreeRegressor`: a regression tree that picks splits by mean squared error
7
+ - `syRandomForestRegressor`: bagging (bootstrap samples) over `syDecisionTreeRegressor`
8
+
9
+ They were built to learn how these models work inside. On an air-quality (AQI) dataset they reach the same R² as scikit-learn's models with the same settings.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install sylearn
15
+ ```
16
+
17
+ ## Use
18
+
19
+ ```python
20
+ import numpy as np
21
+ from sylearn import syLinearRegression, syDecisionTreeRegressor, syRandomForestRegressor
22
+
23
+ X = np.random.rand(200, 3)
24
+ y = 4 * X[:, 0] - 2 * X[:, 1] + np.random.normal(0, 0.1, 200)
25
+
26
+ lr = syLinearRegression()
27
+ weights, intercept = lr.fit(X, y) # fit returns the learned weights and intercept
28
+ print(lr.predict(X[:5]))
29
+
30
+ tree = syDecisionTreeRegressor(max_depth=5, min_samples_split=2).fit(X, y)
31
+ print(tree.predict(X[:5]))
32
+
33
+ np.random.seed(42) # the forest uses np.random, so seed it for repeatable results
34
+ forest = syRandomForestRegressor(n_trees=10, max_depth=5).fit(X, y)
35
+ print(forest.predict(X[:5]))
36
+ ```
37
+
38
+ `X` can be a NumPy array, a list of lists or a pandas DataFrame. `y` is a 1-D array.
39
+
40
+ ## Good to know
41
+
42
+ - The decision tree tries every possible split, so training gets slow on large datasets (tens of thousands of rows).
43
+ - The models do not handle missing values. Fill or drop NaN values first.
44
+
45
+ ## License
46
+
47
+ MIT
@@ -0,0 +1,7 @@
1
+ """My from-scratch ML models (no sklearn inside)."""
2
+ from sylearn.linear_regression import syLinearRegression
3
+ from sylearn.decision_tree import Node, syDecisionTreeRegressor
4
+ from sylearn.random_forest import syRandomForestRegressor
5
+
6
+ __version__ = "0.1.0"
7
+ __all__ = ["syLinearRegression", "Node", "syDecisionTreeRegressor", "syRandomForestRegressor"]
@@ -0,0 +1,98 @@
1
+ import numpy as np
2
+
3
+
4
+ class Node:
5
+ def __init__(self, feature=None,threshold=None,left=None,right=None,prediction=None):
6
+ self.feature = feature
7
+ self.threshold = threshold
8
+ self.left = left
9
+ self.right = right
10
+ self.prediction = prediction
11
+
12
+ class syDecisionTreeRegressor:
13
+
14
+ def __init__(self, max_depth=5, min_samples_split=2):
15
+ self.max_depth = max_depth
16
+ self.min_samples_split = min_samples_split
17
+ self.root = None
18
+
19
+ def fit(self, X, y):
20
+ X = np.array(X)
21
+ y = np.array(y)
22
+ self.root = self.build_me_a_tree(X, y)
23
+ return self
24
+
25
+ def predict(self, X):
26
+ X = np.array(X)
27
+ predictions = []
28
+ for x in X:
29
+ predictions.append(self.predict_one(x, self.root))
30
+ return np.array(predictions)
31
+
32
+ def build_me_a_tree(self, X, y, depth=0):
33
+ if depth>=self.max_depth or len(y)< self.min_samples_split:
34
+ prediction =np.mean(y)
35
+ return Node(prediction=prediction)
36
+
37
+ feature, threshold = self.find_best_split(X, y)
38
+
39
+ if feature is None:
40
+ prediction = np.mean(y)
41
+ return Node(prediction=prediction)
42
+
43
+ left_mask = X[:, feature] <= threshold
44
+ right_mask = X[:, feature] > threshold
45
+
46
+ X_left = X[left_mask]
47
+ X_right = X[right_mask]
48
+ y_left = y[left_mask]
49
+ y_right = y[right_mask]
50
+
51
+ left_node = self.build_me_a_tree(X_left, y_left, depth + 1)
52
+ right_node = self.build_me_a_tree(X_right, y_right, depth + 1)
53
+
54
+ return Node(feature=feature, threshold=threshold, left=left_node, right=right_node)
55
+
56
+ def find_best_split(self, X, y):
57
+ best_mse = float("inf")
58
+ best_feature = None
59
+ best_threshold = None
60
+
61
+ for i in range(X.shape[1]):
62
+ values = np.unique(X[:, i])
63
+ thresholds = (values[:-1] + values[1:])/2
64
+ #This part is not optimized by me. For now I am goining with this unomptimized version
65
+ for threshold in thresholds:
66
+ left = X[:, i] <= threshold
67
+ right = X[:, i] > threshold
68
+
69
+ y_left = y[left]
70
+ y_right = y[right]
71
+
72
+ if len(y_left) == 0 or len(y_right) == 0:
73
+ continue
74
+
75
+ mse_left = self.calculate_mse(y_left)
76
+ mse_right = self.calculate_mse(y_right)
77
+
78
+ weighted_mse = (len(y_left)/len(y))*mse_left + (len(y_right)/len(y))*mse_right
79
+
80
+ if weighted_mse < best_mse:
81
+ best_mse = weighted_mse
82
+ best_feature = i
83
+ best_threshold = threshold
84
+
85
+ return best_feature, best_threshold
86
+
87
+ def calculate_mse(self, y):
88
+ mean = np.mean(y)
89
+ return np.mean((y-mean)**2)
90
+
91
+ def predict_one(self, x, node):
92
+ if node.prediction is not None:
93
+ return node.prediction
94
+
95
+ if x[node.feature] <= node.threshold:
96
+ return self.predict_one(x, node.left)
97
+ else:
98
+ return self.predict_one(x, node.right)
@@ -0,0 +1,24 @@
1
+ import numpy as np
2
+
3
+
4
+ class syLinearRegression:
5
+ """Linear Regression from scratch using OLS: w = pinv(X) @ y."""
6
+
7
+ def __init__(self):
8
+ self.weights = None
9
+ self.intercept_ = None
10
+
11
+ def fit(self, X, y):
12
+ npX = np.array(X)
13
+ npX = np.c_[np.ones(npX.shape[0]), npX]
14
+ npy = np.array(y)
15
+ # self.weights= np.linalg.inv((npX.T@ npX)) @ npX.T @ npy
16
+ mixed = np.linalg.pinv(npX) @ npy
17
+
18
+ self.intercept_ = mixed[0]
19
+ self.weights = mixed[1:]
20
+ return self.weights, self.intercept_
21
+
22
+ def predict(self, X):
23
+ npX = np.array(X)
24
+ return npX @ self.weights + self.intercept_
@@ -0,0 +1,33 @@
1
+ import numpy as np
2
+
3
+ from sylearn.decision_tree import syDecisionTreeRegressor
4
+
5
+
6
+ class syRandomForestRegressor:
7
+ """Random Forest from scratch: bagging over my own decision trees."""
8
+
9
+ def __init__(self, n_trees=10, max_depth=5):
10
+ self.n_trees = n_trees
11
+ self.max_depth = max_depth
12
+ self.trees = []
13
+
14
+ def fit(self, X, y):
15
+ X = np.array(X)
16
+ y = np.array(y)
17
+ self.trees = []
18
+ for i in range(self.n_trees):
19
+ ids = np.random.choice(len(X), len(X), replace=True)
20
+ X_new = X[ids]
21
+ y_new = y[ids]
22
+ tree = syDecisionTreeRegressor(max_depth=self.max_depth)
23
+ tree.fit(X_new, y_new)
24
+ self.trees.append(tree)
25
+ return self
26
+
27
+ def predict(self, X):
28
+ results = []
29
+ for tree in self.trees:
30
+ pred = tree.predict(X)
31
+ results.append(pred)
32
+ final = np.mean(results, axis=0)
33
+ return final