rbg-ml 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.
- rbg_ml-0.1.0/PKG-INFO +171 -0
- rbg_ml-0.1.0/README.md +154 -0
- rbg_ml-0.1.0/pyproject.toml +40 -0
- rbg_ml-0.1.0/pyproject.toml.orig +33 -0
- rbg_ml-0.1.0/src/rbg/Classification/KNN.py +178 -0
- rbg_ml-0.1.0/src/rbg/Classification/LDA.py +187 -0
- rbg_ml-0.1.0/src/rbg/Classification/Logistic.py +189 -0
- rbg_ml-0.1.0/src/rbg/Classification/QDA.py +198 -0
- rbg_ml-0.1.0/src/rbg/Classification/__init__.py +5 -0
- rbg_ml-0.1.0/src/rbg/Classification/classifier.py +63 -0
- rbg_ml-0.1.0/src/rbg/DecisionTree/_ClassificationTree.py +78 -0
- rbg_ml-0.1.0/src/rbg/DecisionTree/_ConstructionTree.py +623 -0
- rbg_ml-0.1.0/src/rbg/DecisionTree/_RegressionTree.py +76 -0
- rbg_ml-0.1.0/src/rbg/DecisionTree/__init__.py +6 -0
- rbg_ml-0.1.0/src/rbg/DecisionTree/decisiontree.py +459 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/GridSearch.py +91 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/GroupKFold.py +76 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/KFold.py +38 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/LOOCV.py +25 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/RandomizedSearch.py +94 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/StratifiedKFold.py +67 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/__init__.py +7 -0
- rbg_ml-0.1.0/src/rbg/ModelSelection/holdoutCV.py +39 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/MLP.py +220 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/NeuralNetwork.py +0 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/__init__.py +20 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/activations.py +58 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/data.py +39 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/gradcheck.py +69 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/layers.py +94 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/losses.py +63 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/metrics.py +25 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/model.py +180 -0
- rbg_ml-0.1.0/src/rbg/NeuralNetwork/optimizers.py +62 -0
- rbg_ml-0.1.0/src/rbg/Preprocessing/MinMaxScaler.py +58 -0
- rbg_ml-0.1.0/src/rbg/Preprocessing/Standard.py +34 -0
- rbg_ml-0.1.0/src/rbg/Preprocessing/ZScoreClipper.py +50 -0
- rbg_ml-0.1.0/src/rbg/Preprocessing/__init__.py +4 -0
- rbg_ml-0.1.0/src/rbg/Preprocessing/_base.py +75 -0
- rbg_ml-0.1.0/src/rbg/Regression/GLM.py +179 -0
- rbg_ml-0.1.0/src/rbg/Regression/Lasso.py +144 -0
- rbg_ml-0.1.0/src/rbg/Regression/Linear.py +43 -0
- rbg_ml-0.1.0/src/rbg/Regression/Polynomial.py +168 -0
- rbg_ml-0.1.0/src/rbg/Regression/Ridge.py +104 -0
- rbg_ml-0.1.0/src/rbg/Regression/__init__.py +18 -0
- rbg_ml-0.1.0/src/rbg/Regression/baseestimator.py +126 -0
- rbg_ml-0.1.0/src/rbg/Regression/linearmodel.py +74 -0
- rbg_ml-0.1.0/src/rbg/Unsupervised/KMeans.py +283 -0
- rbg_ml-0.1.0/src/rbg/Unsupervised/PCA.py +196 -0
- rbg_ml-0.1.0/src/rbg/Unsupervised/__init__.py +2 -0
- rbg_ml-0.1.0/src/rbg/Utilities/ClassificationMetrics.py +177 -0
- rbg_ml-0.1.0/src/rbg/Utilities/CoordinateDescent.py +89 -0
- rbg_ml-0.1.0/src/rbg/Utilities/ExponentialFamily.py +115 -0
- rbg_ml-0.1.0/src/rbg/Utilities/FisherScoring.py +123 -0
- rbg_ml-0.1.0/src/rbg/Utilities/MatrixDesign.py +35 -0
- rbg_ml-0.1.0/src/rbg/Utilities/Optimizer.py +79 -0
- rbg_ml-0.1.0/src/rbg/Utilities/RandomIntervals.py +15 -0
- rbg_ml-0.1.0/src/rbg/Utilities/RegressionMetrics.py +71 -0
- rbg_ml-0.1.0/src/rbg/Utilities/Resample.py +39 -0
- rbg_ml-0.1.0/src/rbg/Utilities/__init__.py +10 -0
- rbg_ml-0.1.0/src/rbg/Utilities/crossvalidation.py +134 -0
- rbg_ml-0.1.0/src/rbg/__init__.py +1 -0
rbg_ml-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: rbg-ml
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Basic Machine Learning Library
|
|
5
|
+
Keywords: machine learning,basic,regression
|
|
6
|
+
Author: fragadmsc, lucca moulin
|
|
7
|
+
Author-email: fragadmsc <damasceno.lucas512@gmail.com>
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: pandas
|
|
10
|
+
Requires-Dist: furo>=2025.12.19 ; extra == 'docs'
|
|
11
|
+
Requires-Dist: sphinx>=9.1.1 ; extra == 'docs'
|
|
12
|
+
Requires-Dist: pytest>=9.0 ; extra == 'test'
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Provides-Extra: docs
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# RBG (Regressão Bem Geral)
|
|
19
|
+
#### *a.k.a Rodrigo do Bigode Grosso*
|
|
20
|
+
|
|
21
|
+
This is the official repository for the machine learning library titled RBG.
|
|
22
|
+
|
|
23
|
+
The documentation is at: [docs](https://rbg.readthedocs.io/en/latest/)
|
|
24
|
+
|
|
25
|
+
This file contains the structure of the library, the workflow with git/github that should be used and the code style guides. This document should be referred to if you have some doubt (it is also a good context base for asking something to AI).
|
|
26
|
+
|
|
27
|
+
### Table of Contents
|
|
28
|
+
|
|
29
|
+
- [Quickstart](#quickstart)
|
|
30
|
+
- [Project Structure](#library-structure)
|
|
31
|
+
- [Build System](#build-system)
|
|
32
|
+
- [File Structure](#file-structure)
|
|
33
|
+
- [Architecture & public API](#architecture--public-api)
|
|
34
|
+
- [Tests](#tests)
|
|
35
|
+
- [Git/GitHub Workflow](#gitgithub-workflow)
|
|
36
|
+
- [Good Practices](#good-practices)
|
|
37
|
+
- [Basic Git Workflow](#basic-git-workflow)
|
|
38
|
+
- [Code Style Guide](#code-style-guide)
|
|
39
|
+
- [General Rules](#general-rules)
|
|
40
|
+
- [Docstrings](#docstrings)
|
|
41
|
+
|
|
42
|
+
### Quickstart
|
|
43
|
+
|
|
44
|
+
Clone the repository locally with `git clone`, and then run `uv sync` to install the necessary dependencies. You need to have Git installed and authenticated with GitHub (usually via SSH, it may be useful to look up a tutorial). You also need to have `uv` installed.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/fragadmsc/rbg.git
|
|
48
|
+
cd rbg #go to the newly cloned repository
|
|
49
|
+
uv sync
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Project Structure
|
|
54
|
+
|
|
55
|
+
#### File Structure
|
|
56
|
+
|
|
57
|
+
The basic file structure is this:
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
The idea is that each person should usually work separately on some topic (like Unsupervised) and some separated branch.
|
|
62
|
+
|
|
63
|
+
#### Architecture & Public API
|
|
64
|
+
|
|
65
|
+
An API (Application Programming interface) is the interface someone will interact with if they use our library. So the public methods in a class are part of the API, whereas the private helper function in some class is not.
|
|
66
|
+
|
|
67
|
+
The final user will use the library with `rbg.Topic.specific.method()`. So, if you wanted to fit a linear regression model you would do: `rbg.Regression.Linear.fit(x)`. The specifics on what should be the interface of a specific model are in the `test` folder (we should add a documentation that describes the api sometime).
|
|
68
|
+
|
|
69
|
+
#### Build System
|
|
70
|
+
|
|
71
|
+
The build system used is `uv`. It provides some nice useful functionalities and it is quite fast. You can use it to create a virtual environment `uv venv`, to run your code `uv run main.py` You can refer to the [documentation](https://docs.astral.sh/uv/) if needed.
|
|
72
|
+
|
|
73
|
+
#### Tests
|
|
74
|
+
|
|
75
|
+
The tests will use pytest. We use a TDD (Test Driven Development) approach, so (usually) all of the tests are written before the function or classes, which in turn defines the classes interfaces. To use pytest, just do `uv run pytest` in the folder that you want to test.
|
|
76
|
+
|
|
77
|
+
### Git/Github workflow
|
|
78
|
+
|
|
79
|
+
#### Good practices
|
|
80
|
+
|
|
81
|
+
Each feature should be developed in a separate branch, and no two people should work in the same branch or feature at the same time. The branches are meant to be incremental: don't create only one branch for all of your work. Instead, create a branch for every feature you want to implement, make a pull request to merge it into the main, and then delete it.
|
|
82
|
+
|
|
83
|
+
#### Basic git workflow
|
|
84
|
+
|
|
85
|
+
To make some change, you should first update your local main branch to the latest version:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git switch main
|
|
89
|
+
git pull origin main
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then create a branch we will follow a name convention: first the type of the change and then the name:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
feature/<name>
|
|
96
|
+
fix/<name>
|
|
97
|
+
refactor/<name>
|
|
98
|
+
docs/<name>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
So if we want to create a branch for crating a test for KNN we would create `feature/test-knn`. Fix is for code fixes and bug corrections, refactor is for refactoring the code (like changing an entire class logic to something else) and docs is for adding documentation or comments.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git switch -c my-new-branch #the switch -c creates a new branch and switchs to it
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
After making some changes (creating files, editing files or deleting files), save them with:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
git add . #the . adds all of the modified files
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
And then commit them with:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
git commit -m "my commit message"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
When you have the necessary commits, tested the new feature you have implemented, and are ready to merge them into the main branch, you should create a pull request. To do this, make:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git fetch #this updates your local information about the github repository without modifying your local branch
|
|
123
|
+
git merge origin/main #this merges the current version of the main branch into your feature branch
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Maybe (probably) the `git merge origin/main` will fail eventually, saying that there was a merge conflict. It is normal, and it is not a big problem (it will not completely destroy the repository or something like this, since you are isolated on your branch).
|
|
127
|
+
|
|
128
|
+
If you find some, conflicts, you can run `git status`, which will tell which files are conflicting, then you solve these conflicts (by directly editing the files, it may be useful to look up a tutorial) and then:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git add .
|
|
132
|
+
git commit -m "a message"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
After solving the conflict (if necessary) and sucessfully merging the origin/main onto your branch, you can push your local branch:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
git push origin my-new-branch
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
You need then to see why the merge conflict is happening, and then decide which changes you want to keep. Usually this means running a `git status` or a `git diff the-branch-you-are-in the-branch-you-want-to-merge` and then checking the differences. It might be useful looking up a tutorial when this happens or asking AI.
|
|
142
|
+
|
|
143
|
+
After doing this, you can open github, and an option to pull request will appear:
|
|
144
|
+
|
|
145
|
+

|
|
146
|
+
|
|
147
|
+
This will show that your branch is now on github, and you can click `Compare e pull request` and then click `Create pull request`. This will create a pull request. A pull request is like asking for permission to put your new feature into the main branch. Someone will revise your code, and then choose to accept or reject your pull request.
|
|
148
|
+
|
|
149
|
+
After someone revises your pull request and merge it into the main branch, you can safely delete both the local and the github branch:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
git push origin --delete my-new-branch
|
|
153
|
+
git switch main
|
|
154
|
+
git branch -d my-new-branch
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
You should not approve or merge your own pull requests. Every PR should be reviewed and approved by at least one other team member.
|
|
158
|
+
|
|
159
|
+
Also, when accepting pull request, we will always use the rebase option for merging on GitHub (this keeps the commit history clean):
|
|
160
|
+
|
|
161
|
+

|
|
162
|
+
|
|
163
|
+
### Code Style Guide
|
|
164
|
+
|
|
165
|
+
#### General rules
|
|
166
|
+
|
|
167
|
+
The code should follow general good practices for python code. Try to make the code pythonic, always use type annotations, make a self-explanatory code, this is, make a code that you can infer the objective of the code just from looking at it, and avoid obvious comments, while using comments when necessary on more complex steps of the code.
|
|
168
|
+
|
|
169
|
+
#### Docstrings
|
|
170
|
+
|
|
171
|
+
For the docstrings, we will follow the [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings). We will also refer to this document when we are in doubt of what is the best way to write something (it is a big and boring document, there is absolutely no need to read it all, just be aware of using good practices and referring to it when necessary).
|
rbg_ml-0.1.0/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# RBG (Regressão Bem Geral)
|
|
2
|
+
#### *a.k.a Rodrigo do Bigode Grosso*
|
|
3
|
+
|
|
4
|
+
This is the official repository for the machine learning library titled RBG.
|
|
5
|
+
|
|
6
|
+
The documentation is at: [docs](https://rbg.readthedocs.io/en/latest/)
|
|
7
|
+
|
|
8
|
+
This file contains the structure of the library, the workflow with git/github that should be used and the code style guides. This document should be referred to if you have some doubt (it is also a good context base for asking something to AI).
|
|
9
|
+
|
|
10
|
+
### Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Quickstart](#quickstart)
|
|
13
|
+
- [Project Structure](#library-structure)
|
|
14
|
+
- [Build System](#build-system)
|
|
15
|
+
- [File Structure](#file-structure)
|
|
16
|
+
- [Architecture & public API](#architecture--public-api)
|
|
17
|
+
- [Tests](#tests)
|
|
18
|
+
- [Git/GitHub Workflow](#gitgithub-workflow)
|
|
19
|
+
- [Good Practices](#good-practices)
|
|
20
|
+
- [Basic Git Workflow](#basic-git-workflow)
|
|
21
|
+
- [Code Style Guide](#code-style-guide)
|
|
22
|
+
- [General Rules](#general-rules)
|
|
23
|
+
- [Docstrings](#docstrings)
|
|
24
|
+
|
|
25
|
+
### Quickstart
|
|
26
|
+
|
|
27
|
+
Clone the repository locally with `git clone`, and then run `uv sync` to install the necessary dependencies. You need to have Git installed and authenticated with GitHub (usually via SSH, it may be useful to look up a tutorial). You also need to have `uv` installed.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/fragadmsc/rbg.git
|
|
31
|
+
cd rbg #go to the newly cloned repository
|
|
32
|
+
uv sync
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Project Structure
|
|
37
|
+
|
|
38
|
+
#### File Structure
|
|
39
|
+
|
|
40
|
+
The basic file structure is this:
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
The idea is that each person should usually work separately on some topic (like Unsupervised) and some separated branch.
|
|
45
|
+
|
|
46
|
+
#### Architecture & Public API
|
|
47
|
+
|
|
48
|
+
An API (Application Programming interface) is the interface someone will interact with if they use our library. So the public methods in a class are part of the API, whereas the private helper function in some class is not.
|
|
49
|
+
|
|
50
|
+
The final user will use the library with `rbg.Topic.specific.method()`. So, if you wanted to fit a linear regression model you would do: `rbg.Regression.Linear.fit(x)`. The specifics on what should be the interface of a specific model are in the `test` folder (we should add a documentation that describes the api sometime).
|
|
51
|
+
|
|
52
|
+
#### Build System
|
|
53
|
+
|
|
54
|
+
The build system used is `uv`. It provides some nice useful functionalities and it is quite fast. You can use it to create a virtual environment `uv venv`, to run your code `uv run main.py` You can refer to the [documentation](https://docs.astral.sh/uv/) if needed.
|
|
55
|
+
|
|
56
|
+
#### Tests
|
|
57
|
+
|
|
58
|
+
The tests will use pytest. We use a TDD (Test Driven Development) approach, so (usually) all of the tests are written before the function or classes, which in turn defines the classes interfaces. To use pytest, just do `uv run pytest` in the folder that you want to test.
|
|
59
|
+
|
|
60
|
+
### Git/Github workflow
|
|
61
|
+
|
|
62
|
+
#### Good practices
|
|
63
|
+
|
|
64
|
+
Each feature should be developed in a separate branch, and no two people should work in the same branch or feature at the same time. The branches are meant to be incremental: don't create only one branch for all of your work. Instead, create a branch for every feature you want to implement, make a pull request to merge it into the main, and then delete it.
|
|
65
|
+
|
|
66
|
+
#### Basic git workflow
|
|
67
|
+
|
|
68
|
+
To make some change, you should first update your local main branch to the latest version:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git switch main
|
|
72
|
+
git pull origin main
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then create a branch we will follow a name convention: first the type of the change and then the name:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
feature/<name>
|
|
79
|
+
fix/<name>
|
|
80
|
+
refactor/<name>
|
|
81
|
+
docs/<name>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
So if we want to create a branch for crating a test for KNN we would create `feature/test-knn`. Fix is for code fixes and bug corrections, refactor is for refactoring the code (like changing an entire class logic to something else) and docs is for adding documentation or comments.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git switch -c my-new-branch #the switch -c creates a new branch and switchs to it
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
After making some changes (creating files, editing files or deleting files), save them with:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
git add . #the . adds all of the modified files
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
And then commit them with:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git commit -m "my commit message"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
When you have the necessary commits, tested the new feature you have implemented, and are ready to merge them into the main branch, you should create a pull request. To do this, make:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git fetch #this updates your local information about the github repository without modifying your local branch
|
|
106
|
+
git merge origin/main #this merges the current version of the main branch into your feature branch
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Maybe (probably) the `git merge origin/main` will fail eventually, saying that there was a merge conflict. It is normal, and it is not a big problem (it will not completely destroy the repository or something like this, since you are isolated on your branch).
|
|
110
|
+
|
|
111
|
+
If you find some, conflicts, you can run `git status`, which will tell which files are conflicting, then you solve these conflicts (by directly editing the files, it may be useful to look up a tutorial) and then:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git add .
|
|
115
|
+
git commit -m "a message"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
After solving the conflict (if necessary) and sucessfully merging the origin/main onto your branch, you can push your local branch:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
git push origin my-new-branch
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
You need then to see why the merge conflict is happening, and then decide which changes you want to keep. Usually this means running a `git status` or a `git diff the-branch-you-are-in the-branch-you-want-to-merge` and then checking the differences. It might be useful looking up a tutorial when this happens or asking AI.
|
|
125
|
+
|
|
126
|
+
After doing this, you can open github, and an option to pull request will appear:
|
|
127
|
+
|
|
128
|
+

|
|
129
|
+
|
|
130
|
+
This will show that your branch is now on github, and you can click `Compare e pull request` and then click `Create pull request`. This will create a pull request. A pull request is like asking for permission to put your new feature into the main branch. Someone will revise your code, and then choose to accept or reject your pull request.
|
|
131
|
+
|
|
132
|
+
After someone revises your pull request and merge it into the main branch, you can safely delete both the local and the github branch:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git push origin --delete my-new-branch
|
|
136
|
+
git switch main
|
|
137
|
+
git branch -d my-new-branch
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
You should not approve or merge your own pull requests. Every PR should be reviewed and approved by at least one other team member.
|
|
141
|
+
|
|
142
|
+
Also, when accepting pull request, we will always use the rebase option for merging on GitHub (this keeps the commit history clean):
|
|
143
|
+
|
|
144
|
+

|
|
145
|
+
|
|
146
|
+
### Code Style Guide
|
|
147
|
+
|
|
148
|
+
#### General rules
|
|
149
|
+
|
|
150
|
+
The code should follow general good practices for python code. Try to make the code pythonic, always use type annotations, make a self-explanatory code, this is, make a code that you can infer the objective of the code just from looking at it, and avoid obvious comments, while using comments when necessary on more complex steps of the code.
|
|
151
|
+
|
|
152
|
+
#### Docstrings
|
|
153
|
+
|
|
154
|
+
For the docstrings, we will follow the [Google style](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings). We will also refer to this document when we are in doubt of what is the best way to write something (it is a big and boring document, there is absolutely no need to read it all, just be aware of using good practices and referring to it when necessary).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "rbg-ml"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Basic Machine Learning Library"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
keywords = [
|
|
7
|
+
"machine learning",
|
|
8
|
+
"basic",
|
|
9
|
+
"regression",
|
|
10
|
+
]
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"numpy",
|
|
14
|
+
"pandas",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[[project.authors]]
|
|
18
|
+
name = "fragadmsc"
|
|
19
|
+
email = "damasceno.lucas512@gmail.com"
|
|
20
|
+
|
|
21
|
+
[[project.authors]]
|
|
22
|
+
name = "lucca moulin"
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
test = ["pytest>=9.0"]
|
|
26
|
+
docs = [
|
|
27
|
+
"furo>=2025.12.19",
|
|
28
|
+
"sphinx>=9.1.1",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.uv.build-backend]
|
|
32
|
+
module-name = "rbg"
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
pythonpath = ["src"]
|
|
36
|
+
testpaths = ["test"]
|
|
37
|
+
|
|
38
|
+
[build-system]
|
|
39
|
+
requires = ["uv_build>=0.12.13,<0.13.0"]
|
|
40
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "rbg-ml"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Basic Machine Learning Library"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "fragadmsc", email = "damasceno.lucas512@gmail.com" },
|
|
8
|
+
{ name = "lucca moulin"}
|
|
9
|
+
]
|
|
10
|
+
keywords = ["machine learning", "basic", "regression"]
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"numpy",
|
|
14
|
+
"pandas",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.uv.build-backend]
|
|
18
|
+
module-name = "rbg"
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
test = ["pytest>=9.0"]
|
|
22
|
+
docs = [
|
|
23
|
+
"furo>=2025.12.19",
|
|
24
|
+
"sphinx>=9.1.1",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["uv_build>=0.12.13,<0.13.0"]
|
|
29
|
+
build-backend = "uv_build"
|
|
30
|
+
|
|
31
|
+
[tool.pytest.ini_options]
|
|
32
|
+
pythonpath = ["src"]
|
|
33
|
+
testpaths = ["test"]
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from rbg.Classification.classifier import Classifier
|
|
3
|
+
|
|
4
|
+
class KNN(Classifier):
|
|
5
|
+
"""K-nearest neighbors classifier for categorical labels."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, labels=None, k=3, reference_class = None):
|
|
8
|
+
"""Initialize the classifier with class labels and the number of neighbors.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
labels: Unique class labels supported by the classifier.
|
|
12
|
+
k: Number of nearest neighbors used to classify each observation.
|
|
13
|
+
Defaults to 3.
|
|
14
|
+
reference_class: Reference label used to initialize the base
|
|
15
|
+
classifier.
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
ValueError: If the class labels or reference label are invalid,
|
|
19
|
+
as defined by the base classifier.
|
|
20
|
+
"""
|
|
21
|
+
Classifier.__init__(self, labels, reference_class)
|
|
22
|
+
self.set_params(k)
|
|
23
|
+
|
|
24
|
+
def set_params(self, k):
|
|
25
|
+
"""Set the number of nearest neighbors.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
k: Positive integer specifying the number of neighbors to consider.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
The classifier instance, allowing method chaining.
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If k is not a positive integer or is a boolean.
|
|
35
|
+
"""
|
|
36
|
+
if isinstance(k, bool) == False:
|
|
37
|
+
if isinstance(k, int) and k>0:
|
|
38
|
+
self.k = k
|
|
39
|
+
else:
|
|
40
|
+
raise ValueError("k must be a positive integer.")
|
|
41
|
+
else:
|
|
42
|
+
raise ValueError("k must be a positive integer.")
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
def get_params(self):
|
|
46
|
+
"""Return the current classifier parameters.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dictionary containing k, the number of nearest neighbors.
|
|
50
|
+
"""
|
|
51
|
+
return {"k": self.k}
|
|
52
|
+
|
|
53
|
+
def fit(self, X,y):
|
|
54
|
+
"""Store the training data after validating their format and contents.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
X: Numerical training feature matrix of shape
|
|
58
|
+
(n_samples, n_features). All values must be finite.
|
|
59
|
+
y: One-dimensional array of class labels of shape (n_samples,).
|
|
60
|
+
All labels must belong to the set declared at initialization.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: If X or y has an invalid dimension, X contains
|
|
64
|
+
non-numerical or non-finite values, y contains unknown labels,
|
|
65
|
+
the numbers of observations do not match, the data are empty,
|
|
66
|
+
or X has no features.
|
|
67
|
+
"""
|
|
68
|
+
self._fit_done = False
|
|
69
|
+
|
|
70
|
+
X = np.asarray(X)
|
|
71
|
+
y = np.asarray(y)
|
|
72
|
+
|
|
73
|
+
if X.ndim != 2:
|
|
74
|
+
raise ValueError("X must be two-dimensional.")
|
|
75
|
+
if np.issubdtype(X.dtype, np.number) == False:
|
|
76
|
+
raise ValueError("X must be a numerical array.")
|
|
77
|
+
else:
|
|
78
|
+
if np.isfinite(X).all() == False:
|
|
79
|
+
raise ValueError("X must have only finite values.")
|
|
80
|
+
if y.ndim != 1:
|
|
81
|
+
raise ValueError("y must be one-dimensional.")
|
|
82
|
+
if X.shape[0] != y.shape[0]:
|
|
83
|
+
raise ValueError("The data do not match.")
|
|
84
|
+
if X.shape[0] == 0 or y.shape[0] == 0 or y.size == 0:
|
|
85
|
+
raise ValueError("X and y must not be empty.")
|
|
86
|
+
if X.shape[1] == 0:
|
|
87
|
+
raise ValueError("X must have at least one feature.")
|
|
88
|
+
if self._labels is None:
|
|
89
|
+
self._set_labels(np.unique(y))
|
|
90
|
+
elif not np.isin(y, self._labels).all():
|
|
91
|
+
raise ValueError("y contains unknown class labels.")
|
|
92
|
+
|
|
93
|
+
self._X_train = X
|
|
94
|
+
self._y_train = y
|
|
95
|
+
self._fit_done = True
|
|
96
|
+
|
|
97
|
+
def predict(self, X):
|
|
98
|
+
"""Predict class labels using the majority vote of the nearest neighbors.
|
|
99
|
+
|
|
100
|
+
For each observation, calculate the Euclidean distances to the training
|
|
101
|
+
observations, select the k nearest neighbors, and assign the most
|
|
102
|
+
frequent class among their labels.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
X: Numerical feature matrix of shape
|
|
106
|
+
(n_new_samples, n_features). The number of features must match
|
|
107
|
+
the training data, and all values must be finite.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Array of predicted class labels of shape (n_new_samples,).
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValueError: If the model has not been fitted, X has an invalid
|
|
114
|
+
dimension, contains non-numerical or non-finite values, is
|
|
115
|
+
empty, or has a different number of features from the
|
|
116
|
+
training data.
|
|
117
|
+
"""
|
|
118
|
+
if self._fit_done == False:
|
|
119
|
+
raise ValueError("The model must be trained before prediction.")
|
|
120
|
+
|
|
121
|
+
X = np.asarray(X)
|
|
122
|
+
|
|
123
|
+
if X.ndim != 2:
|
|
124
|
+
raise ValueError("X must be two-dimensional.")
|
|
125
|
+
if np.issubdtype(X.dtype, np.number) == False:
|
|
126
|
+
raise ValueError("X must be a numerical array.")
|
|
127
|
+
else:
|
|
128
|
+
if np.isfinite(X).all() == False:
|
|
129
|
+
raise ValueError("X must have only finite values.")
|
|
130
|
+
if X.shape[0] == 0:
|
|
131
|
+
raise ValueError("X must not be empty.")
|
|
132
|
+
if X.shape[1] != self._X_train.shape[1]:
|
|
133
|
+
raise ValueError("X must be the same number of features as the training X.")
|
|
134
|
+
|
|
135
|
+
def min_distance(x, k):
|
|
136
|
+
"""Return the indices of the k training observations closest to x.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
x: Feature vector of a single observation.
|
|
140
|
+
k: Number of nearest neighbors to select.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Indices of the k training observations with the smallest
|
|
144
|
+
Euclidean distances to x.
|
|
145
|
+
"""
|
|
146
|
+
dist = [sum((x[i]-amostra[i])**2 for i in range(len(x)))**(1/2) for amostra in self._X_train]
|
|
147
|
+
indice = np.argsort(dist)[:k]
|
|
148
|
+
return indice
|
|
149
|
+
|
|
150
|
+
def class_neighbors(indice):
|
|
151
|
+
"""Retrieve the class labels of the selected neighbors.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
indice: Indices of the selected training observations.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Array of class labels corresponding to the selected neighbors.
|
|
158
|
+
"""
|
|
159
|
+
neighbors = self._y_train[indice]
|
|
160
|
+
return neighbors
|
|
161
|
+
|
|
162
|
+
prediction = [class_neighbors(min_distance(x, self.k)) for x in X]
|
|
163
|
+
|
|
164
|
+
def final_class(prediction):
|
|
165
|
+
"""Determine the most frequent class among the neighbors.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
prediction: Array of class labels assigned to the neighbors.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Class label with the highest frequency. Ties are resolved by
|
|
172
|
+
selecting the first class returned by np.unique.
|
|
173
|
+
"""
|
|
174
|
+
labels, counts = np.unique(prediction, return_counts=True)
|
|
175
|
+
return labels[np.argmax(counts)]
|
|
176
|
+
|
|
177
|
+
predicted_labels = np.array([final_class(neighbors) for neighbors in prediction])
|
|
178
|
+
return predicted_labels
|