{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Interactive Spatial Autocorrelation\n", "\n", "* [Dani Arribas-Bel](http://darribas.org) ([@darribas](http://twitter.com/darribas))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false }, "tags": [ "hide" ] }, "outputs": [], "source": [ "# If you are running this notebook interactively, please run \n", "# all of the other cells before attempting to play with the\n", "# interactive version\n", "%matplotlib inline\n", "\n", "import seaborn as sns\n", "from libpysal.weights import lat2W, lag_spatial\n", "from pysal.model import spreg\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from scipy.linalg import inv\n", "\n", "def draw_map(lamb):\n", " '''\n", " Draw a map with a synthetic variable generated from \n", " a spatially autoregressive GDP of strength lamb\n", " \n", " ...\n", " \n", " Arguments\n", " ---------\n", " lamb : float\n", " Strength of the SAR process that generates \n", " the sythetic data to map\n", " \n", " Returns\n", " -------\n", " None\n", " '''\n", " s = 20\n", " n = s**2\n", " w = lat2W(s, s, rook=False)\n", " w.transform = 'R'\n", " e = np.random.random((n, 1))\n", " u = inv(np.eye(n) - lamb * w.full()[0])\n", " u = np.dot(u, e)\n", " ul = lag_spatial(w, u)\n", " u = (u - u.mean()) / np.std(u)\n", " ul = (ul - ul.mean()) / np.std(ul)\n", " gu = u.reshape((s, s))\n", " # Figure\n", " f = plt.figure(figsize=(15, 8))\n", " ax1 = f.add_subplot(121)\n", " ax1.matshow(gu, cmap=plt.cm.YlGn)\n", " ax1.set_frame_on(False)\n", " ax1.axes.get_xaxis().set_visible(False)\n", " ax1.axes.get_yaxis().set_visible(False)\n", " #---\n", " ax2 = f.add_subplot(122)\n", " ols = spreg.OLS(ul, u)\n", " tag = \"b = %.3f\"%ols.betas[1][0]\n", " sc = sns.regplot(u.ravel(), ul.ravel(), ax=ax2)\n", " sc = sns.regplot(u.ravel(), ul.ravel(), ax=ax2, \\\n", " scatter=False, label=tag)\n", " ax2.axvline(0, c='0.5', linewidth=0.5)\n", " ax2.axhline(0, c='0.5', linewidth=0.5)\n", " ax2.legend()\n", " plt.xlabel('u')\n", " plt.ylabel('W u')\n", " plt.suptitle(\"$\\lambda$ = %.2f\"%lamb)\n", " plt.show()\n", " return None" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Spatial autocorrelation\n", "\n", "> \"*Everything is related to everything else, but near things are more related than distant things*\"\n", "\n", "\n", "Waldo Tobler (1970)\n", "\n", "Spatial autocorrelation has to do with the degree to which the similarity in values between observations in a dataset is related to the similarity in locations of such observations. Not completely unlike the traditional correlation between two variables -which informs us about how the values in one variable change as a function of those in the other- and analogous to its time-series counterpart -which relates the value of a variable at a given point in time with those in previous periods-, spatial autocorrelation relates the value of the variable of interest in a given location, with values of the same variable in surrounding locations.\n", "\n", "A key idea in this context is that of *spatial randomness*: a situation in which the location of an observation gives no information whatsoever about its value. In other words, a variable is spatially random if it is distributed following no discernible pattern over space. Spatial autocorrelation can thus be formally defined as the \"absence of spatial randomness\", which gives room for two main classes of autocorrelation, similar to the traditional case:\n", "\n", "* **Positive** spatial autocorrelation: similar values tend to group together in similar locations.\n", "* **Negative** spatial autocorrelation: similar values tend to be dispersed and further apart from each other.\n", "\n", "This notebook illustrates the concept using some of the widgets available in the Jupyter Notebook. Thanks to the interactivity they afford, it is possible to modify the degree of spatial autocorrelation embedded in a synthetic variable that is generated, mapped, and displayed in the so called Moran Plot." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Interactive example\n", "\n", "Executing the following code cell sets up an interactive exploration of spatial autocorrelation. You can modify the value in the horizontal slider to control how spatially correlated the resulting map will be: 0 implies complete spatial randomness; 0.9 is a high degree of positive spatial autocorrelation; and -0.9 represents extremely high negative spatial autocorrelation.\n", "\n", "Once you have selected a value for the lamb (for *lambda*, $\\lambda$) parameter, the visualization returns two figures:\n", "\n", "- *On the **left** hand side*, you can see a lattice map representing the location of each observation and the value assigned to each of them encoded in a color gradient ranging from light yellow (lowest) to dark green (highest).\n", "\n", "- *On the **right** hand side*, you can see the Moran Plot for the dataset generated. This is a graphical device that displays the values of a variable in each location (X axis) against its \"spatial lag\", that is, the average value in the \"neighborhood\" of each observation.\n", "\n", "Go ahead and play with setting different degrees of spatial autocorrelation in the underlying data:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "d6002f8f2ba245ca80dcfcc8af205455", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(FloatSlider(value=0.0, description='lamb', max=0.9, min=-0.9), Output()), _dom_classes=(…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# IMPORTANT! To run this cell you first need to run the \n", "# cell that defines the function draw_map, which you can\n", "# find below on the section \"Code geek version\"\n", "from ipywidgets import interact\n", "_ = interact(draw_map, lamb=(-0.9, 0.9))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How does the value set for lamb affect the pattern in the map on the left?\n", "\n", "How does the Moran Plot change as the map becomes more or less clustered?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More detail\n", "\n", "If you want to learn more about the process behind the interactive above, you can check out:\n", "\n", "1. This [companion notebook](https://github.com/darribas/int_sp_auto/deep_dive.ipynb)\n", "2. [Block F](https://darribas.org/gds_course/content/bF/concepts_F.html) of the [GDS Course](https://darribas.org/gds_course/)\n", "\n", "## License\n", "\n", "**NOTE**: this notebook is a modified version of [this other one](http://nbviewer.ipython.org/gist/darribas/9943372) and it's part of a Github repository on the following address:\n", "\n", "> [https://github.com/darribas/int_sp_auto](https://github.com/darribas/int_sp_auto)\n", "\n", "A static HTML version can be found [here](http://nbviewer.ipython.org/github/darribas/int_sp_auto/blob/master/index.ipynb).\n", "\n", "\n", "\n", "Binder notebook for interactive spatial autocorrelation, by [Dani Arribas-Bel](https://darribas.org) is licensed under a [Creative Commons\n", "Attribution-NonCommercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0/)." ] } ], "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.7.8" } }, "nbformat": 4, "nbformat_minor": 4 }