Repository: scikit-learn/scikit-learn
File: sklearn/ensemble/tests/test_bagging.py (Python)

Code before:
            {
                "max_samples": 1.0,
                "max_features": 4,
                "bootstrap": True,
                "bootstrap_features": True,
            },
            {"max_features": 2, "bootstrap": False, "bootstrap_features": True},
            {"max_samples": 0.5, "bootstrap": True, "bootstrap_features": False},
        ],
        ["predict", "predict_proba", "predict_log_proba", "decision_function"],
    ),
)
def test_sparse_classification(sparse_container, params, method):
    # Check classification for various parameter settings on sparse input.
    if method in ["predict", "decision_function"]:
        params_svm = dict(kernel="linear", decision_function_shape="ovr")
        svm = SVC
    else:
        svm = CalibratedClassifierCV
        params_svm = dict(
            estimator=SVC(kernel="linear", decision_function_shape="ovr"),
            ensemble=False,
            cv=3,
        )

    class CustomSVC(svm):
        """SVC variant that records the nature of the training set"""

        def fit(self, X, y):
            super().fit(X, y)
            self.data_type_ = type(X)
            return self

    rng = check_random_state(0)
    X_train, X_test, y_train, y_test = train_test_split(
        scale(iris.data), iris.target, random_state=rng
    )

    X_train_sparse = sparse_container(X_train)
    X_test_sparse = sparse_container(X_test)

    # Trained on sparse format
    sparse_classifier = BaggingClassifier(
        estimator=CustomSVC(**params_svm),
        random_state=1,
        **params,
    ).fit(X_train_sparse, y_train)
    print(sparse_classifier, method)
    sparse_results = getattr(sparse_classifier, method)(X_test_sparse)

    # Trained on dense format

Review comment (performance):
Let's remove this useless complexity. The point of the test is completely unrelated to support vector machines and would work for any classifier. We just need to customize the fit method to record the observed type of X container passed to `fit`. So let's rename this class something else unrelated to `SVC`, e.g. `CustomClassifier` and always derive from `LogisticRegression` instead.

Code after:
            {
                "max_samples": 1.0,
                "max_features": 4,
                "bootstrap": True,
                "bootstrap_features": True,
            },
            {"max_features": 2, "bootstrap": False, "bootstrap_features": True},
            {"max_samples": 0.5, "bootstrap": True, "bootstrap_features": False},
        ],
        ["predict", "predict_proba", "predict_log_proba", "decision_function"],
    ),
)
def test_sparse_classification(sparse_container, params, method):
    # Check classification for various parameter settings on sparse input.
    class CustomClassifier(LogisticRegression):
        """LogisticRegression variant that records the nature of the training set"""

        def fit(self, X, y):
            super().fit(X, y)
            self.data_type_ = type(X)
            return self

    rng = check_random_state(0)
    X_train, X_test, y_train, y_test = train_test_split(
        scale(iris.data), iris.target, random_state=rng
    )

    X_train_sparse = sparse_container(X_train)
    X_test_sparse = sparse_container(X_test)

    # Trained on sparse format
    sparse_classifier = BaggingClassifier(
        estimator=CustomClassifier(),
        random_state=1,
        **params,
    ).fit(X_train_sparse, y_train)
    print(sparse_classifier, method)
    sparse_results = getattr(sparse_classifier, method)(X_test_sparse)

    # Trained on dense format
    dense_classifier = BaggingClassifier(
        estimator=CustomClassifier(),
        random_state=1,
        **params,
    ).fit(X_train, y_train)
    dense_results = getattr(dense_classifier, method)(X_test)
    assert_array_almost_equal(sparse_results, dense_results)

    sparse_type = type(X_train_sparse)
    types = [i.data_type_ for i in sparse_classifier.estimators_]