.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "auto_examples/plot_model_agnostic_importance.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_auto_examples_plot_model_agnostic_importance.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_auto_examples_plot_model_agnostic_importance.py:


Variable Selection Under Model Misspecification
=============================================================

In this example, we illustrate the limitations of variable selection methods based on
linear models using the circles dataset. We first use the distilled conditional
randomization test (d0CRT), which is based on linear models :footcite:t:`liu2022fast` and then
demonstrate how model-agnostic methods, such as Leave-One-Covariate-Out (LOCO), can
identify important variables even when classes are not linearly separable.

To evaluate the importance of a variable, LOCO re-fits a sub-model using a subset of the
data where the variable of interest is removed. The importance of the variable is
quantified as the difference in loss between the full model and the sub-model. As shown
in :footcite:t:`williamson_2021_nonparametric` , this loss difference can be interpreted as an
unnormalized generalized ANOVA (difference of R²).  Denoting :math:`\mu` the predictive
model used, :math:`\mu_{-j}` the sub-model where the j-th variable is removed, and
:math:`X^{-j}` the data with the j-th variable removed, the loss difference can be
expressed as:

.. math::
    \psi_{j} = \mathbb{V}(y) \left[ \left[ 1 - \frac{\mathbb{E}[(y - \mu(X))^2]}{\mathbb{V}(y)} \right] - \left[ 1 - \frac{\mathbb{E}[(y - \mu_{-j}(X^{-j}))^2]}{\mathbb{V}(y)} \right] \right]

where :math:`\psi_{j}` is the LOCO importance of the j-th variable.

.. GENERATED FROM PYTHON SOURCE LINES 25-40

.. code-block:: Python


    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    import seaborn as sns
    from scipy.stats import ttest_1samp
    from sklearn.base import clone
    from sklearn.datasets import make_circles
    from sklearn.linear_model import LogisticRegressionCV
    from sklearn.metrics import hinge_loss, log_loss
    from sklearn.model_selection import KFold
    from sklearn.svm import SVC

    from hidimstat import LOCO, dcrt_pvalue, dcrt_zero








.. GENERATED FROM PYTHON SOURCE LINES 41-43

Generate data where classes are not linearly separable
--------------------------------------------------------------

.. GENERATED FROM PYTHON SOURCE LINES 43-61

.. code-block:: Python

    rng = np.random.RandomState(0)
    X, y = make_circles(n_samples=500, noise=0.1, factor=0.6, random_state=rng)


    fig, ax = plt.subplots()
    sns.scatterplot(
        x=X[:, 0],
        y=X[:, 1],
        hue=y,
        ax=ax,
        palette="muted",
    )
    ax.legend(title="Class")
    ax.set_xlabel("X1")
    ax.set_ylabel("X2")
    plt.show()





.. image-sg:: /auto_examples/images/sphx_glr_plot_model_agnostic_importance_001.png
   :alt: plot model agnostic importance
   :srcset: /auto_examples/images/sphx_glr_plot_model_agnostic_importance_001.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 62-68

Compute p-values using d0CRT
---------------------------------------------------------------------------
We first compute the p-values using d0CRT which performs a conditional independence
test (:math:`H_0: X_j \perp\!\!\!\perp y | X_{-j}`) for each variable. However,
this test is based on a linear model (LogisticRegression) and fails to reject the null
in the presence of non-linear relationships.

.. GENERATED FROM PYTHON SOURCE LINES 68-80

.. code-block:: Python

    selection_features, X_residual, sigma2, y_res = dcrt_zero(
        X, y, problem_type="classification", screening=False
    )
    _, pval_dcrt, _ = dcrt_pvalue(
        selection_features=selection_features,
        X_res=X_residual,
        y_res=y_res,
        sigma2=sigma2,
        fdr=0.05,
    )






