diff --git a/.travis.yml b/.travis.yml index 7b6b30b..d66ec5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ branches: python: - 3.6 - 3.7 + - 3.8 env: - PYSAL_PYPI=true MPLBACKEND='pdf' @@ -16,11 +17,11 @@ matrix: allow_failures: #allow travis tests to fail if using the github version of libpysal - python: 3.6 env: PYSAL_PYPI=false MPLBACKEND='pdf' - - python: 3.7 - env: PYSAL_PYPI=false MPLBACKEND='pdf' + - python: 3.6 + env: PYSAL_PYPI=true MPLBACKEND='pdf' before_install: - - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh + - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh - chmod +x miniconda.sh - ./miniconda.sh -b -p ./miniconda - export PATH=`pwd`/miniconda/bin:$PATH @@ -33,6 +34,7 @@ install: - conda install --yes pip - pip install -r requirements.txt - pip install -r requirements_tests.txt + - conda install --yes -c conda-forge pygeos shapely geopandas # configure dual tests (for dependency libpysal) - if "$PYSAL_PYPI"; then echo 'testing pypi libpysal' && pip install libpysal; @@ -45,7 +47,8 @@ install: script: - python setup.py sdist >/dev/null - python setup.py install - - nosetests --verbose --with-coverage --cover-package=pointpats; + # - nosetests --verbose --with-coverage --cover-package=pointpats; + - pytest pointpats notifications: email: diff --git a/notebooks/distance_statistics-numpy-oriented.ipynb b/notebooks/distance_statistics-numpy-oriented.ipynb new file mode 100644 index 0000000..f71b85c --- /dev/null +++ b/notebooks/distance_statistics-numpy-oriented.ipynb @@ -0,0 +1,827 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Distance Based Statistical Method for Planar Point Patterns\n", + "\n", + "**Authors: Serge Rey and Wei Kang **\n", + "\n", + "## Introduction\n", + "\n", + "Distance based methods for point patterns are of three types:\n", + "\n", + "* [Mean Nearest Neighbor Distance Statistics](#Mean-Nearest-Neighbor-Distance-Statistics)\n", + "* [Nearest Neighbor Distance Functions](#Nearest-Neighbor-Distance-Functions)\n", + "* [Interevent Distance Functions](#Interevent-Distance-Functions)\n", + "\n", + "In addition, we are going to introduce a computational technique [Simulation Envelopes](#Simulation-Envelopes) to aid in making inferences about the data generating process. An [example](#CSR-Example) is used to demonstrate how to use and interprete simulation envelopes." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from scipy import spatial\n", + "import libpysal as ps\n", + "import numpy as np\n", + "from pointpats import ripley\n", + "%matplotlib inline\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mean Nearest Neighbor Distance Statistics\n", + "\n", + "The nearest neighbor(s) for a point $u$ is the point(s) $N(u)$ which meet the condition\n", + "$$d_{u,N(u)} \\leq d_{u,j} \\forall j \\in S - u$$\n", + "\n", + "The distance between the nearest neighbor(s) $N(u)$ and the point $u$ is nearest neighbor distance for $u$. After searching for nearest neighbor(s) for all the points and calculating the corresponding distances, we are able to calculate mean nearest neighbor distance by averaging these distances.\n", + "\n", + "It was demonstrated by Clark and Evans(1954) that mean nearest neighbor distance statistics distribution is a normal distribution under null hypothesis (underlying spatial process is CSR). We can utilize the test statistics to determine whether the point pattern is the outcome of CSR. If not, is it the outcome of cluster or regular\n", + "spatial process?" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "points = np.array([[66.22, 32.54], [22.52, 22.39], [31.01, 81.21],\n", + " [9.47, 31.02], [30.78, 60.10], [75.21, 58.93],\n", + " [79.26, 7.68], [8.23, 39.93], [98.73, 77.17],\n", + " [89.78, 42.53], [65.19, 92.08], [54.46, 8.48]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Nearest Neighbor Distance Functions\n", + "\n", + "Nearest neighbour distance distribution functions (including the nearest “event-to-event” and “point-event” distance distribution functions) of a point process are cumulative distribution functions of several kinds -- $G, F, J$. By comparing the distance function of the observed point pattern with that of the point pattern from a CSR process, we are able to infer whether the underlying spatial process of the observed point pattern is CSR or not for a given confidence level." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### $G$ function - event-to-event\n", + "\n", + "The $G$ function is a kind of \"cumulative\" density describing the distribution of distances within a point pattern. For a given distance $d$, $G(d)$ is the proportion of nearest neighbor distances that are less than $d$. To express this, we first need to define the nearest neighbor distance, which is the smallest distance from each observation $i$ to some other observation $j$, where $j \\neq i$:\n", + "$$ min_{j\\neq i}\\{d_{ij}\\} = d^*_i $$\n", + "\n", + "With this, we can define the $G$ function as a cumulative density function:\n", + "$$G(d) = \\frac{1}{N}\\sum_{i=1}^N \\mathcal{I}(d^*_i < d)$$\n", + "where $\\mathcal{I}(.)$ is an *indicator function* that is $1$ when the argument is true and is zero otherwise. In simple terms, $G(d)$ gives the percentage of of nearest neighbor distances ($d^*_i$) that are smaller than $d$; when $d$ is very small, $G(d)$ is close to zero. When $d$ is large, $G(d)$ approaches one. \n", + "\n", + "Analytical results about $G$ are available assuming that the \"null\" process of locating points in the study area is completely spatially random. In a completely spatially random process, the $G(d)$ value should be:\n", + "$$\n", + "G(d) = 1-e^{-\\lambda \\pi d^2}\n", + "$$\n", + "Practically, we assess statistical significance for the $G(d)$ function using simulations, where a known spatially-random process is generated and then analyzed. This partially accounts for issues with irregularly-shaped study areas, where locations of points are constrained. \n", + "\n", + "In practice, we use the `ripley.g_test` function to conduct a test on the $G(d)$. It estimates a value of $G(d)$ for a set of values (called the `support`). To compute the $G$ function for ten values of $d$ ranging from the smallest possible to the largest values in the data:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "g_test = ripley.g_test(points, support=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All statistical tests in the `pointpats.distance_statistics` return a `collections.namedtuple` object with the following properties:\n", + "- `support`, which contains the distance values ($d$) used to compute the distance statistic. \n", + "- `statistic`, which expresses the value of the requested function at each value of $d$ in the `support`. \n", + "- `pvalue`, which expresses the fraction of observed simulations (under a completely spatially random process) that are more extreme than the observed statistics. \n", + "- `simulations`, which stores the simulated values of the statistic under a spatially random process. Generally, this is *not* saved (for efficiency reasons), but can be requested using `keep_simulations`. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0. , 3.84791574, 7.69583148, 11.54374723, 15.39166297,\n", + " 19.23957871, 23.08749445, 26.93541019, 30.78332593, 34.63124168])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g_test.support" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0. , 0. , 0. , 0.16666667, 0.16666667,\n", + " 0.25 , 0.58333333, 0.83333333, 0.91666667, 1. ])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g_test.statistic" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.00e+00, 0.00e+00, 0.00e+00, 2.89e-02, 1.10e-03, 1.00e-04,\n", + " 4.30e-03, 6.10e-02, 7.33e-02, 0.00e+00])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g_test.pvalue" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "g_test.simulations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To make a plot of the statistic, the `statistic` is generally plotted on the vertical axis and the `support` on the horizontal axis. Here, we will show the median simulated value of $G(d)$ as well." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "g_test = ripley.g_test(points, support=10, keep_simulations=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(g_test.support, np.median(g_test.simulations, axis=0), \n", + " color='k', label='simulated')\n", + "plt.plot(g_test.support, g_test.statistic, \n", + " marker='x', color='orangered', label='observed')\n", + "plt.legend()\n", + "plt.xlabel('Distance')\n", + "plt.ylabel('G Function')\n", + "plt.title('G Function Plot')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the $G$ function increases very slowly at small distances and the line is below the typical simulated value (shown in black). We can verify the visual intuition here by looking at the p-value for each point and plotting the simulated $G(d)$ curves, too:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# grab the middle 95% of simulations using numpy:\n", + "middle_95pct = np.percentile(g_test.simulations, q=(2.5, 97.5), axis=0)\n", + "# use the fill_between function to color between the 2.5% and 97.5% envelope\n", + "plt.fill_between(g_test.support, *middle_95pct, \n", + " color='lightgrey', label='simulated')\n", + "\n", + "# plot the line for the observed value of G(d)\n", + "plt.plot(g_test.support, g_test.statistic, \n", + " color='orangered', label='observed')\n", + "# and plot the support points depending on whether their p-value is smaller than .05\n", + "plt.scatter(g_test.support, g_test.statistic, \n", + " cmap='viridis', c=g_test.pvalue < .01)\n", + "plt.legend()\n", + "plt.xlabel('Distance')\n", + "plt.ylabel('G Function')\n", + "plt.title('G Function Plot')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From this, we can see that there is statistically significant \"dispersion\" at small values of $d$, since there are *too few* nearest neighbor distances observed between $0 < d < 25$. Once we get to very large distances, the simulation envelope covers the observed statistic. As such, we can say that the point pattern recorded in `points` is unusally dispersed. \n", + "\n", + "To evaluate the $G(d)$ function without considering any statistical significance or simulations, you can use the `g_function` in the `ripley` module, which simply returns the distances & values of $G(d)$. " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([ 0. , 1.82269693, 3.64539386, 5.46809079, 7.29078772,\n", + " 9.11348465, 10.93618158, 12.75887851, 14.58157544, 16.40427237,\n", + " 18.2269693 , 20.04966623, 21.87236316, 23.69506009, 25.51775702,\n", + " 27.34045395, 29.16315088, 30.98584782, 32.80854475, 34.63124168]),\n", + " array([0. , 0. , 0. , 0. , 0. ,\n", + " 0.16666667, 0.16666667, 0.16666667, 0.16666667, 0.25 ,\n", + " 0.25 , 0.25 , 0.41666667, 0.58333333, 0.75 ,\n", + " 0.83333333, 0.83333333, 0.91666667, 0.91666667, 1. ]))" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ripley.g_function(points)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### $F$ function - \"point-event\" \n", + "\n", + "When the number of events in a point pattern is small, $G$ function is rough. For the pattern contained in `points`, there are only 12 observations! This means that there are only 12 nearest neighbor distances, and thus only 12 possible values for the $G(d)$ statistic, at any $d$. \n", + "\n", + "One way to get around this is to turn to an alternative, the $F(d)$ function. This is analogous to the $G(d)$ function, but measures the nearest neighbor distance *from* a set of known randomly-distributed points *to* a point in the observed pattern. Another way of thinking about $F(d)$ is that it reflects a *between-pattern* measure of dispersion, where one pattern is completely spatially random and the other pattern is our observed pattern. In contrast, $G(d)$ is a *within-pattern* measure of dispersion. \n", + "\n", + "For a randomly simulated point pattern of size $N_s$, this makes the $F(d)$ function:\n", + "\n", + "$$F(d) = \\frac{1}{N_s} \\sum_k^{N_s} \\mathcal{I}(d^*_k < d)$$\n", + "\n", + "This can have $N_s$ possible values for any $d$, and thus can give a much more fine-grained view of the point pattern. In this sense, the $F(d)$ function is often called the *empty space function*, as it measures the distance from random points in \"empty space\" to the \"filled\" points in our point pattern. The number of those random points governs how \"fine-grained\" our measure of the observed point pattern can be. \n", + "\n", + "Just like the `ripley.g_test`, this function is evaluated for every $d$ in a support. Further, we can provide *custom* values for `support`, just in case we have known distance values of interest. \n", + "\n", + "Below, we'll use the same ten `support` values from $G(d)$ function. And, let's constrain the \"simulated\" point patterns to fall within the convex hull of our original point pattern: " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "f_test = ripley.f_test(points, support = g_test.support, keep_simulations=True, hull='convex')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the $F(d)$ function is very smooth, we can see the $F(d)$ statistic and its simulations clearly by plotting their values directly as lines. For the simulated values, we will make them very transparent. As before we will visualize statistical significance using the `pvalue` attribute:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(f_test.support, f_test.simulations.T, alpha=.01, color='k')\n", + "plt.plot(f_test.support, f_test.statistic, color='red')\n", + "\n", + "plt.scatter(f_test.support, f_test.statistic, \n", + " cmap='viridis', c=f_test.pvalue < .05,\n", + " zorder=4 # make sure they plot on top\n", + " )\n", + "\n", + "plt.xlabel('Distance')\n", + "plt.ylabel('F Function')\n", + "plt.title('F Function Plot')\n", + "plt.show()\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From this we see that the values of the $F$ function are *too high* for distances from about 15 to 25, and (in contrast) for values between $5 < d < 10$, the $F(d)$ function has too few short distances. When the observed $F(d)$ values are too large, then the pattern is too dispersed, or regular. If the empirical $F(d)$ tends to fall below the simulated values, then it reflects clustering. This is the *opposite* of the interpretation of the $G(d)$ function above, so be careful!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### $J$ function - a combination of \"event-event\" and \"point-event\"\n", + "\n", + "The $J$ function combines the $G$ and $F$ function, in an attempt to provide an immediate graphical indication of the clustering both internally and with respect to the empty space distribution. Practically, the $J(d)$ function is computed as a kind of \"relative clustering ratio\":\n", + "\n", + "$$J(d) = \\frac{1-G(d)}{1-F(d)}$$\n", + "\n", + "where the numerator captures the clustering due to within-pattern distances and the denominator captures that for the pattern-to-empty distances. This means that when $J(d)<1$, the underlying point process is a cluster point process, and when $J(d)=1$, the underlying point process is a random point process; otherwise, it is a dispersed point process.\n", + "\n", + "This function can suffer from numerical stability issues; as $G(d)$ and $F(d)$ both approach $1$, the $J$ ratio can become chaotic. Further, when $G$ or $F$ reaches one, the $J$ function changes abruptly. As such, the $J$ function is often *truncated* to the first $1$ (either in $F(d)$ or $G(d)$), and any $d$ where both $F$ and $G$ are $1$ is assigned a $J$ value of $1$. " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/lw17329/Dropbox/dev/pointpats/pointpats/ripley.py:894: UserWarning: requested 20 bins to evaluate the J function, but it reaches infinity at d=25.5178, meaning only 14 bins will be used to characterize the J function.\n", + " tree, distances=distances, **core_kwargs\n" + ] + } + ], + "source": [ + "jp1 = ripley.j_test(points, support=20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see from the warning above, the $J$ function did encounter numerical stability issues at about $d=25$. To address this, `pointpats` truncated the $J$ function to only have 14 values in its support, rather than the $20$ requested. " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'J Function')" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(jp1.support, jp1.statistic, color='orangered')\n", + "plt.axhline(1, linestyle=':', color='k')\n", + "plt.xlabel('Distance')\n", + "plt.ylabel('J Function')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From the above figure, we see that the $J$ function is above the $J(d)=1$ horizontal line, especially as $d$ gets large. This suggests that the process is over-dispersed. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interevent Distance Functions\n", + "\n", + "While both the $F(d)$ and $G(d)$ functions are useful, they only consider the distance between each point $i$ and its nearest point. Earlier we spelled this distance $d_i^*$, and the distance between $i$ and $j$ was $d_{ij}$. So, note that $d_{i}^*$ is the *only* term that matters for $F$ and $G$, if $d_{ij}$ changes (but $j$ isn't closest to $i$), then the $F$ and $G$ functions generally remain the same. \n", + "\n", + "So, further statistical summary functions have been developed to consider the *whole* distance distribution, not only the nearest neighbor distances. These functions (still considered part of the \"Ripley\" alphabet, are the $K$, and $L$ functions. \n", + "\n", + "#### $K$ function\n", + "\n", + "The $K(d)$ function is a scaled version of the cumulative density function for *all* distances within a point pattern. As such, it's a \"relative\" of the $G$ function that considers all distances, not just the nearest neighbor distances. Practically, the $K(d)$ function can be thought of as the percentage of all distances that are less than $d$. Therefore, for a threshold distance $d$, the $K$ function is defined as:\n", + "\n", + "$$K(d) = \\frac{1}{N\\hat\\lambda} \\underset{i=1}{\\overset{N}{\\sum}}\\underset{j=1}{\\overset{N}{\\sum}} \\mathcal{I}\\left(d_ij < d\\right)$$\n", + "\n", + "In this equation, $\\hat\\lambda$ is the *intensity* of the point process. This represents how many points (on average) you would expect in a unit area. You can think of this as an analogue to the *density* of the points in the pattern: large values of $\\hat\\lambda$ mean many points per area, and small values of $\\hat\\lambda$ mean there are fewer points per area. Generally, this parameter is unknown, and is modelled using the average number of points in the study area. This assumes that the intensity of the point pattern is *constant* or *homogeneous* over the study area.\n", + "\n", + "In the same manner as before, we can construct a set of $K(d)$ function evaluations for random point patterns, and compare them to the observed $K(d)$ function we saw in our original data." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "k_test = ripley.k_test(points, keep_simulations=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(k_test.support, k_test.simulations.T, color='k', alpha=.01)\n", + "plt.plot(k_test.support, k_test.statistic, color='orangered')\n", + "\n", + "plt.scatter(k_test.support, k_test.statistic, \n", + " cmap='viridis', c=k_test.pvalue < .05,\n", + " zorder=4 # make sure they plot on top\n", + " )\n", + "\n", + "plt.xlabel('Distance')\n", + "plt.ylabel('K Function')\n", + "plt.title('K Function Plot')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again, we can see that the envelopes are generally above the observed function, meaining that our point pattern is dispersed. We can draw this conclusion because the distances are *too small*, suggesting the pattern is less clustered than otherwise woudl be expected. When points are too regular, their distances tend to be smaller than if they were distributed randomly. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### $L$ function - \"interevent\"\n", + "\n", + "The $L$ function is a scaled version of $K$ function, defined in order to assist with interpretation. The expected value of the $K(d)$ function *increases* with $d$; this makes sense, since the number of pairs of points closer than $d$ will increase as $d$ increases. So, we can define a normalization of $K$ that *removes* this increase as $d$ increases. \n", + "\n", + "$$L(d) = \\sqrt{\\frac{K(d)}{\\pi}}-d$$\n", + "\n", + "For a pattern that is spatially random, $L(d)$ is $0$ at all $d$ values. So, we can use this standardization to make it easier to visualize the results of the $K$ function:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "l_test = ripley.l_test(points, keep_simulations=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(l_test.support, l_test.simulations.T, color='k', alpha=.01)\n", + "plt.plot(l_test.support, l_test.statistic, color='orangered')\n", + "\n", + "plt.scatter(l_test.support, l_test.statistic, \n", + " cmap='viridis', c=l_test.pvalue < .05,\n", + " zorder=4 # make sure they plot on top\n", + " )\n", + "\n", + "plt.xlabel('Distance')\n", + "plt.ylabel('K Function')\n", + "plt.title('K Function Plot')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## CSR Example\n", + "\n", + "In this example, we are going to generate a point pattern as the \"observed\" point pattern. This ensures that the data generating process is completely spatially random. Then, we will simulate CSR in the same domain for 100 times and construct evaluate the ripley functions for these simulations. " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas\n", + "df = geopandas.read_file(ps.examples.get_path(\"vautm17n.shp\"))\n", + "state = df.geometry.cascaded_union" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Generate the point pattern **pp** (size 100) from CSR as the \"observed\" point pattern." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "pattern = ripley.simulate(state, size=100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "before we go any further, let's visualize these simulated values:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZUAAACxCAYAAAAMESCvAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nOy9d5xldX3//zy39zZ1p8/ubO99AY0Eo2IsYBRFwBZLDJrEJP40Rg1fFSRY0CBiCSEWQEQ0YkEQkbqwZWa278zOzk7v7c7t/Z7fH7fsvXPr7C5b4PN8PPaxc8895XPv7J73ebfXW5JlGYFAIBAIzgWKC70AgUAgELxyEEZFIBAIBOcMYVQEAoFAcM4QRkUgEAgE5wxhVAQCgUBwzlBd6AWcTyorK+WWlpYLvQyBQCC4pOjo6JiRZbmqnH1fVUalpaWF9vb2C70MgUAguKSQJGmw3H1F+EsgEAgE5wxhVAQCgUBwzhBGRSAQCATnDGFUBAKBQHDOEEZFIBAUJR4X+oCC8hFGRSAQFOWGe/fwXM/0hV6G4BJBGBWBQFCUk5Ne3n/fPj72k3ZcgciFXo7gIkcYFYFAUBRJSvz9x+OTvOOe3fRNey/sggQXNcKoCASCgrgCEbyhaPp137SPa+7ezRPHJi7gqgQXM8KoCASCgsz5wtRadFnbPKEof/fTDm5/rItoLH6BVia4WHlVybQIBILF0VJhoNqiY2DWn/PeD57r48DwPHe/dzPVCwxPKYKRGN9/9hSuQIRNjTb+anUNRq24Hb0SEL9FgUBQkEc6RtjXP1fw/X39c7zlOy9w93s3s3NpRdnnvePxbv5390D6tU6toLXShF6t4Afv20aVWXs2yxZcQET4SyAQ5GXGG+K2x7pK7jftCXHDvXv54XOnkOXSPS3BSIwfvziwYFucrnE3nUPzfPz+Dk5MeM502YILjDAqAoEgL7f+7jjz/vJKiGNxma8+1s2Xfns8K7Gfj6E5P8X6KTsGnbzp28/xDz87gD9c/FyCiw9hVAQCQV4isew7/xZvFzdPPMwWb37vxW5Qc/+eQa65+wV6pwqXHT+wpzwV9d8eGuOOP3SXv2DBRYEwKgKBIIdAOMb+gdO5lC3eLh44+Xn+Zex+Hjj5+byGZXmNiWhc5tS0j2u/m7/seGjWz4P7hspeR3OF8cw+gOCCIYyKQCDI4fDIPFOeUPr1Lu8R1HIUFXHUcpTLvEfY1mJne4s9vc/4fDD9szdZdvz1J7qJZcS6ftk5kuMBFcKqV3PVqupz8GkE5xNhVAQCQQ7bWhxsabKlX+8xrSciqYiiICKpcLZdQfuAk/4ZHwArakwMOwM55/nu06f40I/24/SFAeiZLC8BbzOoefxTr6WlUngqlxqipFggEOQw7Qkx7T3tqXSaVnPj8tvY5T3CHtN6fMZV1KuiNNj1zHjnsOjUBc/1XM80b7v7Be6+YTNHRl1lXT8ak1li1Z/15xCcf4RREQgEOdz6++MMz2V7Hp2m1XSaVideTHpQSDDuCiRfBheeIosRZ4Dbft/FSB5vJh/eUJT/fq6P9+5swiSaIi8pRPhLIBCkGZz18Y0nTvD40dLaXnE58UejlJhwFTcqVSYt+weci1rLbY918amHDop5LpcY4hFAIBAAEIrG+NhPOjiRzHvUWrTYDBpkYN4XZjIjcZ/JpiZ70a57gNYqY1Y4rVz+1DXJu3/wEttbHbgCEVyBCKtrzLx7RyPV5sVJwwjOD8KoCAQCAL7xxIm0QVlbZ2bEGWRiwkODXV/QoLRWGmkfKG5QdrY62FvC6BSjfdBJ+2DCy9GpFBwcnucHz/XxqTes4AOXNaNSioDLxYT4bQgEAv5wZJzDIy4cRg2QkE1JDeQKReNY9PmfPxvs+oLd8QoJtrfYz8qgLGRdvZVRZwBPKMpXfnecX3SMnLNzC84NwlMRCF7lyLLM/XsH2ds/h0ohsbHByqGR01Va054QK2pMuANerHoV1WYtJp0ajVLBwaF5llYZserVqJUKxuYD6WT8FW2V7OmbPWfr3NxkS3ssKToGnbx3R9M5u4bg7BFGRSB4lRONy2mvJBqXswxKCr1aiUGjxBWI4gpk63F5pk+/turVtFWbsBvUPH9yBo1KwZolZsw6FU5/mN4pb1Hdr0LUWrT05BGZ/HP3FF/67TGWWHUsseq5cmUV5iLlzYKXH2FUBIJXKcFIDK1Kwbee7OHoqLvovgpJwh+OlTynKxCh2WFIV3qFo3GOj58+t1GjZFmVCZ1ayZQnmHdOS+61waxTM+HOzevM+cJZEvpXr63lezdtQUrNQBacd4RREQhepdz+WBcdQ06OjRU3KJBQFq6xaJnMc2PPRKdSMFWkyssXjnE4owHSZlDTWmlEq1Qw5PQzNp9bmry9xUHkyG5uTjZepntl8tA75eHvftrBh65o5bJl5c93EZw7hFERCF6FdE+4+UXHSFneB8CsL8yGemtRo9Lk0KNTK+mZLKxQvJB5f4Rjoy5koNKkxaRR4s1Y05olFiJHd/PAyc+jlqNEJBU3Lr8tr2FZUW1kZD5I77SPF3pn6PjCG9BrlGWvRXBuENVfAsGrhBMTHn7y0gBHR118+EftZRsUgGVVRo6MFZdYqbHoFmVQUrRVm4nEZMZdQVqqjLRVJ/S+rHoVU54guzzZYpa7vEdyzrG2zkwcsOoSz8n+cIw7Hu8ua2iY4NwiPBWB4FXAjDfEPz10gClPCKc/zGLvtWaduuAxOpWC5TUmFGeQx1hXZ8lqikzldsxaJc0OI4dHXWkxS5KeyonqrWxqtCIh4Q1HOTnppW/aR4PdgFKpYFmVkRlviB+/OMDrV1fz2uVVi16X4Mwp21ORJEkpSdIBSZJ+l3z9FUmSDkuSdFCSpD9KklSXse/nJEnqlSTphCRJb8rYvlWSpCPJ9+6Sktk0SZK0kiT9PLl9ryRJLRnHfECSpJPJPx/I2N6a3Pdk8ljN2X0VAsErk3A0znt+8BLdEx7mfIs3KFa9qujQrU1NNo6Mus+oH2XN/DHe2fPTnPksGxvtHB9PeEYpMcs7627ixuW34Vm6nYPDLg4Mz2NQJ8JbgUick1NeptxB9Gol4ZiMWadCrxLhr/PNYsJf/wRk/ua/LsvyBlmWNwG/A/4DQJKkNcD1wFrgauAeSZJSv9nvAR8Dlif/XJ3c/mHAKctyG/At4I7kuRzALcBOYAdwiyRJqQEOdwDfkmV5OeBMnkMgECzg2Z5pTk37zuhYhQT1Nn3BEcE7Wh0cGnFlzVUply3eLr7y4qfyDv56qW+WlbWW9OtO02ruqX03nabV7BuYo9qsZXm1icMLyp/X1ls5OuYmEI7hDkazigIE54eyjIokSQ3AW4B7U9tkWc4sGTECqeefa4CHZFkOybLcD/QCOyRJWgJYZFl+SU4EOn8CXJtxzI+TPz8CvD7pxbwJeFKW5TlZlp3Ak8DVyfeuSu5L8tjUuQSCVx1j8wG+9ng30Vg8571fdZ551/mHHBNceeB/8k56rDZr6Rx0JqdEOllXb8FhKL9HZJf3CMp4JG+uJBaXOTbmptmhp8GeK4E/5QlxcsrLQqfr5KSHepses07FliYba5aYy16P4NxQbk7l28BngKzfkCRJtwHvB1zAXyY31wN7MnYbSW6LJH9euD11zDCALMtRSZJcQEXm9gXHVADzsixH85wrC0mSPkbCO6KpSXTeCl55RGNx/vFnB2gfdHJ83M133rs53QDYNe7m8TxjfcvhRsson/7TP6CRo0QUam5ouzWr6qql0pg1HfLoqJuWCgN2o6YszyiVK5GSuZI9pvU5+6iUChqsOpZWGnnu5EzJc3pDMbyhREd/59A8CoWoRTrflPzGJUl6KzAly3LHwvdkWf68LMuNwAPAJ1OH5DmNXGT7mRxT7FwL1/hDWZa3ybK8rapKJOwErywC4Rj/8vChtHzJMyemeef3XmR4LtFU+I0nTiw6hwIJzS5rz27UchQlcVTxCG+RezCoE7eMtiojh4bnc44bmPUXHdiVSSpX8q369/EPm7+RUybsMKiZcAXpnfIyOh+gxqxd9OcIRcuvcBOcG8ox41cAb5ckaQB4CLhKkqT7F+zzIPDO5M8jQGPGew3AWHJ7Q57tWcdIkqQCrMBckXPNALbkvgvPJRC8ahid9/ObQ9n/9HsmvVz73d08uHeQp7qnFn3OGrOWo6PunBHCv5dWsKM1MWa4d9pHKJobagNY5TzGzRMP5w2ZLaTTtJq7a65jumFzzntt1WYqTFqWWPWcmvbhDUXZ0GAt+3MoJJDyPn8KXk5KGhVZlj8ny3KDLMstJBLwf5Zl+SZJkpZn7PZ2oDv582+A65MVXa0kEvL7ZFkeBzySJO1K5kTeDzyacUyqsutdyWvIwBPAGyVJsicT9G8Enki+93RyX5LHps4lELxqKNQNP+sLc8tvjmXNmS8Hs1aJ3aghEIllV12tuA31+ivoGHTSOZTroaTY4u3iP57/B/41T/K9GIdGXKyts2Rti8XjDM35OZj0iHzhGJPuIEqFxPYWO6oid69rN9XRc+ubuaJNdNWfb86mT+U/JUlaCcSBQeDjALIsH5Mk6WHgOBAFPiHLcsoH/XvgR4Ae+EPyD8D/AD+VJKmXhIdyffJcc5IkfQXYn9zvy7Isp+oWPws8JEnSrcCB5DkEglcV9+8ZLPheJCbTOTTPjlZHySFa21rszHrD+EJRujOEG1MjhNcsMdNZRsnwLu+RdMhMI0d5beAo/dUbcPojJY91ByKoFBLRpOJkRx7jlero3z/gxKxTsWaJJaeUeWOjjTvetUHMWblALMqoyLL8DPBM8ud3FtnvNuC2PNvbgXV5tgeB6wqc6z7gvjzb+0iUGQsEr0oGZnzEjr6Yo4m1xdvFroxt+/rn2NJk48iIi0geieBtzXbaS4z6HS8xLjhFZqNiVKHmaMUWKk3asozKsDOwqIFenmCUvf1z7GhxMOEOMucL4w1FuWJZBVrRn3LBEB31AsElSuRIriYWkFcnq3NonhU1JqY9oZwbfDljfp3+CGqlRCRWPOufCpm9jR6e0qzmBcUy9PMBdrQ42FdiQiTA0VEXDqOaOV9pI5RiyhPk6U9fyRu+9SzXLK3jg1e0lH2s4NwjjIpAcAlyYMhJdM+TtCY1scjo81Av2JbyYHomvdRadDQ5VAzNJcpul1UZyyr/NetU+Ao0QC5EXnMZXx1dkzZAgXCMvhkvSglK2CR84Rirl1iY8xX3nDK5ZlM9SoXEQx/dRbVFzK2/0Iigo0BwiRGNxfnkgwe4fbQmqzprj2l9TsXWwt6PCXeQWW84nRRPjQ8uhlohsaXRVtZwrR0tDg4Mzed4NDPeMJvKLBpoH3TSVm0qa1+AazYlFKKEQbk4EJ6KQHCJ8aeuSUbnA4wmQ027FuRU8m1Lkcq37HWu53WbX8vzZTQUbm6y8WLfLOvrLRwpMsyrVEGAq4y8SiZqhcT6BivBSBxfKMrgXO5Arw0NVpZWlW+ABC8/wqgIBJcY92VMOkxVZ2WSbxskDEpmvuVLNXcRlxty9sukwaZnXzKJ3z3hYW2dJW8ZczkJ9gqTlt4yNch6p7xsbLCmy5cVEmxtsiEffynLYF6zKa+QhuACIsJfAsF5Ys4X5uYHOtjTN3vGcz6OjrpKlgcXIlXum9Laqu7fU/KYmoyQUiQmc3LKm6Ontb7eUlYSfm//HFubywuBpYQqU8RlkI+/xM9OfSEtQLnV18XbNi4p63yC84fwVASC88R9L/Tz2JEJHjsywbp6Cx95zVKu2VS3qHnq9+3uP+PrZ5b7xhRqXtCvLbr/8moT/bPZnkU4GufUtI+dSx3MekMoJQV9076ypWAODM2zudHGgTwSL+nr1pjoGMxN1O/yHkEZSwhQSnKUm/T9VJtFHuViQxgVgeA80T54+mn+6KibT/38IPV2PdtbHGUdP+UJ8rtD42d8/c6MHIyz7Qo6vYVDR412PQOzvrwlxKFonEPD87RUGLMaJReysF8GEh5H94QHh1HDnC+cc4xJq8QdiBDLUxWwcFiX4/I3lvOxBecZYVQEgvNAMBLLO5Pkv5/rK9uoPLBniHAeafvFkMq3rDKZWWGUGZj1E86j4eUwahh2BgqeJxiJMzjrZ1Wtie6J3AFeC/M3mXPlA5EYa+steY3K8mpzQS8m0ygecmziv//6LeV+bMF5RORUBILzwD3PnEqPys3kya5J+mdKJ6+DkSgP7C0sybJYuic89Ex6c/S2UmiLCWslCURiDM4FWFmTW321MH+zcK58x6CTxuSclC3errQAZb4Kr0xSw7rqL78Kg0Y8E1+MCKMiEJwHDJr8siGyDP/zQl/J47/z515aKo1p6fly2NpsQ19i/8EZPztaHdTZsnMT/nAMk7a01EkgHGPIGaDZYcjaXqpfRpZhiVWf9mhSyffNnvIEKAdn/Wdc7CB4eRFGRSA4DyyxFk4o379niGvufoH7Xuhnyp2rsbW3b5Z7njlF+4ATs15d1jTDlTVmOgbnMWhUqBWFCwHm/GH29c/h8kfY1GhjWZWRdcmRvM0VxrI+WyAco9KUPetk4Vz5fCXOR8ZcXKc+leXRrJzKGduUl739czx+9MyGjwleXoRREQjOA4oSFV6HRlx8+XfH2XX7U9x0714ebh/GHYzwfwdG+Nsf7U9XV026Qxwf97C9xV7UCzFqldTb9bRVm/KKSC7EF45xcHieU9M+guFE7icYKX/AVceQk63N2XPqM+fKq5XZn9+sVbK+3sovIstOezQKNbPLLi/7mrc91lVwjbG4zAsnZ7jzyR7h0ZxnRFBSIHiZmXIH+fLvjpe1b1yGF3pneKF3hrueOslIgWT5/gEntRYdrZUajo/n5mp8oSguf5jRIsn2fGxvsbM/2exYYdKWpQsGiebEwdn8+1r1KmosOqJxGV8oilKSMOvVCfXkNZfxxapvs3X+MLuN63DXbYIyuvwB1EoFTn+YJdZEbiYelzkw7OQ3B8f4/ZEJZpJCmZsarVy1qqascwrOHmFUBIKXmXueOcW0p7AScL7SWyA5lrewUZhwB5lwB9neYufoqItA5HQV14nJ3IqsUph1Knoyjosv4gnfZtDgDZ6ubjNqlKyqNROIxDk55ck6LwBJKf3OoXmGTG08om2CKJiH5lm9xIxerSw6DAygzqrjtt93sazKRDAS43eHxxmdz/2+7nyyh79cWb2ofiDBmSOMikDwMpOvkS9FodJbpUT6SbsU+wec1Fp1tBjUdI0X7hspxapac9pLAWgfcGZ5LsWY84XT8vZ2gxqzTp13yNZCtjTZsoyHJxSla9xDpUnD2joLerUShQSdQ04yK59X1pjZNzBXUoofEj1Bfzw+yZvW1pbcV3D2iJyKQPAy8XD7MOtueYKjY66C+xQqvV3XYGWqiHezkAlXkK5krkVXRjlwJmuWmNneYqc9j/FrH0wYFq2q9FO+XqNgQ70Vh1HDUInSYICllcaC45BnvGGOjblpH3Qy5Qmxrj5b3iUSj5dlUFL89KUBImfZ4yMoD2FUBIKXgeE5P1/6zTG8oWhRCZM9pvXEUBBDIoaCPab1WPQqjowUNkTF2D/gxG7UsLnJht2gLrn/zlYHx8c97B9w5l2nLCfO2WjPLhnO7C0B2Nhg5bmTMxwedZWVh3EY1HhCUUJ5Gi8XEpNlvKEoq2tPV73Z9KU/GySUjjc0WNk/4OSup06WdYzg7BDhL4HgZaBvxocvXGb1lCQhyTIkY/6rai1ZopGFci6FGHcFGXcFqTBqaKs20juV/ya/qdHK3v65ss4/6wuzrt5COBLH1Lc/HbKLKtT8+65v8auRpvI+K4kbfaVZm5tnKcBwcqCYSatie4udQCSGXq1kS5ONsfkA9TYDSPnDjFqVgsNJA/3dp3t53YoqtpWpYCA4M4SnIhC8DFy+tAKbvvQz2y7vEZRyDAWglGO8PtJF+0C2QclsDkx5BpnvZ3oMmcz6wgzPBdjcaC14/bdrBrOUf/OdBxLjhI+OulEoJN6p7k2H7FTxCLUDpdWOM9nQaCvboGTiDUXpHnfj9EXYfWqWzqF5YjIcG3cx6crt74G0nQYSlXX//PBBPMHFzXURLA5hVASCc8yJCQ/v+N5umiqMWEoYloWd5311W7MmLBaTOyllcCAh/nhg2MXO1tNP51ub7SyrMnJw2EXD0N608q9ajnKt8iSFUjI7Wx10T3j4ZaSNqEJdsFu+GDtaHUULF4ohAU0VxqwKr2lPCLtBg16rzKsAIC1o/ByeC3D57X/mwz/az73P93FszEW8nJGWgrIR4S+B4BzSO+Xlvf+9Jy2WWGXWsqpWX1DNt3PB9EaqNkJGNdRCZd7MG3imwVk4j34he/vn2NJkQ6mQsqq5Fp7/17HlGLVqVtaYmPNH6J1KeBTLq03pRH6naTU3tN26qJAcwNo6S5YXtli2F5gsOe4KovMraLAbmPOGiMoy7kCivPmv5D5qJ/ZkrdMTivJU9xRPdU8BYDeo2bW0gsuWVXD5sgqWVZlE+fFZIL2auk23bdsmt7e3X+hlCF6hjM0HeNf3XmQsTyimucJAVVLKZMYTYmBBddSyKiMz3jCuQG5oplDOo5gScCGWVRlzEunFcir1dj0NVj2uQITuyTMvV6636XEHInjyKDWXw9Zme9kejkJKNHEunT7K/9v9jyhjkbK/H0g8CFy2NGFgLl9WSaND/6o3MpIkdciyvK2sfYVREQjOnjlfmOu+/2LZHegGjZKWCiNmnQqdWsGzPeV1kWdSadJwtaKfusG9/Emdf4RwJhqlhEqpwF9uAUEGCgnW11uJyYnpk4vBpFFiM2oKqgOUYkWNif6Z/LNdCtFWbeKNh+/jX8buR0WcKArurLuJe2rfvejrr6o188BHdlKxQN/s1cRijIoIfwkE54CucXfZBgUSKsCZ8iora8xE4/FFnWNZlYn7++upaX4vk+7SPS0bG21lNTLmIy6THu+7xKqjyWHgxKSHeX+EjY1WVAoFg7M+6mx6dCoFoWgclUKic2ielipjXtn/YqS8pxPVWzngXbUog7K82sTJKS+WZGhPSlap9dVtg3hCNmZVrYVAJMbArC8dKitE94SHD/+4nZ99dBf6AmrTFzPdE24cBg3VlvMzJVMYFYHgHLC3b/asjj8x6Sl7fnuKaDzR41Fh1JZlVAKL9FAKhcVSJcvbmu2cmPBwaPi05zLjzR68VWnS4AvF2NxoQ61UMO0NMeEKZEnK5LtuumR54ufc0HYrc2XmbYB0f87CfFWfbiU7a8z4w1H2JnMzzRUGVJLEnL94RdjB4XkePzbOOzY3lL2O80k0Fmdwzs/JSS86tYIrV1YDid/5Zx85zM//7rLzthZhVASCs0SWZe7fO3RW59jZ6kjf6MqlY3CeGrO2qK5YJipl+cWepfI1NoOanklPyRzJjDecY2hW1ZoZnvNn9fFkGrCsAoR4pGgBQl4y8h+pSZcA+CPs7Z+jyaFPvz8466fWomN7i6mkF+cwXvjwVygao3fKS++Ul1NTXk4mf84c/fzIx08bkIf2D/H3V7ahU58/D0sYFYHgLInLoFnEDTsfAwUUfksxWcCgrKo1Y9apCEXiHE7mQJSLSDaXqixbVmU649Lg7gkPaqXEujoLR8fcOQbsSw0fJaZUQzLBXqxkOZ83tS9Z6VZIkHJoLsCqWnO6Ii9TmLOQYdnRYmdppZFwNI5mkTI455Iv/N9RftExUvD9v1pdnW7uDEfjHB9z88HLW87T6hIIoyIQnCU/eWmAiTzDtcplaaURo1YFcmEjsVh0aiX7B5xsqE80PpbrCUnApiYbfYptRCd+DvHcG/uaJeYzNigpIjGZo2NutjXb2bE324CtMUV477LSJcuFvKkt3i7ecryHRscmHg035z3WmkfmZXgugM2gxh2IpHuFJGBbi51T0z4++pN2+qZ9vGFNDe/Z3shr2ipRFBmAdq6Z84V5pLOwQZEk+PSbVqZf//rgKG9cW3veK9eEUREIzgJZlvneM6fO+HiLXoU3FKVvxodBo2Rbc35hx8Uy4vSzrdlOMBLjL5ZX8lyZM0q2NNnpGHICS/nXbd+keWRf1o1drZRwlUhsL4ajYy5WtF1BLGnAogo1f1CsolO/omTIK583BWQZmjWbPoVrciLHOEXjcSSJLL2z1INBrVVHk90AyMTipL2X2WTv0e+PjPP7I+PU2/S8Z3sj121rSM90eTmIxuIcH3fzm4NjRXXkrt1Uz6paS/qYh/cP8+BHd71s6yqEMCoCwVlwfNy9KDXhhehVyrR34g/HaB90sqHBysicv2TyuBgz3jCNDgNHx9y0VZvKPs4birKz1YEM/L4fqG3Jev+ypRXpcNq5IBiJ82CkHvk1d7F8op2e2q285C0vGZ6vMTTb0ET48MFvgizn5IU6BufRqRRoVArcwWwjOeEKMpHsNWqtNNBWbUo3gWYyOh/gzid7+PafenjP9ka+8JY1CY/zHPOhH+3n+RIPBWqlxD//1QogMazsB8/1sabOckFCdcKoCARnwZ1/7Dmr45srjDkhr8MjLqx6NZsabBwcKT2TpBAKEmGP3ikvG+qtZRmDE0UaHJsdel48NYtSIbG9xU7ftC/99H42SMBjsRZcpnqW6o3g9aFWSCXHIC+s7koZjKhCDfEIMhIKOY4SOW9eKBiNs6HRlrdLP0X/TKJJdXuLnWNj7rw9PnEZfrZvmBdPzfKt92xiS5M9Z5+z4TNvWsULvS8U9VJu2NFEU0VCSToSj1Nv0/OurRemUq1sMyZJklKSpAOSJP0u+frrkiR1S5J0WJKk/5MkyZax7+ckSeqVJOmEJElvyti+VZKkI8n37pKSwT5JkrSSJP08uX2vJEktGcd8QJKkk8k/H8jY3prc92TyWM3ZfRUCweJ4+sRpqY8zocqkpWcq/03cFYhwcGSerc22vJpW5RCKnb4B+sJnH7LSqJRE4zKhaJz9A04MWiXGc9C3saLGlFYS6Jv2UWfVUW3VFRXCTNFpWs09te9OG4uTleu5oe1W7qy7iS82fpywVFyjbF//HOvqLFy9tpbXragqeJ39A07W1xdfz+Csn+u+/xLferLnnM5uWd9g5frtjQXf16uVfPKq5enXWpWSazfXU3Oe+lIWshjf6J+ATMW6J/u43M0AACAASURBVIF1sixvAHqAzwFIkrQGuB5YC1wN3CNJUupf3veAjwHLk3+uTm7/MOCUZbkN+BZwR/JcDuAWYCewA7hFkqTUY8AdwLdkWV4OOJPnEAjOCw/uHeIfH+wsKL5YikqTBrVKYr5EiKtjcB69RsXaOsuir5H5ZHtq2seGhtI36ULsaHFwckEIaHgukPZa2qqNZ3xuqyH7eXDMFWTUGWA+EKXaXF4Zr1mnosasxROM0uVYxz217+ahqjdz4/LbuLPuJm79i+8UzNEcHXNjN2r48d/u4AtvKZzH6ZpwlwwnxeIy//XUSZ48PlnWusvlg5e3Fnzvw69pparM7+l8UNZ/CUmSGoC3APemtsmy/EdZllOPP3uAlK91DfCQLMshWZb7gV5ghyRJSwCLLMsvyQltmJ8A12Yc8+Pkz48Ar096MW8CnpRleU6WZScJQ3Z18r2rkvuSPDZ1LoHgZUOWZb7xxAl+fXAUkIjGEze0zY3lNy5WmjRoVArG5surGJv2hDg25mZHmRMY19Vb2NJky5mq6A2embdSbdZypMD0Sncwyv4BJ71TPpocBna2OnAYyxuglWLclV++pX/Gx7Q3xNamxHdb7LM32PU4AxF2tjpYs8SS9qBSnkxvZXEl5Z/tG6LrqSf4yMjPuXtT/t+LOxAte6rmiQIComfKiDP/JE2bQc3HXrf0nF7rbCn3OevbwGeAQj7d3wJ/SP5cDwxnvDeS3Faf/Hnh9qxjkobKBVQUOVcFMJ9h1DLPlYUkSR+TJKldkqT26enp4p9SICiCLMt884893P10L/v659KNf55glIFZH+Yyw1RNDkPZBiWTfQNOKk06WiuN6TkqlwVPsKzqtJfQWmmka9yTt0ejb8bHxjPwVmos2rK68Yfm/BwZmUeWE7Pny6m2XWLVpYdw5UOWoXN4nhU1JkCi0ZFbZSWRGOAVjsbZ2z/HoeF51i4IVZUSedni7aL169cg//iLvPWB93HPltziC4UE1RYdq5eYixqXtXWWc9YbEo7Guff5Pj710MG87//965Zh0S3OiL/clDQqkiS9FZiSZbmjwPufB6LAA6lNeXaTi2w/k2OKnSt7oyz/UJblbbIsb6uqKhwzFQhKsbd/jruf7s37ntMfYXmNGVuJEb4Og5ojZ1E9NTofoH78QHqw1n3d/459sIMdLQ42NVqRSIRgCuEuMKCq0LCvLU02jixCt2tdgw2nP0Ln0Dxbm7MT1vmu0WgvXYory9Az6SUUjWPOU121bUHTYjQuMzLnx6A+fXvb1z/HjlYHq2rN6NW5t71d3iOo4hGkeAwiYf46doJbr12Xfr/SpEGSJHqnvHSNe6gwadnR6sgJS66vt/LAR3ZiN559ivf4mJurv/0ct/6+K0e5YIu3i884f8UH7eNnfZ1zTTnVX1cAb5ck6a8BHWCRJOl+WZZvSibO3wq8Xj4tdzwCZGaVGoCx5PaGPNszjxmRJEkFWIG55PYrFxzzDDAD2CRJUiW9lcxzCQTnlFhc5oG9g/zwub6i+6W8gzqrDrtRgz8cQ6NU4AqEmUhqc62tt3Js1HVW5cIbZg+iikVQJmVMdrgPc8/AqrKO7Z/xs7HRmqXXVaiJ0KJX0TdTfqe/ViXRk1E9tn/ASVu1CaUkYTy1L+81vKHF6ZFpF8iNrKo15RWrHHMFWb3ETNf46fXsS45O/jfFSR6NL8/KsWSWJytUGhQbr+SmNc0oJImf7RskHJOz5GZG5wOMzgdoTlZcpfj6dRuwGc5NzdDTJ6byfv+p35eWGIrP/wzueArWnD9tr1KU9FRkWf6cLMsNsiy3kEjA/zlpUK4GPgu8XZblzIDfb4DrkxVdrSQS8vtkWR4HPJIk7UrmRN4PPJpxTKqy613Ja8jAE8AbJUmyJxP0bwSeSL73dHJfksemziUQnFOeODbBfzx6rGzpdrVSwbQnRP+MjxOTHvzhGGvrLFxvGmH98/fwBqn/rNazx7SeqPLMJi8COH2RrPxEoemSy6vNJQsJMtnYYMvZv3fKiyTlv4ZJqyxawpwPlUJCnYyrbWiwcmraRyCS3zAlpkKe9hxTN+Mbu+/lZ71f4LPx5/hq6He8QzvIfMs2bt74Ne6su4m/XX07983VEoxEuWFnE199xwbMWlVeT2tw1s+KGlM6BNm3CJXpUozOB/Jec5f3CBo5ikJOeFUceuacXfNccDZ9KncDWuDJZGXwHlmWPy7L8jFJkh4GjpMIi31CluXUb/3vgR8BehI5mFQe5n+An0qS1EvCQ7keQJblOUmSvgLsT+73ZVmWU0XlnwUekiTpVuBA8hwCwTlFlmWeP7m4XJzDpEGCdFOkOxhFe3Ivt/ScVt492Xbr4kQSM+g0rebT2+6kcXjvoiYvphia82fpXOVrIlxXZ1mUFItaIRX0aronPNRVbyUy/lDWNZZXmzkwvLg+nP0DTlbVmjBq1KiUUlFJ/DlfmG0tp6dFZgtVhvnIwW8iIfOOpOc0vHQ7+/UrqbcZ+FXnCJ1DTr7z3s2sb7Dy8Gsh8ugXUMZzB371THrZ2mQHfBwZdfHX65cs6jMVQt+b37vbY1oPag3EIom/N155Tq53rliUUZFl+RkS4SeS5b+F9rsNuC3P9nZgXZ7tQeC6Aue6D7gvz/Y+EmXGAsHLgizL/Ofj3fxs33DBfQxqBf4FMu4SoFJkBwF2eUor7xZr+FtebcJmULN/wMnGBisd3pU81diac+1yyQzl5GsibCvw9F+ITU3Zs1oWCj16l23nRn/2NbacoSRV90SitLnCqGFppbGgMYvLMOUOpqddZhpPGQkF2Y2Re/rgo5pT/Nq9gpe0Kzg65uayZRXcuLMZxZFn0chRpAICmyk90e7xxc2NKYTLH8HWszuvqOcJxzp8H3oCS8/uhEG5iEJfIDrqBYKCHBtz84NnC+dRdrQ66Jv24o9kd5XP+yNUmbXsaLEzNh9kZD5QdNY8JAZHuQJRNjfZOLCgcqvGrGXEGeDklJdlVcb0sCydWsHWZjt9016ci8zRDM76eH38FCunOtI3+dRNsqXCgMOkhUWEcjKNVL4cTcfgGjavvZx26QqCoQjSuCen72WxzPrCVJm1GDXKLBn9TAZm/Sgk2NZs59jY2rTxnFOauWXkv9NrnFOa02t+u0LFjW0Jr+BLvz3OliY7qzdeiaTREAuHiaDM+f0Fo3Gu29rAZ99cXm6rFP/5eBcntGv4SJ5/M75wjNsHbdz+3s+dk2uda4RREQjyIMsyu3uL6y2NzPmZ90dQLwjD6DXKLEXgWqsOVcvlfL3xu+i6ns8bsvKHYmxvseftR2iuMLAv6QVkToYMRuJ0DDrPaBbLJk8X3+39Aqp4hJhSzZdfexf7dCuJxOL4wzH29c/RUmGgyqyle8KDp0iPy4Z6K4HI6fcLyeZnCmWurbOgUyvpn/ExdxZSL90THra32JHlhDxJZgFCirgM7YNOVtaaCVbu5J7xZNhK31JghsvpNYejcT7xYCe//eRrMN7xFL69T/KxQyY6dSuzrnF4xIXTH2beH6byLMcO7x+YS3jHBWRo1tVb+MJb1pzVNV5OhFERCPLwQu8Mt/+hu+g+dqMGhSQxMn86gb+jxcG+gewbfEqgMFi/isO1+ePtkbicFT5aYtVRZdIy7gpycOTcCTim2OU9gjqerCCLRbD27OZkbXar18Csn4HZhJEza1WsWmJmcNafzhVVGDXYjZq0pti6eguz3hB7vMW9MiCrMbPZYaDGokOSIBiJoVMr8Yai6X3aqo30ThX2mlLf2+ZGG00OA7PeUF7P5cSEB0lKGEGNSsHxsbVZxj215qhCnbXmvmkfX3z0KHe++zIsay5j55M97HnqZM75h+cCOeKUiyUcjfPvvzqSfp01ZIxEk+d9H9z+sghXnisu3pUJBBcQdRlDtxZ2rK+ts9A+WNhjKHf2hlYlIQMalYLmSgPtJSYSTnlC1Nl0i2qo3GNaT1ihRp1nXko+PKFo+uZda9HRXKEnGicrmX901M2qWnPeHM3mJhtHR1x5c0aDc34G5xLGq8GuT1fZbW224w1FOTHhYXOTDZVCon3AWbCR8cDwPGqFxKYmGxISrmAkp7NdlkkbwW3Ndnqnvcz7IzlrZs1lkBGG/FXnKFub7dywo4l/uKqNN6yp4dDIPIeHXRwamadn0oNBo0rPrzlTfvjcqYJhQZtBzY//dgfV5guj6VUuklxM+vIVxrZt2+T29vYLvQzBRc6kO8i7f/ASg7P5pTHysbzaxLgriLfIeN2tzfayK6pqLVqm3CEaHAaG5spbh1opsaHBVtY1NjXYWOc+hr33RXYb1i26gqzGomXSnd113mjX4w5GcuatpLy3csJ0xaYvAulpkeXQVmWkt0ReqNqsxROMEFhQ8GBQK3CYtDll5G3VJq7f3sg7NtdTkRHmCoRjDDv9rKgxl7W2fIw4/Vz1zWcJR/MXX3z5mrW8/7KWMz7/2SBJUocsy9vK2ffCzcUUCC5C4nGZD9y3r2yDolFK7Gx1cGraW9SgQPFO94VMuENsa3GUbVAgMU3x2JiLJdbiMX2LXsWsL8T97nq+U33dGZU2K/JMExx2Bmh0GNjeYmdLkw1Hsqs8lW/Z2z/HxgYrFUW6zUup+8YW8RBcZ9OXFOL0hqJsaLDlyK74I3GcvnCOplvvlJdbf9/Frtuf4hMPdPJczzTxuIxeozwrg7K3b5Z/ffhQUWmbb//pJI8UGSV8sSDCXwJBBnFZZrqMoVt2g5pqsxZvKFZ2klwpSTlJ/UI02PQFRQSLEYzEqTLrGHdlf4aWCgN6tZJoPI7doM3J+ywk3+z3TPTq/DpnCklKexp6tZKdrQ6iGcb0UHJWjERCV0khQaatLRZ2XFVr5uSkp6THo5RAqZDY2z+HVqVgR6sDfyia5eFkfr694dU0OQxEYnHGXadDiL5wDIVCotaio96mZ8zlT3+vkZicNQHyum0NXLetkXrbmU2ADEVjmHQqtjbbCUfjhKJxDidzaQaNkhf/7apz1qn/ciPCXwJBBsFIjDse7yYel/n1wbH0nI8UBrWCtfVWusY9JT2TfDTa9WhViqJhmc1NNnomPAXLZEshSWDXa5jzn66q2t7iYH8JQ5KikGzLwjUuLH2GxOcbLkN5oMKowR2MUGnSopAkRpPFDga1ArNenQ6ttVUbiURl5nxhfOEoW5rtdI25WVdv5fDIfE7YChLzWXqnvFnGSiHBujor4VicTZ4ubnnhHxONqAo1/7bjTn4dbmZtnYWucXfWcfV2PaPJz9NcYaDarEVCYsTpZ8yVncOSJHjvjiZuu3bdoufCf+aRQzzcnu2FbE2Odk509S9ONeFcs5jwl/BUBIIMdGolt7xtLQB/tbqG//rzSSKxOCfGPSytMjLrC6efxDc2WDk04ir5VJ/JsDOAQoKdrQ7aB+bIdFpaKgzU2/QMOf1nbFAgkYxeVm1kbiBhVJQSBBYxpKtQSXCK5TWmvAZFIZH1pF+M1MTI1P5WvZq2aiMjcwEMGmX6O53Q7eL/Qk3p2TDtA06sehV7++dQKSRWJkU8J91BBmb9bG605e3Sj2ck6N8XOoBGjqa10zbPH+LU8vXMeEIsjFDOZ5Q7D87602HR5goDX3/XBlbUmOmf8dE75aV/1sdHX7t00QYlHpd59GCudGE8WZIwOOvjj8cmeP3qGpRlFntcSIRREQjycGTExWd+eTh90zNpVRwfz64kmvOFy3qqX0hcTuQXMiudAGwGDbtPzSJJ0OTQM1REEr4UmaWt9XZ92cltyC/bkkmh29oSq47RM5D0h8Sky3lfBItezXr3MW4/9QWUsQiRiZ8zsEDSxmHUIJOYb5LSDpOSDY5Of27Py0Kj/yf1av5GUiV+Zwo1PbVbOTLq5nUrKhlfUHzQWmXMK1g5OOvn/3vkMG9cU0MoGscTjGDWqVliXXxl1pgrgE6tJLQgQa9AornCwJYmO62VxkvCoIAwKgJBFqPzAb72eHfOk2O+UNewM8DHVb1Fn+qLkZpWmJpR70nK0ssyVBi1Z2VUTkx42NZs58joPKPOQFqqpBwKzX4HCnoCkFjzmRqVFTUmxuYT1XNvmNiLMhZJS9q8lR46Ob2G/hk/9TYd7owqMznZ4Li5yYZeo0zPf8ln9J9e+Pncif6cF3pn095ningJFZxJdzBr/689foL/eNviGhNrLTpu/5v13PxAZ9b267Y38J5tjYv2fC40wqgIBBl8PY9BKcazurX8TYlGv0J0jXswapRsa7GzcvYo1d17eFa7hk7Tag4Mz9NaaaR/EdLzCzk17WVbi4NQNE7PhKfk+RY+0efTJht3FzYaugLJ+1KsXmJmYMafVhte6CmdqtsGSWdBISWMcSHjdWBono0NVvpmfHiC0YKhvHyfLxaXOT7uZm2dJd2DNOvNLdrI/J5kRbbu1n27+/mLFZVcubK67M+vUip44thEzvZlVaZLzqCAMCqCEky5g4y5gmxaxLjcS5k1dRa0KiU/by8sIplCqZCYWLKZG735n+rLwReOsWT0AF/Y9y+o5SgfywihqZVSliezWNqqTezunU2/liSJlgpDuks+k7KS88029vUX7iGJL2hLNGuVxGWK5ofW1VnomfJm9WYs9JSCxtXgTtzk43Ji+qIrUFg3TKdWEonF2d5ipyOwgch4+UY/EpOx6FRUm7VUmTVMLKiiW/g9dbzxF9xvrufxDKPw6V8c5vFPvbZsuZZQNMafu6aytlWaNIy7gkRi8bIacS8mhFERFOToqIuP/Lgdg0bJU//6ukvyqWmxvGltLbcOdJXekYTcx4Hhecjz1FsOtRYtTQ4jbR2/zPs03TPpRa2UWFVrRiElbpYnp7xFdbhS562z6XOaCF2BSMF8TankvEWvomus+OwTb8a6Gu16qsxaDo+4sOhUqJQSS6x6Tky4SdmP1kojJyY9eUusszyJcTebGhMd9d0T7oLlzJDwZE5NewlG4uwfcLKsaSufNt9J88i+tEG5eeLh9ANAg01PvV3PiUkPVSYtDqOGA8PzrK+30jHozEncL/ye2sY7uPODf82bu2r5j0eP4QpEmPGG+Owjh7n3A9vK+j/zYu9s1mTHSpOGn310F8vPou/lQiKMiiAvTxyb4FMPHWRjo5UvvnXNq8KgfP2Jbu555hQvV5X9ujoLapUiXTllM2jYNzBHVLuGjxUIoUViMt0ZUiObGqxMekIFq6yWWHXUWLR5Z9RDQkFZISXkUKqHO9PeQKnk/KpaS3ouST5qLVrsBg12g5q2ahODs/70GlJFA3O+CJsbbZya8eIORKkyacoO7x1M5nEqTRrcRRSZNzTY0vtCQoDzFC0sW7+WnYEebnnhH1HFI4ST3pjUfHm65yVzwNj+ASef/Ms21tVb+J8X+gvOnrn5iJn4vXv59Seu4LKlFfzTQwd5qW+Wp7qn+GXnKO/a2kApHj962supMGp48BI2KCCMimABsizz/Wf7+NoT3cgyXLe1kbV1Z6dndLHQM+nhB8/20T/j5b+u30yjIzEKtm/ay1cf6+LpE9NlG5TWSsOiBkxtb0lItOjUSrY12/GHY+lu+U7Tar7U8FHePP8if7BdXtDrUUqJUlyrXo07EMkJKzmMGqJxmYN5lHozmfNF2ODu4vtJleJUuKtQcr7epqOziPTLzlYH7YNOJtyJUFsxmZUjoy62NtuQZeidXrz0/Yw3jFsVzSo8MOtUGd5b/l/gqWkfb5rYjTIpoqmRo1ynPsWvpMsLXutHLw7wx3/+C37ytzu5+YEOnj4xnROai6zcxZKk1Eu1Rce3r9/EX37jGfzhWMEquUyisThPdk2mX1+/o/GsOvMvBoRREaQJRWN8/v+OZklB/NuvDmPUKrl63bmZZnchcPkjfPWxLn7RMUxcTsTFXf/7WxrffA3DtRu56d69OY1sxdCpFYuKc+9oPT190B+OZUnAQ2I9qdkeO7zH6NG3JIZYLUic242JiZLdyaT7vD+cnqOyucmGQaPMyqEUY810J6p4JCvcdU/tu/MatEpT4aquzU22RcnuV1u0tA84KUNUoCDhaByzTs3yahOuQISWSiP7+ue4bKmjqErwQi/jceVKuic8OV39WpXEpkY7cVnmjse7+cZ1G/nh+7fx6V8c4tGDY1mhudZQlLveuzldpFBj0fHJq9r42uMn2NJsL/lZ9g84s6T/bfpLo2u+GMKoCIBEz8XHf9qRI98Ricl84sED/PB9Cl6/uuYCra4w8bjMIx0jrG+wsqrWnBOme/rEFP/2y8PpDu1UolXTEyX0p2/zz8tvY8xQ/mAlq17Fihpz0afxlDE4Ub2VmeYtRcNGkD+fASTWKUeIo+CLjR/nId5Mk0NPnU1H/4yP7S12QtE4E64gB4fnqbWU3yOReYONKtT0121DBSzUMlxVa84qmc3EoFYwOLu46rRoTD4rg5IiFo+jUSmY9oZorjCwuclGXM5Vjs5koZcxV7MRmyzn5Kg0qsx5OE7sBg3/7+1r+da7N2HRqfnpnsH0vv0zPr75xxN8PmO+yYdf08oSq46WCkPJz7Gw6stqUJfx6S9uLq2yAsHLQu+Uh2u/u7ugHlQsLuetGLoYeKRjhM/88jBv/q/nmU6Wf3pDUdzBCJ995DAf+t/9WWq6qRu4kjjKWIQd7sNlX2t7i524DJ1D82xrtucVbkwZrX8Zu5+7D38GZdeekudN3eCjKNL5jF3eI2jkCEpkVMT4yvD3ud44QiQmMzYfpNaixaBRcnjERTQeR5YT3ekbGsoLVaZusHfW3cRNK27jiHVt3ifrYgKPm5vszPkWN3ESEkoEZ8uRUTfHxtzIcuJpf8IVJH7sRW6eeJgt3sKFFp2m1WmPbGDWz/BcgBpL9u/RE0xM4EzxoxcHeGjfEAqFxJevWcs/XJU9Sf3eF/rpyBh5oFUpecfmhjPKQ1r1l75REZ7Kq5zneqb5xIOdJSuKHMaL8x/7dEYfwZVff4Z/ev1yvvbECSC/KnDmE3pMqS5ZYrq50YpCoWB8PpDlnbQPOlEqJLY22xl3BdKzTLInCJ6eRV9MyqVQs2EcBQoSsXmJOMvG23nE1sj2FjvTnhDP9iQmU2be2KOLcAMywzhrdGr29TvZ0erg+JgLbyjGliZbwYT/zlZH3u71Ukx5Qkx7Q7RWGqkya0t6ceWwxKrlbaoB/rXn84kkvELFB1Z+lcPmNSytMuIORhlxBqg2a9GpFTmVb6tqLUy6p7O2LexP+eKjR2l0GLiirZJ/feNKbAYNX/ndcSA5o2XExdZmx6LX/pq2Sn704kD6tTAqgkuan7w0wJd+e7wsSfa7nurlDWtqMV1kE+c+8tpWHto/xPBcAH84VnJaY+YNvNO2kROWNZBMeKcUdSOxOKemPKxeYs3Jf6RIGwn3eibMq9nabGfCFcyaepiaIFisB2RlTaLBrXMiuyy507SaLzZ+nK8Mfx+JODGFhj8oVhJdMCFyIcfH3TQ7DOmhV+Vg1ipRKxNP1fv656ixaFlaacLpj7CxwYo7GM2q0trWYqdzyElLhbHsa2Qiy4mwUaXp7PIHBrUCu1GDNxRD3/sCqmQSXh2PssN9hH36VRwf97Cx0ZacKhlnyhPColdldeOvXmLm2Z7pnPOrFBIbGqx0Ds0TicnceO9ert1Ux6fftJIPv6YVi07F7X/o5uvv2nDGoeGdSx0oFVL6/6DtFRD+urjuEILzQjQW5yu/O86PXxosvXOS/hkf7/7+S/znO9ezoeHsGyEfPzrO5iY7NYvIAyxElmX+cGSiLKn6TDKf0LctMROIxJEgK+G8pclW1KAsNBKHR9YQi8vUrLmMDym/yjWKXh5TrKRTvZybJx4u2ANiNWjY1z+X1cWd4qGqN6fnqI8376Qz1FzW56sya8s2KhVGDZJEVt5k0h3KChlqVAq2t9hRSBL9M770JMpEY155Uv75GJz1s7zaxMkpL1uSkx3jUHTSpUmjZE2dlbgso1YqeKlvli1NNiaVuwiPPJj+nbxoXJc+5tDwPGatKt0LsrHhdHGBXq1kS1Nu2G/EGWBLkx1fKMqWJhsjzgBTnhC/PjjGY0cn+NAVLdx8ZRtv21h3xkoCAGadmo1JwwXCUxFcgriDEW559Bj/d2B00cceH3fzn3/o5sGP7jqrNezrn+OTDx5gbZ2Fn//dZYv6TxkIx9BrlJyc9PDFR4+yp+/Mwyd1Nh3uYJSeydzS1mAeSXUAlQL+RtWbVrmV5ChvjHUzatyERCLf8poNr+FzJ08n/4v1gHQOzlFl0tIzmb+xsNO0Gt+y7ZzIs8ZCHBl15TyNF2JZtalkCCocjef1juJyYn7KunozBrWKSU+Awdny9cqmPKGEKGdGmE2nUtBaaaTarM1bVeYNx4jE4gw7/cST9d+dQ/N00kBPgZJoIG1QDGoFcVlmR6ud9fVW/v7KNhwGDXe+eyPhaJw/dU3xp65J4jJMuIOsWWKhZ9LDlCfErqUOPMEox8bc/ODZPh7eP8w/vn45N+5sRqM68/T0a9oqhVERXJoMzPj48I8TieuVNaZF3ahSvHhqlu89c4qdSx0EIzEuW1qxqISkLMvc90I/0bjMoREXn3zwAN96z0bMuvz/meJxmRlviJH5APfvGcQfihKMxnn+5MyiJikuZHuLnaOj7oJz3Y+Pu3PEE9VKiZYKI78ZXME7M+a7/1G5ikl3iLV1FibcoZxxsMUEGqPx7LxQPjSqxT0Jh6JxGh0G/CFvTjVXJjqVgq7x8tWLFxKXZULReJaKb6VJQ0uFkVhcTjc5FiMal7PyNhsbE17E0Jw/b06nyqTFpFOhVipochjY1z+X7kzJp+e1kJW1lrSBrLXosenVKBQSf7OlgWgszls31HHzg510jbv52Ud30egwEAjHeMt3nmfeH+HUtJellUb6Znw4/RG+9Nvj/PbQGL+6+YpFfHPZXNFWyV1/7kWjVBRVC7hUEEblFU4sLnNiwkP74Bzf/GNPeuiU6iz0hO54/HTeaoG7+gAAIABJREFU4i0blvDdG7aUPCYel/nj8Unufvpk1k3oT12TvP3u3Xz3hi2sSY5+HZjxcddTJ+kYcjI+HyScrEBqdOiZ90dKFhUUQquSWFVrIVYiL5Gid8pLtVnLlOf0tL+TU17amrdya/Vd1A3t4ZBjM53yUiAhvw75peHLueHl4y2qAZo79qFepK5Y75SXDQ1WZr2hvD0mDTY9bdVGnkkm+88VM94wM95EAl+SYFmVkUqTFlcgwslJT8FyYrNOxcoaM51Did9LLGlsWioMqJQKbHo1U54QlSYNz59MrHncFWRbs71gmDITk0bJ2npr+vwAvzk0xrDTj1qhYHQ+wIQ7yKpaMz9431aCkTgVJg1PdU3y+tU13HX9Zm79/XEiMZlgJIZRo0w3n3YOzTPlDlJp0uIJRvGGo4uaALm5yY5ercSoVb0ilCuEUXmFMu8P8/P9w/zkpcH0VL1Mzkb9NpPdvTP85x+6+PjrlhUcd/rSqVk+9fMDWXH6hWt5xz27ueVtazgy6ubh9uG8XsjwXIDWSgMKCVxlhHZSrK+3oFBInJjwFOy5yIcnFKXerk8blRS9U156qQf7O9naaMOc1ONSJeddLNZ/KlQZtsXbxZ29X0CZ0fW+GMOSGke7vt7CkaQhX1aVSK77wzFmz6AcOJN4CfkBWU7KpCQ73w0aJcurTWhUCuyDHbSNt7PHtJ4exzr0amVe47CwlL3KnF3+qyhjxkiVSUtMlvOG0xYOGzs25uaD/7uf+z+8k689foLhOT+vX13D8hoTHcn1pQRWM+VgvvpYF5IkoVEqODzq4pGPX4axzKIWjUrBa5dXll0OfrEjjMorjK5xNz9+cYBfHxwtmBeAxE2lkGLtYlhZY+b7z/bx5PFJfvHxy3EYsw3LU12T/NuvjhRNpqf0om75zbGSSd/+GT/NFQYUkpTuJi/Ehnor/kgMCYlDJaRLCtE94Sn6PXUMzVNt1tJWbaJ/xkezw4BaqWBjgxVZhmg8njPcK5NilWFvjHahXND1fibezuCsn9curyQcjWfdWBWSxNYmG8PJJPRiiS8y/OgPx9KTMv/r5OfRJIdk3bTiNtrLbEDtGEyUPbsCEaKxOIMlHo7USgmLXlX2LBlIPDRc+93dTLiDLK82IssyXePZwpcHh+fZ0WJnX9LjHZ4L0JHhBX3mkcPcfcPmsj2P79645ZJTIy6EMCqvEHb3zvCrzhF+2Vl+Ar7aojsro7KuzpKevHdq2se7vvcibdUm4rJMLC7jC8UKNlRmUmHSlhWOSjE466fRoUdCQqWUsm6IerWCVUssuPyR9PhYhQTr6i0oJQmtWslUcvRsuZRKnk55Qkx5QjgMamZ94azKqzqrjq1NNibdibzQQoqpAw817CTSf/8ZzWrJxB2MMu4K0juVnUMbnQ8wOh9gY4P1jIzKmWa0Mgsd5HiiAbVcowKJQo8dLY6y/m1taLClPYzFMOFOjTnW4AlFaa0wsnqJhb5pLyatillfmAPD82kNsilPgKtWVXP5sgo2NdpYW2ddVCjrlWJQQBiVSx5vKMrtj3XxwN4h1iVzEuXSPe5Go5QIL6Ik1KBWsLbeyuERV86I2r4ZH32LDKtt8XZxtfsEf9asZo9uZdnHpTqhVQoFerWCJVY9Bq2SExOenJBGXIajo25aKw30LyL8lUKbJ3naUmHAptdwcOT0tVoqjTmJ5TFXYh7NhnprXqNSqDKs0qThF4EGuotUNJVLnU2XY1Ay6Z7wsLnJxsGheept+rzrzMeZqDlvb7Hzq2NtvDM1zvcMjWWp+7VSgp1LK3jxVHlaaAXPo5CYcodoqzbx0w/v4NO/OMQ3r9vIZ395mD91TTHtCXHFskqi8Th/7p5i/P9v78zDG8uqA/+7b9FiyZIt7+WlXFWuKte+uau7GsJ0OuxL00B60g0MZEJCBvjykZD5QnqAAEkHPrICE6CTIUAIhG4CDQPNNL0G0lBU115de5XdZbtcLttly6skW9udP96TLMmS/GQbrOp+v+/Tp6er9/SOnp7uufecc8+ZnGUyEkNTFXY2+y2Z515sCPnLyvNdhnR1dcmjR4+uthgrxvNPPMbBR77LE1onx71bllQpcGtTJReHCjtQM1lT5SKZnB/FLZdbIxf4l4sfycqUW2rHucbvYm1NBUf7xhc1nemqYFdL4fUnhbilvTo9k9rTVsXozBxXzVXZ+9YanfG+9kDR0FxFwL611fSPhRcU3crnU3mH7xr+Sz/niG8nR0oYxefj1nUBS0kfKxwq4WiCzsZKItHEomtdOhu99IyEiJVgBksFPhTLMGCFTNNTLrtbq+gbMyo/7mzxk0hKLgxNMRcvra9z6woOTeXBd+7jwIYaAEJzcTxOjY99/0w6B9imBm/esPQvv6uLV24tv3x5S0EIcUxK2WVlX3umchMSmovzja8+xLu+9x62yjjvNjvkC47tVDpVpucKV9rL5dz1aTobK6noPrzgT+7WFTbUefE4tXTxoVRkz0qwb/J5dHMV9FJ8BpUujdpKJ7+wsFYlUKHjdenElxCGPBtLoAijKuTZwamskOFjfRM4NGXRtR5JM0fVLe3VC5RKbmTYy2OX+Oh/3o8jGSM2pPM/tn+GizXbaKmuKMlMmGJw0trMI2xGM10YmmZXq5+r49nZe9fVeqj1OhBCIKURQbe50RiUWMXj1GB6bsnRcCny/Y5eh8rmJl+WuSs1c3x75TU6h4/xtGMLP9U2WjrHliYfx/sneO7KGF3t1eiqkna+/9euVh45PkAomkApMG3aUO8t9Wu9KLCVyk3IJ35wlrpDT+a1xWemWbdK2/ApPtf90fS6i9SMwaGpWSau/esCjM4sP1dTikPeHURNM0gqpYlVutZWc/76VDrCaTE2NVQSTSSz0oxbYWO9l5m5ONvW+NFUsWANCpC3rRDH+saLjrIB3pC8lFa2MhnjlunnuVy3gyO942xq8HJ5eMayP2OpwRinrk5S63XQ4HPhdWoMTkS4MhpaMBO+ODRNR70Xl6YsMIfmw62vjO8gd1ba2VhJMBRNK5TMmRDAx04YwRD3KDoff9ln6andWdDX0uR3Ul/pSiukzz51ma//oo+7dq3hnbe10VFfyY4WPw/+t338zteOEEskafK7sgqn3bouQGu19bDiFxOWf2EhhCqEOCGEeNR8fY8Q4qwQIimE6MrZ934hRLcQ4qIQ4jUZ7fuEEKfN9z4vTE+WEMIphHjYbH9OCNGeccy7hRCXzce7M9rXmfteNo+9+QsRWGRLky9vZlswOq21gdJu5o7rR9HNKCPdVFCqgEgse8ZzrG+cPStYqz5VnOp0fRefaP7dkkaus7FE0drnuQxPz+K89ByvP/u1ollsU2xf42NHs5/uGzNcGQ1z+tokZ65NLohuK5WkhMO94/jc8+O5vTPn09l1K3SFHymbSKh6+rd9Wt/CtXFjtnFpeIbNjUYRp/3tAVoW6bjql5EGZ3QmikNTeO5KkKvjEfbOnOeB/i/wQP8Xsq5h98gMAxMR/G4t67vko9SFnIWYixu/va4I9rcHuDA0nQ42yMwU/c3LH+GtwWfSAzA9GaO25xeoeWYXLk3h1nUBRmeiC0LPg6EoPzw1yNRsnKNmgMCvbazjb+7ZRc+NUDqC8VVbG/ju+27n4d8/sKy1YDczpcxUPgicB1Le4DPAW4F/zNxJCLEVuBfYBqwBnhJCbJJSJoAvAe8FDgH/D3gt8BjwHmBcStkhhLgX+AzwW0KIAPBxoAsj2OSYEOIHUspxc5+/l1I+JIR40PyML5V6AW5Ghg79B7fNnOaTLb9HIDGdZbLaNXWOt4W7+W68w3Infci7g4SqQyKWVlBd7Qvt8Imk5NTABNvX+CyNSgvh1hWaq93cEr7Ix09+GS0Zo1OcTBenKobHobKu1oPHqVFdoS8aVpyiuvcYX+n+KI6MUrL5zlXjceB2qHm/n64qTC4hM28+UuV5D8xe5CtmSHFC1fnwLX/HwcAO7tvwQEGfw4WhaZyawuHeIJvqvQts+pkL80qtd5JL0DR37p05z7cu/y8c0rje94w+yX2bPp2WbSIc4zdkD1/s+ShqYn7G21u/k2DGb6StkOM6YobES1gQBZYbUQcsCIZQc+Z6e9v8XB2fLep7iiclb/vSQaSEd9zaxp+9aStv3t3M2EyUzz19mS+8fQ8v31i3It/vZsaSUhFCtABvAP4S+BCAlPK8+V7u7m8GHpJSzgFXhBDdwH4hRC/gk1L+wjzu68DdGErlzcAnzOO/A/yDOYt5DfCklDJoHvMk8FohxEPAncDbzWP+xTz+Ra9UBg4+zYeeej9aMkZCqPx7zSvT76ULUMk4b7Xo+HbrCuGO/fxV2xdYf/0IJ6p2E/Jt5WKBP1dSGiNYpyZKdnxuaarEoSqcHZyieyTEq4cOZlUfvEu5jNp+O8f6xrNs+boi2NlaxfRsjMsjM+kO32pYKZgdTSqLbQH/jaYYJXkvF4iUSkqJU1MIF1n/Y5WekRl2tfrZd/T5+TxiiRhr+p9jpHEtI4v4HOZMk5tTVzh9bYpta3x4nRpSSk5cNeq9CFG8tO9iqIL0wlmjo46lswVoJBZcw83Dx1ATxu8pzN/zR/W3kxyZSdd/V1dIqaypcnO8P39wRm5E3SOBO3kkcGeWkt5r3mDVFTprazwIIRZNTJrKRgHwzef6uTIa4t9+7zZ+5+XruHtPc9YsNjQX54MPnWBXSxV372lOl65+KWB1pvJZ4E8AK8WTmzFmIikGzLaYuZ3bnjrmKoCUMi6EmARqMttzjqkBJqSU8TyflYUQ4r0YsyPa2tosiF9+zMYSfO/ENf796FVuO/x1PmR2xKpMct/oj3nb2NPp3FKpAlS6jPMmLnGcwh3TpgYvA+MRLgxNc4FGcL4JIkCkuON1aGrWckRRCgFUOrUsX0Ju9cEfJDdyvHecdbUeovEkAY9OhUPj0vB0Xvt371iIQIWDoIXZQ77Q3RqPg3gyScDjpM7rIBxLZKWQyUUVAt2iUlksuqnJ7+LU1UlUz3Y+YPqVrNR3yaXCYfyFMzMc7505z/5rxrm7tt2+INptX1t11kK9QrQE3OkEkcb109MzlTjqAllzr/EPkhuRSYlDVdjbVoWuKsSTkt2tVVwZDbGu1oMiYHAiwlCBbAu5BCp0mqrcRe+9QrnWMn+HvrEQe1qreGE0xMmrE+xuLX01+7ra+dT/uWbRgz1jZnLKEf72yUv8wZ0d/PGrN3Oif5wHf9pDaC6BS1dwaipOXeFPX9dJfeXSTZXlxKJKRQjxRmBESnlMCHGHhc/MNxSRRdqXckyxz8pulPKfgH8CI6Q43z7lzIn+cf7426fm1394d5AQKoo00rWryPTIO/dP/UM20bW2mjPXJpk1R7ZNfidNfjeaqnD22mQ64qdUjvaNU+t1FI0GC1Q46GjwMj0bY8/0BXZcfJS2ql18J9IKGH/yP7v9czT0HuKgZ3v6T59yBudLL5PJyPQc6+s8RGJxIot09KmO5jf1Hs7W7WFY2UAimmBmLkE4GrYUir2hzpteUFmM+VXyMWSqFHDd67L2SUURFUs2aYXcKKjcFfqfbPw86rqdxJMSRcDw1Cz949Yc9wGPM61Ujnu3cN/GT/He4e/SEAvycM2rLBUb25WU6cWhuaTSnDRXuemo89C9yKr37c0+Bidmi5YMzpSl0LWsrtBpCVRkJQu9ODyTt/xAIfa0VbGhzptWjmDMTg72jNHZWMn6uuxaMw8d7ufi0DRPnBvO+3n71lbzjlutlTYod6zMVF4G3CWEeD3gAnxCiG9IKd9ZYP8BoDXjdQswaLa35GnPPGZACKEBfiBott+Rc8xPgFGgSgihmbOVzM960TAXT/C2Lx1kQfRkxtqiOCI98s7bQfWN01LtZjaWYF2Nh57RUMFqfqWQSEo21HkZnQlS6VRRVSVt4gDDNHV6cJLDV4LsnTnPx8yO7m5FZ3rvX/N4cj27Wvx8/7og1tBa5EzFeeFGiO3NPs4NTi28Tjkc924h2XqAy8PT1HgVJsJGtI5VtepyWHMyp0xFGhJplgLO9RdlKoPlhNdmmmTmzz3vTwh0H+Shmfm/3Y5mH/3BKUtrmhx5HM3/Zeo4uozTGenN6wPL/S4JC+vgrk1EWOMvPErXFNi3trSZcSFSs6TctD2RaIIXLKZySUVYnh2c4hWb6jh/fYoHf9LNE+dGiMQS/O09u7hr9xo0RaR/59ZARUGFAvDE2eEXjVJZNDxBSnm/lLJFStmO4YB/pohCAfgBcK8Z0bUO2AgcllJeB6aFELeZ/pJ3Af8345hUZNdvmueQwOPAq4UQ1UKIauDVwOPme/9h7ot5bOqzXjQ4NTVt3khx28xpVJIoQALBqbouPrz/75nbtB/IrsGdYmA8wlwsyZG+cRp9C+uqL4UmvxNVCNprKpieS7ChzojJb65y0dlYyeHeIBFzFpTZ0enJGHvHT7Gj2c/ZwaklF3jK5My1KfbkKbSUia4I9q8LcGpgglA0QX8JlRHBWLwYjlpLYnnIuwOJkp5SC5LcNnM6a59SQ5sLkTubKxQVmOL0tSmEsBbaOxvPVrdZv6M5O14Mq+UJBidn2dniZ1ND9tqO5ioXa2s8y1YolS7NyBpwdWKBIk5Rn5GsslAU262mQnFoCn/4yo385Y/O8brPPcul4Zl0tGR/MIyuKqytmfejDIwbyVDzsXfmPDue/SKhk88u6zuWC0tepyKEeAvwv4E64EdCiJNSytdIKc8KIb4NnAPiwAfMyC+A9wFfA9wYDvrHzPZ/Bv7VdOoHMZQXUsqgEOIvgCPmfn+ectoDHwYeEkI8AJwwP+NFxUQ4umANRK6J63vbfpceb2fRNBwwX6TIW6BuSSlsqPPQHwxzfXI+BcbzAxPsaa3i7PWpBanWc2V+XO3E7VCXtBCxEMf6xgv6edbVVJCE9KypVFPT5oZK+sZCRf0tmeSWAo6JbF+JImDAogmqGA2VzrwLKRczpzk1a3U7xnJMm8WKjRWilJo3qTVHd2yuo28sTKBC59zQNJHo8jI4pFLk5KbvyaXR76IvGC6Y5DN1fzk0hS+9Yy+f/OE5Q4EogsnZGLeuC7C5sZI7O+t55PgA62uNvGD1lQ4EMJLHb5R5LvGRh+Gvn4GtB5b1fVcbO01LGfOVn13hzx89t6B9qSkuHKpgV2uV5YigfOfZ1eLn+uQsisCyc7XQZ21t8nFuGUWi8pFZX6NCV9jeXMXlkWkSUtJx43TBjMDFaAu46Q9ar2iYotDvtLbGXVKFxEJkprQvFSGMtTiFjm/yu6iqcCwo4lXqvZcqaFUKt64LcLx/nNZABXOxRHqQ0hZwU+nSODtobQV/vhX2hUiVNQZ4/9C3+dDgN9BIEkfh79a8k6O3/X46yORNu9awf12Aj33/DDBvDnvttgZ6x8JcMDMM3NlZT2guTl8wzNBkfsWYea6EUFF/+y/gvvstfb9fJXaalpuRc7+AUz+BXXdw3NvJF57p5khvMOtmT5Fpt97SVInPpZOUEonhbpFmluCkBK9TI5pIcm5wingyf1nYfOSO1n5/x2eIdOxPH7++zoOuRC3nfcrnNxidmSs5oeWi5+kfZ3eLnxfGQmxp9KVnLl3t1ey/UjgjcD6a/C5aq91FV78XlaWAr6QmwwG+HHJNo6UgJQxOzBYMDa/1OrgwNE2t18HaGk86FPj6xF4eU7ss55iLJZcWfh1LSF64EcKtq2xq8BKJJugPRrilvbiZM8W2NT5GpucsZyjOjN7KnZFNdLws6x6Yno3x+acvp19HTTNhMBxLKxRFwNBkJF32oLPRy4WhhdaE3HOp23+Nm30Vt61UVplnLgxz6IeP8sfPfAAtESOm6DzQ8UC6M2ookni4o85wtharm6KrAk0Ri0ZHZRLwOPjNuZ50ByxknNcPPUn/6AkS5gj1hRshutqrObqMdRAj03OWK/dZktuMNjtjRrVlmsKO9Y3T1Lqf2PWHLJlvOuo8pqlQ5DUzlULu6H6l0pwv13w4Foqyu9XPyRyn9c5mfzrKLbOSI4DL9MVsaarkfJE6MWkZlzBgyDSeRGIJvD1HeKV5/YS4veixbjOL9nLuy0wTYrDjdh4OZa9W+OmlG1kypkzUqZT8sWSS7uHprDo6XqeOIlgQTJJrrvwDxyZ+fcmSlwe2UllFLg9P875vHOc9A/+JmpjP9ZQ5gr48MsO+tdWcHpggmpB01HsNp/Fcghsz0aIKBYwR37Y1/qwqdYVQBexdW82xvnG+E9vAW8wRVEKovGXkCVSSWWajo73jeeuIl8JyHNY+t0ZHnRddVQhHE1wcni6Y90xKOF+9fVF/gyqgqz3Akd5gVgfg1BQafC6qKnRcumo5v9remfN869L9aCSIo/LBrr/jWnRlKvwVcjgXkyX3uzvUbN+KrgpGZgor0NT9dmU0xI5mPw5NYSoSIyklV4NhogmJ16kyYyY1jSVKn6kk5Pwx+UKkD2cFkc7T2VjJRDhWskJZX+vhWF/273ncu4XklgM8PzCxIMV/7uvMdEaFFuMe7RtHCCPTt9epZWVAyJzRPnF2mF/fXF+S/OWGrVRWibl4gg8+dJK5eHJRB+ixvnHaayqo9To5OzhFJJZACOv1LKyuYs5M3545gloTvcG9o4/nNRudvz5Na8CdTgVfKi+MhgqmDs9He00F9T4XE+Eo3SMzJSm07pEZurbdzhf78pu8GiqdeF1aXmf/XDxJfzBMf9Ao2qWrglhCcuu6AFIaZp6+sfACJfnW4DM4iCMAhTiv6HuMv3EvL5V9isXW8WRSyPksc5Z37WmrtqQwZ2NJTues2XE7VHY0egmGomysdzIbTzBgMcouU+ElkvOO6twQ6Y3Xj0JltlJxaoKdLdZ9hbnUVjoX+H12NPs4c21y0TB1wPJar5TJ0evUaPI7uT6Zf+1OIilXLPPAamArlVXis09dTjuprUTsjEzNEokl0qOiUuIrFosMS5FbHjY1gto7c563jT2dV+lFYgmQhtmhFBNbJt5Fank3+px0Nvo4MzhJ71h4WdUqL4/MUOnSmJ7NDg/2OFRqK52WFr9NRmLsbq3CoYoFCqjJ72JNldsMZJiF/uxjdVVZkXDiUk1yhSpMZs52Gn1OSzPaQkSiibSiSSk8h7p455ir8D5W91lOYmS/yB1w/ad7W9axmxq8hOYSS1YoDT4nPTn/j21rKrkwNG3ZvDg1ay3UPMXMXJyZufiCaEWXrvDbt6/l5NVx9q0NlPSZ5YStVFaJMzmjvGIL4Bp9Tpy6St8SO9PJSIzNDZXp0r+ZdNR5CHidDE5ECvo2FlN6V8cjWYWsSuX0tUkCFXpW4sEUrQE31ydmOXRljKpFyvpaYTISyyvrpobKrBXWxbilvZr+YJjhPNFv1ydns1Kg/7jpVdwz9hSajBMXGs9teIORCmeZ1PtKUyqFZsOZIb/1PldJEX1WsBKEkavwtt04wXe8hlLJvPeO+ndx2KyFoquCPW3VHOkNLqkCZYq2QHaNms5GLz03QpbXTymC9HqsUsg3kNrUUMmbdzfzr7/os5WKzeLEE0m6b8wwPDXH8NQs2sVDvH/kuKXQzLYaT8k1UnKpqpjvkCt0hR0tRt6j7hshsLCSeLFV32cHp5aUZDJQodNW40lHqcXiSXRNITQXJ5pIMhmOEU9K4klJoNa5Ip3e0b7xdG1xMAITrKRfSXGif8LyKPZn+ibu2/iptELWG3dDkd/Sasiu2+Lq/hSFBgY1Xic9N0LsaPZZrk1jFaempBNfFiNX4R2r2mWscDM57d9KcO0+esfCBDwOdqzx8cJYaFn/idR1DnoOcMRMANJR76U/GFnUT5mJ3209U3YmkWh8wSy3s7ESl65S73MipSypxn05YSuVXxLReJKjvUGO9I5zpDfI8f7xtO21kH07Hztb/PQvw9yTYnAyggD2tVfTMzKz4IZebnnXcDTBvrVVHOvLHu3n+9y7HX3cMvU8P3Vv44nweoJhazOEc9enViRaTEpQhKC12k1tpZNovLTiXVUVekkVMDMV8p4inWwp98US/N95BwbH+8bZtsbH2Aqt8M/EpauWlEquwtNb92Yp3j1t1YTn4qiKoKPey7Pdo5Z8HYXIus5DD3Op4wHG27sYnpwtOReex6EtSankmwh1Nhqhni/rqKU/GGZtjWfhTjcBtlJZYa5NRPjqz67wvRPXCv5RC9m3M2muclPt0Vds9Hg1GGHHGl/eyJhSOrNihHLKGO+dOc83zYqScUXnky//PJFYnE8f+iP0EtLzZ3JxaJqAx5FWAktVhpdHZvj1zXX8x8Ublo9JUet1FlQq9ZVOKl0aL9wI5c1wOjJdeHW4lfsixZSFyK/NDV62TJylpf9wVsLOTAIeB25d5XpsaYlFi+HQrIdOpxSex6Gi5iy4PHV1HFUIGn0uBLC+zkvPiPXql7lkXedkjN9y9PCp0M501olSqHAurehYKtAjk06z+Fqt17ksk95qYyuVJSKlpG8sTDwpjTDM6TlOXZ3gn392Jcumno9i0V4pW/GJ/vGSonus4CiQ76mUzqwYF4amaa5yZ9XgcKRq0CdjVHf/nGpY1rmm5+LsqvMTDEV5i7OPT51YujK00jHnw1fAtyMwckz13AjlLWTmdqgLUthkUkoalAEL90bDtRN8+vSH0WWcD+S5PsbC0yRH+8bZvy5AOBq3nIrGCs4SlEqKbc3+BWYtw6QqCU9E0vdWhUNle559rZB7nU/V7GFyemn3wlK+Ixi56GIJmWWGTVX0BDh9bYI7OxuW9Nmrja1USiSeSPLjs0N8+dkrS46UKeb43t1atWz/SSFE3ooBS8vpVIiW6nmlcsi7g5iiI5OxrM9d7rlODUyyu7WKjc8/smQF5dQE4SWOzs8PTlJX6VxQ1Eli+CjC0QSjedZ6tFVX5A2WSGE1Db7VyK/toyeKXp+W6op0KO3561Nsbqiko97DRDhWknmvELqFyK9MfG6NsxZ9W+FogsO/+MytAAAKcElEQVRXgksyh+Ze57CnE6atpX7JJV8mZyuoqgKxJHPxJG/Z08zPu0ep8c4ntDzeN8Ge1mqql1m+ejWwlYpF5uIJvn6wj68d7F2RGUQ++7YiKDl7bikMTeUfJS+3pkcm3SMzCIw0GaHEft7OwrK4yz1XoMLByasTbG65hVjvNywpqH1t1UgkwVCUWq+T8XDU0orwfEzPJdi6xp+3UmCxAUFmXfpCpO6Llio3FLjPrEZ+LTZYqM4I3piejXO0bxy/W2d2hUxhmlJah5uZVieXXDNn6vUN9wGOUnrphNR1rnSq6MvIlqBrClUVOh6Hhtep4XGqeJya8dqlUenSqHRqVLp0vC5jn0qXhsdp7u/QaPA7FyTe/J+v2bxkmVYbW6lY5KlzI1ybiHDH5jqi8STRRJJoPEksYYw2EmaEUiLnEYklLOdJ2tVatWgm1eUwMB5ZELrbUe9hOhLnOEuv6ZHJWCjKbesDHHrB7BzyOYeXUT8EIBiOckt7Nb2i2pKCuqW9mucHJtNO4+Wsc0lhNQ1+Jlbt5B313qKlba1Gfi02WNDyjLJLXaVfjFLS0QQqdE4N5L/3F6yqb/k9Pj7wf9BlnPjQw/RkpDXKh0NTcKoKzlSlRU0x2jSF9loP46EoXpdGhUPD4zCVglOjwjGvICqcKl6zzXg2lIPLQrbnlxq2UrHIG3Y28YadTUs69mhvkEvDM4SjcSPhI9J8Juu1S1f4tY11xBJJ4okksYQklshWWMZzknjCeB1LSpKZikzO75NIYj7Pv7e+1kvvWAhdVdjUUEnvaIiudh+9YyGSZjLKlExJadRlV4Xgy+/uwq2r6QWYs7Eks7GE+UgyF0/QPxbm0dPX5xXKL5HU2oJbtt/OF3uLK6hEUlqKQiqF3MWTVrASYdYacONzaXSPzKApkE/sUiK/iinwmSU4pkvB6qpwh6awvdnPxeFpGnwqbl3Fqau4dQWXrnL3hcdwYJTJFiR4r3ISJ3EUkigyzgMbxuh91V48Dg2fy5gVpJ6dmoJyE69OvxmxlcqvgK72AF3tN+9iJqv0jYXY3uxnIhJlIhxjIhxjMmI8JsJRJsztyXCMiUispFobhTgzOEWjr/j6laWEfC6GlXokmWiKoD+4+IzVpakc75/AoSk0+Zz05Ul/s9QAg0x2tfoXVD+0iltX8bt1fG4Nn0un0qXhc+sLtgMeHU0xFINLn392aipuh6E8XLq6uPI5l4BzX4FYFFV3sP7ud8GDxyEWRdEdbH3NXWzdurQBn83KYysVmxVjbY3Hcmy9lIZpMDUbiyWSxOKSWHJ+O2q2x819Uq9T78+ZJkhNEQRDUebixoxpLp5kzpw9Rc22Rp+LcDSenmlFokki0Tiz8aRRNqBE/bZYuKyuCnRVST/aAm5uzMyhCoGqpB4KqkK6rdbrxO/WObChBoDZaIL964xz6aqSNtsoQjA1GyMSTSAlqKpIf4YiBKoCipLbZjx0VVDldlDjNRzAQoAQRgjH/LMR1KEYZSuNGYBbx+82lMZKZVm2zNYD8Jmn06Uh2HoA1u3Ifm1TNthKxWZVEEIsqx7ILwsp5bwZkGzzJJBWQImkZDISQzE7akeGAtFVcdOuhi5bth7IVh65r23KhvL7V9vYrCJCCIxI2MWVQqH1KjY2L2V+xfNYGxsbG5sXM7ZSsbGxsbFZMWylYmNjY2OzYthKxcbGxsZmxbCVio2NjY3NiiHkzZxjuUSEEDeAvlU6fS0wukrntoot48pgy7h8yl0+eGnJuFZKWWdlx5eUUllNhBBHpZRdqy1HMWwZVwZbxuVT7vKBLWMhbPOXjY2Njc2KYSsVGxsbG5sVw1Yqvzr+abUFsIAt48pgy7h8yl0+sGXMi+1TsbGxsbFZMeyZio2NjY3NimErFRsbGxublcOo9Gc/Cj0AF3AYOAWcBT5ptgeAJ4HL5nN1xjH3A93AReA1Ge37gNPme59n3vzoBB42258D2jOOebd5jsvAu4vIqQIngEfLUT5z317z808CR8tNTqAK+A5wATgPHCgz+Tab1y71mAL+sJxkNPf7I4z/yhngWxj/oXKT8YOmfGeBPyyHexH4CjACnMloW22Z1pn7XjaPdSzaZ5bSwb4UHxg50L3mtm5e4NuAvwL+1Gz/U+Az5vZWDAXkNH+QHkA13zuM0VEJ4DHgdWb7+4EHze17gYczbqgXzOdqc7u6gJwfAv6NeaVSVvKZ+/cCtTltZSMn8C/A75rbDgwlUzby5ciqAkPA2nKSEWgGrgBu8/W3gd8uMxm3YyiUCozyH08BG1dbRuAVwF6ylcpqy/Rt4F5z+0HgfYv2mavVWd+MD/MmPA7cijE6aDLbm4CL5vb9wP0Zxzxu/sBNwIWM9vuAf8zcx9zWMFbAisx9zPf+Ebgvj1wtwNPAncwrlbKRL+P9XhYqlbKQE/BhdIaiHOXLcy1fDfy83GTEUCpXMTooDXjUlLWcZLwH+HLG648Bf1IOMgLtZCuVVZPJfG8U0Mz2A8Djxe5LKaXtU7GCEEIVQpzEmJo+KaV8DmiQUl4HMJ/rzd1Tf6oUA2Zbs7md2551jJQyDkwCNUU+K5fPYvwpkhlt5SRfCgk8IYQ4JoR4b5nJuR64AXxVCHFCCPFlIYSnjOTL5V4M0xLlJKOU8hrwN0A/cB2YlFI+UU4yYsxSXiGEqBFCVACvB1rLTMYUqylTDTBh7mtFVsB21FtCSpmQUu7GmBHsF0JsL7J7vpKBskj7Uo8xDhTijcCIlPJYEZlWTb4cXial3Au8DviAEOIVZSSnhmF6+JKUcg8QwjA3lIt88ycWwgHcBfx7EflWRUYhRDXwZgyTzBrAI4R4ZznJKKU8D3wGw0fxYwwzUjx3v9WU0QK/CpmWJKutVEpASjkB/AR4LTAshGgCMJ9HzN0GMEY9KVqAQbO9JU971jFCCA3wA8Ein5XJy4C7hBC9wEPAnUKIb5SRfGmklIPm8wjwPWB/Gck5AAyYs1AwHPZ7y0i+TF4HHJdSDpuvy0nGVwJXpJQ3pJQx4BHg9jKTESnlP0sp90opX2Eee7ncZDRZTZlGgSpzXyuyGixmH3upP4A6oMrcdgPPAm8E/ppsB9pfmdvbyHagvcC8A+0IhpM/5UB7vdn+AbIdaN82twMYdv5q83EFCBSR9Q7mfSplJR/gASoztg9iKOeykdP8bTeb258wZSsb+TLkfAj47xmvy0ZGDH/jWQz/o8AIfviDcpLR3LfefG7DiParLgcZWehTWVWZMGbDmY769y/aZ65GR30zPYCdGKG6z2PYYv/MbK/BcI5fNp8zb4yPYERjXMSMvDDbu8zP6AH+gflQP5f543VjRG6szzjmd8z2bjI6kgKy3sG8Uikr+TB8FqeYD83+SLnJCewGjpq/9ffNP1jZyGfuVwGMAf6MtnKT8ZMYHfUZ4F8xOr5yk/FZ4BzG/fgb5XAdMXxk14EYxuzhPWUg03pz327zWOdifaadpsXGxsbGZsWwfSo2NjY2NiuGrVRsbGxsbFYMW6nY2NjY2KwYtlKxsbGxsVkxbKViY2NjY7Ni2ErFxsbGxmbFsJWKjY2Njc2K8f8BxhOurnQSBSQAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "df.plot()\n", + "plt.scatter(*pattern.T, color='orangered', marker='.')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And, let's check if there are 100 points:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(100, 2)" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pattern.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Yep! So, next to simulate a set of realizations in the same manner, we can use the `size` argument again, just like the `numpy.random` simulators. This means that, to simulate $K$ realizations of a pattern of size $N$, then we use `simulate(hull, size=(N,K)`. For just one realization, we can use `size=N`. " + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [], + "source": [ + "random_realizations = ripley.simulate(state, size=(100,100))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To show the random pattern is truly random, we can visualize all of the points:" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "df.plot(facecolor='none', edgecolor='k')\n", + "plt.scatter(*random_realizations.T, marker='.', s=2)\n", + "plt.scatter(*pattern.T, color='orangered', marker='.')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now compute the `G` function for the observed pattern as well as all the realizations we just made. " + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "observed_g = ripley.g_function(pattern)\n", + "comparison_g = [ripley.g_function(realization, support=observed_g[0]) \n", + " for realization in random_realizations]" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(*observed_g, color='orangered')\n", + "[plt.plot(*comparison, color='k', alpha=.01) \n", + " for comparison in comparison_g]\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All other functions work identically!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Analysis", + "language": "python", + "name": "ana" + }, + "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.6" + }, + "widgets": { + "state": {}, + "version": "1.1.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/pointpats/_deprecated_distance_statistics.py b/pointpats/_deprecated_distance_statistics.py new file mode 100644 index 0000000..085c323 --- /dev/null +++ b/pointpats/_deprecated_distance_statistics.py @@ -0,0 +1,1097 @@ +""" +Distance statistics for planar point patterns + +""" +__author__ = "Serge Rey sjsrey@gmail.com" +__all__ = [ + "DStatistic", + "G", + "F", + "J", + "K", + "L", + "Envelopes", + "Genv", + "Fenv", + "Jenv", + "Kenv", + "Lenv", +] + +from .process import PoissonPointProcess as csr +import numpy as np +from matplotlib import pyplot as plt +import warnings + + +class DStatistic(object): + """ + Abstract Base Class for distance statistics. + + Parameters + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + + Attributes + ---------- + d : array + The distance domain sequence. + + """ + + def __init__(self, name): + self.name = name + warnings.warn( + f"This class is deprecated! Please use the alternative function" + f" {name.lower()} in pointpats.distance_statistics.", + DeprecationWarning, + stacklevel=2, + ) + + def plot(self, qq=False): + """ + Plot the distance function + + Parameters + ---------- + qq: Boolean + If False the statistic is plotted against distance. If Frue, the + quantile-quantile plot is generated, observed vs. CSR. + """ + + # assuming mpl + x = self.d + if qq: + plt.plot(self.ev, self._stat) + plt.plot(self.ev, self.ev) + else: + plt.plot(x, self._stat, label="{}".format(self.name)) + plt.ylabel("{}(d)".format(self.name)) + plt.xlabel("d") + plt.plot(x, self.ev, label="CSR") + plt.title("{} distance function".format(self.name)) + + +class G(DStatistic): + """ + Estimates the nearest neighbor distance distribution function G for a + point pattern. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intervals : int + The length of distance domain sequence. + dmin : float + The minimum of the distance domain. + dmax : float + The maximum of the distance domain. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + + Attributes + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + d : array + The distance domain sequence. + G : array + The cumulative nearest neighbor distance distribution over d. + + Notes + ----- + In the analysis of planar point processes, the estimate of :math:`G` is + typically compared to the value expected from a completely spatial + random (CSR) process given as: + + .. math:: + + G(d) = 1 - e^{-\lambda \pi d^2} + + where :math:`\lambda` is the intensity (points per unit area) of the point + process and :math:`d` is distance. + + For a clustered pattern, the empirical function will be above the + expectation, while for a uniform pattern the empirical function falls below + the expectation. + + """ + + def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None): + res = _g(pp, intervals, dmin, dmax, d) + self.d = res[:, 0] + self.G = self._stat = res[:, 1] + self.ev = 1 - np.exp(-pp.lambda_window * np.pi * self.d * self.d) + self.pp = pp + super(G, self).__init__(name="G") + + +class F(DStatistic): + """ + Estimates the empty space distribution function for a point pattern: F(d). + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intervals : int + The length of distance domain sequence. + dmin : float + The minimum of the distance domain. + dmax : float + The maximum of the distance domain. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + + Attributes + ---------- + d : array + The distance domain sequence. + G : array + The cumulative empty space nearest event distance distribution + over d. + + Notes + ----- + In the analysis of planar point processes, the estimate of :math:`F` is + typically compared to the value expected from a process that displays + complete spatial randomness (CSR): + + .. math:: + + F(d) = 1 - e^{-\lambda \pi d^2} + + where :math:`\lambda` is the intensity (points per unit area) of the point + process and :math:`d` is distance. + + The expectation is identical to the expectation for the :class:`G` function + for a CSR process. However, for a clustered pattern, the empirical G + function will be below the expectation, while for a uniform pattern the + empirical function falls above the expectation. + + """ + + def __init__(self, pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): + res = _f(pp, n, intervals, dmin, dmax, d) + self.d = res[:, 0] + self.F = self._stat = res[:, 1] + self.ev = 1 - np.exp(-pp.lambda_window * np.pi * self.d * self.d) + super(F, self).__init__(name="F") + + +class J(DStatistic): + """ + Estimates the J function for a point pattern :cite:`VanLieshout1996` + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intervals : int + The length of distance domain sequence. + dmin : float + The minimum of the distance domain. + dmax : float + The maximum of the distance domain. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + + Attributes + ---------- + d : array + The distance domain sequence. + j : array + F function over d. + + Notes + ----- + + The :math:`J` function is a ratio of the hazard functions defined for + :math:`G` and :math:`F`: + + .. math:: + + J(d) = \\frac{1-G(d) }{1-F(d)} + + where :math:`G(d)` is the nearest neighbor distance distribution function + (see :class:`G`) + and :math:`F(d)` is the empty space function (see :class:`F`). + + For a CSR process the J function equals 1. Empirical values larger than 1 + are indicative of uniformity, while values below 1 suggest clustering. + + + """ + + def __init__(self, pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): + res = _j(pp, n, intervals, dmin, dmax, d) + self.d = res[:, 0] + self.j = self._stat = res[:, 1] + self.ev = self.j / self.j + super(J, self).__init__(name="J") + + +class K(DStatistic): + """ + Estimates the K function for a point pattern. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intervals : int + The length of distance domain sequence. + dmin : float + The minimum of the distance domain. + dmax : float + The maximum of the distance domain. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + + Attributes + ---------- + d : array + The distance domain sequence. + k : array + K function over d. + + Notes + ----- + + The :math:`K` function is estimated using + + .. math:: + + \\hat{K}(h) = \\frac{a}{n (n-1)} \\sum_{i} \\sum_{j \\ne i} I(d_{i,j} \\le h) + + where :math:`a` is the area of the window, :math:`n` the number of event points, and :math:`I(d_{i,j} \le h)` is an indicator function returning 1 when points i and j are separated by a distance of :math:`h` or less, 0 otherwise. + + """ + + def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None): + res = _k(pp, intervals, dmin, dmax, d) + self.d = res[:, 0] + self.k = self._stat = res[:, 1] + self.ev = np.pi * self.d * self.d + super(K, self).__init__(name="K") + + +class L(DStatistic): + """ + Estimates the :math:`L` function for a point pattern :cite:`Sullivan2010`. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intervals : int + The length of distance domain sequence. + dmin : float + The minimum of the distance domain. + dmax : float + The maximum of the distance domain. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + + Attributes + ---------- + d : array + The distance domain sequence. + l : array + L function over d. + + Notes + ----- + + In the analysis of planar point processes, the :math:`L` function + is a scaled version of :math:`K` function. Its estimate is also + typically compared to the value expected from a process that displays + complete spatial randomness (CSR): + + .. math:: + + L(d) = \\sqrt{\\frac{K(d)}{\\pi}}-d + + where :math:`K(d)` is the estimator for the :math:`K` function + and :math:`d` is distance. + + The expectation under the null of CSR is 0 (a horizonal line at 0). + For a clustered pattern, the empirical :math:`L` + function will be above the expectation, while for a uniform pattern the + empirical function falls below the expectation. + + """ + + def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None): + res = _l(pp, intervals, dmin, dmax, d) + self.d = res[:, 0] + self.l = self._stat = res[:, 1] + super(L, self).__init__(name="L") + + def plot(self): + # assuming mpl + x = self.d + plt.plot(x, self._stat, label="{}".format(self.name)) + plt.ylabel("{}(d)".format(self.name)) + plt.xlabel("d") + plt.title("{} distance function".format(self.name)) + + +def _g(pp, intervals=10, dmin=0.0, dmax=None, d=None): + """ + Estimate the nearest neighbor distances function G. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intevals : int + Number of intervals to evaluate F over. + dmin : float + Lower limit of distance range. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be set + to maximum nearest neighor distance. + d : sequence + The distance domain sequence. If d is specified, intervals, dmin + and dmax are ignored. + + Returns + ------- + : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the point pattern. The second + column is the cumulative nearest neighbor distance distribution. + + Notes + ----- + See :class:`G`. + + """ + if d is None: + w = pp.max_nnd / intervals + if dmax: + w = dmax / intervals + d = [w * i for i in range(intervals + 2)] + cdf = [0] * len(d) + for i, d_i in enumerate(d): + smaller = [nndi for nndi in pp.nnd if nndi <= d_i] + cdf[i] = len(smaller) * 1.0 / pp.n + return np.vstack((d, cdf)).T + + +def _f(pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): + """ + F empty space function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intevals : int + Number of intervals to evaluate F over. + dmin : float + Lower limit of distance range. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be set + to maximum nearest neighor distance. + d : sequence + The distance domain sequence. If d is specified, intervals, dmin + and dmax are ignored. + + Returns + ------- + : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the point pattern. The second + column is corresponding F function. + + Notes + ----- + See :class:`.F` + + """ + + # get a csr pattern in window of pp + c = csr(pp.window, n, 1, asPP=True).realizations[0] + # for each point in csr pattern find the closest point in pp and the + # associated distance + nnids, nnds = pp.knn_other(c, k=1) + + if d is None: + w = pp.max_nnd / intervals + if dmax: + w = dmax / intervals + d = [w * i for i in range(intervals + 2)] + cdf = [0] * len(d) + + for i, d_i in enumerate(d): + smaller = [nndi for nndi in nnds if nndi <= d_i] + cdf[i] = len(smaller) * 1.0 / n + return np.vstack((d, cdf)).T + + +def _j(pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): + """ + J function: Ratio of hazard functions for F and G. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intevals : int + Number of intervals to evaluate F over. + dmin : float + Lower limit of distance range. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be set + to maximum nearest neighor distance. + d : sequence + The distance domain sequence. If d is specified, intervals, dmin + and dmax are ignored. + + Returns + ------- + : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the point pattern. The second + column is corresponding J function. + + Notes + ----- + See :class:`.J` + + """ + + F = _f(pp, n, intervals=intervals, dmin=dmin, dmax=dmax, d=d) + G = _g(pp, intervals=intervals, dmin=dmin, dmax=dmax, d=d) + FC = 1 - F[:, 1] + GC = 1 - G[:, 1] + last_id = len(GC) + 1 + if np.any(FC == 0): + last_id = np.where(FC == 0)[0][0] + + return np.vstack((F[:last_id, 0], GC[:last_id] / FC[:last_id])).T + + +def _k(pp, intervals=10, dmin=0.0, dmax=None, d=None): + """ + Interevent K function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intevals : int + Number of intervals to evaluate F over. + dmin : float + Lower limit of distance range. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be set to one-quarter of the minimum side of the minimum bounding rectangle. + d : sequence + The distance domain sequence. If d is specified, intervals, dmin + and dmax are ignored. + + Returns + ------- + kcdf : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the point pattern. The second + column is corresponding K function. + + Notes + ----- + + See :class:`.K` + """ + + if d is None: + w = pp.rot / intervals + if dmax: + w = dmax / intervals + d = [w * i for i in range(intervals + 2)] + den = pp.lambda_window * (pp.n - 1) + kcdf = np.asarray([(di, len(pp.tree.query_pairs(di)) * 2 / den) for di in d]) + return kcdf + + +def _l(pp, intervals=10, dmin=0.0, dmax=None, d=None): + """ + Interevent L function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intevals : int + Number of intervals to evaluate F over. + dmin : float + Lower limit of distance range. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be set + to length of bounding box diagonal. + d : sequence + The distance domain sequence. If d is specified, intervals, dmin + and dmax are ignored. + + Returns + ------- + kf : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the point pattern. The second + column is corresponding L function. + + Notes + ----- + See :class:`.L` + + """ + + kf = _k(pp, intervals, dmin, dmax, d) + kf[:, 1] = np.sqrt(kf[:, 1] / np.pi) - kf[:, 0] + return kf + + +class Envelopes(object): + """ + Abstract base class for simulation envelopes. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intervals : int + The length of distance domain sequence. Default is 10. + dmin : float + The minimum of the distance domain. + dmax : float + The maximum of the distance domain. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + pct : float + 1-alpha, alpha is the significance level. Default is 0.05, + 1-alpha is the confidence level for the envelope. + realizations: :class:`.PointProcess` + Point process instance with more than 1 realizations. + + Attributes + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + observed : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the observed point pattern. + The second column is the specific function ("G", "F", "J", + "K" or "L") over the distance domain sequence for the + observed point pattern. + low : array + A 1-dimensional numpy array. Lower bound of the simulation + envelope. + high : array + A 1-dimensional numpy array. Higher bound of the simulation + envelope. + mean : array + A 1-dimensional numpy array. Mean values of the simulation + envelope. + + """ + + def __init__(self, *args, **kwargs): + # setup arguments + self.name = kwargs["name"] + warnings.warn( + f"This class is deprecated! Please use the alternative statistical test" + f" {self.name.lower()}_test in pointpats.distance_statistics.", + DeprecationWarning, + stacklevel=2, + ) + + # calculate observed function + self.pp = args[0] + self.observed = self.calc(*args, **kwargs) + self.d = self.observed[:, 0] # domain to be used in all realizations + + # do realizations + self.mapper(kwargs["realizations"]) + + def mapper(self, realizations): + reals = realizations.realizations + res = np.asarray([self.calc(reals[p]) for p in reals]) + + # When calculating the J function for all the simulations, the length + # of the returned interval domains might be different. + + if self.name == "J": + res = [] + for p in reals: + j = self.calc(reals[p]) + if j.shape[0] < self.d.shape[0]: + diff = self.d.shape[0] - j.shape[0] + for i in range(diff): + j = np.append(j, [[self.d[i + diff], np.inf]], axis=0) + res.append(j) + res = np.array(res) + + res = res[:, :, -1] + res.sort(axis=0) + nres = len(res) + self.low = res[np.int(nres * self.pct / 2.0)] + self.high = res[np.int(nres * (1 - self.pct / 2.0))] + self.mean = res.mean(axis=0) + + def calc(self, *args, **kwargs): + print("implement in subclass") + + def plot(self): + # assuming mpl + x = self.d + plt.plot(x, self.observed[:, 1], label="{}".format(self.name)) + plt.plot(x, self.mean, "g-.", label="CSR") + plt.plot(x, self.low, "r-.", label="LB") + plt.plot(x, self.high, "r-.", label="UB") + plt.ylabel("{}(d)".format(self.name)) + plt.xlabel("d") + plt.title("{} Simulation Envelopes".format(self.name)) + plt.legend(loc=0) + + +class Genv(Envelopes): + """ + Simulation envelope for G function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intervals : int + The length of distance domain sequence. Default is 10. + dmin : float + The minimum of the distance domain. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be + set to maximum nearest neighbor distance. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + pct : float + 1-alpha, alpha is the significance level. Default is 0.05, + which means 95% confidence level for the envelopes. + realizations: :class:`.PointProcess` + Point process instance with more than 1 realizations. + + Attributes + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + observed : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the observed point pattern. + The second column is cumulative nearest neighbor distance + distribution (G function) for the observed point pattern. + low : array + A 1-dimensional numpy array. Lower bound of the simulation + envelope. + high : array + A 1-dimensional numpy array. Higher bound of the simulation + envelope. + mean : array + A 1-dimensional numpy array. Mean values of the simulation + envelope. + + Examples + -------- + .. plot:: + + >>> import libpysal as ps + >>> from pointpats import Genv, PoissonPointProcess, Window + >>> from libpysal.cg import shapely_ext + >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) + >>> polys = [shp for shp in va] + >>> state = shapely_ext.cascaded_union(polys) + >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] + >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) + >>> genv_bb = Genv(pp, realizations=csrs) + >>> genv_bb.plot() + + """ + + def __init__( + self, pp, intervals=10, dmin=0.0, dmax=None, d=None, pct=0.05, realizations=None + ): + self.pp = pp + self.intervals = intervals + self.dmin = dmin + self.dmax = dmax + self.d = d + self.pct = pct + super(Genv, self).__init__(pp, realizations=realizations, name="G") + + def calc(self, *args, **kwargs): + pp = args[0] + return _g( + pp, intervals=self.intervals, dmin=self.dmin, dmax=self.dmax, d=self.d + ) + + +class Fenv(Envelopes): + """ + Simulation envelope for F function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intervals : int + The length of distance domain sequence. Default is 10. + dmin : float + The minimum of the distance domain. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be + set to maximum nearest neighbor distance. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + pct : float + 1-alpha, alpha is the significance level. Default is 0.05, + which means 95% confidence level for the envelopes. + realizations: :class:`.PointProcess` + Point process instance with more than 1 realizations. + + Attributes + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + observed : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the observed point pattern. + The second column is F function for the observed point + pattern. + low : array + A 1-dimensional numpy array. Lower bound of the simulation + envelope. + high : array + A 1-dimensional numpy array. Higher bound of the simulation + envelope. + mean : array + A 1-dimensional numpy array. Mean values of the simulation + envelope. + + Examples + -------- + .. plot:: + + >>> import libpysal as ps + >>> from libpysal.cg import shapely_ext + >>> from pointpats import PoissonPointProcess,Window,Fenv + >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) + >>> polys = [shp for shp in va] + >>> state = shapely_ext.cascaded_union(polys) + >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] + >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) + >>> fenv = Fenv(pp, realizations=csrs) + >>> fenv.plot() + + """ + + def __init__( + self, + pp, + n=100, + intervals=10, + dmin=0.0, + dmax=None, + d=None, + pct=0.05, + realizations=None, + ): + self.pp = pp + self.n = n + self.intervals = intervals + self.dmin = dmin + self.dmax = dmax + self.d = d + self.pct = pct + super(Fenv, self).__init__(pp, realizations=realizations, name="F") + + def calc(self, *args, **kwargs): + pp = args[0] + return _f( + pp, + self.n, + intervals=self.intervals, + dmin=self.dmin, + dmax=self.dmax, + d=self.d, + ) + + +class Jenv(Envelopes): + """ + Simulation envelope for J function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + n : int + Number of empty space points (random points). + intervals : int + The length of distance domain sequence. Default is 10. + dmin : float + The minimum of the distance domain. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be + set to maximum nearest neighbor distance. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + pct : float + 1-alpha, alpha is the significance level. Default is 0.05, + which means 95% confidence level for the envelopes. + realizations: :class:`.PointProcess` + Point process instance with more than 1 realizations. + + Attributes + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + observed : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the observed point pattern. + The second column is J function for the observed point + pattern. + low : array + A 1-dimensional numpy array. Lower bound of the simulation + envelope. + high : array + A 1-dimensional numpy array. Higher bound of the simulation + envelope. + mean : array + A 1-dimensional numpy array. Mean values of the simulation + envelope. + + Examples + -------- + .. plot:: + + >>> import libpysal as ps + >>> from pointpats import Jenv, PoissonPointProcess, Window + >>> from libpysal.cg import shapely_ext + >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) + >>> polys = [shp for shp in va] + >>> state = shapely_ext.cascaded_union(polys) + >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] + >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) + >>> jenv = Jenv(pp, realizations=csrs) + >>> jenv.plot() + + """ + + def __init__( + self, + pp, + n=100, + intervals=10, + dmin=0.0, + dmax=None, + d=None, + pct=0.05, + realizations=None, + ): + self.pp = pp + self.n = n + self.intervals = intervals + self.dmin = dmin + self.dmax = dmax + self.d = d + self.pct = pct + super(Jenv, self).__init__(pp, realizations=realizations, name="J") + + def calc(self, *args, **kwargs): + pp = args[0] + return _j( + pp, + self.n, + intervals=self.intervals, + dmin=self.dmin, + dmax=self.dmax, + d=self.d, + ) + + +class Kenv(Envelopes): + """ + Simulation envelope for K function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intervals : int + The length of distance domain sequence. Default is 10. + dmin : float + The minimum of the distance domain. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be + set to maximum nearest neighbor distance. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + pct : float + 1-alpha, alpha is the significance level. Default is 0.05, + which means 95% confidence level for the envelope. + realizations: :class:`.PointProcess` + Point process instance with more than 1 realizations. + + Attributes + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + observed : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the observed point pattern. + The second column is K function for the observed point + pattern. + low : array + A 1-dimensional numpy array. Lower bound of the simulation + envelope. + high : array + A 1-dimensional numpy array. Higher bound of the simulation + envelope. + mean : array + A 1-dimensional numpy array. Mean values of the simulation + envelope. + + Examples + -------- + .. plot:: + + >>> import libpysal as ps + >>> from pointpats import Kenv, PoissonPointProcess, Window + >>> from libpysal.cg import shapely_ext + >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) + >>> polys = [shp for shp in va] + >>> state = shapely_ext.cascaded_union(polys) + >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] + >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) + >>> kenv = Kenv(pp, realizations=csrs) + >>> kenv.plot() + + """ + + def __init__( + self, pp, intervals=10, dmin=0.0, dmax=None, d=None, pct=0.05, realizations=None + ): + self.pp = pp + self.intervals = intervals + self.dmin = dmin + self.dmax = dmax + self.d = d + self.pct = pct + super(Kenv, self).__init__(pp, realizations=realizations, name="K") + + def calc(self, *args, **kwargs): + pp = args[0] + return _k( + pp, intervals=self.intervals, dmin=self.dmin, dmax=self.dmax, d=self.d + ) + + +class Lenv(Envelopes): + """ + Simulation envelope for L function. + + Parameters + ---------- + pp : :class:`.PointPattern` + Point Pattern instance. + intervals : int + The length of distance domain sequence. Default is 10. + dmin : float + The minimum of the distance domain. + dmax : float + Upper limit of distance range. If dmax is None, dmax will be + set to maximum nearest neighbor distance. + d : sequence + The distance domain sequence. + If d is specified, intervals, dmin and dmax are ignored. + pct : float + 1-alpha, alpha is the significance level. Default is 0.05, + which means 95% confidence level for the envelopes. + realizations: :class:`.PointProcess` + Point process instance with more than 1 realizations. + + Attributes + ---------- + name : string + Name of the function. ("G", "F", "J", "K" or "L") + observed : array + A 2-dimensional numpy array of 2 columns. The first column is + the distance domain sequence for the observed point pattern. + The second column is L function for the observed point + pattern. + low : array + A 1-dimensional numpy array. Lower bound of the simulation + envelope. + high : array + A 1-dimensional numpy array. Higher bound of the simulation + envelope. + mean : array + A 1-dimensional numpy array. Mean values of the simulation + envelope. + + Examples + -------- + .. plot:: + + >>> import libpysal as ps + >>> from pointpats import Lenv, PoissonPointProcess, Window + >>> from libpysal.cg import shapely_ext + >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) + >>> polys = [shp for shp in va] + >>> state = shapely_ext.cascaded_union(polys) + >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] + >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) + >>> lenv = Lenv(pp, realizations=csrs) + >>> lenv.plot() + + """ + + def __init__( + self, pp, intervals=10, dmin=0.0, dmax=None, d=None, pct=0.05, realizations=None + ): + self.pp = pp + self.intervals = intervals + self.dmin = dmin + self.dmax = dmax + self.d = d + self.pct = pct + super(Lenv, self).__init__(pp, realizations=realizations, name="L") + + def calc(self, *args, **kwargs): + pp = args[0] + return _l( + pp, intervals=self.intervals, dmin=self.dmin, dmax=self.dmax, d=self.d + ) diff --git a/pointpats/distance_statistics.py b/pointpats/distance_statistics.py index 4269e15..185b8f4 100644 --- a/pointpats/distance_statistics.py +++ b/pointpats/distance_statistics.py @@ -1,1027 +1,918 @@ -""" -Distance statistics for planar point patterns - -""" -__author__ = "Serge Rey sjsrey@gmail.com" -__all__ = ['DStatistic', 'G', 'F', 'J', 'K', 'L', 'Envelopes', 'Genv', 'Fenv', 'Jenv', 'Kenv', 'Lenv'] - -from .process import PoissonPointProcess as csr -import numpy as np -from matplotlib import pyplot as plt - - -class DStatistic(object): - """ - Abstract Base Class for distance statistics. - - Parameters - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - - Attributes - ---------- - d : array - The distance domain sequence. - - """ - def __init__(self, name): - self.name = name - - def plot(self, qq=False): - """ - Plot the distance function - - Parameters - ---------- - qq: Boolean - If False the statistic is plotted against distance. If Frue, the - quantile-quantile plot is generated, observed vs. CSR. - """ - - # assuming mpl - x = self.d - if qq: - plt.plot(self.ev, self._stat) - plt.plot(self.ev, self.ev) - else: - plt.plot(x, self._stat, label='{}'.format(self.name)) - plt.ylabel("{}(d)".format(self.name)) - plt.xlabel('d') - plt.plot(x, self.ev, label='CSR') - plt.title("{} distance function".format(self.name)) - - -class G(DStatistic): - """ - Estimates the nearest neighbor distance distribution function G for a - point pattern. - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intervals : int - The length of distance domain sequence. - dmin : float - The minimum of the distance domain. - dmax : float - The maximum of the distance domain. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - - Attributes - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - d : array - The distance domain sequence. - G : array - The cumulative nearest neighbor distance distribution over d. - - Notes - ----- - In the analysis of planar point processes, the estimate of :math:`G` is - typically compared to the value expected from a completely spatial - random (CSR) process given as: - - .. math:: - - G(d) = 1 - e^{-\lambda \pi d^2} - - where :math:`\lambda` is the intensity (points per unit area) of the point - process and :math:`d` is distance. - - For a clustered pattern, the empirical function will be above the - expectation, while for a uniform pattern the empirical function falls below - the expectation. - - """ - - def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None): - res = _g(pp, intervals, dmin, dmax, d) - self.d = res[:, 0] - self.G = self._stat = res[:, 1] - self.ev = 1 - np.exp(-pp.lambda_window * np.pi * self.d * self.d) - self.pp = pp - super(G, self).__init__(name="G") - - -class F(DStatistic): - """ - Estimates the empty space distribution function for a point pattern: F(d). +import numpy +import warnings +from scipy import spatial, interpolate +from collections import namedtuple +from .geometry import ( + area as _area, + k_neighbors as _k_neighbors, + build_best_tree as _build_best_tree, + prepare_hull as _prepare_hull, + TREE_TYPES, +) +from .random import poisson + + +from ._deprecated_distance_statistics import * + +__all__ = [ + "f", + "g", + "k", + "j", + "l", + "f_test", + "g_test", + "k_test", + "j_test", + "l_test", +] + + +def _prepare(coordinates, support, distances, metric, hull, edge_correction): + """ + prepare the arguments to convert into a standard format + 1. cast the coordinates to a numpy array + 2. precomputed metrics must have distances provided + 3. metrics must be callable or string + 4. warn if distances are specified and metric is not default + 5. make distances a numpy.ndarray + 6. construct the support, accepting: + - num_steps -> a linspace with len(support) == num_steps + from zero to a quarter of the bounding box's smallest side + - (stop, ) -> a linspace with len(support) == 20 + from zero to stop + - (start, stop) -> a linspace with len(support) == 20 + from start to stop + - (start, stop, num_steps) -> a linspace with len(support) == num_steps + from start to stop + - numpy.ndarray -> passed through + """ + # Throw early if edge correction is requested + if edge_correction is not None: + raise NotImplementedError("Edge correction is not currently implemented.") + + # cast to coordinate array + if isinstance(coordinates, TREE_TYPES): + tree = coordinates + coordinates = tree.data + else: + coordinates = numpy.asarray(coordinates) + hull = _prepare_hull(coordinates, hull) + + # evaluate distances + if (distances is None) and metric == "precomputed": + raise ValueError( + "If metric =`precomputed` then distances must" + " be provided as a (n,n) numpy array." + ) + if not (isinstance(metric, str) or callable(metric)): + raise TypeError( + f"`metric` argument must be callable or a string. Recieved: {metric}" + ) + if distances is not None and metric != "euclidean": + warnings.warn( + "Distances were provided. The specified metric will be ignored." + " To use precomputed distances with a custom distance metric," + " do not specify a `metric` argument.", + stacklevel=2, + ) + metric = "euclidean" + + if support is None: + support = 20 + + if isinstance(support, int): # if just n_steps, use the max nnd + # this is O(n log n) for kdtrees & balltrees + tmp_tree = _build_best_tree(coordinates, metric=metric) + max_dist = _k_neighbors(tmp_tree, coordinates, 1)[0].max() + support = numpy.linspace(0, max_dist, num=support) + # otherwise, we need to build it using (start, stop, step) semantics + elif isinstance(support, tuple): + if len(support) == 1: # assuming this is with zero implicit start + support = numpy.linspace(0, support[0], num=20) # default support n bins + elif len(support) == 2: + support = numpy.linspace(*support, num=20) # default support n bins + elif len(support) == 3: + support = numpy.linspace(support[0], support[1], num=support[2]) + else: # try to use it as is + try: + support = numpy.asarray(support) + except: + raise TypeError( + "`support` must be a tuple (either (start, stop, step), (start, stop) or (stop,))," + " an int describing the number of breaks to use to evalute the function," + " or an iterable containing the breaks to use to evaluate the function." + " Recieved object of type {}: {}".format(type(support), support) + ) + + return coordinates, support, distances, metric, hull, edge_correction + + +# ------------------------------------------------------------# +# Statistical Functions # +# ------------------------------------------------------------# + + +def f( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, +): + """ + Ripley's F function + + The so-called "empty space" function, this is the cumulative density function of + the distances from a random set of points to the known points in the pattern. Parameters ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intervals : int - The length of distance domain sequence. - dmin : float - The minimum of the distance domain. - dmax : float - The maximum of the distance domain. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - - Attributes - ---------- - d : array - The distance domain sequence. - G : array - The cumulative empty space nearest event distance distribution - over d. - - Notes - ----- - In the analysis of planar point processes, the estimate of :math:`F` is - typically compared to the value expected from a process that displays - complete spatial randomness (CSR): - - .. math:: - - F(d) = 1 - e^{-\lambda \pi d^2} - - where :math:`\lambda` is the intensity (points per unit area) of the point - process and :math:`d` is distance. - - The expectation is identical to the expectation for the :class:`G` function - for a CSR process. However, for a clustered pattern, the empirical G - function will be below the expectation, while for a uniform pattern the - empirical function falls above the expectation. - - """ - - def __init__(self, pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): - res = _f(pp, n, intervals, dmin, dmax, d) - self.d = res[:, 0] - self.F = self._stat = res[:, 1] - self.ev = 1 - np.exp(-pp.lambda_window * np.pi * self.d * self.d) - super(F, self).__init__(name="F") - - -class J(DStatistic): - """ - Estimates the J function for a point pattern :cite:`VanLieshout1996` - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intervals : int - The length of distance domain sequence. - dmin : float - The minimum of the distance domain. - dmax : float - The maximum of the distance domain. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - - Attributes - ---------- - d : array - The distance domain sequence. - j : array - F function over d. - - Notes - ----- - - The :math:`J` function is a ratio of the hazard functions defined for - :math:`G` and :math:`F`: - - .. math:: - - J(d) = \\frac{1-G(d) }{1-F(d)} - - where :math:`G(d)` is the nearest neighbor distance distribution function - (see :class:`G`) - and :math:`F(d)` is the empty space function (see :class:`F`). - - For a CSR process the J function equals 1. Empirical values larger than 1 - are indicative of uniformity, while values below 1 suggest clustering. - - - """ - def __init__(self, pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): - res = _j(pp, n, intervals, dmin, dmax, d) - self.d = res[:, 0] - self.j = self._stat = res[:, 1] - self.ev = self.j / self.j - super(J, self).__init__(name="J") - - -class K(DStatistic): - """ - Estimates the K function for a point pattern. - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intervals : int - The length of distance domain sequence. - dmin : float - The minimum of the distance domain. - dmax : float - The maximum of the distance domain. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - - Attributes - ---------- - d : array - The distance domain sequence. - k : array - K function over d. - - Notes - ----- - - The :math:`K` function is estimated using - - .. math:: - - \\hat{K}(h) = \\frac{a}{n (n-1)} \\sum_{i} \\sum_{j \\ne i} I(d_{i,j} \\le h) - - where :math:`a` is the area of the window, :math:`n` the number of event points, and :math:`I(d_{i,j} \le h)` is an indicator function returning 1 when points i and j are separated by a distance of :math:`h` or less, 0 otherwise. - - """ - def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None): - res = _k(pp, intervals, dmin, dmax, d) - self.d = res[:, 0] - self.k = self._stat = res[:, 1] - self.ev = np.pi * self.d * self.d - super(K, self).__init__(name="K") - - -class L(DStatistic): - """ - Estimates the :math:`L` function for a point pattern :cite:`Sullivan2010`. - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intervals : int - The length of distance domain sequence. - dmin : float - The minimum of the distance domain. - dmax : float - The maximum of the distance domain. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - - Attributes - ---------- - d : array - The distance domain sequence. - l : array - L function over d. - - Notes - ----- - - In the analysis of planar point processes, the :math:`L` function - is a scaled version of :math:`K` function. Its estimate is also - typically compared to the value expected from a process that displays - complete spatial randomness (CSR): - - .. math:: - - L(d) = \\sqrt{\\frac{K(d)}{\\pi}}-d - - where :math:`K(d)` is the estimator for the :math:`K` function - and :math:`d` is distance. - - The expectation under the null of CSR is 0 (a horizonal line at 0). - For a clustered pattern, the empirical :math:`L` - function will be above the expectation, while for a uniform pattern the - empirical function falls below the expectation. - - """ - def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None): - res = _l(pp, intervals, dmin, dmax, d) - self.d = res[:, 0] - self.l = self._stat = res[:, 1] - super(L, self).__init__(name="L") - - def plot(self): - # assuming mpl - x = self.d - plt.plot(x, self._stat, label='{}'.format(self.name)) - plt.ylabel("{}(d)".format(self.name)) - plt.xlabel('d') - plt.title("{} distance function".format(self.name)) - - -def _g(pp, intervals=10, dmin=0.0, dmax=None, d=None): - """ - Estimate the nearest neighbor distances function G. - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intevals : int - Number of intervals to evaluate F over. - dmin : float - Lower limit of distance range. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be set - to maximum nearest neighor distance. - d : sequence - The distance domain sequence. If d is specified, intervals, dmin - and dmax are ignored. + coordinates : numpy.ndarray of shape (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. Returns ------- - : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the point pattern. The second - column is the cumulative nearest neighbor distance distribution. - - Notes - ----- - See :class:`G`. - - """ - if d is None: - w = pp.max_nnd/intervals - if dmax: - w = dmax/intervals - d = [w*i for i in range(intervals + 2)] - cdf = [0] * len(d) - for i, d_i in enumerate(d): - smaller = [nndi for nndi in pp.nnd if nndi <= d_i] - cdf[i] = len(smaller)*1./pp.n - return np.vstack((d, cdf)).T - - -def _f(pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): - """ - F empty space function. + a tuple containing the support values used to evalute the function + and the values of the function at each distance value in the support. + """ + coordinates, support, distances, metric, hull, _ = _prepare( + coordinates, support, distances, metric, hull, edge_correction + ) + if distances is not None: + n = coordinates.shape[0] + if distances.ndim == 2: + k, p = distances.shape + if k == p == n: + warnings.warn( + f"A full distance matrix is not required for this function, and" + f" the intput matrix is a square {n},{n} matrix. Only the" + f" distances from p random points to their nearest neighbor within" + f" the pattern is required, as an {n},p matrix. Assuming the" + f" provided distance matrix has rows pertaining to input" + f" pattern and columns pertaining to the output points.", + stacklevel=2, + ) + distances = distances.min(axis=0) + elif k == n: + distances = distances.min(axis=0) + else: + raise ValueError( + f"Distance matrix should have the same rows as the input" + f" coordinates with p columns, where n may be equal to p." + f" Recieved an {k},{p} distance matrix for {n} coordinates" + ) + elif distances.ndim == 1: + p = len(distances) + else: + # Do 1000 empties. Users can control this by computing their own + # empty space distribution. + n_empty_points = 1000 + + randoms = poisson(hull=hull, size=(n_empty_points, 1)) + try: + tree + except NameError: + tree = _build_best_tree(coordinates, metric) + finally: + distances, _ = tree.query(randoms, k=1) + distances = distances.squeeze() + + counts, bins = numpy.histogram(distances, bins=support) + fracs = numpy.cumsum(counts) / counts.sum() + + return bins, numpy.asarray([0, *fracs]) + + +def g( + coordinates, support=None, distances=None, metric="euclidean", edge_correction=None, +): + """ + Ripley's G function + + The G function is computed from the cumulative density function of the nearest neighbor + distances between points in the pattern. Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intevals : int - Number of intervals to evaluate F over. - dmin : float - Lower limit of distance range. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be set - to maximum nearest neighor distance. - d : sequence - The distance domain sequence. If d is specified, intervals, dmin - and dmax are ignored. + ----------- + coordinates : numpy.ndarray of shape (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, n) or (n,) + distances from every point in the point to another point in `coordinates` + metric: str or callable + distance metric to use when building search tree + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. Returns ------- - : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the point pattern. The second - column is corresponding F function. - - Notes - ----- - See :class:`.F` - - """ - - # get a csr pattern in window of pp - c = csr(pp.window, n, 1, asPP=True).realizations[0] - # for each point in csr pattern find the closest point in pp and the - # associated distance - nnids, nnds = pp.knn_other(c, k=1) - - if d is None: - w = pp.max_nnd/intervals - if dmax: - w = dmax/intervals - d = [w*i for i in range(intervals + 2)] - cdf = [0] * len(d) - - for i, d_i in enumerate(d): - smaller = [nndi for nndi in nnds if nndi <= d_i] - cdf[i] = len(smaller)*1./n - return np.vstack((d, cdf)).T - - -def _j(pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None): - """ - J function: Ratio of hazard functions for F and G. - + a tuple containing the support values used to evalute the function + and the values of the function at each distance value in the support. + + """ + + coordinates, support, distances, metric, *_ = _prepare( + coordinates, support, distances, metric, None, edge_correction + ) + if distances is not None: + if distances.ndim == 2: + if distances.shape[0] == distances.shape[1] == coordinates.shape[0]: + warnings.warn( + "The full distance matrix is not required for this function," + " only the distance to the nearest neighbor within the pattern." + " Computing this and discarding the rest.", + stacklevel=2, + ) + distances = distances.min(axis=1) + else: + k, p = distances.shape + n = coordinates.shape[0] + raise ValueError( + " Input distance matrix has an invalid shape: {k},{p}." + " Distances supplied can either be 2 dimensional" + " square matrices with the same number of rows" + " as `coordinates` ({n}) or 1 dimensional and contain" + " the shortest distance from each point in " + " `coordinates` to some other point in coordinates." + ) + elif distances.ndim == 1: + if distances.shape[0] != coordinates.shape[0]: + raise ValueError( + f"Distances are not aligned with coordinates! Distance" + f" matrix must be (n_coordinates, n_coordinates), but recieved" + f" {distances.shape} instead of ({coordinates.shape[0]},)" + ) + else: + raise ValueError( + "Distances supplied can either be 2 dimensional" + " square matrices with the same number of rows" + " as `coordinates` or 1 dimensional and contain" + " the shortest distance from each point in " + " `coordinates` to some other point in coordinates." + " Input matrix was {distances.ndim} dimensioanl" + ) + else: + try: + tree + except NameError: + tree = _build_best_tree(coordinates, metric) + finally: + distances, indices = _k_neighbors(tree, coordinates, k=1) + + counts, bins = numpy.histogram(distances.squeeze(), bins=support) + fracs = numpy.cumsum(counts) / counts.sum() + + return bins, numpy.asarray([0, *fracs]) + + +def j( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + truncate=True, +): + """ + Ripely's J function + + The so-called "spatial hazard" function, this is a function relating the F and G functions. + Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intevals : int - Number of intervals to evaluate F over. - dmin : float - Lower limit of distance range. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be set - to maximum nearest neighor distance. - d : sequence - The distance domain sequence. If d is specified, intervals, dmin - and dmax are ignored. + ----------- + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: tuple of numpy.ndarray + precomputed distances to use to evaluate the j function. + The first must be of shape (n,n) or (n,) and is used in the g function. + the second must be of shape (n,p) or (p,) (with p possibly equal to n) + used in the f function. + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern for the f function. + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + truncate: bool (default: True) + whether or not to truncate the results when the F function reaches one. If the + F function is one but the G function is less than one, this function will return + numpy.nan values. Returns ------- - : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the point pattern. The second - column is corresponding J function. - - Notes - ----- - See :class:`.J` - - """ - - F = _f(pp, n, intervals=intervals, dmin=dmin, dmax=dmax, d=d) - G = _g(pp, intervals=intervals, dmin=dmin, dmax=dmax, d=d) - FC = 1 - F[:, 1] - GC = 1 - G[:, 1] - last_id = len(GC) + 1 - if np.any(FC == 0): - last_id = np.where(FC == 0)[0][0] - - return np.vstack((F[:last_id, 0], GC[:last_id]/FC[:last_id])).T - - -def _k(pp, intervals=10, dmin=0.0, dmax=None, d=None): - """ - Interevent K function. - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intevals : int - Number of intervals to evaluate F over. - dmin : float - Lower limit of distance range. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be set to one-quarter of the minimum side of the minimum bounding rectangle. - d : sequence - The distance domain sequence. If d is specified, intervals, dmin - and dmax are ignored. + a tuple containing the support values used to evalute the function + and the values of the function at each distance value in the support. + """ + if distances is not None: + g_distances, f_distances = distances + else: + g_distances = f_distances = None + fsupport, fstats = f( + coordinates, + support=support, + distances=f_distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + ) + + gsupport, gstats = g( + coordinates, + support=support, + distances=g_distances, + metric=metric, + edge_correction=edge_correction, + ) + + if isinstance(support, numpy.ndarray): + if not numpy.allclose(gsupport, support): + gfunction = interpolate.interp1d(gsupport, gstats, fill_value=1) + gstats = gfunction(support) + gsupport = support + if not (numpy.allclose(gsupport, fsupport)): + ffunction = interpolate.interp1d(fsupport, fstats, fill_value=1) + fstats = ffunction(gsupport) + fsupport = gsupport + + with numpy.errstate(invalid="ignore", divide="ignore"): + hazard_ratio = (1 - gstats) / (1 - fstats) + if truncate: + both_zero = (gstats == 1) & (fstats == 1) + hazard_ratio[both_zero] = 1 + is_inf = numpy.isinf(hazard_ratio) + first_inf = is_inf.argmax() + if not is_inf.any(): + first_inf = len(hazard_ratio) + if first_inf < len(hazard_ratio) and isinstance(support, int): + warnings.warn( + f"requested {support} bins to evaluate the J function, but" + f" it reaches infinity at d={gsupport[first_inf]:.4f}, meaning only" + f" {first_inf} bins will be used to characterize the J function.", + stacklevel=2, + ) + else: + first_inf = len(gsupport) + 1 + return (gsupport[:first_inf], hazard_ratio[:first_inf]) + + +def k( + coordinates, support=None, distances=None, metric="euclidean", edge_correction=None, +): + """ + Ripley's K function + + This function counts the number of pairs of points that are closer than a given distance. + As d increases, K approaches the number of point pairs. + + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. Returns ------- - kcdf : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the point pattern. The second - column is corresponding K function. - - Notes - ----- - - See :class:`.K` - """ - - if d is None: - w = pp.rot/intervals - if dmax: - w = dmax/intervals - d = [w*i for i in range(intervals + 2)] - den = pp.lambda_window * (pp.n - 1) - kcdf = np.asarray([(di, len(pp.tree.query_pairs(di)) * 2 / den ) for di in d]) - return kcdf - - -def _l(pp, intervals=10, dmin=0.0, dmax=None, d=None): - """ - Interevent L function. + a tuple containing the support values used to evalute the function + and the values of the function at each distance value in the support. + """ + coordinates, support, distances, metric, hull, edge_correction = _prepare( + coordinates, support, distances, metric, None, edge_correction + ) + n = coordinates.shape[0] + upper_tri_n = n * (n - 1) * 0.5 + if distances is not None: + if distances.ndim == 1: + if distances.shape[0] != upper_tri_n: + raise ValueError( + f"Shape of inputted distances is not square, nor is the upper triangular" + f" matrix matching the number of input points. The shape of the input matrix" + f" is {distances.shape}, but required shape is ({upper_tri_n},) or ({n},{n})" + ) + upper_tri_distances = distances + elif distances.shape[0] == distances.shape[1] == n: + upper_tri_distances = distances[numpy.triu_indices_from(distances, k=1)] + else: + raise ValueError( + f"Shape of inputted distances is not square, nor is the upper triangular" + f" matrix matching the number of input points. The shape of the input matrix" + f" is {distances.shape}, but required shape is ({upper_tri_n},) or ({n},{n})" + ) + else: + upper_tri_distances = spatial.distance.pdist(coordinates, metric=metric) + n_pairs_less_than_d = (upper_tri_distances < support.reshape(-1, 1)).sum(axis=1) + intensity = n / _area(hull) + k_estimate = ((n_pairs_less_than_d * 2) / n) / intensity + return support, k_estimate + + +def l( + coordinates, + support=None, + permutations=9999, + distances=None, + metric="euclidean", + edge_correction=None, + linearized=False, +): + """ + Ripley's L function + + This is a scaled and shifted version of the K function that accounts for the K function's + increasing expected value as distances increase. This means that the L function, for a + completely random pattern, should be close to zero at all distance values in the support. Parameters ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intevals : int - Number of intervals to evaluate F over. - dmin : float - Lower limit of distance range. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be set - to length of bounding box diagonal. - d : sequence - The distance domain sequence. If d is specified, intervals, dmin - and dmax are ignored. + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + linearized : bool + whether or not to subtract l from its expected value (support) at each + distance bin. This centers the l function on zero for all distances. + Proposed by Besag (1977) Returns ------- - kf : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the point pattern. The second - column is corresponding L function. - - Notes - ----- - See :class:`.L` - - """ - - kf = _k(pp, intervals, dmin, dmax, d) - kf[:, 1] = np.sqrt(kf[:, 1] / np.pi) - kf[:, 0] - return kf - - -class Envelopes(object): - """ - Abstract base class for simulation envelopes. - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intervals : int - The length of distance domain sequence. Default is 10. - dmin : float - The minimum of the distance domain. - dmax : float - The maximum of the distance domain. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - pct : float - 1-alpha, alpha is the significance level. Default is 0.05, - 1-alpha is the confidence level for the envelope. - realizations: :class:`.PointProcess` - Point process instance with more than 1 realizations. - - Attributes - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - observed : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the observed point pattern. - The second column is the specific function ("G", "F", "J", - "K" or "L") over the distance domain sequence for the - observed point pattern. - low : array - A 1-dimensional numpy array. Lower bound of the simulation - envelope. - high : array - A 1-dimensional numpy array. Higher bound of the simulation - envelope. - mean : array - A 1-dimensional numpy array. Mean values of the simulation - envelope. - - """ - def __init__(self, *args, **kwargs): - # setup arguments - self.name = kwargs['name'] - - # calculate observed function - self.pp = args[0] - self.observed = self.calc(*args, **kwargs) - self.d = self.observed[:, 0] # domain to be used in all realizations - - # do realizations - self.mapper(kwargs['realizations']) - - def mapper(self, realizations): - reals = realizations.realizations - res = np.asarray([self.calc(reals[p]) for p in reals]) - - # When calculating the J function for all the simulations, the length - # of the returned interval domains might be different. - - if self.name == "J": - res = [] - for p in reals: - j = self.calc(reals[p]) - if j.shape[0] < self.d.shape[0]: - diff = self.d.shape[0]-j.shape[0] - for i in range(diff): - j = np.append(j, [[self.d[i+diff], np.inf]], axis=0) - res.append(j) - res = np.array(res) - - res = res[:, :, -1] - res.sort(axis=0) - nres = len(res) - self.low = res[np.int(nres * self.pct/2.)] - self.high = res[np.int(nres * (1-self.pct/2.))] - self.mean = res.mean(axis=0) - - def calc(self, *args, **kwargs): - print('implement in subclass') - - def plot(self): - # assuming mpl - x = self.d - plt.plot(x, self.observed[:, 1], label='{}'.format(self.name)) - plt.plot(x, self.mean, 'g-.', label='CSR') - plt.plot(x, self.low, 'r-.', label='LB') - plt.plot(x, self.high, 'r-.', label="UB") - plt.ylabel("{}(d)".format(self.name)) - plt.xlabel('d') - plt.title("{} Simulation Envelopes".format(self.name)) - plt.legend(loc=0) - - -class Genv(Envelopes): - """ - Simulation envelope for G function. + a tuple containing the support values used to evalute the function + and the values of the function at each distance value in the support. + """ + + support, k_estimate = k( + coordinates, + support=support, + distances=distances, + metric=metric, + edge_correction=edge_correction, + ) + + l = numpy.sqrt(k_estimate / numpy.pi) + + if linearized: + return support, l - support + return support, l + + +# ------------------------------------------------------------# +# Statistical Tests based on Ripley Functions # +# ------------------------------------------------------------# + +FtestResult = namedtuple( + "FtestResult", ("support", "statistic", "pvalue", "simulations") +) +GtestResult = namedtuple( + "GtestResult", ("support", "statistic", "pvalue", "simulations") +) +JtestResult = namedtuple( + "JtestResult", ("support", "statistic", "pvalue", "simulations") +) +KtestResult = namedtuple( + "KtestResult", ("support", "statistic", "pvalue", "simulations") +) +LtestResult = namedtuple( + "LtestResult", ("support", "statistic", "pvalue", "simulations") +) + +_ripley_dispatch = { + "F": (f, FtestResult), + "G": (g, GtestResult), + "J": (j, JtestResult), + "K": (k, KtestResult), + "L": (l, LtestResult), +} + + +def _ripley_test( + calltype, + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, + **kwargs, +): + stat_function, result_container = _ripley_dispatch.get(calltype) + core_kwargs = dict(support=support, metric=metric, edge_correction=edge_correction,) + tree = _build_best_tree(coordinates, metric=metric) + hull = _prepare_hull(coordinates, hull) + if calltype in ("F", "J"): # these require simulations + core_kwargs["hull"] = hull + # amortize to avoid doing this every time + empty_space_points = poisson(coordinates, size=(1000, 1)) + if distances is None: + empty_space_distances, _ = _k_neighbors(tree, empty_space_points, k=1) + if calltype == "F": + distances = empty_space_distances.squeeze() + else: # calltype == 'J': + n_distances, _ = _k_neighbors(tree, coordinates, k=1) + distances = (n_distances.squeeze(), empty_space_distances.squeeze()) + else: + pass + core_kwargs.update(**kwargs) + + observed_support, observed_statistic = stat_function( + tree, distances=distances, **core_kwargs + ) + core_kwargs["support"] = observed_support + n_observations = coordinates.shape[0] + + if keep_simulations: + simulations = numpy.empty((len(observed_support), n_simulations)).T + pvalues = numpy.ones_like(observed_support) + for i_replication in range(n_simulations): + random_i = poisson(hull, size=n_observations) + if calltype in ("F", "J"): + random_tree = _build_best_tree(random_i, metric) + empty_distances, _ = random_tree.query(empty_space_points, k=1) + if calltype == "F": + core_kwargs["distances"] = empty_distances.squeeze() + else: # calltype == 'J': + n_distances, _ = _k_neighbors(random_tree, random_i, k=1) + core_kwargs["distances"] = ( + n_distances.squeeze(), + empty_distances.squeeze(), + ) + rep_support, simulations_i = stat_function(random_i, **core_kwargs) + pvalues += simulations_i >= observed_statistic + if keep_simulations: + simulations[i_replication] = simulations_i + pvalues /= n_simulations + 1 + pvalues = numpy.minimum(pvalues, 1 - pvalues) + return result_container( + observed_support, + observed_statistic, + pvalues, + simulations if keep_simulations else None, + ) + + +def f_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, +): + """ + Ripley's F function + + The so-called "empty space" function, this is the cumulative density function of + the distances from a random set of points to the known points in the pattern. + + When the estimated statistic is larger than simulated values at a given distance, then + the pattern is considered "dispersed" or "regular" Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intervals : int - The length of distance domain sequence. Default is 10. - dmin : float - The minimum of the distance domain. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be - set to maximum nearest neighbor distance. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - pct : float - 1-alpha, alpha is the significance level. Default is 0.05, - which means 95% confidence level for the envelopes. - realizations: :class:`.PointProcess` - Point process instance with more than 1 realizations. - - Attributes - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - observed : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the observed point pattern. - The second column is cumulative nearest neighbor distance - distribution (G function) for the observed point pattern. - low : array - A 1-dimensional numpy array. Lower bound of the simulation - envelope. - high : array - A 1-dimensional numpy array. Higher bound of the simulation - envelope. - mean : array - A 1-dimensional numpy array. Mean values of the simulation - envelope. - - Examples - -------- - .. plot:: - - >>> import libpysal as ps - >>> from pointpats import Genv, PoissonPointProcess, Window - >>> from libpysal.cg import shapely_ext - >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) - >>> polys = [shp for shp in va] - >>> state = shapely_ext.cascaded_union(polys) - >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] - >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) - >>> genv_bb = Genv(pp, realizations=csrs) - >>> genv_bb.plot() - - """ - - def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None, pct=0.05, - realizations=None): - self.pp = pp - self.intervals = intervals - self.dmin = dmin - self.dmax = dmax - self.d = d - self.pct = pct - super(Genv, self).__init__(pp, realizations=realizations, name="G") - - def calc(self, *args, **kwargs): - pp = args[0] - return _g(pp, intervals=self.intervals, dmin=self.dmin, dmax=self.dmax, - d=self.d) - + ----------- + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. -class Fenv(Envelopes): - """ - Simulation envelope for F function. + Returns + ------- + a named tuple with properties + - support, the exact distance values used to evalute the statistic + - statistic, the values of the statistic at each distance + - pvalue, the percent of simulations that were as extreme as the observed value + - simulations, the distribution of simulated statistics (shaped (n_simulations, n_support_points)) + or None if keep_simulations=False (which is the default) + """ + + return _ripley_test( + "F", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) + + +def g_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, +): + """ + Ripley's G function + + The G function is computed from the cumulative density function of the nearest neighbor + distances between points in the pattern. + + When the G function is below the simulated values, it suggests dispersion. Parameters ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intervals : int - The length of distance domain sequence. Default is 10. - dmin : float - The minimum of the distance domain. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be - set to maximum nearest neighbor distance. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - pct : float - 1-alpha, alpha is the significance level. Default is 0.05, - which means 95% confidence level for the envelopes. - realizations: :class:`.PointProcess` - Point process instance with more than 1 realizations. - - Attributes - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - observed : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the observed point pattern. - The second column is F function for the observed point - pattern. - low : array - A 1-dimensional numpy array. Lower bound of the simulation - envelope. - high : array - A 1-dimensional numpy array. Higher bound of the simulation - envelope. - mean : array - A 1-dimensional numpy array. Mean values of the simulation - envelope. - - Examples - -------- - .. plot:: - - >>> import libpysal as ps - >>> from libpysal.cg import shapely_ext - >>> from pointpats import PoissonPointProcess,Window,Fenv - >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) - >>> polys = [shp for shp in va] - >>> state = shapely_ext.cascaded_union(polys) - >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] - >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) - >>> fenv = Fenv(pp, realizations=csrs) - >>> fenv.plot() + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. - """ - def __init__(self, pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None, - pct=0.05, realizations=None): - self.pp = pp - self.n = n - self.intervals = intervals - self.dmin = dmin - self.dmax = dmax - self.d = d - self.pct = pct - super(Fenv, self).__init__(pp, realizations=realizations, name="F") - - def calc(self, *args, **kwargs): - pp = args[0] - return _f(pp, self.n, intervals=self.intervals, dmin=self.dmin, - dmax=self.dmax, d=self.d) - - -class Jenv(Envelopes): - """ - Simulation envelope for J function. - - Parameters - ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - n : int - Number of empty space points (random points). - intervals : int - The length of distance domain sequence. Default is 10. - dmin : float - The minimum of the distance domain. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be - set to maximum nearest neighbor distance. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - pct : float - 1-alpha, alpha is the significance level. Default is 0.05, - which means 95% confidence level for the envelopes. - realizations: :class:`.PointProcess` - Point process instance with more than 1 realizations. - - Attributes - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - observed : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the observed point pattern. - The second column is J function for the observed point - pattern. - low : array - A 1-dimensional numpy array. Lower bound of the simulation - envelope. - high : array - A 1-dimensional numpy array. Higher bound of the simulation - envelope. - mean : array - A 1-dimensional numpy array. Mean values of the simulation - envelope. - - Examples - -------- - .. plot:: - - >>> import libpysal as ps - >>> from pointpats import Jenv, PoissonPointProcess, Window - >>> from libpysal.cg import shapely_ext - >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) - >>> polys = [shp for shp in va] - >>> state = shapely_ext.cascaded_union(polys) - >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] - >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) - >>> jenv = Jenv(pp, realizations=csrs) - >>> jenv.plot() + Returns + ------- + a named tuple with properties + - support, the exact distance values used to evalute the statistic + - statistic, the values of the statistic at each distance + - pvalue, the percent of simulations that were as extreme as the observed value + - simulations, the distribution of simulated statistics (shaped (n_simulations, n_support_points)) + or None if keep_simulations=False (which is the default) + """ + return _ripley_test( + "G", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) + + +def j_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + truncate=True, + keep_simulations=False, + n_simulations=9999, +): + """ + Ripley's J function + + The so-called "spatial hazard" function, this is a function relating the F and G functions. + + When the J function is consistently below 1, then it indicates clustering. + When consistently above 1, it suggests dispersion. + + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. - """ - def __init__(self, pp, n=100, intervals=10, dmin=0.0, dmax=None, d=None, - pct=0.05, realizations=None): - self.pp = pp - self.n = n - self.intervals = intervals - self.dmin = dmin - self.dmax = dmax - self.d = d - self.pct = pct - super(Jenv, self).__init__(pp, realizations=realizations, name="J") - - def calc(self, *args, **kwargs): - pp = args[0] - return _j(pp, self.n, intervals=self.intervals, dmin=self.dmin, - dmax=self.dmax, d=self.d) - - -class Kenv(Envelopes): - """ - Simulation envelope for K function. + Returns + ------- + a named tuple with properties + - support, the exact distance values used to evalute the statistic + - statistic, the values of the statistic at each distance + - pvalue, the percent of simulations that were as extreme as the observed value + - simulations, the distribution of simulated statistics (shaped (n_simulations, n_support_points)) + or None if keep_simulations=False (which is the default) + """ + return _ripley_test( + "J", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + truncate=truncate, + ) + + +def k_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, +): + """ + Ripley's K function + + This function counts the number of pairs of points that are closer than a given distance. + As d increases, K approaches the number of point pairs. + + When the K function is below simulated values, it suggests that the pattern is dispersed. Parameters ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intervals : int - The length of distance domain sequence. Default is 10. - dmin : float - The minimum of the distance domain. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be - set to maximum nearest neighbor distance. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - pct : float - 1-alpha, alpha is the significance level. Default is 0.05, - which means 95% confidence level for the envelope. - realizations: :class:`.PointProcess` - Point process instance with more than 1 realizations. - - Attributes - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - observed : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the observed point pattern. - The second column is K function for the observed point - pattern. - low : array - A 1-dimensional numpy array. Lower bound of the simulation - envelope. - high : array - A 1-dimensional numpy array. Higher bound of the simulation - envelope. - mean : array - A 1-dimensional numpy array. Mean values of the simulation - envelope. - - Examples - -------- - .. plot:: - - >>> import libpysal as ps - >>> from pointpats import Kenv, PoissonPointProcess, Window - >>> from libpysal.cg import shapely_ext - >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) - >>> polys = [shp for shp in va] - >>> state = shapely_ext.cascaded_union(polys) - >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] - >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) - >>> kenv = Kenv(pp, realizations=csrs) - >>> kenv.plot() + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. - """ - def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None, - pct=0.05, realizations=None): - self.pp = pp - self.intervals = intervals - self.dmin = dmin - self.dmax = dmax - self.d = d - self.pct = pct - super(Kenv, self).__init__(pp, realizations=realizations, name="K") - - def calc(self, *args, **kwargs): - pp = args[0] - return _k(pp, intervals=self.intervals, dmin=self.dmin, dmax=self.dmax, - d=self.d) - - -class Lenv(Envelopes): - """ - Simulation envelope for L function. + Returns + ------- + a named tuple with properties + - support, the exact distance values used to evalute the statistic + - statistic, the values of the statistic at each distance + - pvalue, the percent of simulations that were as extreme as the observed value + - simulations, the distribution of simulated statistics (shaped (n_simulations, n_support_points)) + or None if keep_simulations=False (which is the default) + """ + return _ripley_test( + "K", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) + + +def l_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + linearized=False, + keep_simulations=False, + n_simulations=9999, +): + """ + Ripley's L function + + This is a scaled and shifted version of the K function that accounts for the K function's + increasing expected value as distances increase. This means that the L function, for a + completely random pattern, should be close to zero at all distance values in the support. + + When the L function is negative, this suggests dispersion. Parameters ---------- - pp : :class:`.PointPattern` - Point Pattern instance. - intervals : int - The length of distance domain sequence. Default is 10. - dmin : float - The minimum of the distance domain. - dmax : float - Upper limit of distance range. If dmax is None, dmax will be - set to maximum nearest neighbor distance. - d : sequence - The distance domain sequence. - If d is specified, intervals, dmin and dmax are ignored. - pct : float - 1-alpha, alpha is the significance level. Default is 0.05, - which means 95% confidence level for the envelopes. - realizations: :class:`.PointProcess` - Point process instance with more than 1 realizations. - - Attributes - ---------- - name : string - Name of the function. ("G", "F", "J", "K" or "L") - observed : array - A 2-dimensional numpy array of 2 columns. The first column is - the distance domain sequence for the observed point pattern. - The second column is L function for the observed point - pattern. - low : array - A 1-dimensional numpy array. Lower bound of the simulation - envelope. - high : array - A 1-dimensional numpy array. Higher bound of the simulation - envelope. - mean : array - A 1-dimensional numpy array. Mean values of the simulation - envelope. - - Examples - -------- - .. plot:: - - >>> import libpysal as ps - >>> from pointpats import Lenv, PoissonPointProcess, Window - >>> from libpysal.cg import shapely_ext - >>> va = ps.io.open(ps.examples.get_path("vautm17n.shp")) - >>> polys = [shp for shp in va] - >>> state = shapely_ext.cascaded_union(polys) - >>> pp = PoissonPointProcess(Window(state.parts), 100, 1, asPP=True).realizations[0] - >>> csrs = PoissonPointProcess(pp.window, 100, 100, asPP=True) - >>> lenv = Lenv(pp, realizations=csrs) - >>> lenv.plot() - - """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. - def __init__(self, pp, intervals=10, dmin=0.0, dmax=None, d=None, - pct=0.05, realizations=None): - self.pp = pp - self.intervals = intervals - self.dmin = dmin - self.dmax = dmax - self.d = d - self.pct = pct - super(Lenv, self).__init__(pp, realizations=realizations, name="L") - - def calc(self, *args, **kwargs): - pp = args[0] - return _l(pp, intervals=self.intervals, dmin=self.dmin, dmax=self.dmax, - d=self.d) + Returns + ------- + a named tuple with properties + - support, the exact distance values used to evalute the statistic + - statistic, the values of the statistic at each distance + - pvalue, the percent of simulations that were as extreme as the observed value + - simulations, the distribution of simulated statistics (shaped (n_simulations, n_support_points)) + or None if keep_simulations=False (which is the default) + """ + return _ripley_test( + "L", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + linearized=linearized, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) diff --git a/pointpats/geometry.py b/pointpats/geometry.py new file mode 100644 index 0000000..a21de4b --- /dev/null +++ b/pointpats/geometry.py @@ -0,0 +1,416 @@ +import numpy +from scipy import spatial +from functools import singledispatch +from libpysal.cg import alpha_shape_auto +from libpysal.cg.kdtree import Arc_KDTree +import warnings + +# ------------------------------------------------------------# +# Utilities and dispatching # +# ------------------------------------------------------------# + +TREE_TYPES = (spatial.KDTree, spatial.cKDTree, Arc_KDTree) +try: + from sklearn.neighbors import KDTree, BallTree + + TREE_TYPES = (*TREE_TYPES, KDTree, BallTree) +except ModuleNotFoundError: + pass + +HULL_TYPES = ( + numpy.ndarray, + spatial.qhull.ConvexHull, +) + +## Define default dispatches and special dispatches without GEOS + +### AREA +@singledispatch +def area(shape): + """ + If a shape has an area attribute, return it. + Works for: + shapely.geometry.Polygon + """ + try: + return shape.area + except AttributeError: + return area(numpy.asarray(shape)) + + +@area.register +def _(shape: spatial.qhull.ConvexHull): + """ + If a shape is a convex hull from scipy, + assure it's 2-dimensional and then use its volume. + """ + assert shape.points.shape[1] == 2 + return shape.volume + + +@area.register +def _(shape: numpy.ndarray): + """ + If a shape describes a bounding box, compute length times width + """ + assert len(shape) == 4, "shape is not a bounding box!" + width, height = shape[2] - shape[0], shape[3] - shape[1] + return numpy.abs(width * height) + + +### bounding box +@singledispatch +def bbox(shape): + """ + If a shape can be cast to an array, use that. + Works for: + lists of tuples + scikit memory arrays + """ + return bbox(numpy.asarray(shape)) + + +@bbox.register +def _(shape: numpy.ndarray): + """ + If a shape is an array of points, compute the minima/maxima + or let it pass through if it's 1 dimensional & length 4 + """ + if (shape.ndim == 1) & (len(shape) == 4): + return shape + return numpy.array([*shape.min(axis=0), *shape.max(axis=0)]) + + +@bbox.register +def _(shape: spatial.qhull.ConvexHull): + """ + For scipy.spatial.ConvexHulls, compute the bounding box from + their boundary points. + """ + return bbox(shape.points[shape.vertices]) + + +### contains + + +@singledispatch +def contains(shape, x, y): + """ + Try to use the shape's contains method directly on XY. + Does not currently work on anything. + """ + raise NotImplementedError() + return shape.contains((x, y)) + + +@contains.register +def _(shape: numpy.ndarray, x: float, y: float): + """ + If provided an ndarray, assume it's a bbox + and return whether the point falls inside + """ + xmin, xmax = shape[0], shape[2] + ymin, ymax = shape[1], shape[3] + in_x = (xmin <= x) and (x <= xmax) + in_y = (ymin <= y) and (y <= ymax) + return in_x & in_y + + +@contains.register +def _(shape: spatial.Delaunay, x: float, y: float): + """ + For points and a delaunay triangulation, use the find_simplex + method to identify whether a point is inside the triangulation. + + If the returned simplex index is -1, then the point is not + within a simplex of the triangulation. + """ + return shape.find_simplex((x, y)) > 0 + + +@contains.register +def _(shape: spatial.qhull.ConvexHull, x: float, y: float): + """ + For convex hulls, convert their exterior first into a Delaunay triangulation + and then use the delaunay dispatcher. + """ + exterior = shape.points[shape.vertices] + delaunay = spatial.Delaunay(exterior) + return contains(delaunay, x, y) + + +### centroid +@singledispatch +def centroid(shape): + """ + Assume the input is a shape with a centroid method: + """ + return shape.centroid + + +@centroid.register +def _(shape: numpy.ndarray): + """ + Handle point arrays or bounding boxes + """ + from .centrography import mean_center + + if shape.ndim == 2: + return mean_center(shape).squeeze() + elif shape.ndim == 1: + assert shape.shape == (4,) + xmin, ymin, xmax, ymax = shape + return numpy.column_stack( + (numpy.mean((xmin, xmax)), numpy.mean((ymin, ymax))) + ).squeeze() + else: + raise TypeError( + f"Centroids are only implemented in 2 dimensions," + f" but input has {shape.ndim} dimensinos" + ) + + +@centroid.register +def _(shape: spatial.qhull.ConvexHull): + """ + Treat convex hulls as arrays of points + """ + return centroid(shape.points[shape.vertices]) + + +try: + from shapely.geometry.base import BaseGeometry as _BaseGeometry + from shapely.geometry import ( + Polygon as _ShapelyPolygon, + MultiPolygon as _ShapelyMultiPolygon, + ) + from shapely.geometry import Point as _ShapelyPoint + + HULL_TYPES = (*HULL_TYPES, _ShapelyPolygon, _ShapelyMultiPolygon) + HAS_SHAPELY = True + + @contains.register + def _(shape: _BaseGeometry, x: float, y: float): + """ + If we know we're working with a shapely polygon, + then use the contains method & cast input coords to a shapely point + """ + return shape.contains(_ShapelyPoint((x, y))) + + @bbox.register + def _(shape: _BaseGeometry): + """ + If a shape is an array of points, compute the minima/maxima + or let it pass through if it's 1 dimensional & length 4 + """ + return numpy.asarray(list(shape.bounds)) + + @centroid.register + def _(shape: _BaseGeometry): + """ + Handle shapely, which requires explicit centroid extraction + """ + return numpy.asarray(list(shape.centroid.coords)).squeeze() + + +except ModuleNotFoundError: + HAS_SHAPELY = False + + +try: + import pygeos + + HAS_PYGEOS = True + HULL_TYPES = (*HULL_TYPES, pygeos.Geometry) + + @area.register + def _(shape: pygeos.Geometry): + """ + If we know we're working with a pygeos polygon, + then use pygeos.area + """ + return pygeos.area(shape) + + @contains.register + def _(shape: pygeos.Geometry, x: float, y: float): + """ + If we know we're working with a pygeos polygon, + then use pygeos.within casting the points to a pygeos object too + """ + return pygeos.within(pygeos.points((x, y)), shape) + + @bbox.register + def _(shape: pygeos.Geometry): + """ + If we know we're working with a pygeos polygon, + then use pygeos.bounds + """ + return pygeos.bounds(shape) + + @centroid.register + def _(shape: pygeos.Geometry): + """ + if we know we're working with a pygeos polygon, + then use pygeos.centroid + """ + return pygeos.coordinates.get_coordinates(pygeos.centroid(shape)).squeeze() + + +except ModuleNotFoundError: + HAS_PYGEOS = False + +# ------------------------------------------------------------# +# Constructors for trees, prepared inputs, & neighbors # +# ------------------------------------------------------------# + + +def build_best_tree(coordinates, metric): + """ + Build the best query tree that can support the application. + Chooses from: + 1. sklearn.KDTree if available and metric is simple + 2. sklearn.BallTree if available and metric is complicated + 3. scipy.spatial.cKDTree if nothing else + + Parameters + ---------- + coordinates : numpy.ndarray + array of coordinates over which to build the tree. + metric : string or callable + either a metric supported by sklearn KDTrees or BallTrees, or a callabe function. + If sklearn is not installed, then this must be euclidean. + + Returns + ------- + : a distance tree + a KDTree from either scikit-learn or scipy, or a BallTree if available. + + Notes + ----- + This will return a scikit-learn KDTree if the metric is supported and + sklearn can be imported. + If the metric is not supported by KDTree, a BallTree will be used if + sklearn can be imported. + If the metric is a user-defined callable function, a Ball Tree will be used + if sklearn can be imported. + If sklearn can't be imported, then a scipy.spatial.KDTree will be used + if the metric is euclidean. + Otherwise, an error will be raised. + """ + coordinates = numpy.asarray(coordinates) + tree = spatial.cKDTree + try: + from sklearn.neighbors import KDTree, BallTree + + if metric in KDTree.valid_metrics: + tree = lambda coordinates: KDTree(coordinates, metric=metric) + elif metric in BallTree.valid_metrics: + tree = lambda coordinates: BallTree(coordinates, metric=metric) + elif callable(metric): + warnings.warn( + "Distance metrics defined in pure Python may " + " have unacceptable performance!", + stacklevel=2, + ) + tree = lambda coordinates: BallTree(coordinates, metric=metric) + else: + raise KeyError( + f"Metric {metric} not found in set of available types." + f"BallTree metrics: {BallTree.valid_metrics}, and" + f"scikit KDTree metrics: {KDTree.valid_metrics}." + ) + except ModuleNotFoundError as e: + if metric not in ("l2", "euclidean"): + raise KeyError( + f"Metric {metric} requested, but this requires" + f" scikit-learn to use. Without scikit-learn, only" + f" euclidean distance metric is supported." + ) + return tree(coordinates) + + +def k_neighbors(tree, coordinates, k, **kwargs): + """ + Query a kdtree for k neighbors, handling the self-neighbor case + in the case of coincident points. + + Arguments + ---------- + tree : distance tree + a distance tree, such as a scipy KDTree or sklearn KDTree or BallTree + that supports a query argument. + coordinates : numpy.ndarray of shape n,2 + coordinates to query for their neighbors within the tree. + k : int + number of neighbors to query in the tree + **kwargs : mappable + arguments that may need to be passed down to the tree.query() function + + Returns + -------- + a tuple of (distances, indices) that is assured to not include the point itself + in its query result. + """ + distances, indices = tree.query(coordinates, k=k + 1, **kwargs) + n, ks = distances.shape + assert ks == k + 1 + full_indices = numpy.arange(n) + other_index_mask = indices != full_indices.reshape(n, 1) + has_k_indices = other_index_mask.sum(axis=1) == (k + 1) + other_index_mask[has_k_indices, -1] = False + distances = distances[other_index_mask].reshape(n, k) + indices = indices[other_index_mask].reshape(n, k) + return distances, indices + + +def prepare_hull(coordinates, hull=None): + """ + Construct a hull from the coordinates given a hull type + Will either return: + - a bounding box array of [xmin, ymin, xmax, ymax] + - a scipy.spatial.ConvexHull object from the Qhull library + - a shapely shape using alpha_shape_auto + + Parameters + --------- + coordinates : numpy.ndarray of shape (n,2) + Points to use to construct a hull + hull : string or a pre-computed hull + A string denoting what kind of hull to compute (if required) or a hull + that has already been computed + + Returns + -------- + hull : A geometry-like object + This encodes the "space" in which to simulate the normal pattern. All points will + lie within this hull. Supported values are: + - a bounding box encoded in a numpy array as numpy.array([xmin, ymin, xmax, ymax]) + - an (N,2) array of points for which the bounding box will be computed & used + - a shapely polygon/multipolygon + - a pygeos geometry + - a scipy.spatial.qhull.ConvexHull + """ + if isinstance(hull, numpy.ndarray): + assert len(hull) == 4, f"bounding box provided is not shaped correctly! {hull}" + assert hull.ndim == 1, f"bounding box provided is not shaped correctly! {hull}" + return hull + if (hull is None) or (hull == "bbox"): + return bbox(coordinates) + if HAS_SHAPELY: # protect the isinstance check if import has failed + if isinstance(hull, (_ShapelyPolygon, _ShapelyMultiPolygon)): + return hull + if HAS_PYGEOS: + if isinstance(hull, pygeos.Geometry): + return hull + if isinstance(hull, str): + if hull.startswith("convex"): + return spatial.ConvexHull(coordinates) + elif hull.startswith("alpha") or hull.startswith("α"): + return alpha_shape_auto(coordinates) + elif isinstance(hull, spatial.qhull.ConvexHull): + return hull + raise ValueError( + f"Hull type {hull} not in the set of valid options:" + f" (None, 'bbox', 'convex', 'alpha', 'α', " + f" shapely.geometry.Polygon, pygeos.Geometry)" + ) diff --git a/pointpats/process.py b/pointpats/process.py index 4fea614..a2c54d2 100644 --- a/pointpats/process.py +++ b/pointpats/process.py @@ -9,15 +9,16 @@ """ __author__ = "Serge Rey sjsrey@gmail.com" -__all__ = ['PointProcess', 'PoissonPointProcess', 'PoissonClusterPointProcess'] +__all__ = ["PointProcess", "PoissonPointProcess", "PoissonClusterPointProcess"] import numpy as np import libpysal as ps from numpy.random import poisson from .pointpattern import PointPattern as PP +import warnings -def runif_in_circle(n, radius=1.0, center=(0., 0.), burn=2, verbose=False): +def runif_in_circle(n, radius=1.0, center=(0.0, 0.0), burn=2, verbose=False): """ Generate n points within a circle of given radius. @@ -44,20 +45,20 @@ def runif_in_circle(n, radius=1.0, center=(0., 0.), burn=2, verbose=False): r2 = r * r it = 0 while c < n: - x = np.random.uniform(-r, r, (burn*n, 1)) - y = np.random.uniform(-r, r, (burn*n, 1)) - ids = np.where(x*x + y*y <= r2) + x = np.random.uniform(-r, r, (burn * n, 1)) + y = np.random.uniform(-r, r, (burn * n, 1)) + ids = np.where(x * x + y * y <= r2) candidates = np.hstack((x, y))[ids[0]] nc = candidates.shape[0] need = n - c if nc > need: # more than we need good[c:] = candidates[:need] else: # use them all and keep going - good[c:c+nc] = candidates + good[c : c + nc] = candidates c += nc it += 1 if verbose: - print('Iterations: {}'.format(it)) + print("Iterations: {}".format(it)) return good + np.asarray(center) @@ -110,6 +111,12 @@ def __init__(self, window, n, samples, asPP=False, **args): for sample in self.realizations: points = self.realizations[sample] self.realizations[sample] = PP(points, window=self.window) + warnings.warn( + "These point pattern simulators are deprecated! Please replace them" + " with equivalent functions in pointpats.random", + DeprecationWarning, + stacklevel=2, + ) def draw(self, parameter): """ @@ -129,7 +136,7 @@ def draw(self, parameter): """ c = 0 sample = [] - n = parameter['n'] + n = parameter["n"] while c < n: pnts = self.realize(n) pnts = [ps.cg.shapes.Point((x, y)) for x, y in pnts] @@ -254,10 +261,10 @@ def setup(self): if self.conditioning: lambdas = poisson(self.n, self.samples) for i, l in enumerate(lambdas): - self.parameters[i] = {'n': l} + self.parameters[i] = {"n": l} else: for i in range(self.samples): - self.parameters[i] = {'n': self.n} + self.parameters[i] = {"n": self.n} def realize(self, n): """ @@ -391,16 +398,24 @@ class PoissonClusterPointProcess(PointProcess): """ - def __init__(self, window, n, parents, radius, samples, keep=False, - asPP=False, conditioning=False): + def __init__( + self, + window, + n, + parents, + radius, + samples, + keep=False, + asPP=False, + conditioning=False, + ): self.conditioning = conditioning self.parents = parents - self.children = int(np.ceil(n * 1. / parents)) + self.children = int(np.ceil(n * 1.0 / parents)) self.radius = radius self.keep = keep - super(PoissonClusterPointProcess, self).__init__(window, n, samples, - asPP) + super(PoissonClusterPointProcess, self).__init__(window, n, samples, asPP) def setup(self): """ @@ -418,11 +433,11 @@ def setup(self): lambdas = poisson(self.parents, self.samples) for i, l in enumerate(lambdas): num = l * self.children - self.parameters[i] = {'n': num} + self.parameters[i] = {"n": num} self.num_parents[i] = l else: for i in range(self.samples): - self.parameters[i] = {'n': self.n} + self.parameters[i] = {"n": self.n} self.num_parents[i] = self.parents def realize(self, n): @@ -444,8 +459,8 @@ def realize(self, n): l, b, r, t = self.window.bbox d = self.radius # get parent points - pxs = np.random.uniform(l, r, (int(n/self.children), 1)) - pys = np.random.uniform(b, t, (int(n/self.children), 1)) + pxs = np.random.uniform(l, r, (int(n / self.children), 1)) + pys = np.random.uniform(b, t, (int(n / self.children), 1)) cents = np.hstack((pxs, pys)) # generate children points pnts = [runif_in_circle(self.children, d, center) for center in cents] diff --git a/pointpats/random.py b/pointpats/random.py new file mode 100644 index 0000000..b447956 --- /dev/null +++ b/pointpats/random.py @@ -0,0 +1,462 @@ +import numpy +from .geometry import ( + spatial, + area as _area, + centroid as _centroid, + contains as _contains, + bbox as _bbox, + prepare_hull as _prepare_hull, + HULL_TYPES, +) + +# ------------------------------------------------------------ # +# Utilities # +# ------------------------------------------------------------ # + + +def parse_size_and_intensity(hull, intensity=None, size=None): + """ + Given a hull, an intensity, and a size int/tuple, correctly + compute the resulting missing quantities. Defaults to 100 points in one + replication, meaning the intensity will be computed on the fly + if nothing is provided. + + Parameters + ---------- + hull : A geometry-like object + This encodes the "space" in which to simulate the normal pattern. All points will + lie within this hull. Supported values are: + - a bounding box encoded in a numpy array as numpy.array([xmin, ymin, xmax, ymax]) + - an (N,2) array of points for which the bounding box will be computed & used + - a shapely polygon/multipolygon + - a pygeos geometry + - a scipy convexh hull + intensity : float + the number of observations per unit area in the hull to use. If provided, + then the number of observations is determined using the intensity * area(hull) and + the size is assumed to represent n_replications (if provided). + size : tuple or int + a tuple of (n_observations, n_replications), where the first number is the number + of points to simulate in each replication and the second number is the number of + total replications. So, (10, 4) indicates 10 points, 4 times. + If an integer is provided and intensity is None, n_replications is assumed to be 1. + If size is an integer and intensity is also provided, then size indicates n_replications, + and the number of observations is computed on the fly using intensity and area. + """ + if size is None: + if intensity is not None: + # if intensity is provided, assume + # n_observations + n_observations = int(_area(hull) * intensity) + else: + # default to 100 points + n_observations = 100 + intensity = n_observations / _area(hull) + n_simulations = 1 + size = (n_observations, n_simulations) + elif isinstance(size, tuple): + if len(size) == 2 and intensity is None: + n_observations, n_simulations = size + intensity = n_observations / _area(hull) + elif len(size) == 2 and intensity is not None: + raise ValueError( + "Either intensity or size as (n observations, n simulations)" + " can be provided. Providing both creates statistical conflicts." + " between the requested intensity and implied intensity by" + " the number of observations and the area of the hull. If" + " you want to specify the intensity, use the intensity argument" + " and set size equal to the number of simulations." + ) + else: + raise ValueError( + f"Intensity and size not understood. Provide size as a tuple" + f" containing (number of observations, number of simulations)" + f" with no specified intensity, or an intensity and size equal" + f" to the number of simulations." + f" Recieved: `intensity={intensity}, size={size}`" + ) + elif isinstance(size, int): + # assume int size with specified intensity means n_simulations at x intensity + if intensity is not None: + n_observations = int(intensity * _area(hull)) + n_simulations = size + else: # assume we have one replication at the specified number of points + n_simulations = 1 + n_observations = size + intensity = n_observations / _area(hull) + else: + raise ValueError( + f"Intensity and size not understood. Provide size as a tuple" + f" containing (number of observations, number of simulations)" + f" with no specified intensity, or an intensity and size equal" + f" to the number of simulations." + f" Recieved: `intensity={intensity}, size={size}`" + ) + return (n_observations, n_simulations, intensity) + + +# ------------------------------------------------------------ # +# Distributions # +# ------------------------------------------------------------ # + + +def poisson(hull, intensity=None, size=None): + """ + Simulate a poisson random point process with a specified intensity. + + Parameters + ---------- + hull : A geometry-like object + This encodes the "space" in which to simulate the normal pattern. All points will + lie within this hull. Supported values are: + - a bounding box encoded in a numpy array as numpy.array([xmin, ymin, xmax, ymax]) + - an (N,2) array of points for which the bounding box will be computed & used + - a shapely polygon/multipolygon + - a pygeos geometry + - a scipy convexh hull + intensity : float + the number of observations per unit area in the hull to use. If provided, then + size must be an integer describing the number of replications to use. + size : tuple or int + a tuple of (n_observations, n_replications), where the first number is the number + of points to simulate in each replication and the second number is the number of + total replications. So, (10, 4) indicates 10 points, 4 times. + If an integer is provided and intensity is None, n_replications is assumed to be 1. + If size is an integer and intensity is also provided, then size indicates n_replications, + and the number of observations is computed from the intensity. + + Returns + -------- + : numpy.ndarray + either an (n_replications, n_observations, 2) or (n_observations,2) array containing + the simulated realizations. + """ + if isinstance(hull, numpy.ndarray): + if hull.shape == (4,): + hull = hull + else: + hull = _prepare_hull(hull) + n_observations, n_simulations, intensity = parse_size_and_intensity( + hull, intensity=intensity, size=size + ) + + result = numpy.empty((n_simulations, n_observations, 2)) + + bbox = _bbox(hull) + + for i_replication in range(n_simulations): + generating = True + i_observation = 0 + while i_observation < n_observations: + x, y = ( + numpy.random.uniform(bbox[0], bbox[2]), + numpy.random.uniform(bbox[1], bbox[3]), + ) + if _contains(hull, x, y): + result[i_replication, i_observation] = (x, y) + i_observation += 1 + return result.squeeze() + + +def normal(hull, center=None, cov=None, size=None): + """ + Simulate a multivariate random normal point cluster + + Parameters + ---------- + hull : A geometry-like object + This encodes the "space" in which to simulate the normal pattern. All points will + lie within this hull. Supported values are: + - a bounding box encoded in a numpy array as numpy.array([xmin, ymin, xmax, ymax]) + - an (N,2) array of points for which the bounding box will be computed & used + - a shapely polygon/multipolygon + - a pygeos geometry + - a scipy convexh hull + center : iterable of shape (2, ) + A point where the simulations will be centered. + cov : float or a numpy array of shape (2,2) + either the standard deviation of an independent and identically distributed + normal distribution, or a 2 by 2 covariance matrix expressing the covariance + of the x and y for the distribution. Default is half of the width or height + of the hull's bounding box, whichever is larger. + size : tuple or int + a tuple of (n_observations, n_replications), where the first number is the number + of points to simulate in each replication and the second number is the number of + total replications. So, (10, 4) indicates 10 points, 4 times. + If an integer is provided, n_replications is assumed to be 1. + + Returns + -------- + : numpy.ndarray + either an (n_replications, n_observations, 2) or (n_observations,2) array containing + the simulated realizations. + """ + if isinstance(hull, numpy.ndarray): + if hull.shape == (4,): + hull = hull + else: + hull = _prepare_hull(hull) + if center is None: + center = _centroid(hull) + n_observations, n_simulations, intensity = parse_size_and_intensity( + hull, intensity=None, size=size + ) + if cov is None: + bbox = _bbox(hull) + width = bbox[2] - bbox[0] + height = bbox[3] - bbox[1] + cov = numpy.maximum(width / 2, height / 2) ** 2 + + if isinstance(cov, (int, float)): + sd = cov + cov = numpy.eye(2) * sd + elif isnstance(cov, numpy.ndarray): + if cov.ndim == 2: + assert cov.shape == (2, 2), "Bivariate covariance matrices must be 2 by 2" + elif cov.ndim == 3: + assert cov.shape[1:] == ( + 2, + 2, + ), "3-dimensional covariance matrices must have shape (n_simulations, 2,2)" + assert ( + cov.shape[0] == n_simulations + ), "3-dimensional covariance matrices must have shape (n_simulations, 2,2)" + else: + raise ValueError( + "`cov` argument must be a float (signifying a standard deviation)" + " or a 2 by 2 array expressing the covariance matrix of the " + " bivariate normal distribution." + ) + + result = numpy.empty((n_simulations, n_observations, 2)) + + bbox = _bbox(hull) + + for i_replication in range(n_simulations): + generating = True + i_observation = 0 + replication_cov = cov[i] if cov.ndim == 3 else cov + replication_sd = numpy.diagonal(replication_cov) ** 0.5 + replication_cor = (1 / replication_sd) * replication_cov * (1 / replication_sd) + + while i_observation < n_observations: + candidate = numpy.random.multivariate_normal((0, 0), replication_cor) + x, y = center + candidate * replication_sd + if _contains(hull, x, y): + result[i_replication, i_observation] = (x, y) + i_observation += 1 + return result.squeeze() + + +def cluster_poisson( + hull, intensity=None, size=None, n_seeds=2, cluster_radius=None, +): + """ + Simulate a cluster poisson random point process with a specified intensity & number of seeds. + A cluster poisson process is a poisson process where the center of each "cluster" is + itself distributed according to a spatial poisson process. + + Parameters + ---------- + hull : A geometry-like object + This encodes the "space" in which to simulate the normal pattern. All points will + lie within this hull. Supported values are: + - a bounding box encoded in a numpy array as numpy.array([xmin, ymin, xmax, ymax]) + - an (N,2) array of points for which the bounding box will be computed & used + - a shapely polygon/multipolygon + - a pygeos geometry + - a scipy convexh hull + intensity : float + the number of observations per unit area in the hull to use. If provided, then + size must be an integer describing the number of replications to use. + size : tuple or int + a tuple of (n_observations, n_replications), where the first number is the number + of points to simulate in each replication and the second number is the number of + total replications. So, (10, 4) indicates 10 points, 4 times. + If an integer is provided and intensity is None, n_replications is assumed to be 1. + If size is an integer and intensity is also provided, then size indicates n_replications, + and the number of observations is computed from the intensity. + n_seeds : int + the number of sub-clusters to use. + cluster_radius : float or iterable + the radius of each cluster. If a float, the same radius is used for all clusters. + If an array, then there must be the same number of radii as clusters. + If None, 50% of the minimum inter-point distance is used, which may fluctuate across + replications. + + Returns + -------- + : numpy.ndarray + either an (n_replications, n_observations, 2) or (n_observations,2) array containing + the simulated realizations. + """ + if isinstance(hull, numpy.ndarray): + if hull.shape == (4,): + hull = hull + else: + hull = _prepare_hull(hull) + + if isinstance(cluster_radius, numpy.ndarray): + cluster_radii = cluster_radius.flatten() + assert len(cluster_radii) == n_seeds, ( + f"number of radii provided ({len(cluster_radii)})" + f"does not match number of clusters requested" + f" ({n_seeds})." + ) + elif isinstance(cluster_radius, (int, float)): + cluster_radii = [cluster_radius] * n_seeds + + n_observations, n_simulations, intensity = parse_size_and_intensity( + hull, intensity=intensity, size=size + ) + + result = numpy.empty((n_simulations, n_observations, 2)) + hull_area = _area(hull) + for i_replication in range(n_simulations): + seeds = poisson(hull, size=(n_seeds, n_simulations)) + if cluster_radius is None: + # default cluster radius is one half the minimum distance between seeds + cluster_radii = [spatial.distance.pdist(seeds).min() * 0.5] * n_seeds + clusters = numpy.array_split(result[i_replication], n_seeds) + for i_cluster, radius in enumerate(cluster_radii): + seed = seeds[i_cluster] + cluster_points = clusters[i_cluster] + n_in_cluster = len(cluster_points) + if n_in_cluster == 1: + clusters[i_cluster] = seed + continue + if n_in_cluster < 1: + raise Exception( + "There are too many clusters requested for the " + " inputted number of samples. Reduce `n_seeds` or" + " increase the number of sampled points." + ) + candidates = _uniform_circle( + n_in_cluster - 1, radius=radius, center=seed, hull=hull + ) + clusters[i_cluster] = numpy.row_stack((seed, candidates)) + result[i_replication] = numpy.row_stack(clusters) + return result.squeeze() + + +def cluster_normal(hull, cov=None, size=None, n_seeds=2): + """ + Simulate a cluster poisson random point process with a specified intensity & number of seeds. + A cluster poisson process is a poisson process where the center of each "cluster" is + itself distributed according to a spatial poisson process. + + Parameters + ---------- + hull : A geometry-like object + This encodes the "space" in which to simulate the normal pattern. All points will + lie within this hull. Supported values are: + - a bounding box encoded in a numpy array as numpy.array([xmin, ymin, xmax, ymax]) + - an (N,2) array of points for which the bounding box will be computed & used + - a shapely polygon/multipolygon + - a pygeos geometry + - a scipy convexh hull + cov : float, int, or numpy.ndarray of shape (2,2) + The covariance structure for clusters. By default, this is the squared + average distance between cluster seeds. + size : tuple or int + a tuple of (n_observations, n_replications), where the first number is the number + of points to simulate in each replication and the second number is the number of + total replications. So, (10, 4) indicates 10 points, 4 times. + If an integer is provided and intensity is None, n_replications is assumed to be 1. + If size is an integer and intensity is also provided, then size indicates n_replications, + and the number of observations is computed from the intensity. + n_seeds : int + the number of sub-clusters to use. + + Returns + -------- + : numpy.ndarray + either an (n_replications, n_observations, 2) or (n_observations,2) array containing + the simulated realizations. + """ + if isinstance(hull, numpy.ndarray): + if hull.shape == (4,): + hull = hull + else: + hull = _prepare_hull(hull) + n_observations, n_simulations, intensity = parse_size_and_intensity( + hull, intensity=None, size=size + ) + result = numpy.empty((n_simulations, n_observations, 2)) + for i_replication in range(n_simulations): + seeds = poisson(hull, size=(n_seeds, n_simulations)) + if cov is None: + cov = spatial.distance.pdist(seeds).mean() ** 2 + clusters = numpy.array_split(result[i_replication], n_seeds) + for i_cluster, seed in enumerate(seeds): + cluster_points = clusters[i_cluster] + n_in_cluster = len(cluster_points) + if n_in_cluster == 1: + clusters[i_cluster] = seed + continue + if n_in_cluster < 1: + raise Exception( + "There are too many clusters requested for the " + " inputted number of samples. Reduce `n_seeds` or" + " increase the number of sampled points." + ) + candidates = normal(hull, center=seed, cov=cov, size=n_in_cluster - 1) + clusters[i_cluster] = numpy.row_stack((seed, candidates)) + result[i_replication] = numpy.row_stack(clusters) + return result.squeeze() + + +def _uniform_circle(n, radius=1.0, center=(0.0, 0.0), burn=2, verbose=False, hull=None): + """ + Generate n points within a circle of given radius. + + Parameters + ---------- + n : int + Number of points. + radius : float + Radius of the circle. + center : tuple + Coordinates of the center. + burn : int + number of coordinates to simulate at a time. This is the "chunk" + size sent to numpy.random.uniform each iteration of the rejection sampler + + Returns + ------- + : array + (n, 2), coordinates of generated points + + """ + + good = numpy.zeros((n, 2), float) + c = 0 + center_x, center_y = center + r = radius + r2 = r * r + it = 0 + while c < n: + x = numpy.random.uniform(-r, r, (burn * n, 1)) + y = numpy.random.uniform(-r, r, (burn * n, 1)) + if hull is None: + in_hull = True + else: + in_hull = numpy.asarray( + [ + _contains(hull, xi + center_x, yi + center_y) + for xi, yi in numpy.column_stack((x, y)) + ] + ).reshape(-1, 1) + ids, *_ = numpy.where(((x * x + y * y) <= r2) & in_hull) + candidates = numpy.hstack((x, y))[ids] + nc = candidates.shape[0] + need = n - c + if nc > need: # more than we need + good[c:] = candidates[:need] + else: # use them all and keep going + good[c : c + nc] = candidates + c += nc + it += 1 + if verbose: + print("Iterations: {}".format(it)) + return good + numpy.asarray(center) diff --git a/pointpats/ripley.py b/pointpats/ripley.py new file mode 100644 index 0000000..d89ef5a --- /dev/null +++ b/pointpats/ripley.py @@ -0,0 +1,1160 @@ +import numpy +import warnings +from scipy import spatial, interpolate +from functools import singledispatch +from collections import namedtuple +from libpysal.cg import alpha_shape_auto +from libpysal.cg.kdtree import Arc_KDTree + +__all__ = [ + "simulate", + "simulate_from", + "f_function", + "g_function", + "k_function", + "j_function", + "l_function", + "f_test", + "g_test", + "k_test", + "j_test", + "l_test", +] + +# ------------------------------------------------------------# +# Utilities and dispatching # +# ------------------------------------------------------------# + +TREE_TYPES = (spatial.KDTree, spatial.cKDTree, Arc_KDTree) +try: + from sklearn.neighbors import KDTree, BallTree + + TREE_TYPES = (*TREE_TYPES, KDTree, BallTree) +except ModuleNotFoundError: + pass + +## Define default dispatches and special dispatches without GEOS +@singledispatch +def _area(shape): + """ + If a shape has an area attribute, return it. + Works for: + shapely.geometry.Polygon + """ + return shape.area + + +@_area.register +def _(shape: spatial.qhull.ConvexHull): + """ + If a shape is a convex hull from scipy, + assure it's 2-dimensional and then use its volume. + """ + assert shape.points.shape[1] == 2 + return shape.volume + + +@_area.register +def _(shape: numpy.ndarray): + """ + If a shape describes a bounding box, compute length times width + """ + assert len(shape) == 4, "shape is not a bounding box!" + width, height = shape[2] - shape[0], shape[3] - shape[1] + return numpy.abs(width * height) + + +@singledispatch +def _bbox(shape): + """ + If a shape has bounds, use those. + Works for: + shapely.geometry.Polygon + """ + return _bbox(numpy.asarray(shape)) + + +@_bbox.register +def _(shape: numpy.ndarray): + """ + If a shape is an array of points, compute the minima/maxima + or let it pass through if it's 1 dimensional & length 4 + """ + if (shape.ndim == 1) & (len(shape) == 4): + return shape + return numpy.array([*shape.min(axis=0), *shape.max(axis=0)]) + + +@_bbox.register +def _(shape: spatial.qhull.ConvexHull): + """ + For scipy.spatial.ConvexHulls, compute the bounding box from + their boundary points. + """ + return _bbox(shape.points[shape.vertices]) + + +@singledispatch +def _contains(shape, x, y): + """ + Try to use the shape's contains method directly on XY. + Does not currently work on anything. + """ + raise NotImplementedError() + return shape.contains((x, y)) + + +@_contains.register +def _(shape: numpy.ndarray, x: float, y: float): + """ + If provided an ndarray, assume it's a bbox + and return whether the point falls inside + """ + xmin, xmax = shape[0], shape[2] + ymin, ymax = shape[1], shape[3] + in_x = (xmin <= x) and (x <= xmax) + in_y = (ymin <= y) and (y <= ymax) + return in_x & in_y + + +@_contains.register +def _(shape: spatial.Delaunay, x: float, y: float): + """ + For points and a delaunay triangulation, use the find_simplex + method to identify whether a point is inside the triangulation. + + If the returned simplex index is -1, then the point is not + within a simplex of the triangulation. + """ + return shape.find_simplex((x, y)) > 0 + + +@_contains.register +def _(shape: spatial.qhull.ConvexHull, x: float, y: float): + """ + For convex hulls, convert their exterior first into a Delaunay triangulation + and then use the delaunay dispatcher. + """ + exterior = shape.points[shape.vertices] + delaunay = spatial.Delaunay(exterior) + return _contains(delaunay, x, y) + + +try: + from shapely.geometry.base import BaseGeometry as _BaseGeometry + from shapely.geometry import ( + Polygon as _ShapelyPolygon, + MultiPolygon as _ShapelyMultiPolygon, + ) + from shapely.geometry import Point as _ShapelyPoint + + HAS_SHAPELY = True + + @_contains.register + def _(shape: _BaseGeometry, x: float, y: float): + """ + If we know we're working with a shapely polygon, + then use the contains method & cast input coords to a shapely point + """ + return shape.contains(_ShapelyPoint((x, y))) + + @_bbox.register + def _(shape: _BaseGeometry): + """ + If a shape is an array of points, compute the minima/maxima + or let it pass through if it's 1 dimensional & length 4 + """ + return numpy.asarray(list(shape.bounds)) + + +except ModuleNotFoundError: + HAS_SHAPELY = False + + +try: + import pygeos + + HAS_PYGEOS = True + + @_area.register + def _(shape: pygeos.Geometry): + """ + If we know we're working with a pygeos polygon, + then use pygeos.area + """ + return pygeos.area(shape) + + @_contains.register + def _(shape: pygeos.Geometry, x: float, y: float): + """ + If we know we're working with a pygeos polygon, + then use pygeos.within casting the points to a pygeos object too + """ + return pygeos.within(pygeos.points((x, y)), shape) + + @_bbox.register + def _(shape: pygeos.Geometry): + """ + If we know we're working with a pygeos polygon, + then use pygeos.bounds + """ + return pygeos.bounds(shape) + + +except ModuleNotFoundError: + HAS_PYGEOS = False + +# ------------------------------------------------------------# +# Constructors for trees, prepared inputs, & neighbors # +# ------------------------------------------------------------# + + +def _build_best_tree(coordinates, metric): + """ + Build the best query tree that can support the application. + Chooses from: + 1. sklearn.KDTree if available and metric is simple + 2. sklearn.BallTree if available and metric is complicated + 3. scipy.spatial.cKDTree if nothing else + """ + coordinates = numpy.asarray(coordinates) + tree = spatial.cKDTree + try: + from sklearn.neighbors import KDTree, BallTree + + if metric in KDTree.valid_metrics: + tree = lambda coordinates: KDTree(coordinates, metric=metric) + elif metric in BallTree.valid_metrics: + tree = lambda coordinates: BallTree(coordinates, metric=metric) + elif callable(metric): + warnings.warn( + "Distance metrics defined in pure Python may " + " have unacceptable performance!", + stacklevel=2, + ) + tree = lambda coordinates: BallTree(coordinates, metric=metric) + else: + raise KeyError( + f"Metric {metric} not found in set of available types." + f"BallTree metrics: {BallTree.valid_metrics}, and" + f"scikit KDTree metrics: {KDTree.valid_metrics}." + ) + except ModuleNotFoundError as e: + if metric not in ("l2", "euclidean"): + raise KeyError( + f"Metric {metric} requested, but this requires" + f" scikit-learn to use. Without scikit-learn, only" + f" euclidean distance metric is supported." + ) + return tree(coordinates) + + +def _prepare_hull(coordinates, hull): + """ + Construct a hull from the coordinates given a hull type + Will either return: + - a bounding box array of [xmin, ymin, xmax, ymax] + - a scipy.spatial.ConvexHull object from the Qhull library + - a shapely shape using alpha_shape_auto + """ + if isinstance(hull, numpy.ndarray): + assert len(hull) == 4, f"bounding box provided is not shaped correctly! {hull}" + assert hull.ndim == 1, f"bounding box provided is not shaped correctly! {hull}" + return hull + if (hull is None) or (hull == "bbox"): + return _bbox(coordinates) + if HAS_SHAPELY: # protect the isinstance check if import has failed + if isinstance(hull, (_ShapelyPolygon, _ShapelyMultiPolygon)): + return hull + if HAS_PYGEOS: + if isinstance(hull, pygeos.Geometry): + return hull + if isinstance(hull, str): + if hull.startswith("convex"): + return spatial.ConvexHull(coordinates) + elif hull.startswith("alpha") or hull.startswith("α"): + return alpha_shape_auto(coordinates) + elif isinstance(hull, spatial.qhull.ConvexHull): + return hull + raise ValueError( + f"Hull type {hull} not in the set of valid options:" + f" (None, 'bbox', 'convex', 'alpha', 'α', " + f" shapely.geometry.Polygon, pygeos.Geometry)" + ) + + +def _prepare(coordinates, support, distances, metric, hull, edge_correction): + """ + prepare the arguments to convert into a standard format + 1. cast the coordinates to a numpy array + 2. precomputed metrics must have distances provided + 3. metrics must be callable or string + 4. warn if distances are specified and metric is not default + 5. make distances a numpy.ndarray + 6. construct the support, accepting: + - num_steps -> a linspace with len(support) == num_steps + from zero to a quarter of the bounding box's smallest side + - (stop, ) -> a linspace with len(support) == 20 + from zero to stop + - (start, stop) -> a linspace with len(support) == 20 + from start to stop + - (start, stop, num_steps) -> a linspace with len(support) == num_steps + from start to stop + - numpy.ndarray -> passed through + """ + # Throw early if edge correction is requested + if edge_correction is not None: + raise NotImplementedError("Edge correction is not currently implemented.") + + # cast to coordinate array + if isinstance(coordinates, TREE_TYPES): + tree = coordinates + coordinates = tree.data + else: + coordinates = numpy.asarray(coordinates) + hull = _prepare_hull(coordinates, hull) + + # evaluate distances + if (distances is None) and metric == "precomputed": + raise ValueError( + "If metric =`precomputed` then distances must" + " be provided as a (n,n) numpy array." + ) + if not (isinstance(metric, str) or callable(metric)): + raise TypeError( + f"`metric` argument must be callable or a string. Recieved: {metric}" + ) + if distances is not None and metric != "euclidean": + warnings.warn( + "Distances were provided. The specified metric will be ignored." + " To use precomputed distances with a custom distance metric," + " do not specify a `metric` argument.", + stacklevel=2, + ) + metric = "euclidean" + + if support is None: + support = 20 + + if isinstance(support, int): # if just n_steps, use the max nnd + # this is O(n log n) for kdtrees & balltrees + tmp_tree = _build_best_tree(coordinates, metric=metric) + max_dist = _k_neighbors(tmp_tree, coordinates, 1)[0].max() + support = numpy.linspace(0, max_dist, num=support) + # otherwise, we need to build it using (start, stop, step) semantics + elif isinstance(support, tuple): + if len(support) == 1: # assuming this is with zero implicit start + support = numpy.linspace(0, support[0], num=20) # default support n bins + elif len(support) == 2: + support = numpy.linspace(*support, num=20) # default support n bins + elif len(support) == 3: + support = numpy.linspace(support[0], support[1], num=support[2]) + else: # try to use it as is + try: + support = numpy.asarray(support) + except: + raise TypeError( + "`support` must be a tuple (either (start, stop, step), (start, stop) or (stop,))," + " an int describing the number of breaks to use to evalute the function," + " or an iterable containing the breaks to use to evaluate the function." + " Recieved object of type {}: {}".format(type(support), support) + ) + + return coordinates, support, distances, metric, hull, edge_correction + + +def _k_neighbors(tree, coordinates, k, **kwargs): + """ + Query a kdtree for k neighbors, handling the self-neighbor case + in the case of coincident points. + """ + distances, indices = tree.query(coordinates, k=k + 1, **kwargs) + n, ks = distances.shape + assert ks == k + 1 + full_indices = numpy.arange(n) + other_index_mask = indices != full_indices.reshape(n, 1) + has_k_indices = other_index_mask.sum(axis=1) == (k + 1) + other_index_mask[has_k_indices, -1] = False + distances = distances[other_index_mask].reshape(n, k) + indices = indices[other_index_mask].reshape(n, k) + return distances, indices + + +# ------------------------------------------------------------# +# Simulators # +# ------------------------------------------------------------# + + +def simulate(hull, intensity=None, size=None): + """ + Simulate from the given hull with a specified intensity or size. + + Hulls can be: + - bounding boxes (numpy.ndarray with dim==1 and len == 4) + - scipy.spatial.ConvexHull + - shapely.geometry.Polygon + - pygeos.Geometry + + If intensity is specified, size must be an integer reflecting + the number of realizations. + If the size is specified as a tuple, then the intensity is + determined by the area of the hull. + """ + if size is None: + if intensity is not None: + # if intensity is provided, assume + # n_observations + n_observations = int(_area(hull) * intensity) + else: + # default to 100 points + n_observations = 100 + n_simulations = 1 + size = (n_observations, n_simulations) + elif isinstance(size, tuple): + if len(size) == 2 and intensity is None: + n_observations, n_simulations = size + intensity = n_observations / _area(hull) + elif len(size) == 2 and intensity is not None: + raise ValueError( + "Either intensity or size as (n observations, n simulations)" + " can be provided. Providing both creates statistical conflicts." + " between the requested intensity and implied intensity by" + " the number of observations and the area of the hull. If" + " you want to specify the intensity, use the intensity argument" + " and set size equal to the number of simulations." + ) + else: + raise ValueError( + f"Intensity and size not understood. Provide size as a tuple" + f" containing (number of observations, number of simulations)" + f" with no specified intensity, or an intensity and size equal" + f" to the number of simulations." + f" Recieved: `intensity={intensity}, size={size}`" + ) + elif isinstance(size, int): + # assume int size with specified intensity means n_simulations at x intensity + if intensity is not None: + n_observations = int(intensity * _area(hull)) + n_simulations = size + else: # assume we have one replication at the specified number of points + n_simulations = 1 + n_observations = size + intensity = n_observations / _area(hull) + else: + raise ValueError( + f"Intensity and size not understood. Provide size as a tuple" + f" containing (number of observations, number of simulations)" + f" with no specified intensity, or an intensity and size equal" + f" to the number of simulations." + f" Recieved: `intensity={intensity}, size={size}`" + ) + result = numpy.empty((n_simulations, n_observations, 2)) + + bbox = _bbox(hull) + + for i_replication in range(n_simulations): + generating = True + i_observation = 0 + while i_observation < n_observations: + x, y = ( + numpy.random.uniform(bbox[0], bbox[2]), + numpy.random.uniform(bbox[1], bbox[3]), + ) + if _contains(hull, x, y): + result[i_replication, i_observation] = (x, y) + i_observation += 1 + return result.squeeze() + + +def simulate_from(coordinates, hull=None, size=None): + """ + Simulate a pattern from the coordinates provided using a given assumption + about the hull of the process. + + Note: will always assume the implicit intensity of the process. + """ + try: + coordinates = numpy.asarray(coordinates) + assert coordinates.ndim == 2 + except: + raise ValueError("This function requires a numpy array for input." + " If `coordinates` is a shape, use simulate()." + " Otherwise, use the `hull` argument to specify" + " which hull you intend to compute for the input" + " coordinates.") + if isinstance(size, int): + n_observations = coordinates.shape[0] + n_simulations = size + elif isinstance(size, tuple): + assert len(size) == 2, ( + f"`size` argument must be either an integer denoting the number" + f" of simulations or a tuple containing " + f" (n_simulated_observations, n_simulations). Instead, recieved" + f" a tuple of length {len(size)}: {size}" + ) + n_observations, n_simulations = size + elif size is None: + n_observations = coordinates.shape[0] + n_simulations = 1 + hull = _prepare_hull(coordinates, hull) + return simulate(hull, intensity=None, size=(n_observations, n_simulations)) + + +# ------------------------------------------------------------# +# Statistical Functions # +# ------------------------------------------------------------# + + +def f_function( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + """ + coordinates, support, distances, metric, hull, _ = _prepare( + coordinates, support, distances, metric, hull, edge_correction + ) + if distances is not None: + n = coordinates.shape[0] + if distances.ndim == 2: + k, p = distances.shape + if k == p == n: + warnings.warn( + f"A full distance matrix is not required for this function, and" + f" the intput matrix is a square {n},{n} matrix. Only the" + f" distances from p random points to their nearest neighbor within" + f" the pattern is required, as an {n},p matrix. Assuming the" + f" provided distance matrix has rows pertaining to input" + f" pattern and columns pertaining to the output points.", + stacklevel=2, + ) + distances = distances.min(axis=0) + elif k == n: + distances = distances.min(axis=0) + else: + raise ValueError( + f"Distance matrix should have the same rows as the input" + f" coordinates with p columns, where n may be equal to p." + f" Recieved an {k},{p} distance matrix for {n} coordinates" + ) + elif distances.ndim == 1: + p = len(distances) + else: + # Do 1000 empties. Users can control this by computing their own + # empty space distribution. + n_empty_points = 1000 + + randoms = simulate(hull=hull, size=(n_empty_points, 1)) + try: + tree + except NameError: + tree = _build_best_tree(coordinates, metric) + finally: + distances, _ = tree.query(randoms, k=1) + distances = distances.squeeze() + + counts, bins = numpy.histogram(distances, bins=support) + fracs = numpy.cumsum(counts) / counts.sum() + + return bins, numpy.asarray([0, *fracs]) + + +def g_function( + coordinates, support=None, distances=None, metric="euclidean", edge_correction=None, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, n) or (n,) + distances from every point in the point to another point in `coordinates` + metric: str or callable + distance metric to use when building search tree + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + """ + + coordinates, support, distances, metric, *_ = _prepare( + coordinates, support, distances, metric, None, edge_correction + ) + if distances is not None: + if distances.ndim == 2: + if distances.shape[0] == distances.shape[1] == coordinates.shape[0]: + warnings.warn( + "The full distance matrix is not required for this function," + " only the distance to the nearest neighbor within the pattern." + " Computing this and discarding the rest.", + stacklevel=2, + ) + distances = distances.min(axis=1) + else: + k, p = distances.shape + n = coordinates.shape[0] + raise ValueError( + " Input distance matrix has an invalid shape: {k},{p}." + " Distances supplied can either be 2 dimensional" + " square matrices with the same number of rows" + " as `coordinates` ({n}) or 1 dimensional and contain" + " the shortest distance from each point in " + " `coordinates` to some other point in coordinates." + ) + elif distances.ndim == 1: + if distances.shape[0] != coordinates.shape[0]: + raise ValueError( + f"Distances are not aligned with coordinates! Distance" + f" matrix must be (n_coordinates, n_coordinates), but recieved" + f" {distances.shape} instead of ({coordinates.shape[0]},)" + ) + else: + raise ValueError( + "Distances supplied can either be 2 dimensional" + " square matrices with the same number of rows" + " as `coordinates` or 1 dimensional and contain" + " the shortest distance from each point in " + " `coordinates` to some other point in coordinates." + " Input matrix was {distances.ndim} dimensioanl" + ) + else: + try: + tree + except NameError: + tree = _build_best_tree(coordinates, metric) + finally: + distances, indices = _k_neighbors(tree, coordinates, k=1) + + counts, bins = numpy.histogram(distances.squeeze(), bins=support) + fracs = numpy.cumsum(counts) / counts.sum() + + return bins, numpy.asarray([0, *fracs]) + + +def j_function( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + truncate=True, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: tuple of numpy.ndarray + precomputed distances to use to evaluate the j function. + The first must be of shape (n,n) or (n,) and is used in the g function. + the second must be of shape (n,p) or (p,) (with p possibly equal to n) + used in the f function. + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern for the f function. + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + truncate: bool (default: True) + whether or not to truncate the results when the F function reaches one. If the + F function is one but the G function is less than one, this function will return + numpy.nan values. + """ + if distances is not None: + g_distances, f_distances = distances + else: + g_distances = f_distances = None + fsupport, fstats = f_function( + coordinates, + support=support, + distances=f_distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + ) + + gsupport, gstats = g_function( + coordinates, + support=support, + distances=g_distances, + metric=metric, + edge_correction=edge_correction, + ) + + if isinstance(support, numpy.ndarray): + if not numpy.allclose(gsupport, support): + gfunction = interpolate.interp1d(gsupport, gstats, fill_value=1) + gstats = gfunction(support) + gsupport = support + if not (numpy.allclose(gsupport, fsupport)): + ffunction = interpolate.interp1d(fsupport, fstats, fill_value=1) + fstats = ffunction(gsupport) + fsupport = gsupport + + with numpy.errstate(invalid="ignore", divide="ignore"): + hazard_ratio = (1 - gstats) / (1 - fstats) + if truncate: + both_zero = (gstats == 1) & (fstats == 1) + hazard_ratio[both_zero] = 1 + is_inf = numpy.isinf(hazard_ratio) + first_inf = is_inf.argmax() + if not is_inf.any(): + first_inf = len(hazard_ratio) + if first_inf < len(hazard_ratio) and isinstance(support, int): + warnings.warn( + f"requested {support} bins to evaluate the J function, but" + f" it reaches infinity at d={gsupport[first_inf]:.4f}, meaning only" + f" {first_inf} bins will be used to characterize the J function.", + stacklevel=2, + ) + else: + first_inf = len(gsupport) + 1 + return (gsupport[:first_inf], hazard_ratio[:first_inf]) + + +def k_function( + coordinates, support=None, distances=None, metric="euclidean", edge_correction=None, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + """ + coordinates, support, distances, metric, hull, edge_correction = _prepare( + coordinates, support, distances, metric, None, edge_correction + ) + n = coordinates.shape[0] + upper_tri_n = n * (n - 1) * 0.5 + if distances is not None: + if distances.ndim == 1: + if distances.shape[0] != upper_tri_n: + raise ValueError( + f"Shape of inputted distances is not square, nor is the upper triangular" + f" matrix matching the number of input points. The shape of the input matrix" + f" is {distances.shape}, but required shape is ({upper_tri_n},) or ({n},{n})" + ) + upper_tri_distances = distances + elif distances.shape[0] == distances.shape[1] == n: + upper_tri_distances = distances[numpy.triu_indices_from(distances, k=1)] + else: + raise ValueError( + f"Shape of inputted distances is not square, nor is the upper triangular" + f" matrix matching the number of input points. The shape of the input matrix" + f" is {distances.shape}, but required shape is ({upper_tri_n},) or ({n},{n})" + ) + else: + upper_tri_distances = spatial.distance.pdist(coordinates, metric=metric) + n_pairs_less_than_d = (upper_tri_distances < support.reshape(-1, 1)).sum(axis=1) + intensity = n / _area(hull) + k_estimate = ((n_pairs_less_than_d * 2) / n) / intensity + return support, k_estimate + + +def l_function( + coordinates, + support=None, + permutations=9999, + distances=None, + metric="euclidean", + edge_correction=None, + linearized=False, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + linearized : bool + whether or not to subtract l from its expected value (support) at each + distance bin. This centers the l function on zero for all distances. + Proposed by Besag (1977) #TODO: fix besag ref + """ + + support, k_estimate = k_function( + coordinates, + support=support, + distances=distances, + metric=metric, + edge_correction=edge_correction, + ) + + l = numpy.sqrt(k_estimate / numpy.pi) + + if linearized: + return support, l - support + return support, l + + +# ------------------------------------------------------------# +# Statistical Tests based on Ripley Functions # +# ------------------------------------------------------------# + +FtestResult = namedtuple( + "FtestResult", ("support", "statistic", "pvalue", "simulations") +) +GtestResult = namedtuple( + "GtestResult", ("support", "statistic", "pvalue", "simulations") +) +JtestResult = namedtuple( + "JtestResult", ("support", "statistic", "pvalue", "simulations") +) +KtestResult = namedtuple( + "KtestResult", ("support", "statistic", "pvalue", "simulations") +) +LtestResult = namedtuple( + "LtestResult", ("support", "statistic", "pvalue", "simulations") +) + +_ripley_dispatch = { + "F": (f_function, FtestResult), + "G": (g_function, GtestResult), + "J": (j_function, JtestResult), + "K": (k_function, KtestResult), + "L": (l_function, LtestResult), +} + + +def _ripley_test( + calltype, + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, + **kwargs, +): + stat_function, result_container = _ripley_dispatch.get(calltype) + core_kwargs = dict(support=support, metric=metric, edge_correction=edge_correction,) + tree = _build_best_tree(coordinates, metric=metric) + + if calltype in ("F", "J"): # these require simulations + core_kwargs["hull"] = hull + # amortize to avoid doing this every time + empty_space_points = simulate_from(coordinates, size=(1000, 1)) + if distances is None: + empty_space_distances, _ = _k_neighbors(tree, empty_space_points, k=1) + if calltype == "F": + distances = empty_space_distances.squeeze() + else: # calltype == 'J': + n_distances, _ = _k_neighbors(tree, coordinates, k=1) + distances = (n_distances.squeeze(), empty_space_distances.squeeze()) + else: + pass + core_kwargs.update(**kwargs) + + observed_support, observed_statistic = stat_function( + tree, distances=distances, **core_kwargs + ) + core_kwargs["support"] = observed_support + + if keep_simulations: + simulations = numpy.empty((len(observed_support), n_simulations)).T + pvalues = numpy.ones_like(observed_support) + for i_replication in range(n_simulations): + random_i = simulate_from(tree.data) + if calltype in ("F", "J"): + random_tree = _build_best_tree(random_i, metric) + empty_distances, _ = random_tree.query(empty_space_points, k=1) + if calltype == "F": + core_kwargs["distances"] = empty_distances.squeeze() + else: # calltype == 'J': + n_distances, _ = _k_neighbors(random_tree, random_i, k=1) + core_kwargs["distances"] = ( + n_distances.squeeze(), + empty_distances.squeeze(), + ) + rep_support, simulations_i = stat_function(random_i, **core_kwargs) + pvalues += simulations_i >= observed_statistic + if keep_simulations: + simulations[i_replication] = simulations_i + pvalues /= n_simulations + 1 + pvalues = numpy.minimum(pvalues, 1 - pvalues) + return result_container( + observed_support, + observed_statistic, + pvalues, + simulations if keep_simulations else None, + ) + + +def f_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. + """ + + return _ripley_test( + "F", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) + + +def g_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. + """ + return _ripley_test( + "G", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) + + +def j_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + truncate=True, + keep_simulations=False, + n_simulations=9999, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. + """ + return _ripley_test( + "J", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + truncate=truncate, + ) + + +def k_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + keep_simulations=False, + n_simulations=9999, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. + """ + return _ripley_test( + "K", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) + + +def l_test( + coordinates, + support=None, + distances=None, + metric="euclidean", + hull=None, + edge_correction=None, + linearized=False, + keep_simulations=False, + n_simulations=9999, +): + """ + coordinates : numpy.ndarray, (n,2) + input coordinates to function + support : tuple of length 1, 2, or 3, int, or numpy.ndarray + tuple, encoding (stop,), (start, stop), or (start, stop, num) + int, encoding number of equally-spaced intervals + numpy.ndarray, used directly within numpy.histogram + distances: numpy.ndarray, (n, p) or (p,) + distances from every point in a random point set of size p + to some point in `coordinates` + metric: str or callable + distance metric to use when building search tree + hull: bounding box, scipy.spatial.ConvexHull, shapely.geometry.Polygon, or pygeos.Geometry + the hull used to construct a random sample pattern, if distances is None + edge_correction: bool or str + whether or not to conduct edge correction. Not yet implemented. + keep_simulations: bool + whether or not to keep the simulation envelopes. If so, + will be returned as the result's simulations attribute + n_simulations: int + how many simulations to conduct, assuming that the reference pattern + has complete spatial randomness. + """ + return _ripley_test( + "L", + coordinates, + support=support, + distances=distances, + metric=metric, + hull=hull, + edge_correction=edge_correction, + linearized=linearized, + keep_simulations=keep_simulations, + n_simulations=n_simulations, + ) diff --git a/pointpats/tests/test_distance_statistics.py b/pointpats/tests/test_distance_statistics.py index 4d88424..6c075f0 100644 --- a/pointpats/tests/test_distance_statistics.py +++ b/pointpats/tests/test_distance_statistics.py @@ -1,35 +1,77 @@ import unittest import numpy as np -from ..distance_statistics import * +from ..distance_statistics import G, F, J, K, L from ..pointpattern import PointPattern class TestDistanceStatistics(unittest.TestCase): def setUp(self): - self.points = [[66.22, 32.54], [22.52, 22.39], [31.01, 81.21], - [9.47, 31.02], [30.78, 60.10], [75.21, 58.93], - [79.26, 7.68], [8.23, 39.93], [98.73, 77.17], - [89.78, 42.53], [65.19, 92.08], [54.46, 8.48]] + self.points = [ + [66.22, 32.54], + [22.52, 22.39], + [31.01, 81.21], + [9.47, 31.02], + [30.78, 60.10], + [75.21, 58.93], + [79.26, 7.68], + [8.23, 39.93], + [98.73, 77.17], + [89.78, 42.53], + [65.19, 92.08], + [54.46, 8.48], + ] self.pp = PointPattern(self.points) def test_distance_statistics_G(self): g = G(self.pp, intervals=20) distance_domain_sequence = [ - 0.0, 1.73156208378, 3.46312416757, 5.19468625135, - 6.92624833514, 8.65781041892, 10.3893725027, 12.1209345865, - 13.8524966703, 15.5840587541, 17.3156208378, 19.0471829216, - 20.7787450054, 22.5103070892, 24.241869173, 25.9734312568, - 27.7049933406, 29.4365554243, 31.1681175081, 32.8996795919, - 34.6312416757, 36.3628037595 + 0.0, + 1.73156208378, + 3.46312416757, + 5.19468625135, + 6.92624833514, + 8.65781041892, + 10.3893725027, + 12.1209345865, + 13.8524966703, + 15.5840587541, + 17.3156208378, + 19.0471829216, + 20.7787450054, + 22.5103070892, + 24.241869173, + 25.9734312568, + 27.7049933406, + 29.4365554243, + 31.1681175081, + 32.8996795919, + 34.6312416757, + 36.3628037595, ] envelop = [ - 0.0, 0.0146894910196, 0.0574759094992, 0.124697772575, - 0.210831326089, 0.309238901911, 0.413008064625, 0.515735506438, - 0.612136108644, 0.698406273989, 0.772327023751, 0.83314205264, - 0.881278728773, 0.917991964321, 0.945004075083, 0.964194409454, - 0.977368288342, 0.986112304319, 0.991726501147, 0.995214862073, - 0.997313134422, 0.998535316975 + 0.0, + 0.0146894910196, + 0.0574759094992, + 0.124697772575, + 0.210831326089, + 0.309238901911, + 0.413008064625, + 0.515735506438, + 0.612136108644, + 0.698406273989, + 0.772327023751, + 0.83314205264, + 0.881278728773, + 0.917991964321, + 0.945004075083, + 0.964194409454, + 0.977368288342, + 0.986112304319, + 0.991726501147, + 0.995214862073, + 0.997313134422, + 0.998535316975, ] np.testing.assert_array_almost_equal(g.ev, envelop) np.testing.assert_array_almost_equal(g.d, distance_domain_sequence) @@ -37,20 +79,52 @@ def test_distance_statistics_G(self): def test_distance_statistics_F(self): f = F(self.pp, intervals=20) distance_domain_sequence = [ - 0.0, 1.73156208378, 3.46312416757, 5.19468625135, - 6.92624833514, 8.65781041892, 10.3893725027, 12.1209345865, - 13.8524966703, 15.5840587541, 17.3156208378, 19.0471829216, - 20.7787450054, 22.5103070892, 24.241869173, 25.9734312568, - 27.7049933406, 29.4365554243, 31.1681175081, 32.8996795919, - 34.6312416757, 36.3628037595 + 0.0, + 1.73156208378, + 3.46312416757, + 5.19468625135, + 6.92624833514, + 8.65781041892, + 10.3893725027, + 12.1209345865, + 13.8524966703, + 15.5840587541, + 17.3156208378, + 19.0471829216, + 20.7787450054, + 22.5103070892, + 24.241869173, + 25.9734312568, + 27.7049933406, + 29.4365554243, + 31.1681175081, + 32.8996795919, + 34.6312416757, + 36.3628037595, ] envelop = [ - 0.0, 0.0146894910196, 0.0574759094992, 0.124697772575, - 0.210831326089, 0.309238901911, 0.413008064625, 0.515735506438, - 0.612136108644, 0.698406273989, 0.772327023751, 0.83314205264, - 0.881278728773, 0.917991964321, 0.945004075083, 0.964194409454, - 0.977368288342, 0.986112304319, 0.991726501147, 0.995214862073, - 0.997313134422, 0.998535316975 + 0.0, + 0.0146894910196, + 0.0574759094992, + 0.124697772575, + 0.210831326089, + 0.309238901911, + 0.413008064625, + 0.515735506438, + 0.612136108644, + 0.698406273989, + 0.772327023751, + 0.83314205264, + 0.881278728773, + 0.917991964321, + 0.945004075083, + 0.964194409454, + 0.977368288342, + 0.986112304319, + 0.991726501147, + 0.995214862073, + 0.997313134422, + 0.998535316975, ] np.testing.assert_array_almost_equal(f.ev, envelop) np.testing.assert_array_almost_equal(f.d, distance_domain_sequence) @@ -58,10 +132,20 @@ def test_distance_statistics_F(self): def test_distance_statistics_J(self): j = J(self.pp, intervals=20) distance_domain_sequence = [ - 0.0, 1.73156208378, 3.46312416757, 5.19468625135, - 6.92624833514, 8.65781041892, 10.3893725027, 12.1209345865, - 13.8524966703, 15.5840587541, 17.3156208378, 19.0471829216, - 20.7787450054, 22.5103070892 + 0.0, + 1.73156208378, + 3.46312416757, + 5.19468625135, + 6.92624833514, + 8.65781041892, + 10.3893725027, + 12.1209345865, + 13.8524966703, + 15.5840587541, + 17.3156208378, + 19.0471829216, + 20.7787450054, + 22.5103070892, ] for val in j.ev: np.testing.assert_approx_equal(val, 1.0) @@ -70,35 +154,109 @@ def test_distance_statistics_J(self): def test_distance_statistics_K(self): k = K(self.pp, intervals=20) - distance_domain_sequence = [ 0. , 1.055, 2.11 , 3.165, 4.22 , - 5.275, 6.33 , 7.385, 8.44 , 9.495, - 10.55 , 11.605, 12.66 , 13.715, 14.77 , - 15.825, 16.88 , 17.935, 18.99 , 20.045, - 21.1 , 22.155] + distance_domain_sequence = [ + 0.0, + 1.055, + 2.11, + 3.165, + 4.22, + 5.275, + 6.33, + 7.385, + 8.44, + 9.495, + 10.55, + 11.605, + 12.66, + 13.715, + 14.77, + 15.825, + 16.88, + 17.935, + 18.99, + 20.045, + 21.1, + 22.155, + ] - envelope = [0. , 3.49667116, 13.98668465, 31.47004047, - 55.94673861, 87.41677908, 125.88016188, 171.336887, - 223.78695445, 283.23036422, 349.66711633, 423.09721075, - 503.52064751, 590.93742659, 685.347548 , 786.75101173, - 895.1478178 , 1010.53796618, 1132.9214569 , 1262.29828994, - 1398.6684653 , 1542.031983 ] + envelope = [ + 0.0, + 3.49667116, + 13.98668465, + 31.47004047, + 55.94673861, + 87.41677908, + 125.88016188, + 171.336887, + 223.78695445, + 283.23036422, + 349.66711633, + 423.09721075, + 503.52064751, + 590.93742659, + 685.347548, + 786.75101173, + 895.1478178, + 1010.53796618, + 1132.9214569, + 1262.29828994, + 1398.6684653, + 1542.031983, + ] np.testing.assert_array_almost_equal(k.ev, envelope) np.testing.assert_array_almost_equal(k.d, distance_domain_sequence) def test_distance_statistics_L(self): l = L(self.pp, intervals=20) - distance_domain_sequence = [ 0. , 1.055, 2.11 , 3.165, 4.22 , - 5.275, 6.33 , 7.385, 8.44 , 9.495, - 10.55 , 11.605, 12.66 , 13.715, 14.77 , - 15.825, 16.88 , 17.935, 18.99 , 20.045, - 21.1 , 22.155] - l_stats = [0. , -1.055 , -2.11 , -3.165 , - -4.22 , -5.275 , -6.33 , -7.385 , - -8.44 , -3.42556019, -4.48056019, -5.53556019, - -6.59056019, -7.64556019, -8.70056019, -7.24151591, - -8.29651591, -9.35151591, -10.40651591, -11.46151591, - -12.51651591, -10.01612038] + distance_domain_sequence = [ + 0.0, + 1.055, + 2.11, + 3.165, + 4.22, + 5.275, + 6.33, + 7.385, + 8.44, + 9.495, + 10.55, + 11.605, + 12.66, + 13.715, + 14.77, + 15.825, + 16.88, + 17.935, + 18.99, + 20.045, + 21.1, + 22.155, + ] + l_stats = [ + 0.0, + -1.055, + -2.11, + -3.165, + -4.22, + -5.275, + -6.33, + -7.385, + -8.44, + -3.42556019, + -4.48056019, + -5.53556019, + -6.59056019, + -7.64556019, + -8.70056019, + -7.24151591, + -8.29651591, + -9.35151591, + -10.40651591, + -11.46151591, + -12.51651591, + -10.01612038, + ] np.testing.assert_array_almost_equal(l.d, distance_domain_sequence) np.testing.assert_array_almost_equal(l.l, l_stats) diff --git a/pointpats/tests/test_ripley.py b/pointpats/tests/test_ripley.py new file mode 100644 index 0000000..1da3b98 --- /dev/null +++ b/pointpats/tests/test_ripley.py @@ -0,0 +1,316 @@ +import numpy +from scipy import spatial +from pointpats import distance_statistics as ripley, geometry, random +from libpysal.cg import alpha_shape_auto +import pygeos +import warnings +import pytest + +points = numpy.asarray( + [ + [66.22, 32.54], + [22.52, 22.39], + [31.01, 81.21], + [9.47, 31.02], + [30.78, 60.10], + [75.21, 58.93], + [79.26, 7.68], + [8.23, 39.93], + [98.73, 77.17], + [89.78, 42.53], + [65.19, 92.08], + [54.46, 8.48], + ] +) + +tree = spatial.cKDTree(points) + +chull = spatial.ConvexHull(points) +ashape = alpha_shape_auto(points) +pygeos_ashape = pygeos.from_shapely(ashape) + +bbox = numpy.asarray((*points.min(axis=0), *points.max(axis=0))) + +support = numpy.linspace(0, 100, num=15) + +d_self = spatial.distance.pdist(points) +D_self = spatial.distance.squareform(d_self) +try: + numpy.random.seed(2478879) + random_pattern = random.poisson(bbox, size=500) + D_other = spatial.distance.cdist(points, random_pattern) +except: + # will cause failures in all ripley tests later from NameErrors about D_other + # If D_other is missing, then test_simulate should also fail. + pass + + +def test_primitives(): + area_bbox = (bbox[2] - bbox[0]) * (bbox[3] - bbox[1]) + assert area_bbox == geometry.area(bbox) + area_chull = chull.volume + assert area_chull == geometry.area(chull) + area_pgon = geometry.area(ashape) + assert area_pgon == ashape.area + assert area_pgon == geometry.area(pygeos_ashape) + point_in = list(ashape.centroid.coords)[0] + point_out = (100, 100) + + assert geometry.contains(chull, *point_in) + assert geometry.contains(ashape, *point_in) + assert geometry.contains(pygeos_ashape, *point_in) + assert geometry.contains(bbox, *point_in) + + assert not (geometry.contains(chull, *point_out)) + assert not (geometry.contains(ashape, *point_out)) + assert not (geometry.contains(pygeos_ashape, *point_out)) + assert not (geometry.contains(bbox, *point_out)) + + numpy.testing.assert_array_equal(bbox, geometry.bbox(bbox)) + numpy.testing.assert_array_equal(bbox, geometry.bbox(ashape)) + numpy.testing.assert_array_equal(bbox, geometry.bbox(pygeos_ashape)) + numpy.testing.assert_array_equal(bbox, geometry.bbox(chull)) + numpy.testing.assert_array_equal(bbox, geometry.bbox(points)) + + +def test_tree_functions(): + kdtree = ripley._build_best_tree(points, "euclidean") + balltree = ripley._build_best_tree(points, "haversine") + try: + failtree = ripley._build_best_tree(points, "notametric") + except KeyError: + pass + except: + raise AssertionError("Failed to raise an error for _build_best_tree") + + with pytest.warns(UserWarning): + mytree = ripley._build_best_tree(points, lambda u, v: numpy.var(u - v)) + + # check that neighbors are not returned as a self-neighbor + # for self-neighbor queries + distances, indices = ripley._k_neighbors(kdtree, points, k=1) + assert (indices.squeeze() != numpy.arange(points.shape[0])).all() + distances, indices = ripley._k_neighbors(balltree, points, k=1) + assert (indices.squeeze() != numpy.arange(points.shape[0])).all() + distances, indices = ripley._k_neighbors(mytree, points, k=1) + assert (indices.squeeze() != numpy.arange(points.shape[0])).all() + + +def test_prepare(): + tmp_bbox = ripley._prepare_hull(points, "bbox") + numpy.testing.assert_array_equal(bbox, tmp_bbox) + + tmp_bbox = ripley._prepare_hull(points, None) + numpy.testing.assert_array_equal(bbox, tmp_bbox) + + tmp_bbox = ripley._prepare_hull(points, bbox) + assert tmp_bbox is bbox # pass-through with no modification + + tmp_ashape = ripley._prepare_hull(points, "alpha") + assert tmp_ashape.equals(ashape) + + tmp_ashape = ripley._prepare_hull(points, "α") + assert tmp_ashape.equals(ashape) + + tmp_ashape = ripley._prepare_hull(points, ashape) + assert tmp_ashape is ashape # pass-through with no modification + + tmp_ashape = ripley._prepare_hull(points, pygeos_ashape) + assert pygeos.equals(tmp_ashape, pygeos_ashape) + + tmp_chull = ripley._prepare_hull(points, chull) + assert tmp_chull is chull # pass-through with no modification + + tmp_chull = ripley._prepare_hull(points, "convex") + numpy.testing.assert_allclose(tmp_chull.equations, chull.equations) + + # -------------------------------------------------------------------------- + # Now, check the prepare generally + # check edge correction raise + + try: + ripley._prepare(points, None, None, "euclidean", ashape, "ripley") + raise AssertionError() + except NotImplementedError: + pass + except AssertionError: + raise AssertionError("Did not raise an error when edge correction is set") + + # check tree gets converted into data with no tree + out = ripley._prepare(tree, None, None, "euclidean", ashape, None) + numpy.testing.assert_array_equal(points, out[0]) + + # check three distance metrics + out = ripley._prepare(tree, None, None, "euclidean", ashape, None)[3] + assert out == "euclidean" + out = ripley._prepare(tree, None, None, "haversine", ashape, None)[3] + assert out == "haversine" + test_func = lambda u, v: numpy.var(u - v) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + out = ripley._prepare(tree, None, None, test_func, ashape, None)[3] + assert out is test_func + + # check precomputed failure + try: + out = ripley._prepare(tree, None, None, "precomputed", ashape, None) + raise AssertionError() + except ValueError: + pass + except AssertionError: + raise AssertionError( + 'Did not raise when metric="precomputed" but' " no distances provided" + ) + + # check support setting will: + # give 20 breaks from 0 to max dist if none + out = ripley._prepare(tree, None, None, "euclidean", ashape, None)[1] + assert len(out) == 20 + assert out.min() == 0 + numpy.testing.assert_allclose(out.max(), 34.631242) + numpy.testing.assert_allclose(out.min(), 0) + out = ripley._prepare(tree, 30, None, "euclidean", ashape, None)[1] + assert len(out) == 30 + numpy.testing.assert_allclose(out.max(), 34.631242) + numpy.testing.assert_allclose(out.min(), 0) + # give tuple correctly for 1, 2, and 3-length tuples + out = ripley._prepare(tree, (4,), None, "euclidean", ashape, None)[1] + assert out.max() == 4 + out = ripley._prepare(tree, (2, 10), None, "euclidean", ashape, None)[1] + assert out.max() == 10 + assert out.min() == 2 + out = ripley._prepare(tree, (2, 10, 5), None, "euclidean", ashape, None)[1] + assert out.max() == 10 + assert out.min() == 2 + assert len(out) == 5 + # passthrough support + out = ripley._prepare(tree, numpy.arange(40), None, "euclidean", ashape, None)[1] + assert len(out) == 40 + assert (out == numpy.arange(40)).all() + + +def test_simulate(): + + assert random.poisson(ashape).shape == (100, 2) + assert random.poisson(chull).shape == (100, 2) + assert random.poisson(bbox).shape == (100, 2) + + assert random.poisson(ashape, intensity=1e-2).shape == (50, 2) + assert random.poisson(chull, intensity=1e-2).shape == (52, 2) + assert random.poisson(bbox, intensity=1e-2).shape == (76, 2) + + assert random.poisson(ashape, size=90).shape == (90, 2) + assert random.poisson(chull, intensity=1e-2).shape == (52, 2) + assert random.poisson(bbox, intensity=1e-2, size=3).shape == (3, 76, 2) + assert random.poisson(bbox, intensity=None, size=(10, 4)).shape == (4, 10, 2) + + # still need to check the other simulators + # normal + # cluster poisson + # cluster normal + + +def test_f(): + + # -------------------------------------------------------------------------# + # Check f function has consistent performance + + nn_other = D_other.min(axis=0) + n_obs_at_dist, histogram_support = numpy.histogram(nn_other, bins=support) + manual_f = numpy.asarray([0, *numpy.cumsum(n_obs_at_dist) / n_obs_at_dist.sum()]) + numpy.random.seed(2478879) + f_test = ripley.f_test(points, support=support, distances=D_other, n_simulations=99) + + numpy.testing.assert_allclose(support, f_test.support) + numpy.testing.assert_allclose(manual_f, f_test.statistic) + numpy.testing.assert_allclose( + f_test.pvalue < 0.05, [1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1] + ) + assert f_test.simulations is None + + f_test = ripley.f_test( + points, + support=support, + distances=D_other, + n_simulations=99, + keep_simulations=True, + ) + assert f_test.simulations.shape == (99, 15) + + +def test_g(): + # -------------------------------------------------------------------------# + # Check f function works, has statistical results that are consistent + + nn_self = (D_self + numpy.eye(points.shape[0]) * 10000).min(axis=0) + n_obs_at_dist, histogram_support = numpy.histogram(nn_self, bins=support) + numpy.random.seed(2478879) + manual_g = numpy.asarray([0, *numpy.cumsum(n_obs_at_dist) / n_obs_at_dist.sum()]) + g_test = ripley.g_test(points, support=support, n_simulations=99) + + numpy.testing.assert_allclose(support, g_test.support) + numpy.testing.assert_allclose(manual_g, g_test.statistic) + numpy.testing.assert_allclose( + g_test.pvalue < 0.05, [1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1] + ) + assert g_test.simulations is None + + g_test = ripley.g_test( + points, support=support, n_simulations=99, keep_simulations=True + ) + assert g_test.simulations.shape == (99, 15) + + +def test_j(): + # -------------------------------------------------------------------------# + # Check j function works, matches manual, is truncated correctly + + numpy.random.seed(2478879) + j_test = ripley.j_test(points, support=support, n_simulations=99) + numpy.random.seed(2478879) + j_test_fullno = ripley.j_test( + points, support=support, n_simulations=0, truncate=False + ) + numpy.testing.assert_array_equal(j_test.support[:4], support[:4]) + numpy.testing.assert_array_equal(j_test_fullno.support, support) + numpy.random.seed(2478879) + _, f_tmp = ripley.f(points, support=support) + _, g_tmp = ripley.g(points, support=support) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + manual_j = (1 - g_tmp) / (1 - f_tmp) + assert numpy.isnan(manual_j[-1]) + assert len(manual_j) > len(j_test.support) + assert len(manual_j) == len(j_test_fullno.support) + + numpy.testing.assert_allclose(j_test.statistic, manual_j[:4], atol=0.1, rtol=0.05) + + +def test_k(): + # -------------------------------------------------------------------------# + # Check K function works, matches a manual, slower explicit computation + + k_test = ripley.k_test(points, support=support) + n = points.shape[0] + intensity = n / ripley._area(bbox) + manual_unscaled_k = numpy.asarray( + [(d_self < d).sum() for d in support], dtype=float + ) + numpy.testing.assert_allclose( + k_test.statistic, manual_unscaled_k * 2 / n / intensity + ) + + +def test_l(): + # -------------------------------------------------------------------------# + # Check L Function works, can be linearized, and has the right value + _, k = ripley.k(points, support=support) + l_test = ripley.l_test(points, support=support, n_simulations=0) + l_test_lin = ripley.l_test( + points, support=support, n_simulations=0, linearized=True + ) + + numpy.testing.assert_allclose(l_test.statistic, numpy.sqrt(k / numpy.pi)) + numpy.testing.assert_allclose( + l_test_lin.statistic, numpy.sqrt(k / numpy.pi) - l_test.support + ) diff --git a/pointpats/tests/test_spacetime.py b/pointpats/tests/test_spacetime.py index 9156b61..9193462 100644 --- a/pointpats/tests/test_spacetime.py +++ b/pointpats/tests/test_spacetime.py @@ -48,11 +48,7 @@ def setUp(self): def test_jacquez(self): result = jacquez(self.events.space, self.events.t, k=3, permutations=1) - if scp_version > 11: - self.assertEquals(result['stat'], 12) - else: - self.assertEquals(result['stat'], 13) - + self.assertEquals(result['stat'], 12) class ModifiedKnox_Tester(unittest.TestCase): diff --git a/requirements_tests.txt b/requirements_tests.txt index fa8032a..4ec6ce0 100644 --- a/requirements_tests.txt +++ b/requirements_tests.txt @@ -3,3 +3,8 @@ nose-progressive nose-exclude coverage coveralls +scikit-learn +# shapely these need to be gotten from conda because they're incompatible from pypi +# pygeos +# geopandas +pytest