CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
test_public_functions.py404 linesDownload Raw Back to tests
1from importlib import import_module
2from inspect import signature
3from numbers import Integral, Real
4
5import pytest
6
7from sklearn.utils._param_validation import (
8    Interval,
9    InvalidParameterError,
10    generate_invalid_param_val,
11    generate_valid_param,
12    make_constraint,
13)
14
15
16def _get_func_info(func_module):
17    module_name, func_name = func_module.rsplit(".", 1)
18    module = import_module(module_name)
19    func = getattr(module, func_name)
20
21    func_sig = signature(func)
22    func_params = [
23        p.name
24        for p in func_sig.parameters.values()
25        if p.kind not in (p.VAR_POSITIONAL, p.VAR_KEYWORD)
26    ]
27
28    # The parameters `*args` and `**kwargs` are ignored since we cannot generate
29    # constraints.
30    required_params = [
31        p.name
32        for p in func_sig.parameters.values()
33        if p.default is p.empty and p.kind not in (p.VAR_POSITIONAL, p.VAR_KEYWORD)
34    ]
35
36    return func, func_name, func_params, required_params
37
38
39def _check_function_param_validation(
40    func, func_name, func_params, required_params, parameter_constraints
41):
42    """Check that an informative error is raised when the value of a parameter does not
43    have an appropriate type or value.
44    """
45    # generate valid values for the required parameters
46    valid_required_params = {}
47    for param_name in required_params:
48        if parameter_constraints[param_name] == "no_validation":
49            valid_required_params[param_name] = 1
50        else:
51            valid_required_params[param_name] = generate_valid_param(
52                make_constraint(parameter_constraints[param_name][0])
53            )
54
55    # check that there is a constraint for each parameter
56    if func_params:
57        validation_params = parameter_constraints.keys()
58        unexpected_params = set(validation_params) - set(func_params)
59        missing_params = set(func_params) - set(validation_params)
60        err_msg = (
61            "Mismatch between _parameter_constraints and the parameters of"
62            f" {func_name}.\nConsider the unexpected parameters {unexpected_params} and"
63            f" expected but missing parameters {missing_params}\n"
64        )
65        assert set(validation_params) == set(func_params), err_msg
66
67    # this object does not have a valid type for sure for all params
68    param_with_bad_type = type("BadType", (), {})()
69
70    for param_name in func_params:
71        constraints = parameter_constraints[param_name]
72
73        if constraints == "no_validation":
74            # This parameter is not validated
75            continue
76
77        # Mixing an interval of reals and an interval of integers must be avoided.
78        if any(
79            isinstance(constraint, Interval) and constraint.type == Integral
80            for constraint in constraints
81        ) and any(
82            isinstance(constraint, Interval) and constraint.type == Real
83            for constraint in constraints
84        ):
85            raise ValueError(
86                f"The constraint for parameter {param_name} of {func_name} can't have a"
87                " mix of intervals of Integral and Real types. Use the type"
88                " RealNotInt instead of Real."
89            )
90
91        match = (
92            rf"The '{param_name}' parameter of {func_name} must be .* Got .* instead."
93        )
94
95        err_msg = (
96            f"{func_name} does not raise an informative error message when the "
97            f"parameter {param_name} does not have a valid type. If any Python type "
98            "is valid, the constraint should be 'no_validation'."
99        )
100
101        # First, check that the error is raised if param doesn't match any valid type.
102        with pytest.raises(InvalidParameterError, match=match):
103            func(**{**valid_required_params, param_name: param_with_bad_type})
104            pytest.fail(err_msg)
105
106        # Then, for constraints that are more than a type constraint, check that the
107        # error is raised if param does match a valid type but does not match any valid
108        # value for this type.
109        constraints = [make_constraint(constraint) for constraint in constraints]
110
111        for constraint in constraints:
112            try:
113                bad_value = generate_invalid_param_val(constraint)
114            except NotImplementedError:
115                continue
116
117            err_msg = (
118                f"{func_name} does not raise an informative error message when the "
119                f"parameter {param_name} does not have a valid value.\n"
120                "Constraints should be disjoint. For instance "
121                "[StrOptions({'a_string'}), str] is not a acceptable set of "
122                "constraint because generating an invalid string for the first "
123                "constraint will always produce a valid string for the second "
124                "constraint."
125            )
126
127            with pytest.raises(InvalidParameterError, match=match):
128                func(**{**valid_required_params, param_name: bad_value})
129                pytest.fail(err_msg)
130
131
132PARAM_VALIDATION_FUNCTION_LIST = [
133    "sklearn.calibration.calibration_curve",
134    "sklearn.cluster.cluster_optics_dbscan",
135    "sklearn.cluster.compute_optics_graph",
136    "sklearn.cluster.estimate_bandwidth",
137    "sklearn.cluster.kmeans_plusplus",
138    "sklearn.cluster.cluster_optics_xi",
139    "sklearn.cluster.ward_tree",
140    "sklearn.covariance.empirical_covariance",
141    "sklearn.covariance.ledoit_wolf_shrinkage",
142    "sklearn.covariance.log_likelihood",
143    "sklearn.covariance.shrunk_covariance",
144    "sklearn.datasets.clear_data_home",
145    "sklearn.datasets.dump_svmlight_file",
146    "sklearn.datasets.fetch_20newsgroups",
147    "sklearn.datasets.fetch_20newsgroups_vectorized",
148    "sklearn.datasets.fetch_california_housing",
149    "sklearn.datasets.fetch_covtype",
150    "sklearn.datasets.fetch_kddcup99",
151    "sklearn.datasets.fetch_lfw_pairs",
152    "sklearn.datasets.fetch_lfw_people",
153    "sklearn.datasets.fetch_olivetti_faces",
154    "sklearn.datasets.fetch_rcv1",
155    "sklearn.datasets.fetch_openml",
156    "sklearn.datasets.fetch_species_distributions",
157    "sklearn.datasets.get_data_home",
158    "sklearn.datasets.load_breast_cancer",
159    "sklearn.datasets.load_diabetes",
160    "sklearn.datasets.load_digits",
161    "sklearn.datasets.load_files",
162    "sklearn.datasets.load_iris",
163    "sklearn.datasets.load_linnerud",
164    "sklearn.datasets.load_sample_image",
165    "sklearn.datasets.load_svmlight_file",
166    "sklearn.datasets.load_svmlight_files",
167    "sklearn.datasets.load_wine",
168    "sklearn.datasets.make_biclusters",
169    "sklearn.datasets.make_blobs",
170    "sklearn.datasets.make_checkerboard",
171    "sklearn.datasets.make_circles",
172    "sklearn.datasets.make_classification",
173    "sklearn.datasets.make_friedman1",
174    "sklearn.datasets.make_friedman2",
175    "sklearn.datasets.make_friedman3",
176    "sklearn.datasets.make_gaussian_quantiles",
177    "sklearn.datasets.make_hastie_10_2",
178    "sklearn.datasets.make_low_rank_matrix",
179    "sklearn.datasets.make_moons",
180    "sklearn.datasets.make_multilabel_classification",
181    "sklearn.datasets.make_regression",
182    "sklearn.datasets.make_s_curve",
183    "sklearn.datasets.make_sparse_coded_signal",
184    "sklearn.datasets.make_sparse_spd_matrix",
185    "sklearn.datasets.make_sparse_uncorrelated",
186    "sklearn.datasets.make_spd_matrix",
187    "sklearn.datasets.make_swiss_roll",
188    "sklearn.decomposition.sparse_encode",
189    "sklearn.feature_extraction.grid_to_graph",
190    "sklearn.feature_extraction.img_to_graph",
191    "sklearn.feature_extraction.image.extract_patches_2d",
192    "sklearn.feature_extraction.image.reconstruct_from_patches_2d",
193    "sklearn.feature_selection.chi2",
194    "sklearn.feature_selection.f_classif",
195    "sklearn.feature_selection.f_regression",
196    "sklearn.feature_selection.mutual_info_classif",
197    "sklearn.feature_selection.mutual_info_regression",
198    "sklearn.feature_selection.r_regression",
199    "sklearn.inspection.partial_dependence",
200    "sklearn.inspection.permutation_importance",
201    "sklearn.isotonic.check_increasing",
202    "sklearn.isotonic.isotonic_regression",
203    "sklearn.linear_model.enet_path",
204    "sklearn.linear_model.lars_path",
205    "sklearn.linear_model.lars_path_gram",
206    "sklearn.linear_model.lasso_path",
207    "sklearn.linear_model.orthogonal_mp",
208    "sklearn.linear_model.orthogonal_mp_gram",
209    "sklearn.linear_model.ridge_regression",
210    "sklearn.manifold.locally_linear_embedding",
211    "sklearn.manifold.smacof",
212    "sklearn.manifold.spectral_embedding",
213    "sklearn.manifold.trustworthiness",
214    "sklearn.metrics.accuracy_score",
215    "sklearn.metrics.auc",
216    "sklearn.metrics.average_precision_score",
217    "sklearn.metrics.balanced_accuracy_score",
218    "sklearn.metrics.brier_score_loss",
219    "sklearn.metrics.calinski_harabasz_score",
220    "sklearn.metrics.check_scoring",
221    "sklearn.metrics.completeness_score",
222    "sklearn.metrics.class_likelihood_ratios",
223    "sklearn.metrics.classification_report",
224    "sklearn.metrics.cluster.adjusted_mutual_info_score",
225    "sklearn.metrics.cluster.contingency_matrix",
226    "sklearn.metrics.cluster.entropy",
227    "sklearn.metrics.cluster.fowlkes_mallows_score",
228    "sklearn.metrics.cluster.homogeneity_completeness_v_measure",
229    "sklearn.metrics.cluster.normalized_mutual_info_score",
230    "sklearn.metrics.cluster.silhouette_samples",
231    "sklearn.metrics.cluster.silhouette_score",
232    "sklearn.metrics.cohen_kappa_score",
233    "sklearn.metrics.confusion_matrix",
234    "sklearn.metrics.consensus_score",
235    "sklearn.metrics.coverage_error",
236    "sklearn.metrics.d2_absolute_error_score",
237    "sklearn.metrics.d2_log_loss_score",
238    "sklearn.metrics.d2_pinball_score",
239    "sklearn.metrics.d2_tweedie_score",
240    "sklearn.metrics.davies_bouldin_score",
241    "sklearn.metrics.dcg_score",
242    "sklearn.metrics.det_curve",
243    "sklearn.metrics.explained_variance_score",
244    "sklearn.metrics.f1_score",
245    "sklearn.metrics.fbeta_score",
246    "sklearn.metrics.get_scorer",
247    "sklearn.metrics.hamming_loss",
248    "sklearn.metrics.hinge_loss",
249    "sklearn.metrics.homogeneity_score",
250    "sklearn.metrics.jaccard_score",
251    "sklearn.metrics.label_ranking_average_precision_score",
252    "sklearn.metrics.label_ranking_loss",
253    "sklearn.metrics.log_loss",
254    "sklearn.metrics.make_scorer",
255    "sklearn.metrics.matthews_corrcoef",
256    "sklearn.metrics.max_error",
257    "sklearn.metrics.mean_absolute_error",
258    "sklearn.metrics.mean_absolute_percentage_error",
259    "sklearn.metrics.mean_gamma_deviance",
260    "sklearn.metrics.mean_pinball_loss",
261    "sklearn.metrics.mean_poisson_deviance",
262    "sklearn.metrics.mean_squared_error",
263    "sklearn.metrics.mean_squared_log_error",
264    "sklearn.metrics.mean_tweedie_deviance",
265    "sklearn.metrics.median_absolute_error",
266    "sklearn.metrics.multilabel_confusion_matrix",
267    "sklearn.metrics.mutual_info_score",
268    "sklearn.metrics.ndcg_score",
269    "sklearn.metrics.pair_confusion_matrix",
270    "sklearn.metrics.adjusted_rand_score",
271    "sklearn.metrics.pairwise.additive_chi2_kernel",
272    "sklearn.metrics.pairwise.chi2_kernel",
273    "sklearn.metrics.pairwise.cosine_distances",
274    "sklearn.metrics.pairwise.cosine_similarity",
275    "sklearn.metrics.pairwise.euclidean_distances",
276    "sklearn.metrics.pairwise.haversine_distances",
277    "sklearn.metrics.pairwise.laplacian_kernel",
278    "sklearn.metrics.pairwise.linear_kernel",
279    "sklearn.metrics.pairwise.manhattan_distances",
280    "sklearn.metrics.pairwise.nan_euclidean_distances",
281    "sklearn.metrics.pairwise.paired_cosine_distances",
282    "sklearn.metrics.pairwise.paired_distances",
283    "sklearn.metrics.pairwise.paired_euclidean_distances",
284    "sklearn.metrics.pairwise.paired_manhattan_distances",
285    "sklearn.metrics.pairwise.pairwise_distances_argmin_min",
286    "sklearn.metrics.pairwise.pairwise_kernels",
287    "sklearn.metrics.pairwise.polynomial_kernel",
288    "sklearn.metrics.pairwise.rbf_kernel",
289    "sklearn.metrics.pairwise.sigmoid_kernel",
290    "sklearn.metrics.pairwise_distances",
291    "sklearn.metrics.pairwise_distances_argmin",
292    "sklearn.metrics.pairwise_distances_chunked",
293    "sklearn.metrics.precision_recall_curve",
294    "sklearn.metrics.precision_recall_fscore_support",
295    "sklearn.metrics.precision_score",
296    "sklearn.metrics.r2_score",
297    "sklearn.metrics.rand_score",
298    "sklearn.metrics.recall_score",
299    "sklearn.metrics.roc_auc_score",
300    "sklearn.metrics.roc_curve",
301    "sklearn.metrics.root_mean_squared_error",
302    "sklearn.metrics.root_mean_squared_log_error",
303    "sklearn.metrics.top_k_accuracy_score",
304    "sklearn.metrics.v_measure_score",
305    "sklearn.metrics.zero_one_loss",
306    "sklearn.model_selection.cross_val_predict",
307    "sklearn.model_selection.cross_val_score",
308    "sklearn.model_selection.cross_validate",
309    "sklearn.model_selection.learning_curve",
310    "sklearn.model_selection.permutation_test_score",
311    "sklearn.model_selection.train_test_split",
312    "sklearn.model_selection.validation_curve",
313    "sklearn.neighbors.kneighbors_graph",
314    "sklearn.neighbors.radius_neighbors_graph",
315    "sklearn.neighbors.sort_graph_by_row_values",
316    "sklearn.preprocessing.add_dummy_feature",
317    "sklearn.preprocessing.binarize",
318    "sklearn.preprocessing.label_binarize",
319    "sklearn.preprocessing.normalize",
320    "sklearn.preprocessing.scale",
321    "sklearn.random_projection.johnson_lindenstrauss_min_dim",
322    "sklearn.svm.l1_min_c",
323    "sklearn.tree.export_graphviz",
324    "sklearn.tree.export_text",
325    "sklearn.tree.plot_tree",
326    "sklearn.utils.gen_batches",
327    "sklearn.utils.gen_even_slices",
328    "sklearn.utils.resample",
329    "sklearn.utils.safe_mask",
330    "sklearn.utils.extmath.randomized_svd",
331    "sklearn.utils.class_weight.compute_class_weight",
332    "sklearn.utils.class_weight.compute_sample_weight",
333    "sklearn.utils.graph.single_source_shortest_path_length",
334]
335
336
337@pytest.mark.parametrize("func_module", PARAM_VALIDATION_FUNCTION_LIST)
338def test_function_param_validation(func_module):
339    """Check param validation for public functions that are not wrappers around
340    estimators.
341    """
342    func, func_name, func_params, required_params = _get_func_info(func_module)
343
344    parameter_constraints = getattr(func, "_skl_parameter_constraints")
345
346    _check_function_param_validation(
347        func, func_name, func_params, required_params, parameter_constraints
348    )
349
350
351PARAM_VALIDATION_CLASS_WRAPPER_LIST = [
352    ("sklearn.cluster.affinity_propagation", "sklearn.cluster.AffinityPropagation"),
353    ("sklearn.cluster.dbscan", "sklearn.cluster.DBSCAN"),
354    ("sklearn.cluster.k_means", "sklearn.cluster.KMeans"),
355    ("sklearn.cluster.mean_shift", "sklearn.cluster.MeanShift"),
356    ("sklearn.cluster.spectral_clustering", "sklearn.cluster.SpectralClustering"),
357    ("sklearn.covariance.graphical_lasso", "sklearn.covariance.GraphicalLasso"),
358    ("sklearn.covariance.ledoit_wolf", "sklearn.covariance.LedoitWolf"),
359    ("sklearn.covariance.oas", "sklearn.covariance.OAS"),
360    ("sklearn.decomposition.dict_learning", "sklearn.decomposition.DictionaryLearning"),
361    (
362        "sklearn.decomposition.dict_learning_online",
363        "sklearn.decomposition.MiniBatchDictionaryLearning",
364    ),
365    ("sklearn.decomposition.fastica", "sklearn.decomposition.FastICA"),
366    ("sklearn.decomposition.non_negative_factorization", "sklearn.decomposition.NMF"),
367    ("sklearn.preprocessing.maxabs_scale", "sklearn.preprocessing.MaxAbsScaler"),
368    ("sklearn.preprocessing.minmax_scale", "sklearn.preprocessing.MinMaxScaler"),
369    ("sklearn.preprocessing.power_transform", "sklearn.preprocessing.PowerTransformer"),
370    (
371        "sklearn.preprocessing.quantile_transform",
372        "sklearn.preprocessing.QuantileTransformer",
373    ),
374    ("sklearn.preprocessing.robust_scale", "sklearn.preprocessing.RobustScaler"),
375]
376
377
378@pytest.mark.parametrize(
379    "func_module, class_module", PARAM_VALIDATION_CLASS_WRAPPER_LIST
380)
381def test_class_wrapper_param_validation(func_module, class_module):
382    """Check param validation for public functions that are wrappers around
383    estimators.
384    """
385    func, func_name, func_params, required_params = _get_func_info(func_module)
386
387    module_name, class_name = class_module.rsplit(".", 1)
388    module = import_module(module_name)
389    klass = getattr(module, class_name)
390
391    parameter_constraints_func = getattr(func, "_skl_parameter_constraints")
392    parameter_constraints_class = getattr(klass, "_parameter_constraints")
393    parameter_constraints = {
394        **parameter_constraints_class,
395        **parameter_constraints_func,
396    }
397    parameter_constraints = {
398        k: v for k, v in parameter_constraints.items() if k in func_params
399    }
400
401    _check_function_param_validation(
402        func, func_name, func_params, required_params, parameter_constraints
403    )
404 
Aluode/PerceptionLabPortable · CoolFace