227 lines
8.0 KiB
Python
227 lines
8.0 KiB
Python
import itertools
|
|
import inspect
|
|
import unittest
|
|
|
|
import numpy as np
|
|
|
|
from sklearn.linear_model import LogisticRegression
|
|
|
|
from quapy.method import AGGREGATIVE_METHODS, BINARY_METHODS, NON_AGGREGATIVE_METHODS
|
|
from quapy.method.non_aggregative import DMx, EDx, HDx
|
|
from quapy.method.aggregative import ACC, DMy, EDy, KDEyCS, RLLS
|
|
from quapy.method.meta import Ensemble
|
|
from quapy.functional import check_prevalence_vector
|
|
from quapy.tests._synthetic import make_dataset
|
|
import quapy as qp
|
|
|
|
OPTIONAL_AGGREGATIVE_METHODS = {
|
|
'BayesianCC',
|
|
'BayesianKDEy',
|
|
'BayesianMAPLS',
|
|
'PQ',
|
|
'RLLS',
|
|
'EDy',
|
|
}
|
|
|
|
OPTIONAL_NON_AGGREGATIVE_METHODS = {
|
|
'EDx',
|
|
}
|
|
|
|
|
|
class TestMethods(unittest.TestCase):
|
|
|
|
tiny_dataset_multiclass = make_dataset(
|
|
n_train=140, n_test=40, n_classes=3, n_features=12, random_state=11, name='synthetic-multiclass'
|
|
)
|
|
tiny_dataset_binary = make_dataset(
|
|
n_train=140, n_test=40, n_classes=2, n_features=12, random_state=13, name='synthetic-binary'
|
|
)
|
|
datasets = [tiny_dataset_binary, tiny_dataset_multiclass]
|
|
|
|
def test_aggregative(self):
|
|
for dataset in TestMethods.datasets:
|
|
learner = LogisticRegression(max_iter=2000)
|
|
learner.fit(*dataset.training.Xy)
|
|
|
|
for model in AGGREGATIVE_METHODS:
|
|
if model.__name__ in OPTIONAL_AGGREGATIVE_METHODS:
|
|
continue
|
|
if not dataset.binary and model in BINARY_METHODS:
|
|
continue
|
|
|
|
kwargs = {'fit_classifier': False}
|
|
if 'val_split' in inspect.signature(model.__init__).parameters:
|
|
kwargs['val_split'] = None
|
|
q = model(learner, **kwargs)
|
|
q.fit(*dataset.training.Xy)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
|
|
def test_non_aggregative(self):
|
|
for dataset in TestMethods.datasets:
|
|
for model in NON_AGGREGATIVE_METHODS:
|
|
if model.__name__ in OPTIONAL_NON_AGGREGATIVE_METHODS:
|
|
continue
|
|
if not dataset.binary and model in BINARY_METHODS:
|
|
continue
|
|
|
|
q = model()
|
|
q.fit(*dataset.training.Xy)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
|
|
def test_ensembles(self):
|
|
qp.environ['SAMPLE_SIZE'] = 20
|
|
|
|
def policy_supported(policy):
|
|
if policy in {'ave', 'ptr', 'ds'}:
|
|
return True
|
|
err = qp.error.from_name(policy)
|
|
required = [
|
|
p for p in inspect.signature(err).parameters.values()
|
|
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD) and p.default is inspect._empty
|
|
]
|
|
return len(required) <= 2
|
|
|
|
base_quantifier = ACC(LogisticRegression(max_iter=2000))
|
|
for dataset, policy in itertools.product(TestMethods.datasets, Ensemble.VALID_POLICIES):
|
|
if not policy_supported(policy):
|
|
continue
|
|
if not dataset.binary and policy == 'ds':
|
|
continue
|
|
|
|
ensemble = Ensemble(quantifier=base_quantifier, size=3, policy=policy, n_jobs=1)
|
|
ensemble.fit(*dataset.training.Xy)
|
|
estim_prevalences = ensemble.predict(dataset.test.instances)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
|
|
def test_composable(self):
|
|
try:
|
|
from quapy.method.composable import check_compatible_qunfold_version
|
|
from quapy.method.composable import (
|
|
ComposableQuantifier,
|
|
LeastSquaresLoss,
|
|
HellingerSurrogateLoss,
|
|
ClassRepresentation,
|
|
HistogramRepresentation,
|
|
CVClassifier,
|
|
)
|
|
except ImportError:
|
|
return
|
|
|
|
composable_methods = [
|
|
ComposableQuantifier(
|
|
LeastSquaresLoss(),
|
|
ClassRepresentation(CVClassifier(LogisticRegression()))
|
|
),
|
|
ComposableQuantifier(
|
|
HellingerSurrogateLoss(),
|
|
HistogramRepresentation(
|
|
3,
|
|
preprocessor=ClassRepresentation(CVClassifier(LogisticRegression()))
|
|
)
|
|
),
|
|
]
|
|
|
|
if check_compatible_qunfold_version():
|
|
for dataset in TestMethods.datasets:
|
|
for q in composable_methods:
|
|
q.fit(*dataset.training.Xy)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
else:
|
|
from quapy.method.composable import __old_version_message
|
|
print(__old_version_message)
|
|
|
|
def test_rlls(self):
|
|
try:
|
|
import cvxpy # noqa: F401
|
|
except ImportError:
|
|
return
|
|
|
|
dataset = TestMethods.tiny_dataset_multiclass
|
|
q = RLLS(LogisticRegression(max_iter=2000), val_split=3)
|
|
q.fit(*dataset.training.Xy)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
|
|
|
|
def test_edy(self):
|
|
try:
|
|
import quadprog # noqa: F401
|
|
except ImportError:
|
|
return
|
|
|
|
dataset = TestMethods.tiny_dataset_multiclass
|
|
q = EDy(LogisticRegression(max_iter=2000), val_split=3)
|
|
q.fit(*dataset.training.Xy)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
|
|
|
|
def test_edx(self):
|
|
try:
|
|
import quadprog # noqa: F401
|
|
except ImportError:
|
|
return
|
|
|
|
dataset = TestMethods.tiny_dataset_multiclass
|
|
q = EDx()
|
|
q.fit(*dataset.training.Xy)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
|
|
|
|
def test_dmy_noncanonical_labels(self):
|
|
dataset = TestMethods.tiny_dataset_multiclass
|
|
label_names = np.asarray(['class-a', 'class-c', 'class-z'])
|
|
y_train = label_names[dataset.training.y]
|
|
y_test = label_names[dataset.test.y]
|
|
|
|
q = DMy(LogisticRegression(max_iter=2000), val_split=3)
|
|
q.fit(dataset.training.X, y_train)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
self.assertEqual(len(estim_prevalences), len(np.unique(y_test)))
|
|
|
|
|
|
def test_dmx_noncanonical_labels(self):
|
|
dataset = TestMethods.tiny_dataset_multiclass
|
|
label_names = np.asarray(['class-a', 'class-c', 'class-z'])
|
|
y_train = label_names[dataset.training.y]
|
|
|
|
q = DMx()
|
|
q.fit(dataset.training.X, y_train)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
self.assertEqual(len(estim_prevalences), len(np.unique(y_train)))
|
|
|
|
def test_kdeycs_noncanonical_labels(self):
|
|
dataset = TestMethods.tiny_dataset_multiclass
|
|
label_names = np.asarray(['class-a', 'class-c', 'class-z'])
|
|
y_train = label_names[dataset.training.y]
|
|
|
|
q = KDEyCS(LogisticRegression(max_iter=2000), val_split=3)
|
|
q.fit(dataset.training.X, y_train)
|
|
estim_prevalences = q.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(estim_prevalences))
|
|
self.assertEqual(len(estim_prevalences), len(np.unique(y_train)))
|
|
|
|
|
|
def test_historical_distribution_matching_presets(self):
|
|
dataset = TestMethods.tiny_dataset_binary
|
|
|
|
hdy = DMy.HDy(LogisticRegression(max_iter=2000), val_split=3)
|
|
hdy.fit(*dataset.training.Xy)
|
|
prev_hdy = hdy.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(prev_hdy))
|
|
|
|
hdx = HDx()
|
|
hdx.fit(*dataset.training.Xy)
|
|
prev_hdx = hdx.predict(dataset.test.X)
|
|
self.assertTrue(check_prevalence_vector(prev_hdx))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|