class_docstr: Boosting Online Learning Ensemble (BOLE).
  
  A modified version of Oza Online Boosting Algorithm [^1]. For each incoming observation, each
  model's `learn_one` method is called `k` times where `k` is sampled from a Poisson distribution
  of parameter lambda. The first model to be trained will be the one with worst
  `correct_weight / (correct_weight + wrong_weight)`. The worst model not yet trained will
  receive lambda values for training from the models that incorrectly classified an instance, and
  the best model's not yet trained will receive lambda values for training from the models that
  correctly classified an instance. For more details, see [^2].
  
  Parameters
  ----------
  model
      The classifier to boost.
  n_models
      The number of models in the ensemble.
  seed
      Random number generator seed for reproducibility.
  error_bound
      Error bound percentage for allowing models to vote.
  
  Examples
  --------
  
  >>> from river import datasets
  >>> from river import ensemble
  >>> from river import evaluate
  >>> from river import drift
  >>> from river import metrics
  >>> from river import tree
  
  >>> dataset = datasets.Elec2().take(3000)
  
  >>> model = ensemble.BOLEClassifier(
  ...     model=drift.DriftRetrainingClassifier(
  ...         model=tree.HoeffdingTreeClassifier(),
  ...         drift_detector=drift.binary.DDM()
  ...     ),
  ...     n_models=10,
  ...     seed=42
  ... )
  
  >>> metric = metrics.Accuracy()
  
  >>> evaluate.progressive_val_score(dataset, model, metric)
  Accuracy: 93.63%
  
  References
  ----------
  [^1]: [Oza, N.C., 2005, October. Online bagging and boosting. In 2005 IEEE international conference on systems, man and cybernetics (Vol. 3, pp. 2340-2345). Ieee.](https://ti.arc.nasa.gov/m/profile/oza/files/ozru01a.pdf)
  [^2]: R. S. M. d. Barros, S. Garrido T. de Carvalho Santos and P. M. Gonçalves Júnior, "A Boosting-like Online Learning Ensemble," 2016 International Joint Conference on Neural Networks (IJCNN), 2016, pp. 1871-1878, doi: 10.1109/IJCNN.2016.7727427.
class_docstr_tok: ["Boosting","Online","Learning","Ensemble","(","BOLE",")",".","A","modified","version","of","Oza","Online","Boosting","Algorithm","[","^1","]",".","For","each","incoming","observation",",","each","model","'s","`","learn_one","`","method","is","called","`","k","`","times","where","`","k","`","is","sampled","from","a","Poisson","distribution","of","parameter","lambda",".","The","first","model","to","be","trained","will","be","the","one","with","worst","`","correct_weight","\/","(","correct_weight","+","wrong_weight",")","`",".","The","worst","model","not","yet","trained","will","receive","lambda","values","for","training","from","the","models","that","incorrectly","classified","an","instance",",","and","the","best","model","'s","not","yet","trained","will","receive","lambda","values","for","training","from","the","models","that","correctly","classified","an","instance",".","For","more","details",",","see","[","^2","]",".","Parameters","--","--","--","--","--","model","The","classifier","to","boost",".","n_models","The","number","of","models","in","the","ensemble",".","seed","Random","number","generator","seed","for","reproducibility",".","error_bound","Error","bound","percentage","for","allowing","models","to","vote",".","Examples","--","--","--","--",">",">",">","from","river","import","datasets",">",">",">","from","river","import","ensemble",">",">",">","from","river","import","evaluate",">",">",">","from","river","import","drift",">",">",">","from","river","import","metrics",">",">",">","from","river","import","tree",">",">",">","dataset","=","datasets.Elec2","(",")",".take","(","3000",")",">",">",">","model","=","ensemble.BOLEClassifier","(","...","model=drift.DriftRetrainingClassifier","(","...","model=tree.HoeffdingTreeClassifier","(",")",",","...","drift_detector=drift.binary.DDM","(",")","...",")",",","...","n_models=10",",","...","seed=42","...",")",">",">",">","metric","=","metrics.Accuracy","(",")",">",">",">","evaluate.progressive_val_score","(","dataset",",","model",",","metric",")","Accuracy",":","93.63","%","References","--","--","--","--","--","[","^1","]",":","[","Oza",",","N.C.",",","2005",",","October",".","Online","bagging","and","boosting",".","In","2005","IEEE","international","conference","on","systems",",","man","and","cybernetics","(","Vol",".","3",",","pp",".","2340-2345",")",".","Ieee",".","]","(","https",":","\/\/ti.arc.nasa.gov\/m\/profile\/oza\/files\/ozru01a.pdf",")","[","^2","]",":","R.","S.","M.","d.","Barros",",","S.","Garrido","T.","de","Carvalho","Santos","and","P.","M.","Gon\u00e7alves","J\u00fanior",",","``","A","Boosting-like","Online","Learning","Ensemble",",","''","2016","International","Joint","Conference","on","Neural","Networks","(","IJCNN",")",",","2016",",","pp",".","1871-1878",",","doi",":","10.1109\/IJCNN.2016.7727427","."]
code: def learn_one(self, x, y, **kwargs):
      self.instances_seen += 1
  
      correct_rate = [0] * self.n_models
      for i in range(self.n_models):
          correct_rate[i] = (
              self.correct_weight[self.order_position[i]]
              + self.wrong_weight[self.order_position[i]]
          )
          if correct_rate[i] != 0:
              correct_rate[i] = (
                  self.correct_weight[self.order_position[i]]
                  / correct_rate[i]
              )
  
      # sort models in descending order by correct_weight / correct_weight + error_weight (best case insertion sort)
      for i in range(1, self.n_models):
          key_position = self.order_position[i]
          key_correct_rate = correct_rate[i]
          j = i - 1
          while (j >= 0) and (correct_rate[j] < key_correct_rate):
              self.order_position[j + 1] = self.order_position[j]
              correct_rate[j + 1] = correct_rate[j]
              j -= 1
          self.order_position[j + 1] = key_position
          correct_rate[j + 1] = key_correct_rate
  
      correct = False
      max_correct_rate = 0
      min_correct_rate = self.n_models - 1
      lambda_poisson = 1
  
      # does boosting not in a linear way (i.e a model boosts the model created after him in the ensemble creation process)
      # the first model to be trained will be the one with worst correct_weight / correct_weight + wrong_weight
      # The worst model's not yet trained will receive lambda values for training from the model's that incorrectly classified an instance.
      # the best model's not yet trained will receive lambda values for training from the model's that correctly classified an instance.
      # the values of lambda increase in case a mistake is made and decrease in case a right prediction is made.
      # the worst models are more likely to make mistakes, increasing the value of lambda.
      # Then, the best's model are likely to receive a high value of lambda and decreasing gradually throughout the remaning models to be trained
      # It's similar to a system where the rich get richer.
      for i in range(self.n_models):
          if correct:
              pos = self.order_position[max_correct_rate]
              max_correct_rate += 1
          else:
              pos = self.order_position[min_correct_rate]
              min_correct_rate -= 1
  
          for _ in range(
              utils.random.poisson(lambda_poisson, self._rng)
          ):
              self.models[pos].learn_one(x, y, **kwargs)
  
          if self.models[pos].predict_one(x) == y:
              self.correct_weight[pos] += lambda_poisson
              lambda_poisson *= (self.instances_seen) / (
                  2 * self.correct_weight[pos]
              )
              correct = True
          else:
              self.wrong_weight[pos] += lambda_poisson
              lambda_poisson *= (self.instances_seen) / (
                  2 * self.wrong_weight[pos]
              )
              correct = False
  
file_path: river/river/ensemble/boosting.py
imports: from __future__ import annotations
  import collections
  import math
  from river import base, drift, linear_model, utils
label_desc: Use image node_id 2 for calling the BOLEClassifier obj's underlying member method code with example usage: obj.learn_one(x, y) without return types