Repository: scikit-learn-contrib/DESlib

Issue:
Refactor predict and predict_proba
Refactor predict and predict_proba to do only the "high-level" work: separate 1) which instances have all classifiers agreeing; 2) instances that are classified with KNN (if using IH), 3) instances using DS

Discussion:

Patch:
diff --git a/deslib/base.py b/deslib/base.py
index ab0d9dd..af92a7f 100644
--- a/deslib/base.py
+++ b/deslib/base.py
@@ -57,7 +57,6 @@ class BaseDS(BaseEstimator, ClassifierMixin):
         self.n_jobs = n_jobs
         self.voting = voting
 
-
         # Check optional dependency
         if knn_classifier == 'faiss' and not faiss_knn_wrapper.is_available():
             raise ImportError(
@@ -247,7 +246,7 @@ class BaseDS(BaseEstimator, ClassifierMixin):
         # validate the value of k
         self._validate_k()
         self._set_region_of_competence_algorithm()
-        self._fit_region_competence(X_dsel, y_dsel)
+        self.roc_algorithm_.fit(X_dsel, y_dsel)
 
         # validate the IH
         if self.with_IH:
@@ -305,24 +304,10 @@ class BaseDS(BaseEstimator, ClassifierMixin):
         else:
             return self.enc_.transform(y)
 
-    def _fit_region_competence(self, X, y):
-        """Fit the k-NN classifier inside the dynamic selection method.
-
-        Parameters
-        ----------
-        X : array of shape (n_samples, n_features)
-            The Input data.
-
-        y : array of shape (n_samples)
-            class labels of each sample in X.
-
-        """
-        self.roc_algorithm_.fit(X, y)
-
     def _set_dsel(self, X, y):
         """Pre-Process the input X and y data into the dynamic selection
         dataset(DSEL) and get information about the structure of the data
-        (e.g., n_classes, N_samples, classes)
+        (e.g., n_classes, n_samples, classes)
 
         Parameters
         ----------
@@ -337,10 +322,10 @@ class BaseDS(BaseEstimator, ClassifierMixin):
         self.n_classes_ = self.classes_.size
         self.n_features_ = X.shape[1]
         self.n_samples_ = self.DSEL_target_.size
-        self.DSEL_processed_, self.BKS_DSEL_ = self._preprocess_dsel()
+        self.BKS_DSEL_ = self._predict_base(self.DSEL_data_)
+        self.DSEL_processed_ = self.BKS_DSEL_ == y[:, np.newaxis]
 
     def _set_region_of_competence_algorithm(self):
-
         if self.knn_classifier is None or self.knn_classifier in ['knn',
                                                                   'sklearn']:
             knn_class = functools.partial(KNeighborsClassifier,
@@ -411,133 +396,34 @@ class BaseDS(BaseEstimator, ClassifierMixin):
         predicted_labels : array of shape (n_samples)
                            Predicted class label for each sample in X.
         """
-        # Check if the DS model was trained
         check_is_fitted(self,
                         ["DSEL_processed_", "DSEL_data_", "DSEL_target_"])
-
-        # Check if X is a valid input
         X = check_array(X)
-        self._check_num_features(X)
-
-        n_samples = X.shape[0]
-        predicted_labels = np.empty(n_samples, dtype=np.intp)
-
-        if self.needs_proba or self.voting == 'soft':
-            base_probabilities = self._predict_proba_base(X)
-            base_predictions = base_probabilities.argmax(axis=2)
-        else:
-            base_probabilities = None
-            base_predictions = self._predict_base(X)
-
-        all_agree_vector = BaseDS._all_classifier_agree(base_predictions)
-        ind_all_agree = np.where(all_agree_vector)[0]
+        preds = np.empty(X.shape[0], dtype=np.intp)
+        need_proba = self.needs_proba or self.voting == 'soft'
 
-        # Since the predictions are always the same, get the predictions of the
-        # first base classifier.
+        base_preds, base_probas = self._preprocess_predictions(X, need_proba)
+        # predict all agree
+        ind_disagreement, ind_all_agree = self._split_agreement(base_preds)
         if ind_all_agree.size:
-            predicted_labels[ind_all_agree] = base_predictions[
-                ind_all_agree, 0]
-
-        # For the samples with disagreement, perform the dynamic selection
-        # steps. First step is to collect the samples with disagreement
-        # between base classifiers
-        ind_disagreement = np.where(~all_agree_vector)[0]
+            preds[ind_all_agree] = base_preds[ind_all_agree, 0]
+        # predict with IH
         if ind_disagreement.size:
-
-            X_DS = X[ind_disagreement, :]
-
-            # If the method is based on clustering and does not use IH there
-            # is no need to compute the Neighbors
-            if hasattr(self, "clustering_") and not self.with_IH:
-                distances = neighbors = None
-            else:
-                # Then, we estimate the nearest neighbors for all samples that
-                # we need to call DS routines
-                distances, neighbors = self._get_region_competence(X_DS)
-
-            if self.with_IH:
-                # if IH is used, calculate the hardness level associated with
-                # each sample
-                hardness = hardness_region_competence(neighbors,
-                                                      self.DSEL_target_,
-                                                      self.safe_k)
-
-                # Get the index associated with the easy and hard samples.
-                # Samples with low hardness are passed down to the knn
-                # classifier while samples with high hardness are passed down
-                # to the DS methods. So, here we split the samples that are
-                # passed to down to each stage by calculating their indices_.
-                easy_samples_mask = hardness < self.IH_rate
-                ind_knn_classifier = np.where(easy_samples_mask)[0]
-                ind_ds_classifier = np.where(~easy_samples_mask)[0]
-
-                if ind_knn_classifier.size:
-                    # all samples with low hardness should be classified by
-                    # the knn method here:
-                    # First get the class associated with each neighbor
-                    y_neighbors = self.DSEL_target_[
-                        neighbors[ind_knn_classifier, :self.safe_k]]
-
-                    # Accessing which samples in the original matrix are
-                    # associated with the low instance hardness indices_. This
-                    # is important since the low hardness indices
-                    # ind_knn_classifier was estimated based on a subset
-                    # of samples
-                    ind_knn_original_matrix = ind_disagreement[
-                        ind_knn_classifier]
-                    prediction_knn, _ = mode(y_neighbors, axis=1)
-                    predicted_labels[
-                        ind_knn_original_matrix] = prediction_knn.reshape(-1, )
-
-                    # Remove from the neighbors and distance matrices the
-                    # samples that were classified using the KNN
-                    neighbors = np.delete(neighbors, ind_knn_classifier,
-                                          axis=0)
-                    distances = np.delete(distances, ind_knn_classifier,
-                                          axis=0)
-            else:
-                # IH was not considered. So all samples with disagreement are
-                # passed down to the DS algorithm
-                ind_ds_classifier = np.arange(ind_disagreement.size)
-
-            # At this stage the samples which all base classifiers agrees or
-            # that are associated with low hardness were already classified.
-            # The remaining samples are now passed down to the DS techniques
-            # for classification.
-
-            #  First check whether there are still samples to be classified.
+            distances, ind_ds_classifier, neighbors = self._IH_prediction(
+                X, ind_disagreement, preds, is_proba=False
+            )
+            # Predict with DS - Check if there are still samples to be labeled.
             if ind_ds_classifier.size:
+                DFP_mask = self._get_DFP_mask(neighbors)
+                inds, sel_preds, sel_probas = self._prepare_indices_DS(
+                    base_preds, base_probas, ind_disagreement,
+                    ind_ds_classifier)
+                preds_ds = self.classify_with_ds(None, sel_preds, sel_probas,
+                                                 neighbors, distances,
+                                                 DFP_mask)
+                preds[inds] = preds_ds
 
-                # IF the DFP pruning is considered, calculate the DFP mask
-                # for all samples in X
-                if self.DFP:
-                    DFP_mask = frienemy_pruning_preprocessed(
-                        neighbors, self.DSEL_target_, self.DSEL_processed_
-                    )
-                else:
-                    DFP_mask = np.ones(
-                        (ind_ds_classifier.size, self.n_classifiers_))
-
-                # Get the real indices_ of the samples that will be classified
-                # using a DS algorithm.
-                ind_ds_original_matrix = ind_disagreement[ind_ds_classifier]
-
-                if self.needs_proba or self.voting == 'soft':
-                    selected_probabilities = base_probabilities[
-                        ind_ds_original_matrix]
-                else:
-                    selected_probabilities = None
-
-                pred_ds = self.classify_with_ds(X_DS[ind_ds_classifier],
-                                                base_predictions[
-                                                    ind_ds_original_matrix],
-                                                selected_probabilities,
-                                                neighbors=neighbors,
-                                                distances=distances,
-                                                DFP_mask=DFP_mask)
-                predicted_labels[ind_ds_original_matrix] = pred_ds
-
-        return self.classes_.take(predicted_labels)
+        return self.classes_.take(preds)
 
     def predict_proba(self, X):
         """Estimates the posterior probabilities for sample in X.
@@ -552,109 +438,113 @@ class BaseDS(BaseEstimator, ClassifierMixin):
         predicted_proba : array of shape (n_samples, n_classes)
                           Probabilities estimates for each sample in X.
         """
-        # Check if the DS model was trained
         check_is_fitted(self,
                         ["DSEL_processed_", "DSEL_data_", "DSEL_target_"])
-
-        # Check if X is a valid input
-        X = check_array(X, ensure_2d=False)
-
-        # Check if the base classifiers are able to estimate posterior
-        # probabilities (implements predict_proba method).
         self._check_predict_proba()
+        X = check_array(X, ensure_2d=False)
+        probas = np.zeros((X.shape[0], self.n_classes_))
+        base_preds, base_probas = self._preprocess_predictions(X, True)
+        # predict all agree
+        ind_disagreement, ind_all_agree = self._split_agreement(base_preds)
+        if ind_all_agree.size:
+            probas[ind_all_agree] = base_probas[ind_all_agree].mean(axis=1)
+        # predict with IH
+        if ind_disagreement.size:
+            distances, ind_ds_classifier, neighbors = self._IH_prediction(
+                    X, ind_disagreement, probas, is_proba=True)
+            # Predict with DS - Check if there are still samples to be labeled.
+            if ind_ds_classifier.size:
+                DFP_mask = self._get_DFP_mask(neighbors)
+                inds, sel_preds, sel_probas = self._prepare_indices_DS(
+                    base_preds, base_probas, ind_disagreement,
+                    ind_ds_classifier)
+                probas_ds = self.predict_proba_with_ds(None, sel_preds,
+                                                       sel_probas,
+                                                       neighbors, distances,
+                                                       DFP_mask)
+                probas[inds] = probas_ds
+        return probas
+
+    def _preprocess_predictions(self, X, req_proba):
+        if req_proba:
+            base_probabilities = self._predict_proba_base(X)
+            base_predictions = base_probabilities.argmax(axis=2)
+        else:
+            base_probabilities = None
+            base_predictions = self._predict_base(X)
+        return base_predictions, base_probabilities
 
-        base_probabilities = self._predict_proba_base(X)
-        base_predictions = base_probabilities.argmax(axis=2)
-
-        n_samples = X.shape[0]
-        predicted_proba = np.zeros((n_samples, self.n_classes_))
-
+    def _split_agreement(self, base_predictions):
         all_agree_vector = BaseDS._all_classifier_agree(base_predictions)
         ind_all_agree = np.where(all_agree_vector)[0]
-
-        if ind_all_agree.size:
-            predicted_proba[ind_all_agree] = base_probabilities[
-                ind_all_agree].mean(axis=1)
-
         ind_disagreement = np.where(~all_agree_vector)[0]
+        return ind_disagreement, ind_all_agree
 
-        if ind_disagreement.size:
-            X_DS = X[ind_disagreement, :]
-
-            # Always calculating the neighborhood. Passing that to classify
-            # later
-            # TODO: Check problems with DES Clustering method. Maybe add a
-            # check to prevent that here. (or do clustering instead)
-            # Then, we estimate the nearest neighbors for all samples that we
-            # need to call DS routines
-            distances, neighbors = self._get_region_competence(X_DS)
-
-            if self.with_IH:
-                # if IH is used, calculate the hardness level associated with
-                # each sample
-                hardness = hardness_region_competence(neighbors,
-                                                      self.DSEL_target_,
-                                                      self.safe_k)
-
-                # Get the index associated with the easy and hard samples.
-                # Samples with low hardness are passed down to the knn
-                # classifier while samples with high hardness are passed down
-                # to the DS methods. So, here we split the samples that are
-                # passed to down to each stage by calculating their indices_.
-                easy_samples_mask = hardness < self.IH_rate
-                ind_knn_classifier = np.where(easy_samples_mask)[0]
-                ind_ds_classifier = np.where(~easy_samples_mask)[0]
-
-                if ind_knn_classifier.size:
-                    # all samples with low hardness should be classified by
-                    # the knn method here:
-                    # First get the class associated with each neighbor
-
-                    # Accessing which samples in the original matrix are
-                    # associated with the low instance hardness indices_.
-                    ind_knn_original_matrix = ind_disagreement[
-                        ind_knn_classifier]
-
-                    predicted_proba[ind_knn_original_matrix] = \
-                        self.roc_algorithm_.predict_proba(
-                            X_DS[ind_knn_classifier])
-
-                    # Remove from the neighbors and distance matrices the
-                    # samples that were classified using the KNN
-                    neighbors = np.delete(neighbors, ind_knn_classifier,
-                                          axis=0)
-                    distances = np.delete(distances, ind_knn_classifier,
-                                          axis=0)
+    def _IH_prediction(self, X, ind_disagree, predicted_proba, is_proba=False):
+        X_DS = X[ind_disagree, :]
+        distances, region_competence = self._get_region_competence(X_DS)
+        if self.with_IH:
+            ind_hard, ind_easy = self._split_easy_samples(region_competence)
+            distances, region_competence = self._predict_easy_samples(
+                X_DS, distances, ind_disagree, ind_easy,
+                region_competence, predicted_proba, is_proba)
+        else:
+            # IH was not considered. So all samples go to predict with DS
+            ind_hard = np.arange(ind_disagree.size)
+        return distances, ind_hard, region_competence
+
+    def _split_easy_samples(self, neighbors):
+        hardness = hardness_region_competence(neighbors,
+                                              self.DSEL_target_,
+                                              self.safe_k)
+        # Get the index associated with the easy and hard samples.
+        # easy samples are classified by the knn.
+        easy_samples_mask = hardness < self.IH_rate
+        ind_knn_classifier = np.where(easy_samples_mask)[0]
+        ind_ds_classifier = np.where(~easy_samples_mask)[0]
+        return ind_ds_classifier, ind_knn_classifier
+
+    def _predict_easy_samples(self, X_DS, distances, ind_disagreement,
+                              ind_easy, neighbors, predictions, is_proba):
+        if ind_easy.size:
+            # Accessing which samples in the original array.
+            ind_knn_original_matrix = ind_disagreement[ind_easy]
+
+            if is_proba:
+                predictions[ind_knn_original_matrix] = \
+                    self.roc_algorithm_.predict_proba(
+                        X_DS[ind_easy])
             else:
-                # IH was not considered. So all samples with disagreement are
-                # passed down to the DS algorithm
-                ind_ds_classifier = np.arange(ind_disagreement.size)
-
-            if ind_ds_classifier.size:
-                # Check if the dynamic frienemy pruning should be used
-                if self.DFP:
-                    DFP_mask = frienemy_pruning_preprocessed(
-                        neighbors, self.DSEL_target_, self.DSEL_processed_
-                    )
-                else:
-                    DFP_mask = np.ones(
-                        (ind_ds_classifier.size, self.n_classifiers_))
-
-                ind_ds_original_matrix = ind_disagreement[ind_ds_classifier]
-
-                proba_ds = self.predict_proba_with_ds(
-                    X[ind_ds_original_matrix],
-                    base_predictions[
-                        ind_ds_original_matrix],
-                    base_probabilities[
-                        ind_ds_original_matrix],
-                    neighbors=neighbors,
-                    distances=distances,
-                    DFP_mask=DFP_mask)
-
-                predicted_proba[ind_ds_original_matrix] = proba_ds
-
-        return predicted_proba
+                y_neighbors = self.DSEL_target_[neighbors[ind_easy,
+                                                :self.safe_k]]
+                predictions_knn, _ = mode(y_neighbors, axis=1)
+                predictions[ind_knn_original_matrix] = predictions_knn.reshape(
+                    -1, )
+            neighbors = np.delete(neighbors, ind_easy, axis=0)
+            distances = np.delete(distances, ind_easy, axis=0)
+        return distances, neighbors
+
+    def _prepare_indices_DS(self, base_predictions, base_probabilities,
+                            ind_disagreement, ind_ds_classifier):
+        # Get the real indices_ of the samples that will be classified
+        # using a DS algorithm.
+        ind_ds_original_matrix = ind_disagreement[ind_ds_classifier]
+        if base_probabilities is not None:
+            selected_probas = base_probabilities[
+                ind_ds_original_matrix]
+        else:
+            selected_probas = None
+        selected_preds = base_predictions[ind_ds_original_matrix]
+        return ind_ds_original_matrix, selected_preds, selected_probas
+
+    def _get_DFP_mask(self, neighbors):
+        if self.DFP:
+            DFP_mask = frienemy_pruning_preprocessed(neighbors,
+                                                     self.DSEL_target_,
+                                                     self.DSEL_processed_)
+        else:
+            DFP_mask = np.ones((neighbors.shape[0], self.n_classifiers_))
+        return DFP_mask
 
     def _preprocess_dsel(self):
         """Compute the prediction of each base classifier for
@@ -674,7 +564,6 @@ class BaseDS(BaseEstimator, ClassifierMixin):
         """
         BKS_dsel = self._predict_base(self.DSEL_data_)
         processed_dsel = BKS_dsel == self.DSEL_target_[:, np.newaxis]
-
         return processed_dsel, BKS_dsel
 
     def _predict_base(self, X):
@@ -722,26 +611,6 @@ class BaseDS(BaseEstimator, ClassifierMixin):
             probabilities[:, index] = clf.predict_proba(X)
         return probabilities
 
-    def _preprocess_dsel_scores(self):
-        """Compute the output profiles of the dynamic selection dataset (DSEL)
-         Each position of the output profiles vector is the score obtained by a
-         base classifier :math:`c_{i}`
-         for the classes of the input sample.
-
-        Returns
-        -------
-        scores : array of shape (n_samples, n_classifiers, n_classes)
-                 Scores (probabilities) for each class obtained by each base
-                 classifier in the generated_pool
-                 for each sample in X.
-        """
-        scores = np.empty(
-            (self.n_samples_, self.n_classifiers_, self.n_classes_))
-        for index, clf in enumerate(self.pool_classifiers_):
-            scores[:, index, :] = clf.predict_proba(self.DSEL_data_)
-
-        return scores
-
     @staticmethod
     def _all_classifier_agree(predictions):
         """Check whether there is a difference in opinion among the classifiers
@@ -785,11 +654,9 @@ class BaseDS(BaseEstimator, ClassifierMixin):
                     "parameter safe_k must be equal or less than parameter k."
                     "input safe_k is {} and k is {}".format(self.k,
                                                             self.safe_k))
-
         if not isinstance(self.IH_rate, float):
             raise TypeError(
                 "parameter IH_rate should be a float between [0.0, 0.5]")
-
         if self.IH_rate < 0 or self.IH_rate > 0.5:
             raise ValueError("Parameter IH_rate should be between [0.0, 0.5]."
                              "IH_rate = {}".format(self.IH_rate))
@@ -809,28 +676,6 @@ class BaseDS(BaseEstimator, ClassifierMixin):
             raise ValueError("n_classifiers must be greater than one, "
                              "got {}.".format(self.n_classifiers_))
 
-    def _check_num_features(self, X):
-        """ Verify if the number of features (n_features) of X is equals to
-        the number of features used to fit the model. Raises an error if
-        n_features is different.
-
-        Parameters
-        ----------
-        X : array of shape (classes, n_features)
-            The input data.
-
-        Raises
-        -------
-        ValueError
-            If X has a different dimensionality than the traiØning data.
-        """
-        n_features = X.shape[1]
-        if self.n_features_ != n_features:
-            raise ValueError("Number of features of the model must "
-                             "match the input. Model n_features_ is {} and "
-                             "n_features_ is {} ".format(self.n_features_,
-                                                         n_features))
-
     def _check_predict_proba(self):
         """ Checks if each base classifier in the pool implements the
         predict_proba method.
diff --git a/deslib/dcs/a_posteriori.py b/deslib/dcs/a_posteriori.py
index ff83ff0..eb1c1fb 100644
--- a/deslib/dcs/a_posteriori.py
+++ b/deslib/dcs/a_posteriori.py
@@ -153,7 +153,7 @@ class APosteriori(BaseDCS):
         super(APosteriori, self).fit(X, y)
         self._check_predict_proba()
 
-        self.dsel_scores_ = self._preprocess_dsel_scores()
+        self.dsel_scores_ = self._predict_proba_base(self.DSEL_data_)
         return self
 
     def estimate_competence(self, query, neighbors, distances,
diff --git a/deslib/dcs/a_priori.py b/deslib/dcs/a_priori.py
index 1edfe06..0cc845c 100644
--- a/deslib/dcs/a_priori.py
+++ b/deslib/dcs/a_priori.py
@@ -147,7 +147,7 @@ class APriori(BaseDCS):
         super(APriori, self).fit(X, y)
         self._check_predict_proba()
 
-        self.dsel_scores_ = self._preprocess_dsel_scores()
+        self.dsel_scores_ = self._predict_proba_base(self.DSEL_data_)
         return self
 
     def estimate_competence(self, query, neighbors, distances,
diff --git a/deslib/dcs/base.py b/deslib/dcs/base.py
index ab61cba..32d78aa 100644
--- a/deslib/dcs/base.py
+++ b/deslib/dcs/base.py
@@ -189,18 +189,18 @@ class BaseDCS(BaseDS):
         predicted_label : array of shape (n_samples)
             The predicted label for each query
         """
-        if query.ndim < 2:
-            query = query.reshape(1, -1)
+        # if query.ndim < 2:
+        #     query = query.reshape(1, -1)
 
         if predictions.ndim < 2:
             predictions = predictions.reshape(1, -1)
 
-        if query.shape[0] != predictions.shape[0]:
-            raise ValueError(
-                'The arrays query and predictions must have the same shape. '
-                'query.shape is {}'
-                'and predictions.shape is {}'.format(query.shape,
-                                                     predictions.shape))
+        # if query.shape[0] != predictions.shape[0]:
+        #     raise ValueError(
+        #         'The arrays query and predictions must have the same shape. '
+        #         'query.shape is {}'
+        #         'and predictions.shape is {}'.format(query.shape,
+        #                                              predictions.shape))
 
         competences = self.estimate_competence(query, neighbors,
                                                distances=distances,
@@ -259,12 +259,12 @@ class BaseDCS(BaseDS):
         predicted_proba: array of shape (n_samples, n_classes)
             Posterior probabilities estimates for each test example.
         """
-        if query.shape[0] != probabilities.shape[0]:
-            raise ValueError(
-                'The arrays query and predictions must have the same number '
-                'of samples. query.shape is {}'
-                'and predictions.shape is {}'.format(query.shape,
-                                                     predictions.shape))
+        # if query.shape[0] != probabilities.shape[0]:
+        #     raise ValueError(
+        #         'The arrays query and predictions must have the same number '
+        #         'of samples. query.shape is {}'
+        #         'and predictions.shape is {}'.format(query.shape,
+        #                                              predictions.shape))
 
         competences = self.estimate_competence(query, neighbors,
                                                distances=distances,
diff --git a/deslib/des/des_clustering.py b/deslib/des/des_clustering.py
index 12001af..48bba61 100644
--- a/deslib/des/des_clustering.py
+++ b/deslib/des/des_clustering.py
@@ -93,16 +93,13 @@ class DESClustering(BaseDS):
     Information Fusion, vol. 41, pp. 195 – 216, 2018.
     """
 
-    def __init__(self, pool_classifiers=None, clustering=None, with_IH=False,
-                 safe_k=None, IH_rate=0.30, pct_accuracy=0.5, voting='hard',
+    def __init__(self, pool_classifiers=None, clustering=None,
+                 pct_accuracy=0.5, voting='hard',
                  pct_diversity=0.33, more_diverse=True, metric_diversity='DF',
                  metric_performance='accuracy_score', n_clusters=5,
                  random_state=None, DSEL_perc=0.5, n_jobs=-1):
 
         super(DESClustering, self).__init__(pool_classifiers=pool_classifiers,
-                                            with_IH=with_IH,
-                                            safe_k=safe_k,
-                                            IH_rate=IH_rate,
                                             random_state=random_state,
                                             DSEL_perc=DSEL_perc,
                                             n_jobs=n_jobs,
@@ -111,7 +108,6 @@ class DESClustering(BaseDS):
         self.metric_diversity = metric_diversity
         self.metric_performance = metric_performance
         self.voting = voting
-
         self.clustering = clustering
         self.pct_accuracy = pct_accuracy
         self.pct_diversity = pct_diversity
@@ -183,6 +179,11 @@ class DESClustering(BaseDS):
         self._preprocess_clusters()
         return self
 
+    def _get_region_competence(self, query, k=None):
+        distances = self.clustering_.transform(query.astype(np.double))
+        region = self.clustering_.predict(query.astype(np.double))
+        return distances, region
+
     def _preprocess_clusters(self):
         """Preprocess the competence as well as the average diversity of each
         base classifier for each specific cluster.
@@ -232,7 +233,8 @@ class DESClustering(BaseDS):
             self.indices_[cluster_index, :] = performance_indices[
                 diversity_indices]
 
-    def estimate_competence(self, query, predictions=None):
+    def estimate_competence(self, query, neighbors, distances=None,
+                            predictions=None):
         """Get the competence estimates of each base classifier :math:`c_{i}`
         for the classification of the query sample.
 
@@ -254,11 +256,11 @@ class DESClustering(BaseDS):
         competences : array = [n_samples, n_classifiers]
                       The competence level estimated for each base classifier.
         """
-        cluster_index = self.clustering_.predict(query)
-        competences = self.performance_cluster_[cluster_index][:]
+        # cluster_index = self.clustering_.predict(query)
+        competences = self.performance_cluster_[neighbors][:]
         return competences
 
-    def select(self, query):
+    def select(self, competences):
         """Select an ensemble with the most accurate and most diverse
         classifier for the classification of the query.
 
@@ -268,8 +270,8 @@ class DESClustering(BaseDS):
 
         Parameters
         ----------
-        query : array of shape (n_samples, n_features)
-                The test examples.
+        competences : array of shape (n_samples)
+            Array containing closest cluster index.
 
         Returns
         -------
@@ -277,8 +279,7 @@ class DESClustering(BaseDS):
             Indices of the selected base classifier for each test example.
 
         """
-        cluster_index = self.clustering_.predict(query.astype(np.double))
-        selected_classifiers = self.indices_[cluster_index, :]
+        selected_classifiers = self.indices_[competences, :]
         return selected_classifiers
 
     def classify_with_ds(self, query, predictions, probabilities=None,
@@ -347,7 +348,8 @@ class DESClustering(BaseDS):
         predicted_proba : array of shape (n_samples, n_classes)
             Posterior probabilities estimates for each test example.
         """
-        selected_classifiers = self.select(query)
+        selected_classifiers = self.select(neighbors)
+
         if self.voting == 'hard':
             votes = predictions[np.arange(predictions.shape[0])[:, None],
                                 selected_classifiers]
@@ -403,7 +405,6 @@ class DESClustering(BaseDS):
         if self.voting == 'soft':
             self._check_predict_proba()
 
-
     def get_scores_(self, sample_indices):
 
         def precision_function(label_predicted):
diff --git a/deslib/des/des_knn.py b/deslib/des/des_knn.py
index b3d3247..ce7a6e7 100644
--- a/deslib/des/des_knn.py
+++ b/deslib/des/des_knn.py
@@ -217,8 +217,8 @@ class DESKNN(BaseDS):
         targets = self.DSEL_target_[neighbors]
 
         # TODO: optimize this part with numpy instead of for loops
-        diversity = np.zeros((query.shape[0], self.n_classifiers_))
-        for sample_idx in range(query.shape[0]):
+        diversity = np.zeros((neighbors.shape[0], self.n_classifiers_))
+        for sample_idx in range(neighbors.shape[0]):
             this_diversity = compute_pairwise_diversity(targets[sample_idx, :],
                                                         predicted_matrix[
                                                         sample_idx, :, :],
diff --git a/deslib/des/des_mi.py b/deslib/des/des_mi.py
index 6da8e20..52fc820 100644
--- a/deslib/des/des_mi.py
+++ b/deslib/des/des_mi.py
@@ -160,7 +160,7 @@ class DESMI(BaseDS):
         weight = 1. / (1 + np.exp(self.alpha * num))
         weight = normalize(weight, norm='l1')
         correct_num = self.DSEL_processed_[neighbors, :]
-        correct = np.zeros((query.shape[0], self.k_, self.n_classifiers_))
+        correct = np.zeros((neighbors.shape[0], self.k_, self.n_classifiers_))
         for i in range(self.n_classifiers_):
             correct[:, :, i] = correct_num[:, :, i] * weight
 
diff --git a/deslib/des/knop.py b/deslib/des/knop.py
index 8881964..31e390f 100644
--- a/deslib/des/knop.py
+++ b/deslib/des/knop.py
@@ -152,7 +152,7 @@ class KNOP(BaseDES):
             raise ValueError(
                 "Error. KNOP  does not accept one class datasets!")
         self._check_predict_proba()
-        self.dsel_scores_ = self._preprocess_dsel_scores()
+        self.dsel_scores_ = self._predict_proba_base(self.DSEL_data_)
         # Reshape DSEL_scores as a 2-D array for nearest neighbor calculations
         dsel_output_profiles = self.dsel_scores_.reshape(self.n_samples_,
                                                          self.n_classifiers_ *
diff --git a/deslib/des/meta_des.py b/deslib/des/meta_des.py
index 0c95a71..0420005 100644
--- a/deslib/des/meta_des.py
+++ b/deslib/des/meta_des.py
@@ -196,7 +196,7 @@ class METADES(BaseDES):
         # Check if the base classifier is able to estimate probabilities
         self._check_predict_proba()
 
-        self.dsel_scores_ = self._preprocess_dsel_scores()
+        self.dsel_scores_ = self._predict_proba_base(self.DSEL_data_)
 
         # Reshape DSEL_scores as a 2-D array for nearest neighbor calculations
         dsel_output_profiles = self.dsel_scores_.reshape(self.n_samples_,
diff --git a/deslib/des/probabilistic/base.py b/deslib/des/probabilistic/base.py
index 5b89af0..a3056b5 100644
--- a/deslib/des/probabilistic/base.py
+++ b/deslib/des/probabilistic/base.py
@@ -74,7 +74,7 @@ class BaseProbabilistic(BaseDES):
 
         self._check_predict_proba()
 
-        self.dsel_scores_ = self._preprocess_dsel_scores()
+        self.dsel_scores_ = self._predict_proba_base(self.DSEL_data_)
 
         # Pre process the source of competence for the entire DSEL,
         # making the method faster during generalization.
diff --git a/examples/plot_using_instance_hardness.py b/examples/plot_using_instance_hardness.py
index 7f6bcd7..24e5b9b 100644
--- a/examples/plot_using_instance_hardness.py
+++ b/examples/plot_using_instance_hardness.py
@@ -53,7 +53,7 @@ from deslib.des import KNORAU
 
 rng = np.random.RandomState(123456)
 
-data = fetch_openml(name='diabetes', cache=False, as_frame=False)
+data = fetch_openml(name='diabetes', version=1, cache=False, as_frame=False)
 X = data.data
 y = data.target
 X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=rng)


Test patch:
diff --git a/deslib/tests/conftest.py b/deslib/tests/conftest.py
index a9edd94..56c449e 100644
--- a/deslib/tests/conftest.py
+++ b/deslib/tests/conftest.py
@@ -135,18 +135,19 @@ def create_base_classifier(return_value, return_prob=None):
 @pytest.fixture
 def create_pool_classifiers():
     clf_0 = create_base_classifier(return_value=np.zeros(1),
-                                   return_prob=np.atleast_2d([0.5, 0.5]))
+                                   return_prob=np.array([[0.5, 0.5]]))
     clf_1 = create_base_classifier(return_value=np.ones(1),
-                                   return_prob=np.atleast_2d([1.0, 0.0]))
+                                   return_prob=np.array([[1.0, 0.0]]))
     clf_2 = create_base_classifier(return_value=np.zeros(1),
-                                   return_prob=np.atleast_2d([0.33, 0.67]))
+                                   return_prob=np.array([[0.33, 0.67]]))
     pool_classifiers = [clf_0, clf_1, clf_2]
     return pool_classifiers
 
 
 @pytest.fixture
 def create_pool_all_agree():
-    return [create_base_classifier(return_value=np.zeros(1))] * 100
+    return [create_base_classifier(return_value=np.zeros(1),
+                                   return_prob=np.array([[0.61, 0.39]]))] * 100
 
 
 @pytest.fixture
diff --git a/deslib/tests/dcs/test_base.py b/deslib/tests/dcs/test_base.py
index 728de16..3df6fb9 100644
--- a/deslib/tests/dcs/test_base.py
+++ b/deslib/tests/dcs/test_base.py
@@ -269,22 +269,3 @@ def test_predict_proba_instance_all(competences, expected,
     predicted_proba = dcs_test.predict_proba_with_ds(query, predictions,
                                                      probabilities)
     assert np.isclose(predicted_proba, expected).all()
-
-
-def test_classify_with_ds_diff_sizes():
-    query = np.ones((10, 2))
-    predictions = np.ones((5, 3))
-    dcs_test = BaseDCS()
-
-    with pytest.raises(ValueError):
-        dcs_test.classify_with_ds(query, predictions)
-
-
-def test_proba_with_ds_diff_sizes():
-    query = np.ones((10, 2))
-    predictions = np.ones((5, 3))
-    probabilities = np.ones((5, 3, 2))
-    dcs_test = BaseDCS()
-
-    with pytest.raises(ValueError):
-        dcs_test.predict_proba_with_ds(query, predictions, probabilities)
diff --git a/deslib/tests/des/test_des_clustering.py b/deslib/tests/des/test_des_clustering.py
index e3bff56..862d78c 100644
--- a/deslib/tests/des/test_des_clustering.py
+++ b/deslib/tests/des/test_des_clustering.py
@@ -77,20 +77,13 @@ def test_estimate_competence(create_pool_classifiers,
                                     pct_accuracy=0.5, pct_diversity=0.33)
 
     X, y = example_estimate_competence[0:2]
-
-    # Keep the original predict method to change after
-    clustering_test.clustering.predict = MagicMock(
-        return_value=return_cluster_index_ex2)
     clustering_test.fit(X, y)
-
-    clustering_test.clustering_.predict = MagicMock(return_value=0)
-    competences = clustering_test.estimate_competence(query)
+    competences = clustering_test.estimate_competence(query, 0)
 
     assert np.array_equal(competences,
                           clustering_test.performance_cluster_[0, :])
 
-    clustering_test.clustering_.predict = MagicMock(return_value=1)
-    competences = clustering_test.estimate_competence(query)
+    competences = clustering_test.estimate_competence(query, 1)
     assert np.array_equal(competences,
                           clustering_test.performance_cluster_[1, :])
 
@@ -115,16 +108,14 @@ def test_fit_clusters_less_diverse(example_estimate_competence,
 
 
 def test_select():
-    query = np.atleast_2d([1, -1])
     clustering_test = DESClustering()
-
     clustering_test.clustering_ = KMeans()
-    clustering_test.clustering_.predict = MagicMock(return_value=[0])
+    roc = [0]
     clustering_test.indices_ = np.array([[0, 2], [1, 4]])
-    assert np.array_equal(clustering_test.select(query), [[0, 2]])
-
+    assert np.array_equal(clustering_test.select(roc), [[0, 2]])
+    roc = [1]
     clustering_test.clustering_.predict = MagicMock(return_value=[1])
-    assert np.array_equal(clustering_test.select(query), [[1, 4]])
+    assert np.array_equal(clustering_test.select(roc), [[1, 4]])
 
 
 # Since the majority of the base classifiers selected predicts class 0,
diff --git a/deslib/tests/test_base.py b/deslib/tests/test_base.py
index 199867b..1de86e2 100644
--- a/deslib/tests/test_base.py
+++ b/deslib/tests/test_base.py
@@ -168,7 +168,7 @@ def test_preprocess_dsel_scores(create_X_y, create_pool_classifiers):
     X, y = create_X_y
     ds_test = BaseDS(create_pool_classifiers)
     ds_test.fit(X, y)
-    dsel_scores = ds_test._preprocess_dsel_scores()
+    dsel_scores = ds_test._predict_proba_base(X)
     expected = np.array([[0.5, 0.5], [1.0, 0.0], [0.33, 0.67]])
     expected = np.tile(expected, (15, 1, 1))
     assert np.array_equal(dsel_scores, expected)
@@ -212,18 +212,14 @@ def test_input_IH_rate(IH_rate):
 
 
 def test_predict_proba_all_agree(example_estimate_competence,
-                                 create_pool_classifiers):
+                                 create_pool_all_agree):
     X, y, _, _, _, dsel_scores = example_estimate_competence
 
     query = np.atleast_2d([1, 1])
-    ds_test = BaseDS(create_pool_classifiers)
+    ds_test = BaseDS(create_pool_all_agree)
     ds_test.fit(X, y)
     ds_test.DSEL_scores = dsel_scores
-    backup_all_agree = BaseDS._all_classifier_agree
-    BaseDS._all_classifier_agree = MagicMock(return_value=np.array([True]))
     proba = ds_test.predict_proba(query)
-
-    BaseDS._all_classifier_agree = backup_all_agree
     assert np.allclose(proba, np.atleast_2d([0.61, 0.39]))
 
 
diff --git a/deslib/tests/test_integration_DFP_IH.py b/deslib/tests/test_integration_DFP_IH.py
index ce0be14..210e8b3 100644
--- a/deslib/tests/test_integration_DFP_IH.py
+++ b/deslib/tests/test_integration_DFP_IH.py
@@ -86,13 +86,3 @@ def test_aposteriori():
                                with_IH=True, IH_rate=0.1)
     a_posteriori.fit(X_dsel, y_dsel)
     assert np.isclose(a_posteriori.score(X_test, y_test), 0.8333333333333334)
-
-
-def test_des_clustering():
-    pool_classifiers, X_dsel, y_dsel, X_test, y_test = setup_classifiers()
-    rng = np.random.RandomState(123456)
-
-    des_clustering = DESClustering(pool_classifiers, random_state=rng,
-                                   with_IH=True, IH_rate=0.28)
-    des_clustering.fit(X_dsel, y_dsel)
-    assert np.isclose(des_clustering.score(X_test, y_test), 0.9030303030303031)