.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    /home/circleci/project/.venv/lib/python3.13/site-packages/sklearn/linear_model/_coordinate_descent.py:1613: FutureWarning: 'n_alphas' was deprecated in 1.7 and will be removed in 1.9. 'alphas' now accepts an integer value which removes the need to pass 'n_alphas'. The default value of 'alphas' will change from None to 100 in 1.9. Pass an explicit value to 'alphas' and leave 'n_alphas' to its default value to silence this warning.
      warnings.warn(
    /home/circleci/project/.venv/lib/python3.13/site-packages/sklearn/linear_model/_coordinate_descent.py:1632: FutureWarning: 'alphas=None' is deprecated and will be removed in 1.9, at which point the default value will be set to 100. Set 'alphas=100' to silence this warning.
      warnings.warn(
    /home/circleci/project/.venv/lib/python3.13/site-packages/sklearn/linear_model/_coordinate_descent.py:1613: FutureWarning: 'n_alphas' was deprecated in 1.7 and will be removed in 1.9. 'alphas' now accepts an integer value which removes the need to pass 'n_alphas'. The default value of 'alphas' will change from None to 100 in 1.9. Pass an explicit value to 'alphas' and leave 'n_alphas' to its default value to silence this warning.
      warnings.warn(
    /home/circleci/project/.venv/lib/python3.13/site-packages/sklearn/linear_model/_coordinate_descent.py:1632: FutureWarning: 'alphas=None' is deprecated and will be removed in 1.9, at which point the default value will be set to 100. Set 'alphas=100' to silence this warning.
      warnings.warn(
    /home/circleci/project/.venv/lib/python3.13/site-packages/sklearn/linear_model/_coordinate_descent.py:1613: FutureWarning: 'n_alphas' was deprecated in 1.7 and will be removed in 1.9. 'alphas' now accepts an integer value which removes the need to pass 'n_alphas'. The default value of 'alphas' will change from None to 100 in 1.9. Pass an explicit value to 'alphas' and leave 'n_alphas' to its default value to silence this warning.
      warnings.warn(
    /home/circleci/project/.venv/lib/python3.13/site-packages/sklearn/linear_model/_coordinate_descent.py:1632: FutureWarning: 'alphas=None' is deprecated and will be removed in 1.9, at which point the default value will be set to 100. Set 'alphas=100' to silence this warning.
      warnings.warn(




.. GENERATED FROM PYTHON SOURCE LINES 81-88

Compute p-values using LOCO
---------------------------------------------------------------------------
We then compute the p-values using LOCO
with a linear, and then a non-linear model. When using a
misspecified model, such as a linear model for this dataset, LOCO fails to reject the null
similarly to d0CRT. However, when using a non-linear model (SVC), LOCO is able to
identify the important variables.

.. GENERATED FROM PYTHON SOURCE LINES 88-118

.. code-block:: Python

    cv = KFold(n_splits=5, shuffle=True, random_state=0)
    non_linear_model = SVC(kernel="rbf", random_state=0)
    linear_model = LogisticRegressionCV(Cs=np.logspace(-3, 3, 5))

    importances_linear = []
    importances_non_linear = []
    for train, test in cv.split(X):
        non_linear_model_ = clone(non_linear_model)
        linear_model_ = clone(linear_model)
        non_linear_model_.fit(X[train], y[train])
        linear_model_.fit(X[train], y[train])

        vim_linear = LOCO(
            estimator=linear_model_, loss=log_loss, method="predict_proba", n_jobs=2
        )
        vim_non_linear = LOCO(
            estimator=non_linear_model_,
            loss=hinge_loss,
            method="decision_function",
            n_jobs=2,
        )
        vim_linear.fit(X[train], y[train])
        vim_non_linear.fit(X[train], y[train])

        importances_linear.append(vim_linear.importance(X[test], y[test])["importance"])
        importances_non_linear.append(
            vim_non_linear.importance(X[test], y[test])["importance"]
        )









.. GENERATED FROM PYTHON SOURCE LINES 119-121

To select variables using LOCO, we compute the p-values using a t-test over the
importance scores.

.. GENERATED FROM PYTHON SOURCE LINES 121-137

.. code-block:: Python


    _, pval_linear = ttest_1samp(importances_linear, 0, axis=0, alternative="greater")
    _, pval_non_linear = ttest_1samp(
        importances_non_linear, 0, axis=0, alternative="greater"
    )

    df_pval = pd.DataFrame(
        {
            "pval": np.hstack([pval_dcrt, pval_linear, pval_non_linear]),
            "method": ["d0CRT"] * 2 + ["LOCO-linear"] * 2 + ["LOCO-non-linear"] * 2,
            "Feature": ["X1", "X2"] * 3,
        }
    )
    df_pval["minus_log10_pval"] = -np.log10(df_pval["pval"])









.. GENERATED FROM PYTHON SOURCE LINES 138-140

Plot the :math:`-log_{10}(pval)` for each method and variable
---------------------------------------------------------------------------

.. GENERATED FROM PYTHON SOURCE LINES 140-157

.. code-block:: Python

    fig, ax = plt.subplots()
    sns.barplot(
        data=df_pval,
        y="Feature",
        x="minus_log10_pval",
        hue="method",
        palette="muted",
        ax=ax,
    )
    ax.set_xlabel("-$\\log_{10}(pval)$")
    ax.axvline(
        -np.log10(0.05), color="k", lw=3, linestyle="--", label="-$\\log_{10}(0.05)$"
    )
    ax.legend()
    plt.show()





.. image-sg:: /auto_examples/images/sphx_glr_plot_model_agnostic_importance_002.png
   :alt: plot model agnostic importance
   :srcset: /auto_examples/images/sphx_glr_plot_model_agnostic_importance_002.png
   :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 158-162

As expected, when using linear models (d0CRT and LOCO-linear) that are misspecified,
the varibles are not selected. This highlights the benefit of using model-agnostic
methods such as LOCO, which allows for the use of models that are expressive enough
to explain the data.

.. GENERATED FROM PYTHON SOURCE LINES 165-168

References
---------------------------------------------------------------------------
.. footbibliography::


.. rst-class:: sphx-glr-timing

   **Total running time of the script:** (0 minutes 6.635 seconds)


.. _sphx_glr_download_auto_examples_plot_model_agnostic_importance.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: plot_model_agnostic_importance.ipynb <plot_model_agnostic_importance.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: plot_model_agnostic_importance.py <plot_model_agnostic_importance.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: plot_model_agnostic_importance.zip <plot_model_agnostic_importance.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_