text: from typing import NoReturn
  from ...base import BaseEstimator
  import numpy as np
  
  
  class GaussianNaiveBayes(BaseEstimator):
      """
      Gaussian Naive-Bayes classifier
      """
  
      def __init__(self):
          """
          Instantiate a Gaussian Naive Bayes classifier
  
          Attributes
          ----------
          self.classes_ : np.ndarray of shape (n_classes,)
              The different labels classes. To be set in `GaussianNaiveBayes.fit`
  
          self.mu_ : np.ndarray of shape (n_classes,n_features)
              The estimated features means for each class. To be set in `GaussianNaiveBayes.fit`
  
          self.vars_ : np.ndarray of shape (n_classes, n_features)
              The estimated features variances for each class. To be set in `GaussianNaiveBayes.fit`
  
          self.pi_: np.ndarray of shape (n_classes)
              The estimated class probabilities. To be set in `GaussianNaiveBayes.fit`
          """
          super().__init__()
          self.classes_, self.mu_, self.vars_, self.pi_ = None, None, None, None
  
      def _fit(self, X: np.ndarray, y: np.ndarray) -> NoReturn:
          """
          fits a gaussian naive bayes model
  
          Parameters
          ----------
          X : ndarray of shape (n_samples, n_features)
              Input data to fit an estimator for
  
          y : ndarray of shape (n_samples, )
              Responses of input data to fit to
          """
          self.classes_ = np.unique(y)
          c_instances_num = np.array([])
          self.pi_ = np.array([])
          self.mu_ = np.empty([len(self.classes_), X.shape[1]])
          self.vars_ = np.empty([len(self.classes_), X.shape[1]])
          for i, c in enumerate(self.classes_):
              c_indices = y == c
              c_instances_num=np.append(c_instances_num,c_indices.sum())
              c_X_t = X[c_indices].transpose()
              for feature in range(X.shape[1]):
                  self.mu_[i][feature] = c_X_t[feature].sum() / \
                                         c_instances_num[-1]
                  # calculating unbiased vars:
                  self.vars_[i][feature] = (np.square(
                      c_X_t[feature] - self.mu_[i][feature]).sum()) / \
                                           (c_instances_num[-1]-1)
  
              self.pi_ = np.append(self.pi_, c_instances_num[-1] / len(y))
          self.fitted_ = True
  
      def _predict(self, X: np.ndarray) -> np.ndarray:
          """
          Predict responses for given samples using fitted estimator
  
          Parameters
          ----------
          X : ndarray of shape (n_samples, n_features)
              Input data to predict responses for
  
          Returns
          -------
          responses : ndarray of shape (n_samples, )
              Predicted responses of given samples
          """
          return np.argmax(self.likelihood(X), axis=1)
  
      def likelihood(self, X: np.ndarray) -> np.ndarray:
          """
          Calculate the likelihood of a given data over the estimated model
  
          Parameters
          ----------
          X : np.ndarray of shape (n_samples, n_features)
              Input data to calculate its likelihood over the different classes.
  
          Returns
          -------
          likelihoods : np.ndarray of shape (n_samples, n_classes)
              The likelihood for each sample under each of the classes
  
          """
          if not self.fitted_:
              raise ValueError(
                  "Estimator must first be fitted before calling `likelihood` function")
          likelihoods = np.ones([len(X), len(self.classes_)])
          for i, sample in enumerate(X):
              for k, c in enumerate(self.classes_):
                  for feature in range(X.shape[1]):
                      likelihoods[i][k] *= np.exp(
                          np.square((sample[feature] - self.mu_[k][feature])) /
                          (-2 * self.vars_[k][feature])) / np.sqrt(
                          2 * np.pi * self.vars_[k][feature])
                  likelihoods[i][k] *= self.pi_[k]
          return likelihoods
  
      def _loss(self, X: np.ndarray, y: np.ndarray) -> float:
          """
          Evaluate performance under misclassification loss function
  
          Parameters
          ----------
          X : ndarray of shape (n_samples, n_features)
              Test samples
  
          y : ndarray of shape (n_samples, )
              True labels of test samples
  
          Returns
          -------
          loss : float
              Performance under missclassification loss function
          """
          from ...metrics import misclassification_error
          return misclassification_error(y, self.predict(X))
  