2012-12-20 17:31:05 +08:00
|
|
|
import numpy as np
|
2020-02-02 21:44:19 +08:00
|
|
|
import pytest
|
2014-08-05 05:41:31 +08:00
|
|
|
import scipy.sparse as sp
|
2020-03-20 20:57:18 +08:00
|
|
|
from scipy.special import comb
|
2014-08-05 05:41:31 +08:00
|
|
|
from numpy.testing import assert_array_almost_equal
|
2012-12-20 17:31:05 +08:00
|
|
|
|
2019-09-27 15:46:17 +08:00
|
|
|
from sklearn.utils.random import _random_choice_csc, sample_without_replacement
|
2019-04-04 18:31:09 +08:00
|
|
|
from sklearn.utils._random import _our_rand_r_py
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
###############################################################################
|
2012-12-21 18:43:04 +08:00
|
|
|
# test custom sampling without replacement algorithm
|
2012-12-20 17:31:05 +08:00
|
|
|
###############################################################################
|
2012-12-21 18:43:04 +08:00
|
|
|
def test_invalid_sample_without_replacement_algorithm():
|
2020-02-02 21:44:19 +08:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
sample_without_replacement(5, 4, "unknown")
|
2012-12-21 18:43:04 +08:00
|
|
|
|
|
|
|
|
|
2012-12-20 17:31:05 +08:00
|
|
|
def test_sample_without_replacement_algorithms():
|
2012-12-21 18:43:04 +08:00
|
|
|
methods = ("auto", "tracking_selection", "reservoir_sampling", "pool")
|
|
|
|
|
|
|
|
|
|
for m in methods:
|
2021-06-18 02:21:09 +08:00
|
|
|
|
2012-12-22 21:29:59 +08:00
|
|
|
def sample_without_replacement_method(
|
|
|
|
|
n_population, n_samples, random_state=None
|
|
|
|
|
):
|
|
|
|
|
return sample_without_replacement(
|
|
|
|
|
n_population, n_samples, method=m, random_state=random_state
|
|
|
|
|
)
|
2012-12-21 18:43:04 +08:00
|
|
|
|
|
|
|
|
check_edge_case_of_sample_int(sample_without_replacement_method)
|
|
|
|
|
check_sample_int(sample_without_replacement_method)
|
|
|
|
|
check_sample_int_distribution(sample_without_replacement_method)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_edge_case_of_sample_int(sample_without_replacement):
|
|
|
|
|
|
2015-12-08 02:13:40 +08:00
|
|
|
# n_population < n_sample
|
2020-02-02 21:44:19 +08:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
sample_without_replacement(0, 1)
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
sample_without_replacement(1, 2)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
# n_population == n_samples
|
2019-07-01 21:13:32 +08:00
|
|
|
assert sample_without_replacement(0, 0).shape == (0,)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
2019-07-01 21:13:32 +08:00
|
|
|
assert sample_without_replacement(1, 1).shape == (1,)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
# n_population >= n_samples
|
2019-07-01 21:13:32 +08:00
|
|
|
assert sample_without_replacement(5, 0).shape == (0,)
|
|
|
|
|
assert sample_without_replacement(5, 1).shape == (1,)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
# n_population < 0 or n_samples < 0
|
2020-02-02 21:44:19 +08:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
sample_without_replacement(-1, 5)
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
sample_without_replacement(5, -1)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_sample_int(sample_without_replacement):
|
|
|
|
|
# This test is heavily inspired from test_random.py of python-core.
|
|
|
|
|
#
|
|
|
|
|
# For the entire allowable range of 0 <= k <= N, validate that
|
|
|
|
|
# the sample is of the correct length and contains only unique items
|
|
|
|
|
n_population = 100
|
|
|
|
|
|
2013-02-14 09:05:35 +08:00
|
|
|
for n_samples in range(n_population + 1):
|
2012-12-20 17:31:05 +08:00
|
|
|
s = sample_without_replacement(n_population, n_samples)
|
2019-07-01 21:13:32 +08:00
|
|
|
assert len(s) == n_samples
|
2012-12-20 17:31:05 +08:00
|
|
|
unique = np.unique(s)
|
2019-07-01 21:13:32 +08:00
|
|
|
assert np.size(unique) == n_samples
|
2018-11-11 09:05:34 +08:00
|
|
|
assert np.all(unique < n_population)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
# test edge case n_population == n_samples == 0
|
2019-07-01 21:13:32 +08:00
|
|
|
assert np.size(sample_without_replacement(0, 0)) == 0
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_sample_int_distribution(sample_without_replacement):
|
|
|
|
|
# This test is heavily inspired from test_random.py of python-core.
|
|
|
|
|
#
|
|
|
|
|
# For the entire allowable range of 0 <= k <= N, validate that
|
|
|
|
|
# sample generates all possible permutations
|
|
|
|
|
n_population = 10
|
|
|
|
|
|
|
|
|
|
# a large number of trials prevents false negatives without slowing normal
|
|
|
|
|
# case
|
|
|
|
|
n_trials = 10000
|
|
|
|
|
|
2013-02-14 09:05:35 +08:00
|
|
|
for n_samples in range(n_population):
|
2012-12-20 17:31:05 +08:00
|
|
|
# Counting the number of combinations is not as good as counting the
|
|
|
|
|
# the number of permutations. However, it works with sampling algorithm
|
|
|
|
|
# that does not provide a random permutation of the subset of integer.
|
2017-06-10 21:01:02 +08:00
|
|
|
n_expected = comb(n_population, n_samples, exact=True)
|
2012-12-20 17:31:05 +08:00
|
|
|
|
|
|
|
|
output = {}
|
2013-02-14 09:05:35 +08:00
|
|
|
for i in range(n_trials):
|
2012-12-20 17:31:05 +08:00
|
|
|
output[
|
|
|
|
|
frozenset(sample_without_replacement(n_population, n_samples))
|
|
|
|
|
] = None
|
|
|
|
|
|
|
|
|
|
if len(output) == n_expected:
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
raise AssertionError(
|
|
|
|
|
"number of combinations != number of expected (%s != %s)"
|
|
|
|
|
% (len(output), n_expected)
|
2021-06-18 02:21:09 +08:00
|
|
|
)
|
2014-08-05 05:41:31 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_random_choice_csc(n_samples=10000, random_state=24):
|
2014-08-05 23:32:37 +08:00
|
|
|
# Explicit class probabilities
|
2014-08-06 02:04:45 +08:00
|
|
|
classes = [np.array([0, 1]), np.array([0, 1, 2])]
|
2018-04-24 07:32:25 +08:00
|
|
|
class_probabilities = [np.array([0.5, 0.5]), np.array([0.6, 0.1, 0.3])]
|
2014-08-05 05:41:31 +08:00
|
|
|
|
2019-09-27 15:46:17 +08:00
|
|
|
got = _random_choice_csc(n_samples, classes, class_probabilities, random_state)
|
2018-11-11 09:05:34 +08:00
|
|
|
assert sp.issparse(got)
|
2014-08-05 05:41:31 +08:00
|
|
|
|
|
|
|
|
for k in range(len(classes)):
|
2014-08-09 09:17:47 +08:00
|
|
|
p = np.bincount(got.getcol(k).toarray().ravel()) / float(n_samples)
|
2018-04-24 07:32:25 +08:00
|
|
|
assert_array_almost_equal(class_probabilities[k], p, decimal=1)
|
2014-08-05 23:32:37 +08:00
|
|
|
|
|
|
|
|
# Implicit class probabilities
|
2014-08-09 06:48:23 +08:00
|
|
|
classes = [[0, 1], [1, 2]] # test for array-like support
|
2018-04-24 07:32:25 +08:00
|
|
|
class_probabilities = [np.array([0.5, 0.5]), np.array([0, 1 / 2, 1 / 2])]
|
2014-08-05 23:32:37 +08:00
|
|
|
|
2019-09-27 15:46:17 +08:00
|
|
|
got = _random_choice_csc(
|
2021-02-09 21:37:26 +08:00
|
|
|
n_samples=n_samples, classes=classes, random_state=random_state
|
|
|
|
|
)
|
2018-11-11 09:05:34 +08:00
|
|
|
assert sp.issparse(got)
|
2014-08-05 23:32:37 +08:00
|
|
|
|
|
|
|
|
for k in range(len(classes)):
|
2014-08-09 09:17:47 +08:00
|
|
|
p = np.bincount(got.getcol(k).toarray().ravel()) / float(n_samples)
|
2018-04-24 07:32:25 +08:00
|
|
|
assert_array_almost_equal(class_probabilities[k], p, decimal=1)
|
2014-08-09 09:17:47 +08:00
|
|
|
|
2015-12-08 02:13:40 +08:00
|
|
|
# Edge case probabilities 1.0 and 0.0
|
2014-08-09 09:17:47 +08:00
|
|
|
classes = [np.array([0, 1]), np.array([0, 1, 2])]
|
2021-02-09 21:37:26 +08:00
|
|
|
class_probabilities = [np.array([0.0, 1.0]), np.array([0.0, 1.0, 0.0])]
|
2014-08-09 09:17:47 +08:00
|
|
|
|
2019-09-27 15:46:17 +08:00
|
|
|
got = _random_choice_csc(n_samples, classes, class_probabilities, random_state)
|
2018-11-11 09:05:34 +08:00
|
|
|
assert sp.issparse(got)
|
2014-08-09 09:17:47 +08:00
|
|
|
|
|
|
|
|
for k in range(len(classes)):
|
|
|
|
|
p = (
|
|
|
|
|
np.bincount(
|
2018-04-24 07:32:25 +08:00
|
|
|
got.getcol(k).toarray().ravel(), minlength=len(class_probabilities[k])
|
2021-06-18 02:21:09 +08:00
|
|
|
)
|
2018-04-24 07:32:25 +08:00
|
|
|
/ n_samples
|
2021-06-18 02:21:09 +08:00
|
|
|
)
|
2018-04-24 07:32:25 +08:00
|
|
|
assert_array_almost_equal(class_probabilities[k], p, decimal=1)
|
2014-08-09 09:17:47 +08:00
|
|
|
|
|
|
|
|
# One class target data
|
|
|
|
|
classes = [[1], [0]] # test for array-like support
|
2018-04-24 07:32:25 +08:00
|
|
|
class_probabilities = [np.array([0.0, 1.0]), np.array([1.0])]
|
2014-08-09 09:17:47 +08:00
|
|
|
|
2019-09-27 15:46:17 +08:00
|
|
|
got = _random_choice_csc(
|
2021-02-09 21:37:26 +08:00
|
|
|
n_samples=n_samples, classes=classes, random_state=random_state
|
|
|
|
|
)
|
2018-11-11 09:05:34 +08:00
|
|
|
assert sp.issparse(got)
|
2014-08-09 09:17:47 +08:00
|
|
|
|
|
|
|
|
for k in range(len(classes)):
|
|
|
|
|
p = np.bincount(got.getcol(k).toarray().ravel()) / n_samples
|
2018-04-24 07:32:25 +08:00
|
|
|
assert_array_almost_equal(class_probabilities[k], p, decimal=1)
|
2014-08-05 05:41:31 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_random_choice_csc_errors():
|
2018-04-24 07:32:25 +08:00
|
|
|
# the length of an array in classes and class_probabilities is mismatched
|
2014-08-06 02:04:45 +08:00
|
|
|
classes = [np.array([0, 1]), np.array([0, 1, 2, 3])]
|
2018-04-24 07:32:25 +08:00
|
|
|
class_probabilities = [np.array([0.5, 0.5]), np.array([0.6, 0.1, 0.3])]
|
2020-02-02 21:44:19 +08:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
_random_choice_csc(4, classes, class_probabilities, 1)
|
2014-08-05 05:41:31 +08:00
|
|
|
|
2014-08-08 05:00:00 +08:00
|
|
|
# the class dtype is not supported
|
|
|
|
|
classes = [np.array(["a", "1"]), np.array(["z", "1", "2"])]
|
2018-04-24 07:32:25 +08:00
|
|
|
class_probabilities = [np.array([0.5, 0.5]), np.array([0.6, 0.1, 0.3])]
|
2020-02-02 21:44:19 +08:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
_random_choice_csc(4, classes, class_probabilities, 1)
|
2014-08-08 05:00:00 +08:00
|
|
|
|
|
|
|
|
# the class dtype is not supported
|
|
|
|
|
classes = [np.array([4.2, 0.1]), np.array([0.1, 0.2, 9.4])]
|
2018-04-24 07:32:25 +08:00
|
|
|
class_probabilities = [np.array([0.5, 0.5]), np.array([0.6, 0.1, 0.3])]
|
2020-02-02 21:44:19 +08:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
_random_choice_csc(4, classes, class_probabilities, 1)
|
2014-08-08 05:00:00 +08:00
|
|
|
|
2015-12-08 02:13:40 +08:00
|
|
|
# Given probabilities don't sum to 1
|
2014-08-09 09:17:47 +08:00
|
|
|
classes = [np.array([0, 1]), np.array([0, 1, 2])]
|
2018-04-24 07:32:25 +08:00
|
|
|
class_probabilities = [np.array([0.5, 0.6]), np.array([0.6, 0.1, 0.3])]
|
2020-02-02 21:44:19 +08:00
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
_random_choice_csc(4, classes, class_probabilities, 1)
|
2019-04-04 18:31:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_our_rand_r():
|
|
|
|
|
assert 131541053 == _our_rand_r_py(1273642419)
|
|
|
|
|
assert 270369 == _our_rand_r_py(0)
|