The methods are organized according to the taxonomy proposed by [1] and used in [2], resulting in 3 groups:

  • Classify, Count and Correct: this category uses classifiers to individually classify each sample and count them grouping by the class labels. Some methods may include a correction step to the counts obtained by the classifier.
  • Distribution Matching: in this category, the methods model the training distribution and search for the parameters that provide the best match with the test distribution.
  • Adaptation of Classification Algorithms: this category adapts existing classification algorithms to work under quantification tasks.

All quantification methods inherit from BaseQuantifier and BaseEstimator, guaranteeing compatibility with scikit-learn:

  @abstractmethod
  def fit(self, X: np.ndarray, y: np.ndarray) -> T:
    pass

  @abstractmethod
  def predict(self, X: np.ndarray) -> np.ndarray:
    pass

In order to import them, use:

from quack.quantifiers import CC, ACC, PCC, PACC, GAC, GPAC, FM, X, Max, T50, MedianSweep, HDx, ReadMe, HDy, DyS, FormanMM, ED, EM, CDE

Every example below uses the same synthetic setup:

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import numpy as np

X, y = make_classification(n_samples=2000, n_classes=2, weights=[0.65, 0.35], random_state=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=0)

Parallelism (n_jobs / parallel_backend)

Every quantifier built on BaseCalibratedQuantifier (ACC, PACC, X, Max, T50, MedianSweep, DyS, HDy, FormanMM, GAC, GPAC, FM, EM, CDE) accepts n_jobs and parallel_backend, dispatching every cross-validation fold — plus the final full-data refit — as independent joblib jobs:

quantifier = ACC(LogisticRegression(max_iter=1000), cv=10, n_jobs=-1, parallel_backend="loky")
  • n_jobs=None (default): sequential, identical to running without parallelism.
  • n_jobs=-1: uses all available CPU cores.
  • parallel_backend="threading": preferable when the base classifier releases the GIL during fit/predict (most scikit-learn estimators backed by Cython/BLAS, e.g. LogisticRegression, SVC).

ReadMe and ED expose the same n_jobs/parallel_backend parameters, but parallelize their own independent units of work (random subspaces, and class-pair distance sums, respectively) instead of CV folds — see their sections below.


1. Classify, Count and Correct

Classify & Count (CC)

The simplest baseline: classifies every test instance and counts the relative frequency of each predicted class.

from quack.quantifiers import CC

quantifier = CC(classifier=LogisticRegression(max_iter=1000))
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)
print(prevalences)  # array([p_class_0, p_class_1])

Adjusted Classify & Count (ACC)

Corrects CC's counts using the classifier's True Positive Rate / False Positive Rate, estimated out-of-fold. Binary only.

from quack.quantifiers import ACC

