{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Sparse inverse covariance estimation\n\nUsing the GraphicalLasso estimator to learn a covariance and sparse precision\nfrom a small number of samples.\n\nTo estimate a probabilistic model (e.g. a Gaussian model), estimating the\nprecision matrix, that is the inverse covariance matrix, is as important\nas estimating the covariance matrix. Indeed a Gaussian model is\nparametrized by the precision matrix.\n\nTo be in favorable recovery conditions, we sample the data from a model\nwith a sparse inverse covariance matrix. In addition, we ensure that the\ndata is not too much correlated (limiting the largest coefficient of the\nprecision matrix) and that there a no small coefficients in the\nprecision matrix that cannot be recovered. In addition, with a small\nnumber of observations, it is easier to recover a correlation matrix\nrather than a covariance, thus we scale the time series.\n\nHere, the number of samples is slightly larger than the number of\ndimensions, thus the empirical covariance is still invertible. However,\nas the observations are strongly correlated, the empirical covariance\nmatrix is ill-conditioned and as a result its inverse --the empirical\nprecision matrix-- is very far from the ground truth.\n\nIf we use l2 shrinkage, as with the Ledoit-Wolf estimator, as the number\nof samples is small, we need to shrink a lot. As a result, the\nLedoit-Wolf precision is fairly close to the ground truth precision, that\nis not far from being diagonal, but the off-diagonal structure is lost.\n\nThe l1-penalized estimator can recover part of this off-diagonal\nstructure. It learns a sparse precision. It is not able to\nrecover the exact sparsity pattern: it detects too many non-zero\ncoefficients. However, the highest non-zero coefficients of the l1\nestimated correspond to the non-zero coefficients in the ground truth.\nFinally, the coefficients of the l1 precision estimate are biased toward\nzero: because of the penalty, they are all smaller than the corresponding\nground truth value, as can be seen on the figure.\n\nNote that, the color range of the precision matrices is tweaked to\nimprove readability of the figure. The full range of values of the\nempirical precision is not displayed.\n\nThe alpha parameter of the GraphicalLasso setting the sparsity of the model is\nset by internal cross-validation in the GraphicalLassoCV. As can be\nseen on figure 2, the grid to compute the cross-validation score is\niteratively refined in the neighborhood of the maximum.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Authors: The scikit-learn developers\n# SPDX-License-Identifier: BSD-3-Clause" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generate the data\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\nfrom scipy import linalg\n\nfrom sklearn.datasets import make_sparse_spd_matrix\n\nn_samples = 60\nn_features = 20\n\nprng = np.random.RandomState(1)\nprec = make_sparse_spd_matrix(\n n_features, alpha=0.98, smallest_coef=0.4, largest_coef=0.7, random_state=prng\n)\ncov = linalg.inv(prec)\nd = np.sqrt(np.diag(cov))\ncov /= d\ncov /= d[:, np.newaxis]\nprec *= d\nprec *= d[:, np.newaxis]\nX = prng.multivariate_normal(np.zeros(n_features), cov, size=n_samples)\nX -= X.mean(axis=0)\nX /= X.std(axis=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Estimate the covariance\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from sklearn.covariance import GraphicalLassoCV, ledoit_wolf\n\nemp_cov = np.dot(X.T, X) / n_samples\n\nmodel = GraphicalLassoCV()\nmodel.fit(X)\ncov_ = model.covariance_\nprec_ = model.precision_\n\nlw_cov_, _ = ledoit_wolf(X)\nlw_prec_ = linalg.inv(lw_cov_)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot the results\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n\nplt.figure(figsize=(10, 6))\nplt.subplots_adjust(left=0.02, right=0.98)\n\n# plot the covariances\ncovs = [\n (\"Empirical\", emp_cov),\n (\"Ledoit-Wolf\", lw_cov_),\n (\"GraphicalLassoCV\", cov_),\n (\"True\", cov),\n]\nvmax = cov_.max()\nfor i, (name, this_cov) in enumerate(covs):\n plt.subplot(2, 4, i + 1)\n plt.imshow(\n this_cov, interpolation=\"nearest\", vmin=-vmax, vmax=vmax, cmap=plt.cm.RdBu_r\n )\n plt.xticks(())\n plt.yticks(())\n plt.title(\"%s covariance\" % name)\n\n\n# plot the precisions\nprecs = [\n (\"Empirical\", linalg.inv(emp_cov)),\n (\"Ledoit-Wolf\", lw_prec_),\n (\"GraphicalLasso\", prec_),\n (\"True\", prec),\n]\nvmax = 0.9 * prec_.max()\nfor i, (name, this_prec) in enumerate(precs):\n ax = plt.subplot(2, 4, i + 5)\n plt.imshow(\n np.ma.masked_equal(this_prec, 0),\n interpolation=\"nearest\",\n vmin=-vmax,\n vmax=vmax,\n cmap=plt.cm.RdBu_r,\n )\n plt.xticks(())\n plt.yticks(())\n plt.title(\"%s precision\" % name)\n if hasattr(ax, \"set_facecolor\"):\n ax.set_facecolor(\".7\")\n else:\n ax.set_axis_bgcolor(\".7\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# plot the model selection metric\nplt.figure(figsize=(4, 3))\nplt.axes([0.2, 0.15, 0.75, 0.7])\nplt.plot(model.cv_results_[\"alphas\"], model.cv_results_[\"mean_test_score\"], \"o-\")\nplt.axvline(model.alpha_, color=\".5\")\nplt.title(\"Model selection\")\nplt.ylabel(\"Cross-validation score\")\nplt.xlabel(\"alpha\")\n\nplt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.21" } }, "nbformat": 4, "nbformat_minor": 0 }