From 2759947a481661c4ae776614aad053b0f0f4aab7 Mon Sep 17 00:00:00 2001 From: ricca Date: Mon, 2 Oct 2023 20:48:40 +0200 Subject: [PATCH 01/14] Implemented a multinomial naive bayes classifier for text classification --- .../multinomial_naive_bayes_classifier.py | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 machine_learning/multinomial_naive_bayes_classifier.py diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py new file mode 100644 index 000000000000..c290f2c6387b --- /dev/null +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -0,0 +1,135 @@ +""" +Implementation from scratch of a basic Multinomial Naive Bayes classifier for text classification. +""" + + +import numpy as np +import doctest +from scipy import sparse +from sklearn.feature_extraction.text import TfidfVectorizer +from sklearn.datasets import fetch_20newsgroups +from sklearn.metrics import accuracy_score + + +def group_data_by_target(targets): + """ + Associates to each target label the indices of the examples with that label + + Parameters + ---------- + targets : array-like of shape (n_samples,) + Target labels + + Returns + ---------- + grouped_data : dict of (label : list) + Maps each target label to the list of indices of the examples with that label + + Example + ---------- + >>> y = np.array([1, 2, 3, 1, 2, 5]) + >>> group_data_by_target(y) + {1: [0, 3], 2: [1, 4], 3: [2], 5: [5]} + """ + grouped_data = {} + for i, y in enumerate(targets): + if y not in grouped_data: + grouped_data[y] = [] + grouped_data[y].append(i) + return grouped_data + + +class MultinomialNBClassifier: + def __init__(self, alpha=1): + self.classes = None + self.features_probs = None + self.priors = None + self.alpha = alpha + + def fit(self, X, y): + """ + Parameters + ---------- + X : scipy.sparse.csr_matrix of shape (n_samples, n_features) + Multinomial training examples + + y : array-like of shape (n_samples,) + Target labels + """ + if not sparse.issparse(X): + raise ValueError("Matrix X must be an instance of scipy.sparse.csr_matrix") + n_examples, n_features = X.shape + grouped_data = group_data_by_target(y) + self.classes = list(grouped_data.keys()) + self.priors = np.zeros(shape=len(self.classes)) + self.features_probs = np.zeros(shape=(len(self.classes), n_features)) + + for i, class_i in enumerate(self.classes): + data_class_i = X[grouped_data[class_i]] + prior_class_i = data_class_i.shape[0] / n_examples + self.priors[i] = prior_class_i + tot_features_count = data_class_i.sum() # count of all features in class_i + features_count = np.array(data_class_i.sum(axis=0))[0] # count of each feature x_j in class_i + for j, n_j in enumerate(features_count): + self.features_probs[i][j] = (self.alpha + n_j) / (tot_features_count + self.alpha * n_features) + + def predict(self, X): + """ + Parameters + ---------- + X : scipy.sparse.csr_matrix of shape (n_samples, n_features) + Multinomial test examples + + Returns + ---------- + y_pred : ndarray of shape (n_samples,) + Predicted target labels of test examples + + Example + ---------- + Let's test the function following an example taken from the documentation of the MultinomialNB model + from sklearn + >>> rng = np.random.RandomState(1) + >>> X = rng.randint(5, size=(6, 100)) + >>> X = sparse.csr_matrix(X) + >>> y = np.array([1, 2, 3, 4, 5, 6]) + >>> model = MultinomialNBClassifier() + >>> model.fit(X, y) + >>> model.predict(X[2:3]) + array([3]) + """ + if not sparse.issparse(X): + raise ValueError("Matrix X must be an instance of scipy.sparse.csr_matrix") + y_pred = [] + log_features_probs = np.log(self.features_probs) + log_priors = np.log(self.priors) + for instance in X: + theta = instance.multiply(log_features_probs).sum(axis=1) + likelihood = [log_prior_class_i + theta[i] for i, log_prior_class_i in enumerate(log_priors)] + y_pred.append(self.classes[np.argmax(likelihood)]) + return np.array(y_pred) + + +def main(): + newsgroups_train = fetch_20newsgroups(subset='train') + newsgroups_test = fetch_20newsgroups(subset='test') + X_train = newsgroups_train['data'] + y_train = newsgroups_train['target'] + X_test = newsgroups_test['data'] + y_test = newsgroups_test['target'] + vectorizer = TfidfVectorizer(stop_words='english') + X_train = vectorizer.fit_transform(X_train) + X_test = vectorizer.transform(X_test) + + model = MultinomialNBClassifier() + print("Start training") + model.fit(X_train, y_train) + + y_pred = model.predict(X_test) + print("Accuracy of Naive Bayes text classifier: " + str(accuracy_score(y_test, y_pred))) + + +if __name__ == "__main__": + main() + doctest.testmod() + From 37184e21deba2f30ced8c16a65f86cb27c9ce13e Mon Sep 17 00:00:00 2001 From: ricca Date: Tue, 3 Oct 2023 18:28:37 +0200 Subject: [PATCH 02/14] Implemented input check --- .../multinomial_naive_bayes_classifier.py | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index c290f2c6387b..a13a9080492e 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -11,7 +11,7 @@ from sklearn.metrics import accuracy_score -def group_data_by_target(targets): +def group_indices_by_target(targets): """ Associates to each target label the indices of the examples with that label @@ -22,21 +22,21 @@ def group_data_by_target(targets): Returns ---------- - grouped_data : dict of (label : list) - Maps each target label to the list of indices of the examples with that label + grouped_indices : dict of (label : list) + Maps each target label to the list of indices of the examples with that label Example ---------- >>> y = np.array([1, 2, 3, 1, 2, 5]) - >>> group_data_by_target(y) + >>> group_indices_by_target(y) {1: [0, 3], 2: [1, 4], 3: [2], 5: [5]} """ - grouped_data = {} + grouped_indices = {} for i, y in enumerate(targets): - if y not in grouped_data: - grouped_data[y] = [] - grouped_data[y].append(i) - return grouped_data + if y not in grouped_indices: + grouped_indices[y] = [] + grouped_indices[y].append(i) + return grouped_indices class MultinomialNBClassifier: @@ -46,6 +46,16 @@ def __init__(self, alpha=1): self.priors = None self.alpha = alpha + def _check_X(self, X): + if not sparse.issparse(X): + raise ValueError("Matrix X must be an instance of scipy.sparse.csr_matrix") + + def _check_X_y(self, X, y): + self._check_X(X) + if X.shape[0] != len(y): + raise ValueError( + "The expected dimension for array y is (" + str(X.shape[0]) + ",), but got (" + str(len(y)) + ",)") + def fit(self, X, y): """ Parameters @@ -56,16 +66,15 @@ def fit(self, X, y): y : array-like of shape (n_samples,) Target labels """ - if not sparse.issparse(X): - raise ValueError("Matrix X must be an instance of scipy.sparse.csr_matrix") + self._check_X_y(X, y) n_examples, n_features = X.shape - grouped_data = group_data_by_target(y) - self.classes = list(grouped_data.keys()) + grouped_indices = group_indices_by_target(y) + self.classes = list(grouped_indices.keys()) self.priors = np.zeros(shape=len(self.classes)) self.features_probs = np.zeros(shape=(len(self.classes), n_features)) for i, class_i in enumerate(self.classes): - data_class_i = X[grouped_data[class_i]] + data_class_i = X[grouped_indices[class_i]] prior_class_i = data_class_i.shape[0] / n_examples self.priors[i] = prior_class_i tot_features_count = data_class_i.sum() # count of all features in class_i @@ -98,8 +107,7 @@ def predict(self, X): >>> model.predict(X[2:3]) array([3]) """ - if not sparse.issparse(X): - raise ValueError("Matrix X must be an instance of scipy.sparse.csr_matrix") + self._check_X(X) y_pred = [] log_features_probs = np.log(self.features_probs) log_priors = np.log(self.priors) @@ -126,7 +134,7 @@ def main(): model.fit(X_train, y_train) y_pred = model.predict(X_test) - print("Accuracy of Naive Bayes text classifier: " + str(accuracy_score(y_test, y_pred))) + print("Accuracy of naive bayes text classifier: " + str(accuracy_score(y_test, y_pred))) if __name__ == "__main__": From 5c4e412a356eb712b3cd5951266639608ca7fbf7 Mon Sep 17 00:00:00 2001 From: ricca Date: Tue, 3 Oct 2023 18:46:20 +0200 Subject: [PATCH 03/14] Comments added --- machine_learning/multinomial_naive_bayes_classifier.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index a13a9080492e..2c994f2f8707 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -1,5 +1,6 @@ """ -Implementation from scratch of a basic Multinomial Naive Bayes classifier for text classification. +Implementation from scratch of a Multinomial Naive Bayes Classifier. +The algorithm is trained and tested on the twenty_newsgroup dataset from sklearn to perform text classification """ @@ -54,7 +55,7 @@ def _check_X_y(self, X, y): self._check_X(X) if X.shape[0] != len(y): raise ValueError( - "The expected dimension for array y is (" + str(X.shape[0]) + ",), but got (" + str(len(y)) + ",)") + "The expected shape for array y is (" + str(X.shape[0]) + ",), but got (" + str(len(y)) + ",)") def fit(self, X, y): """ From e9f3d61643ea1b7416b5fc1da2e1d939bd8d80b5 Mon Sep 17 00:00:00 2001 From: ricca Date: Tue, 3 Oct 2023 19:07:57 +0200 Subject: [PATCH 04/14] Add comments --- machine_learning/multinomial_naive_bayes_classifier.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 2c994f2f8707..980aadca85eb 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -1,6 +1,9 @@ """ Implementation from scratch of a Multinomial Naive Bayes Classifier. The algorithm is trained and tested on the twenty_newsgroup dataset from sklearn to perform text classification + +Here the Wikipedia page to understand the theory behind this kind of probabilistic models: +https://en.wikipedia.org/wiki/Naive_Bayes_classifier """ From c1664e876db2e7f13066caba989c8f315afce176 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:16:52 +0000 Subject: [PATCH 05/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../multinomial_naive_bayes_classifier.py | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 980aadca85eb..1886f8c2aab3 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -58,7 +58,12 @@ def _check_X_y(self, X, y): self._check_X(X) if X.shape[0] != len(y): raise ValueError( - "The expected shape for array y is (" + str(X.shape[0]) + ",), but got (" + str(len(y)) + ",)") + "The expected shape for array y is (" + + str(X.shape[0]) + + ",), but got (" + + str(len(y)) + + ",)" + ) def fit(self, X, y): """ @@ -81,10 +86,14 @@ def fit(self, X, y): data_class_i = X[grouped_indices[class_i]] prior_class_i = data_class_i.shape[0] / n_examples self.priors[i] = prior_class_i - tot_features_count = data_class_i.sum() # count of all features in class_i - features_count = np.array(data_class_i.sum(axis=0))[0] # count of each feature x_j in class_i + tot_features_count = data_class_i.sum() # count of all features in class_i + features_count = np.array(data_class_i.sum(axis=0))[ + 0 + ] # count of each feature x_j in class_i for j, n_j in enumerate(features_count): - self.features_probs[i][j] = (self.alpha + n_j) / (tot_features_count + self.alpha * n_features) + self.features_probs[i][j] = (self.alpha + n_j) / ( + tot_features_count + self.alpha * n_features + ) def predict(self, X): """ @@ -117,19 +126,22 @@ def predict(self, X): log_priors = np.log(self.priors) for instance in X: theta = instance.multiply(log_features_probs).sum(axis=1) - likelihood = [log_prior_class_i + theta[i] for i, log_prior_class_i in enumerate(log_priors)] + likelihood = [ + log_prior_class_i + theta[i] + for i, log_prior_class_i in enumerate(log_priors) + ] y_pred.append(self.classes[np.argmax(likelihood)]) return np.array(y_pred) def main(): - newsgroups_train = fetch_20newsgroups(subset='train') - newsgroups_test = fetch_20newsgroups(subset='test') - X_train = newsgroups_train['data'] - y_train = newsgroups_train['target'] - X_test = newsgroups_test['data'] - y_test = newsgroups_test['target'] - vectorizer = TfidfVectorizer(stop_words='english') + newsgroups_train = fetch_20newsgroups(subset="train") + newsgroups_test = fetch_20newsgroups(subset="test") + X_train = newsgroups_train["data"] + y_train = newsgroups_train["target"] + X_test = newsgroups_test["data"] + y_test = newsgroups_test["target"] + vectorizer = TfidfVectorizer(stop_words="english") X_train = vectorizer.fit_transform(X_train) X_test = vectorizer.transform(X_test) @@ -138,10 +150,12 @@ def main(): model.fit(X_train, y_train) y_pred = model.predict(X_test) - print("Accuracy of naive bayes text classifier: " + str(accuracy_score(y_test, y_pred))) + print( + "Accuracy of naive bayes text classifier: " + + str(accuracy_score(y_test, y_pred)) + ) if __name__ == "__main__": main() doctest.testmod() - From 2de3ac6ec9e24c0bb31263e59a90e35739035f24 Mon Sep 17 00:00:00 2001 From: ricca Date: Tue, 3 Oct 2023 19:52:45 +0200 Subject: [PATCH 06/14] Add typing hints and naming conventions --- .../multinomial_naive_bayes_classifier.py | 54 ++++++++----------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 980aadca85eb..56c730529e0d 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -13,9 +13,10 @@ from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.datasets import fetch_20newsgroups from sklearn.metrics import accuracy_score +from numpy.typing import ArrayLike -def group_indices_by_target(targets): +def group_indices_by_target(targets: ArrayLike) -> dict: """ Associates to each target label the indices of the examples with that label @@ -50,35 +51,24 @@ def __init__(self, alpha=1): self.priors = None self.alpha = alpha - def _check_X(self, X): - if not sparse.issparse(X): - raise ValueError("Matrix X must be an instance of scipy.sparse.csr_matrix") - - def _check_X_y(self, X, y): - self._check_X(X) - if X.shape[0] != len(y): - raise ValueError( - "The expected shape for array y is (" + str(X.shape[0]) + ",), but got (" + str(len(y)) + ",)") - - def fit(self, X, y): + def fit(self, data: sparse.csr_matrix, y: ArrayLike) -> None: """ Parameters ---------- - X : scipy.sparse.csr_matrix of shape (n_samples, n_features) + data : scipy.sparse.csr_matrix of shape (n_samples, n_features) Multinomial training examples y : array-like of shape (n_samples,) Target labels """ - self._check_X_y(X, y) - n_examples, n_features = X.shape + n_examples, n_features = data.shape grouped_indices = group_indices_by_target(y) self.classes = list(grouped_indices.keys()) self.priors = np.zeros(shape=len(self.classes)) self.features_probs = np.zeros(shape=(len(self.classes), n_features)) for i, class_i in enumerate(self.classes): - data_class_i = X[grouped_indices[class_i]] + data_class_i = data[grouped_indices[class_i]] prior_class_i = data_class_i.shape[0] / n_examples self.priors[i] = prior_class_i tot_features_count = data_class_i.sum() # count of all features in class_i @@ -86,11 +76,11 @@ def fit(self, X, y): for j, n_j in enumerate(features_count): self.features_probs[i][j] = (self.alpha + n_j) / (tot_features_count + self.alpha * n_features) - def predict(self, X): + def predict(self, data: sparse.csr_matrix) -> np.array: """ Parameters ---------- - X : scipy.sparse.csr_matrix of shape (n_samples, n_features) + data : scipy.sparse.csr_matrix of shape (n_samples, n_features) Multinomial test examples Returns @@ -103,41 +93,43 @@ def predict(self, X): Let's test the function following an example taken from the documentation of the MultinomialNB model from sklearn >>> rng = np.random.RandomState(1) - >>> X = rng.randint(5, size=(6, 100)) - >>> X = sparse.csr_matrix(X) + >>> data = rng.randint(5, size=(6, 100)) + >>> data = sparse.csr_matrix(data) >>> y = np.array([1, 2, 3, 4, 5, 6]) >>> model = MultinomialNBClassifier() - >>> model.fit(X, y) - >>> model.predict(X[2:3]) + >>> model.fit(data, y) + >>> model.predict(data[2:3]) array([3]) """ - self._check_X(X) y_pred = [] log_features_probs = np.log(self.features_probs) log_priors = np.log(self.priors) - for instance in X: + for instance in data: theta = instance.multiply(log_features_probs).sum(axis=1) likelihood = [log_prior_class_i + theta[i] for i, log_prior_class_i in enumerate(log_priors)] y_pred.append(self.classes[np.argmax(likelihood)]) return np.array(y_pred) -def main(): +def main() -> None: + """ + Performs the text classification on the twenty_newsgroup dataset from sklearn + """ newsgroups_train = fetch_20newsgroups(subset='train') newsgroups_test = fetch_20newsgroups(subset='test') - X_train = newsgroups_train['data'] + x_train = newsgroups_train['data'] y_train = newsgroups_train['target'] - X_test = newsgroups_test['data'] + x_test = newsgroups_test['data'] y_test = newsgroups_test['target'] vectorizer = TfidfVectorizer(stop_words='english') - X_train = vectorizer.fit_transform(X_train) - X_test = vectorizer.transform(X_test) + x_train = vectorizer.fit_transform(x_train) + x_test = vectorizer.transform(x_test) model = MultinomialNBClassifier() print("Start training") - model.fit(X_train, y_train) + model.fit(x_train, y_train) - y_pred = model.predict(X_test) + y_pred = model.predict(x_test) print("Accuracy of naive bayes text classifier: " + str(accuracy_score(y_test, y_pred))) From f7d56fa6cb3957d4f16d5f34be09a40d541b9684 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 18:04:15 +0000 Subject: [PATCH 07/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../multinomial_naive_bayes_classifier.py | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 56c730529e0d..d99b5ecae627 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -71,10 +71,14 @@ def fit(self, data: sparse.csr_matrix, y: ArrayLike) -> None: data_class_i = data[grouped_indices[class_i]] prior_class_i = data_class_i.shape[0] / n_examples self.priors[i] = prior_class_i - tot_features_count = data_class_i.sum() # count of all features in class_i - features_count = np.array(data_class_i.sum(axis=0))[0] # count of each feature x_j in class_i + tot_features_count = data_class_i.sum() # count of all features in class_i + features_count = np.array(data_class_i.sum(axis=0))[ + 0 + ] # count of each feature x_j in class_i for j, n_j in enumerate(features_count): - self.features_probs[i][j] = (self.alpha + n_j) / (tot_features_count + self.alpha * n_features) + self.features_probs[i][j] = (self.alpha + n_j) / ( + tot_features_count + self.alpha * n_features + ) def predict(self, data: sparse.csr_matrix) -> np.array: """ @@ -106,7 +110,10 @@ def predict(self, data: sparse.csr_matrix) -> np.array: log_priors = np.log(self.priors) for instance in data: theta = instance.multiply(log_features_probs).sum(axis=1) - likelihood = [log_prior_class_i + theta[i] for i, log_prior_class_i in enumerate(log_priors)] + likelihood = [ + log_prior_class_i + theta[i] + for i, log_prior_class_i in enumerate(log_priors) + ] y_pred.append(self.classes[np.argmax(likelihood)]) return np.array(y_pred) @@ -115,13 +122,13 @@ def main() -> None: """ Performs the text classification on the twenty_newsgroup dataset from sklearn """ - newsgroups_train = fetch_20newsgroups(subset='train') - newsgroups_test = fetch_20newsgroups(subset='test') - x_train = newsgroups_train['data'] - y_train = newsgroups_train['target'] - x_test = newsgroups_test['data'] - y_test = newsgroups_test['target'] - vectorizer = TfidfVectorizer(stop_words='english') + newsgroups_train = fetch_20newsgroups(subset="train") + newsgroups_test = fetch_20newsgroups(subset="test") + x_train = newsgroups_train["data"] + y_train = newsgroups_train["target"] + x_test = newsgroups_test["data"] + y_test = newsgroups_test["target"] + vectorizer = TfidfVectorizer(stop_words="english") x_train = vectorizer.fit_transform(x_train) x_test = vectorizer.transform(x_test) @@ -130,10 +137,12 @@ def main() -> None: model.fit(x_train, y_train) y_pred = model.predict(x_test) - print("Accuracy of naive bayes text classifier: " + str(accuracy_score(y_test, y_pred))) + print( + "Accuracy of naive bayes text classifier: " + + str(accuracy_score(y_test, y_pred)) + ) if __name__ == "__main__": main() doctest.testmod() - From 9fa816445886364137347603b72f245da7d78bbc Mon Sep 17 00:00:00 2001 From: ricca Date: Tue, 3 Oct 2023 20:18:09 +0200 Subject: [PATCH 08/14] Fixed comments --- .../multinomial_naive_bayes_classifier.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 56c730529e0d..eddbec074c1c 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -1,8 +1,10 @@ """ Implementation from scratch of a Multinomial Naive Bayes Classifier. -The algorithm is trained and tested on the twenty_newsgroup dataset from sklearn to perform text classification +The algorithm is trained and tested on the twenty_newsgroup dataset +from sklearn to perform text classification -Here the Wikipedia page to understand the theory behind this kind of probabilistic models: +Here the Wikipedia page to understand the theory behind this kind +of probabilistic models: https://en.wikipedia.org/wiki/Naive_Bayes_classifier """ @@ -28,7 +30,8 @@ def group_indices_by_target(targets: ArrayLike) -> dict: Returns ---------- grouped_indices : dict of (label : list) - Maps each target label to the list of indices of the examples with that label + Maps each target label to the list of indices of the + examples with that label Example ---------- @@ -90,8 +93,8 @@ def predict(self, data: sparse.csr_matrix) -> np.array: Example ---------- - Let's test the function following an example taken from the documentation of the MultinomialNB model - from sklearn + Let's test the function following an example taken from the documentation + of the MultinomialNB model from sklearn >>> rng = np.random.RandomState(1) >>> data = rng.randint(5, size=(6, 100)) >>> data = sparse.csr_matrix(data) From 40c39a81f6b6302d53fee888bd964ea21518d78f Mon Sep 17 00:00:00 2001 From: ricca Date: Tue, 3 Oct 2023 20:35:49 +0200 Subject: [PATCH 09/14] Fixed imports --- machine_learning/multinomial_naive_bayes_classifier.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 46b72e88b9da..0587fcb116e6 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -8,14 +8,15 @@ https://en.wikipedia.org/wiki/Naive_Bayes_classifier """ - -import numpy as np import doctest +import numpy as np +from numpy.typing import ArrayLike from scipy import sparse from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.datasets import fetch_20newsgroups from sklearn.metrics import accuracy_score -from numpy.typing import ArrayLike + + def group_indices_by_target(targets: ArrayLike) -> dict: From d4d8fbca4153af57ad5ede5ba37c8d188785aeea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 18:36:31 +0000 Subject: [PATCH 10/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- machine_learning/multinomial_naive_bayes_classifier.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 0587fcb116e6..58bd475fb43f 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -17,8 +17,6 @@ from sklearn.metrics import accuracy_score - - def group_indices_by_target(targets: ArrayLike) -> dict: """ Associates to each target label the indices of the examples with that label From f6404ccb10c7caf49d9fbce9086b52084a1b1b81 Mon Sep 17 00:00:00 2001 From: ricca Date: Fri, 6 Oct 2023 17:13:04 +0200 Subject: [PATCH 11/14] Fixed imports --- .../multinomial_naive_bayes_classifier.py | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index 0587fcb116e6..f461e3142739 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -9,17 +9,16 @@ """ import doctest + import numpy as np -from numpy.typing import ArrayLike +import numpy.typing as npt from scipy import sparse -from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.datasets import fetch_20newsgroups +from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics import accuracy_score - - -def group_indices_by_target(targets: ArrayLike) -> dict: +def group_indices_by_target(targets: npt.ArrayLike) -> dict: """ Associates to each target label the indices of the examples with that label @@ -49,24 +48,24 @@ def group_indices_by_target(targets: ArrayLike) -> dict: class MultinomialNBClassifier: - def __init__(self, alpha=1): + def __init__(self, alpha: int = 1): self.classes = None self.features_probs = None self.priors = None self.alpha = alpha - def fit(self, data: sparse.csr_matrix, y: ArrayLike) -> None: + def fit(self, data: sparse.csr_matrix, targets: npt.ArrayLike) -> None: """ Parameters ---------- data : scipy.sparse.csr_matrix of shape (n_samples, n_features) Multinomial training examples - y : array-like of shape (n_samples,) + targets : array-like of shape (n_samples,) Target labels """ n_examples, n_features = data.shape - grouped_indices = group_indices_by_target(y) + grouped_indices = group_indices_by_target(targets) self.classes = list(grouped_indices.keys()) self.priors = np.zeros(shape=len(self.classes)) self.features_probs = np.zeros(shape=(len(self.classes), n_features)) @@ -76,15 +75,13 @@ def fit(self, data: sparse.csr_matrix, y: ArrayLike) -> None: prior_class_i = data_class_i.shape[0] / n_examples self.priors[i] = prior_class_i tot_features_count = data_class_i.sum() # count of all features in class_i - features_count = np.array(data_class_i.sum(axis=0))[ - 0 - ] # count of each feature x_j in class_i + features_count = np.array(data_class_i.sum(axis=0))[0] for j, n_j in enumerate(features_count): self.features_probs[i][j] = (self.alpha + n_j) / ( tot_features_count + self.alpha * n_features ) - def predict(self, data: sparse.csr_matrix) -> np.array: + def predict(self, data: sparse.csr_matrix) -> np.ndarray: """ Parameters ---------- @@ -123,9 +120,6 @@ def predict(self, data: sparse.csr_matrix) -> np.array: def main() -> None: - """ - Performs the text classification on the twenty_newsgroup dataset from sklearn - """ newsgroups_train = fetch_20newsgroups(subset="train") newsgroups_test = fetch_20newsgroups(subset="test") x_train = newsgroups_train["data"] From 694ba686e46bad8d0ac57870f4ecc02311d05b52 Mon Sep 17 00:00:00 2001 From: ricca Date: Fri, 6 Oct 2023 17:32:14 +0200 Subject: [PATCH 12/14] Handle mypy errors --- .../multinomial_naive_bayes_classifier.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index f461e3142739..d0bff15e69eb 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -11,14 +11,12 @@ import doctest import numpy as np -import numpy.typing as npt -from scipy import sparse from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics import accuracy_score -def group_indices_by_target(targets: npt.ArrayLike) -> dict: +def group_indices_by_target(targets): """ Associates to each target label the indices of the examples with that label @@ -48,13 +46,13 @@ def group_indices_by_target(targets: npt.ArrayLike) -> dict: class MultinomialNBClassifier: - def __init__(self, alpha: int = 1): + def __init__(self, alpha=1): self.classes = None self.features_probs = None self.priors = None self.alpha = alpha - def fit(self, data: sparse.csr_matrix, targets: npt.ArrayLike) -> None: + def fit(self, data, targets): """ Parameters ---------- @@ -81,7 +79,7 @@ def fit(self, data: sparse.csr_matrix, targets: npt.ArrayLike) -> None: tot_features_count + self.alpha * n_features ) - def predict(self, data: sparse.csr_matrix) -> np.ndarray: + def predict(self, data): """ Parameters ---------- @@ -97,6 +95,7 @@ def predict(self, data: sparse.csr_matrix) -> np.ndarray: ---------- Let's test the function following an example taken from the documentation of the MultinomialNB model from sklearn + >>> from scipy import sparse >>> rng = np.random.RandomState(1) >>> data = rng.randint(5, size=(6, 100)) >>> data = sparse.csr_matrix(data) @@ -119,7 +118,7 @@ def predict(self, data: sparse.csr_matrix) -> np.ndarray: return np.array(y_pred) -def main() -> None: +def main(): newsgroups_train = fetch_20newsgroups(subset="train") newsgroups_test = fetch_20newsgroups(subset="test") x_train = newsgroups_train["data"] From 040a292ecadd50076295ee905552d6a673f4ef23 Mon Sep 17 00:00:00 2001 From: ricca Date: Tue, 28 Nov 2023 19:08:28 +0100 Subject: [PATCH 13/14] Add type hints --- .../multinomial_naive_bayes_classifier.py | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/machine_learning/multinomial_naive_bayes_classifier.py b/machine_learning/multinomial_naive_bayes_classifier.py index d0bff15e69eb..ccbe6309ebcc 100644 --- a/machine_learning/multinomial_naive_bayes_classifier.py +++ b/machine_learning/multinomial_naive_bayes_classifier.py @@ -11,12 +11,13 @@ import doctest import numpy as np +import scipy from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics import accuracy_score -def group_indices_by_target(targets): +def group_indices_by_target(targets: np.ndarray) -> dict[int, list[int]]: """ Associates to each target label the indices of the examples with that label @@ -37,7 +38,7 @@ def group_indices_by_target(targets): >>> group_indices_by_target(y) {1: [0, 3], 2: [1, 4], 3: [2], 5: [5]} """ - grouped_indices = {} + grouped_indices: dict[int, list[int]] = {} for i, y in enumerate(targets): if y not in grouped_indices: grouped_indices[y] = [] @@ -46,13 +47,13 @@ def group_indices_by_target(targets): class MultinomialNBClassifier: - def __init__(self, alpha=1): - self.classes = None - self.features_probs = None - self.priors = None + def __init__(self, alpha: int = 1) -> None: + self.classes: list[int] = [] + self.features_probs: np.ndarray = np.array([]) + self.priors: np.ndarray = np.array([]) self.alpha = alpha - def fit(self, data, targets): + def fit(self, data: scipy.sparse.csr_matrix, targets: np.ndarray) -> None: """ Parameters ---------- @@ -61,6 +62,17 @@ def fit(self, data, targets): targets : array-like of shape (n_samples,) Target labels + + Example + ---------- + >>> from scipy import sparse + >>> rng = np.random.RandomState(1) + >>> data = rng.randint(5, size=(6, 100)) + >>> data = sparse.csr_matrix(data) + >>> y = np.array([1, 2, 3, 4, 5, 6]) + >>> model = MultinomialNBClassifier() + >>> print(model.fit(data, y)) + None """ n_examples, n_features = data.shape grouped_indices = group_indices_by_target(targets) @@ -79,7 +91,7 @@ def fit(self, data, targets): tot_features_count + self.alpha * n_features ) - def predict(self, data): + def predict(self, data: scipy.sparse.csr_matrix) -> np.ndarray: """ Parameters ---------- @@ -93,8 +105,6 @@ def predict(self, data): Example ---------- - Let's test the function following an example taken from the documentation - of the MultinomialNB model from sklearn >>> from scipy import sparse >>> rng = np.random.RandomState(1) >>> data = rng.randint(5, size=(6, 100)) @@ -118,7 +128,7 @@ def predict(self, data): return np.array(y_pred) -def main(): +if __name__ == "__main__": newsgroups_train = fetch_20newsgroups(subset="train") newsgroups_test = fetch_20newsgroups(subset="test") x_train = newsgroups_train["data"] @@ -138,8 +148,4 @@ def main(): "Accuracy of naive bayes text classifier: " + str(accuracy_score(y_test, y_pred)) ) - - -if __name__ == "__main__": - main() doctest.testmod() From 3ab8390b8dbf4aa962ac3d280ea7473cbad67242 Mon Sep 17 00:00:00 2001 From: cclauss Date: Tue, 8 Sep 2026 13:58:42 +0000 Subject: [PATCH 14/14] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 69bdb2b6c9d0..dd65af6ede07 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -685,6 +685,7 @@ * [Lstm Prediction](machine_learning/lstm/lstm_prediction.py) * [Mfcc](machine_learning/mfcc.py) * [Multilayer Perceptron Classifier](machine_learning/multilayer_perceptron_classifier.py) + * [Multinomial Naive Bayes Classifier](machine_learning/multinomial_naive_bayes_classifier.py) * [Polynomial Regression](machine_learning/polynomial_regression.py) * [Principle Component Analysis](machine_learning/principle_component_analysis.py) * [Random Forest Classifier](machine_learning/random_forest_classifier.py) @@ -876,6 +877,7 @@ * [Test Factorial](maths/test_factorial.py) * [Test Prime Check](maths/test_prime_check.py) * [Three Sum](maths/three_sum.py) + * [Tonelli Shanks](maths/tonelli_shanks.py) * [Trailing Zeroes](maths/trailing_zeroes.py) * [Trapezoidal Rule](maths/trapezoidal_rule.py) * [Triplet Sum](maths/triplet_sum.py)