quantifier = ACC(classifier=LogisticRegression(max_iter=1000), cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Probabilistic Classify & Count (PCC)

Uses soft predict_proba scores instead of hard predictions, averaging the posterior probabilities directly.

from quack.quantifiers import PCC

quantifier = PCC(classifier=LogisticRegression(max_iter=1000))
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Probabilistic Adjusted Classify & Count (PACC)

The probabilistic counterpart of ACC: corrects PCC's averaged probabilities using out-of-fold expected scores. Binary only.

from quack.quantifiers import PACC

quantifier = PACC(classifier=LogisticRegression(max_iter=1000), cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Generalized Adjusted Classify & Count (GAC)

Multiclass generalization of ACC: builds a full confusion matrix out-of-fold and solves a distance-minimization problem (via CVXPY, with a Golden Section Search fallback for the binary case).

from quack.quantifiers import GAC

quantifier = GAC(classifier=LogisticRegression(max_iter=1000), distance_metric="L2", cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)  # also works with 3+ classes
prevalences = quantifier.predict(X_test)

Generalized Probabilistic Adjusted Classify & Count (GPAC)

Same idea as GAC, but using soft predict_proba scores instead of hard confusion-matrix counts.

from quack.quantifiers import GPAC

quantifier = GPAC(classifier=LogisticRegression(max_iter=1000), distance_metric="L2", cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Friedman's Method (FM)

Compares soft classifier probabilities against the training prevalence, thresholding them into a binary indicator matrix before solving the mixture.

from quack.quantifiers import FM

quantifier = FM(classifier=LogisticRegression(max_iter=1000), distance_metric="L2", cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Threshold Selectors: X, Max, T50, MedianSweep

Binary methods that adjust the decision threshold dynamically over an out-of-fold TPR/FPR grid, each with a different selection criterion (see each class's docstring for the exact formula). All share the same interface:

from quack.quantifiers import X, Max, T50, MedianSweep

for QuantifierCls in (X, Max, T50):
    quantifier = QuantifierCls(classifier=LogisticRegression(max_iter=1000), cv=10, n_jobs=-1)
    quantifier.fit(X_train, y_train)
    print(QuantifierCls.__name__, quantifier.predict(X_test))

# MedianSweep also exposes delta_min, filtering out unstable thresholds
quantifier = MedianSweep(classifier=LogisticRegression(max_iter=1000), cv=10, delta_min=0.25, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

2. Distribution Matching

Feature Based

Hellinger Distance x (HDx)

Operates directly on (categorical/discretized) features, without a classifier, minimizing the Hellinger Distance between per-feature marginal distributions.

from quack.quantifiers import HDx

quantifier = HDx(use_convex_solver=True)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

ReadMe

Ensemble of mixture models trained on random feature subspaces — useful for high-dimensional categorical/bag-of-words data. n_jobs parallelizes fitting/predicting the n_subsets independent sub-quantifiers; random_state makes the random subspaces reproducible.

from quack.quantifiers import ReadMe

quantifier = ReadMe(n_subsets=100, n_features=None, random_state=0, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Energy Distance (ED)

Minimizes the Energy Distance between training class distributions and the test bag, with an exact analytical solution for binary problems and quadratic programming for multiclass. n_jobs parallelizes the per-class-pair pairwise-distance computations (most beneficial with large per-class sample counts).

from quack.quantifiers import ED

quantifier = ED(n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Label Based

Hellinger Distance y (HDy)

Bins classifier scores into histograms and minimizes the Hellinger Distance between the training and test score distributions. Binary only.

from quack.quantifiers import HDy

quantifier = HDy(classifier=LogisticRegression(max_iter=1000), n_bins=10, cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Distribution y-Similarity (DyS)

Generalizes HDy to any supported distance metric ('L1', 'L2', 'HD', 'TS'). Binary only.

from sklearn.svm import SVC
from quack.quantifiers import DyS

quantifier = DyS(classifier=SVC(), distance_metric="TS", n_bins=10, cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Forman's Mixture Model (FormanMM)

Minimizes the L1 distance over the Cumulative Distribution Function (CDF) of classifier scores instead of a fixed-bin histogram. Binary only.

from quack.quantifiers import FormanMM

quantifier = FormanMM(classifier=SVC(), cv=10, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Iterator Based

Expectation Maximization Quantifier (EM)

Iteratively adjusts posterior probabilities and prior estimates until convergence, maximizing the likelihood of the test data. Multiclass-capable.

from quack.quantifiers import EM

quantifier = EM(classifier=LogisticRegression(max_iter=1000), cv=10, epsilon=1e-6, max_iter=1000, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

3. Adaptation of Classification Algorithms

Class Distribution Estimation (CDE)

Iteratively adjusts the decision threshold based on directional weights derived from the training prior. Binary only.

from quack.quantifiers import CDE

quantifier = CDE(classifier=LogisticRegression(max_iter=1000), cv=10, epsilon=1e-6, max_iter=1000, n_jobs=-1)
quantifier.fit(X_train, y_train)
prevalences = quantifier.predict(X_test)

Choosing a method: quick reference

Method Binary only? Needs predict_proba? Notes
CC No No Fastest, least accurate baseline
PCC No Yes Sensitive to classifier calibration
ACC Yes No Classic correction, denominator instability possible
PACC Yes Yes Probabilistic counterpart of ACC
GAC No No Multiclass generalization of ACC
GPAC No Yes Multiclass generalization of PACC
FM No Yes Threshold vs. training prior
X, Max, T50 Yes Yes Threshold-selection strategies (Forman, 2008)
MedianSweep Yes Yes Robust median across many thresholds
HDx No N/A (no classifier) Categorical/discretized features only
ReadMe No N/A (no classifier) High-dimensional / bag-of-words data
ED No N/A (no classifier) Exact binary solution, QP for multiclass
HDy Yes Yes Hellinger distance on score histograms
DyS Yes Yes Configurable distance metric
FormanMM Yes Yes CDF-based, no fixed bin count
EM No Yes Iterative, theoretically well-grounded
CDE Yes Yes Iterative threshold adjustment

References

[1] González, P., Castaño, A., Chawla, N. V., & Coz, J. J. D. (2017). A review on quantification learning. ACM Computing Surveys (CSUR), 50(5), 1-40.

[2] Donyavi, Z., Serapião, A. B., & Batista, G. (2024). MC-SQ and MC-MQ: Ensembles for multi-class quantification. IEEE Transactions on Knowledge and Data Engineering, 36(8), 4007-4019.