import warnings
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy
import seaborn as sbn
from esda.moran import Moran, Moran_BV, Moran_Local, Moran_Local_BV
from libpysal.weights.spatial_lag import lag_spatial
from matplotlib import colors, patches
from spreg import OLS
from ._viz_utils import mask_local_auto, moran_hot_cold_spots, splot_colors
"""
Lightweight visualizations for esda using Matplotlib and Geopandas
TODO
* geopandas plotting, change round shapes in legends to boxes
* prototype moran_facet using `seaborn.FacetGrid`
"""
__author__ = "Stefanie Lumnitz <stefanie.lumitz@gmail.com>"
def _create_moran_fig_ax(ax, figsize, aspect_equal):
"""
Creates matplotlib figure and axes instances
for plotting moran visualizations. Adds common viz design.
"""
if ax is None:
fig = plt.figure(figsize=figsize)
ax = fig.add_subplot(111)
else:
fig = ax.get_figure()
ax.spines["left"].set_position(("axes", -0.05))
ax.spines["right"].set_color("none")
ax.spines["bottom"].set_position(("axes", -0.05))
ax.spines["top"].set_color("none")
if aspect_equal is True:
ax.set_aspect("equal")
return fig, ax
[docs]def moran_scatterplot(
moran,
zstandard=True,
p=None,
aspect_equal=True,
ax=None,
scatter_kwds=None,
fitline_kwds=None,
):
"""
Moran Scatterplot
Parameters
----------
moran : esda.moran instance
Values of Moran's I Global, Bivariate and Local
Autocorrelation Statistics
zstandard : bool, optional
If True, Moran Scatterplot will show z-standardized attribute and
spatial lag values. Default =True.
p : float, optional
If given, the p-value threshold for significance
for Local Autocorrelation analysis. Points will be colored by
significance. By default it will not be colored.
Default =None.
aspect_equal : bool, optional
If True, Axes will show the same aspect or visual proportions
for Moran Scatterplot.
ax : Matplotlib Axes instance, optional
If given, the Moran plot will be created inside this axis.
Default =None.
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline.
Default =None.
Returns
-------
fig : Matplotlib Figure instance
Moran scatterplot figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import (Moran, Moran_BV,
... Moran_Local, Moran_Local_BV)
>>> from splot.esda import moran_scatterplot
Load data and calculate weights
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> x = gdf['Suicids'].values
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
Calculate esda.moran Objects
>>> moran = Moran(y, w)
>>> moran_bv = Moran_BV(y, x, w)
>>> moran_loc = Moran_Local(y, w)
>>> moran_loc_bv = Moran_Local_BV(y, x, w)
Plot
>>> fig, axs = plt.subplots(2, 2, figsize=(10,10),
... subplot_kw={'aspect': 'equal'})
>>> moran_scatterplot(moran, p=0.05, ax=axs[0,0])
>>> moran_scatterplot(moran_loc, p=0.05, ax=axs[1,0])
>>> moran_scatterplot(moran_bv, p=0.05, ax=axs[0,1])
>>> moran_scatterplot(moran_loc_bv, p=0.05, ax=axs[1,1])
>>> plt.show()
"""
if isinstance(moran, Moran):
if p is not None:
warnings.warn(
"`p` is only used for plotting `esda.moran.Moran_Local`\n"
"or `Moran_Local_BV` objects"
)
fig, ax = _moran_global_scatterplot(
moran=moran,
zstandard=zstandard,
ax=ax,
aspect_equal=aspect_equal,
scatter_kwds=scatter_kwds,
fitline_kwds=fitline_kwds,
)
elif isinstance(moran, Moran_BV):
if p is not None:
warnings.warn(
"`p` is only used for plotting `esda.moran.Moran_Local` "
"or `Moran_Local_BV` objects."
)
fig, ax = _moran_bv_scatterplot(
moran_bv=moran,
ax=ax,
aspect_equal=aspect_equal,
scatter_kwds=scatter_kwds,
fitline_kwds=fitline_kwds,
)
elif isinstance(moran, Moran_Local):
fig, ax = _moran_loc_scatterplot(
moran_loc=moran,
zstandard=zstandard,
ax=ax,
p=p,
aspect_equal=aspect_equal,
scatter_kwds=scatter_kwds,
fitline_kwds=fitline_kwds,
)
elif isinstance(moran, Moran_Local_BV):
fig, ax = _moran_loc_bv_scatterplot(
moran_loc_bv=moran,
ax=ax,
p=p,
aspect_equal=aspect_equal,
scatter_kwds=scatter_kwds,
fitline_kwds=fitline_kwds,
)
ax.xaxis.set_ticks_position("bottom")
ax.yaxis.set_ticks_position("left")
return fig, ax
def _moran_global_scatterplot(
moran,
zstandard=True,
aspect_equal=True,
ax=None,
scatter_kwds=None,
fitline_kwds=None,
):
"""
Global Moran's I Scatterplot.
Parameters
----------
moran : esda.moran.Moran instance
Values of Moran's I Global Autocorrelation Statistics
zstandard : bool, optional
If True, Moran Scatterplot will show z-standardized attribute and
spatial lag values. Default =True.
aspect_equal : bool, optional
If True, Axes will show the same aspect or visual proportions.
ax : Matplotlib Axes instance, optional
If given, the Moran plot will be created inside this axis.
Default =None.
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline.
Default =None.
Returns
-------
fig : Matplotlib Figure instance
Moran scatterplot figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran
>>> from splot.esda import moran_scatterplot
Load data and calculate weights
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
Calculate Global Moran
>>> moran = Moran(y, w)
plot
>>> moran_scatterplot(moran)
>>> plt.show()
customize plot
>>> fig, ax = moran_scatterplot(moran, zstandard=False,
... fitline_kwds=dict(color='#4393c3'))
>>> ax.set_xlabel('Donations')
>>> plt.show()
"""
# to set default as an empty dictionary that is later filled with defaults
if scatter_kwds is None:
scatter_kwds = dict()
if fitline_kwds is None:
fitline_kwds = dict()
# define customization defaults
scatter_kwds.setdefault("alpha", 0.6)
scatter_kwds.setdefault("color", splot_colors["moran_base"])
scatter_kwds.setdefault("s", 40)
fitline_kwds.setdefault("alpha", 0.9)
fitline_kwds.setdefault("color", splot_colors["moran_fit"])
# get fig and ax
fig, ax = _create_moran_fig_ax(ax, figsize=(7, 7), aspect_equal=aspect_equal)
# set labels
ax.set_xlabel("Attribute")
ax.set_ylabel("Spatial Lag")
ax.set_title("Moran Scatterplot" + " (" + str(round(moran.I, 2)) + ")")
# plot and set standards
if zstandard is True:
lag = lag_spatial(moran.w, moran.z)
fit = OLS(moran.z[:, None], lag[:, None])
# plot
ax.scatter(moran.z, lag, **scatter_kwds)
ax.plot(lag, fit.predy, **fitline_kwds)
# v- and hlines
ax.axvline(0, alpha=0.5, color="k", linestyle="--")
ax.axhline(0, alpha=0.5, color="k", linestyle="--")
else:
lag = lag_spatial(moran.w, moran.y)
b, a = numpy.polyfit(moran.y, lag, 1)
# plot
ax.scatter(moran.y, lag, **scatter_kwds)
ax.plot(moran.y, a + b * moran.y, **fitline_kwds)
# dashed vert at mean of the attribute
ax.vlines(moran.y.mean(), lag.min(), lag.max(), alpha=0.5, linestyle="--")
# dashed horizontal at mean of lagged attribute
ax.hlines(lag.mean(), moran.y.min(), moran.y.max(), alpha=0.5, linestyle="--")
return fig, ax
[docs]def plot_moran_simulation(
moran, aspect_equal=True, ax=None, fitline_kwds=None, **kwargs
):
"""
Global Moran's I simulated reference distribution.
Parameters
----------
moran : esda.moran.Moran instance
Values of Moran's I Global Autocorrelation Statistics
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
ax : Matplotlib Axes instance, optional
If given, the Moran plot will be created inside this axis.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the
vertical moran fitline. Default =None.
**kwargs : keyword arguments, optional
Keywords used for creating and designing the figure,
passed to seaborn.kdeplot.
Returns
-------
fig : Matplotlib Figure instance
Simulated reference distribution figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran
>>> from splot.esda import plot_moran_simulation
Load data and calculate weights
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
Calculate Global Moran
>>> moran = Moran(y, w)
plot
>>> plot_moran_simulation(moran)
>>> plt.show()
customize plot
>>> plot_moran_simulation(moran, fitline_kwds=dict(color='#4393c3'))
>>> plt.show()
"""
# to set default as an empty dictionary that is later filled with defaults
if fitline_kwds is None:
fitline_kwds = dict()
figsize = kwargs.pop("figsize", (7, 7))
# get fig and ax
fig, ax = _create_moran_fig_ax(ax, figsize, aspect_equal=aspect_equal)
# plot distribution
shade = kwargs.pop("shade", True)
color = kwargs.pop("color", splot_colors["moran_base"])
sbn.kdeplot(moran.sim, fill=shade, color=color, ax=ax, **kwargs)
# customize plot
fitline_kwds.setdefault("color", splot_colors["moran_fit"])
ax.vlines(moran.I, 0, 1, **fitline_kwds)
ax.vlines(moran.EI, 0, 1)
ax.set_title("Reference Distribution")
ax.set_xlabel("Moran I: " + str(round(moran.I, 2)))
return fig, ax
[docs]def plot_moran(
moran,
zstandard=True,
aspect_equal=True,
scatter_kwds=None,
fitline_kwds=None,
**kwargs
):
"""
Global Moran's I simulated reference distribution and scatterplot.
Parameters
----------
moran : esda.moran.Moran instance
Values of Moran's I Global Autocorrelation Statistics
zstandard : bool, optional
If True, Moran Scatterplot will show z-standardized attribute and
spatial lag values. Default =True.
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline
and vertical fitline. Default =None.
**kwargs : keyword arguments, optional
Keywords used for creating and designing the figure,
passed to seaborne.kdeplot.
Returns
-------
fig : Matplotlib Figure instance
Moran scatterplot and reference distribution figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran
>>> from splot.esda import plot_moran
Load data and calculate weights
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
Calculate Global Moran
>>> moran = Moran(y, w)
plot
>>> plot_moran(moran)
>>> plt.show()
customize plot
>>> plot_moran(moran, zstandard=False,
... fitline_kwds=dict(color='#4393c3'))
>>> plt.show()
"""
figsize = kwargs.pop("figsize", (10, 4))
fig, axs = plt.subplots(1, 2, figsize=figsize, subplot_kw={"aspect": "equal"})
plot_moran_simulation(moran, ax=axs[0], fitline_kwds=fitline_kwds, **kwargs)
moran_scatterplot(
moran,
zstandard=zstandard,
ax=axs[1],
scatter_kwds=scatter_kwds,
fitline_kwds=fitline_kwds,
)
axs[0].set(aspect="auto")
if aspect_equal is True:
axs[1].set_aspect("equal", "datalim")
else:
axs[1].set_aspect("auto")
return fig, axs
def _moran_bv_scatterplot(
moran_bv, ax=None, aspect_equal=True, scatter_kwds=None, fitline_kwds=None
):
"""
Bivariate Moran Scatterplot.
Parameters
----------
moran_bv : esda.moran.Moran_BV instance
Values of Bivariate Moran's I Autocorrelation Statistics
ax : Matplotlib Axes instance, optional
If given, the Moran plot will be created inside this axis.
Default =None.
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline.
Default =None.
Returns
-------
fig : Matplotlib Figure instance
Bivariate moran scatterplot figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran_BV
>>> from splot.esda import moran_scatterplot
Load data and calculate weights
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> x = gdf['Suicids'].values
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
Calculate Bivariate Moran
>>> moran_bv = Moran_BV(x, y, w)
plot
>>> moran_scatterplot(moran_bv)
>>> plt.show()
customize plot
>>> moran_scatterplot(moran_bv,
... fitline_kwds=dict(color='#4393c3'))
>>> plt.show()
"""
# to set default as an empty dictionary that is later filled with defaults
if scatter_kwds is None:
scatter_kwds = dict()
if fitline_kwds is None:
fitline_kwds = dict()
# define customization
scatter_kwds.setdefault("alpha", 0.6)
scatter_kwds.setdefault("color", splot_colors["moran_base"])
scatter_kwds.setdefault("s", 40)
fitline_kwds.setdefault("alpha", 0.9)
fitline_kwds.setdefault("color", splot_colors["moran_fit"])
# get fig and ax
fig, ax = _create_moran_fig_ax(ax, figsize=(7, 7), aspect_equal=aspect_equal)
# set labels
ax.set_xlabel("Attribute X")
ax.set_ylabel("Spatial Lag of Y")
ax.set_title("Bivariate Moran Scatterplot" + " (" + str(round(moran_bv.I, 2)) + ")")
# plot and set standards
lag = lag_spatial(moran_bv.w, moran_bv.zy)
fit = OLS(moran_bv.zy[:, None], lag[:, None])
# plot
ax.scatter(moran_bv.zx, lag, **scatter_kwds)
ax.plot(lag, fit.predy, **fitline_kwds)
# v- and hlines
ax.axvline(0, alpha=0.5, color="k", linestyle="--")
ax.axhline(0, alpha=0.5, color="k", linestyle="--")
return fig, ax
[docs]def plot_moran_bv_simulation(
moran_bv, ax=None, aspect_equal=True, fitline_kwds=None, **kwargs
):
"""
Bivariate Moran's I simulated reference distribution.
Parameters
----------
moran_bv : esda.moran.Moran_BV instance
Values of Bivariate Moran's I Autocorrelation Statistics
ax : Matplotlib Axes instance, optional
If given, the Moran plot will be created inside this axis.
Default =None.
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the
vertical moran fitline. Default =None.
**kwargs : keyword arguments, optional
Keywords used for creating and designing the figure,
passed to seaborne.kdeplot.
Returns
-------
fig : Matplotlib Figure instance
Bivariate moran reference distribution figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran_BV
>>> from splot.esda import plot_moran_bv_simulation
Load data and calculate weights
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> x = gdf['Suicids'].values
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
Calculate Bivariate Moran
>>> moran_bv = Moran_BV(x, y, w)
plot
>>> plot_moran_bv_simulation(moran_bv)
>>> plt.show()
customize plot
>>> plot_moran_bv_simulation(moran_bv,
... fitline_kwds=dict(color='#4393c3'))
>>> plt.show()
"""
# to set default as an empty dictionary that is later filled with defaults
if fitline_kwds is None:
fitline_kwds = dict()
figsize = kwargs.pop("figsize", (7, 7))
# get fig and ax
fig, ax = _create_moran_fig_ax(ax, figsize, aspect_equal=aspect_equal)
# plot distribution
shade = kwargs.pop("shade", True)
color = kwargs.pop("color", splot_colors["moran_base"])
sbn.kdeplot(moran_bv.sim, fill=shade, color=color, ax=ax, **kwargs)
# customize plot
fitline_kwds.setdefault("color", splot_colors["moran_fit"])
ax.vlines(moran_bv.I, 0, 1, **fitline_kwds)
ax.vlines(moran_bv.EI_sim, 0, 1)
ax.set_title("Reference Distribution")
ax.set_xlabel("Bivariate Moran I: " + str(round(moran_bv.I, 2)))
return fig, ax
[docs]def plot_moran_bv(
moran_bv, aspect_equal=True, scatter_kwds=None, fitline_kwds=None, **kwargs
):
"""
Bivariate Moran's I simulated reference distribution and scatterplot.
Parameters
----------
moran_bv : esda.moran.Moran_BV instance
Values of Bivariate Moran's I Autocorrelation Statistics
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline
and vertical fitline. Default =None.
**kwargs : keyword arguments, optional
Keywords used for creating and designing the figure,
passed to seaborne.kdeplot.
Returns
-------
fig : Matplotlib Figure instance
Bivariate moran scatterplot and reference distribution figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran_BV
>>> from splot.esda import plot_moran_bv
Load data and calculate weights
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> x = gdf['Suicids'].values
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
Calculate Bivariate Moran
>>> moran_bv = Moran_BV(x, y, w)
plot
>>> plot_moran_bv(moran_bv)
>>> plt.show()
customize plot
>>> plot_moran_bv(moran_bv, fitline_kwds=dict(color='#4393c3'))
>>> plt.show()
"""
figsize = kwargs.pop("figsize", (10, 4))
fig, axs = plt.subplots(1, 2, figsize=figsize, subplot_kw={"aspect": "equal"})
plot_moran_bv_simulation(moran_bv, ax=axs[0], fitline_kwds=fitline_kwds, **kwargs)
moran_scatterplot(
moran_bv,
ax=axs[1],
aspect_equal=aspect_equal,
scatter_kwds=scatter_kwds,
fitline_kwds=fitline_kwds,
)
axs[0].set(aspect="auto")
if aspect_equal is True:
axs[1].set_aspect("equal", "datalim")
else:
axs[1].set(aspect="auto")
return fig, axs
def _moran_loc_scatterplot(
moran_loc,
zstandard=True,
p=None,
aspect_equal=True,
ax=None,
scatter_kwds=None,
fitline_kwds=None,
):
"""
Moran Scatterplot with option of coloring of Local Moran Statistics
Parameters
----------
moran_loc : esda.moran.Moran_Local instance
Values of Moran's I Local Autocorrelation Statistics
p : float, optional
If given, the p-value threshold for significance. Points will
be colored by significance. By default it will not be colored.
Default =None.
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
ax : Matplotlib Axes instance, optional
If given, the Moran plot will be created inside this axis.
Default =None.
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline.
Default =None.
Returns
-------
fig : Matplotlib Figure instance
Moran Local scatterplot figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> import geopandas as gpd
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> from esda.moran import Moran_Local
>>> from splot.esda import moran_scatterplot
Load data and calculate Moran Local statistics
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
>>> m = Moran_Local(y, w)
plot
>>> moran_scatterplot(m)
>>> plt.show()
customize plot
>>> moran_scatterplot(m, p=0.05,
... fitline_kwds=dict(color='#4393c3'))
>>> plt.show()
"""
# to set default as an empty dictionary that is later filled with defaults
if scatter_kwds is None:
scatter_kwds = dict()
if fitline_kwds is None:
fitline_kwds = dict()
if p is not None:
if not isinstance(moran_loc, Moran_Local):
raise ValueError(
"`moran_loc` is not a\n " + "esda.moran.Moran_Local instance"
)
if "color" in scatter_kwds or "c" in scatter_kwds or "cmap" in scatter_kwds:
warnings.warn(
"To change the color use cmap with a colormap of 5,\n"
+ " color defines the LISA category"
)
# colors
spots = moran_hot_cold_spots(moran_loc, p)
hmap = colors.ListedColormap(
["#bababa", "#d7191c", "#abd9e9", "#2c7bb6", "#fdae61"]
)
# define customization
scatter_kwds.setdefault("alpha", 0.6)
scatter_kwds.setdefault("s", 40)
fitline_kwds.setdefault("alpha", 0.9)
# get fig and ax
fig, ax = _create_moran_fig_ax(ax, figsize=(7, 7), aspect_equal=aspect_equal)
# set labels
ax.set_xlabel("Attribute")
ax.set_ylabel("Spatial Lag")
ax.set_title("Moran Local Scatterplot")
# plot and set standards
if zstandard is True:
lag = lag_spatial(moran_loc.w, moran_loc.z)
fit = OLS(moran_loc.z[:, None], lag[:, None])
# v- and hlines
ax.axvline(0, alpha=0.5, color="k", linestyle="--")
ax.axhline(0, alpha=0.5, color="k", linestyle="--")
if p is not None:
fitline_kwds.setdefault("color", "k")
scatter_kwds.setdefault("cmap", hmap)
scatter_kwds.setdefault("c", spots)
ax.plot(lag, fit.predy, **fitline_kwds)
ax.scatter(moran_loc.z, fit.predy, **scatter_kwds)
else:
scatter_kwds.setdefault("color", splot_colors["moran_base"])
fitline_kwds.setdefault("color", splot_colors["moran_fit"])
ax.plot(lag, fit.predy, **fitline_kwds)
ax.scatter(moran_loc.z, fit.predy, **scatter_kwds)
else:
lag = lag_spatial(moran_loc.w, moran_loc.y)
b, a = numpy.polyfit(moran_loc.y, lag, 1)
# dashed vert at mean of the attribute
ax.vlines(moran_loc.y.mean(), lag.min(), lag.max(), alpha=0.5, linestyle="--")
# dashed horizontal at mean of lagged attribute
ax.hlines(
lag.mean(), moran_loc.y.min(), moran_loc.y.max(), alpha=0.5, linestyle="--"
)
if p is not None:
fitline_kwds.setdefault("color", "k")
scatter_kwds.setdefault("cmap", hmap)
scatter_kwds.setdefault("c", spots)
ax.plot(moran_loc.y, a + b * moran_loc.y, **fitline_kwds)
ax.scatter(moran_loc.y, lag, **scatter_kwds)
else:
scatter_kwds.setdefault("c", splot_colors["moran_base"])
fitline_kwds.setdefault("color", splot_colors["moran_fit"])
ax.plot(moran_loc.y, a + b * moran_loc.y, **fitline_kwds)
ax.scatter(moran_loc.y, lag, **scatter_kwds)
return fig, ax
[docs]def lisa_cluster(
moran_loc, gdf, p=0.05, ax=None, legend=True, legend_kwds=None, **kwargs
):
"""
Create a LISA Cluster map
Parameters
----------
moran_loc : esda.moran.Moran_Local or Moran_Local_BV instance
Values of Moran's Local Autocorrelation Statistic
gdf : geopandas dataframe instance
The Dataframe containing information to plot. Note that `gdf` will be
modified, so calling functions should use a copy of the user
provided `gdf`. (either using gdf.assign() or gdf.copy())
p : float, optional
The p-value threshold for significance. Points will
be colored by significance.
ax : matplotlib Axes instance, optional
Axes in which to plot the figure in multiple Axes layout.
Default = None
legend : boolean, optional
If True, legend for maps will be depicted. Default = True
legend_kwds : dict, optional
Dictionary to control legend formatting options. Example:
``legend_kwds={'loc': 'upper left', 'bbox_to_anchor': (0.92, 1.05)}``
Default = None
**kwargs : keyword arguments, optional
Keywords designing and passed to geopandas.GeoDataFrame.plot().
Returns
-------
fig : matplotlip Figure instance
Figure of LISA cluster map
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran_Local
>>> from splot.esda import lisa_cluster
Data preparation and statistical analysis
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
>>> moran_loc = Moran_Local(y, w)
Plotting
>>> fig = lisa_cluster(moran_loc, gdf)
>>> plt.show()
"""
# retrieve colors5 and labels from mask_local_auto
_, colors5, _, labels = mask_local_auto(moran_loc, p=p)
# define ListedColormap
hmap = colors.ListedColormap(colors5)
if ax is None:
figsize = kwargs.pop("figsize", None)
fig, ax = plt.subplots(1, figsize=figsize)
else:
fig = ax.get_figure()
# check for Polygon, else no edgecolor
if gdf.geom_type.isin(["Polygon", "MultiPolygon"]).any():
gdf.assign(cl=labels).plot(
column="cl",
categorical=True,
k=2,
cmap=hmap,
linewidth=0.1,
ax=ax,
edgecolor="white",
legend=legend,
legend_kwds=legend_kwds,
**kwargs
)
else:
gdf.assign(cl=labels).plot(
column="cl",
categorical=True,
k=2,
cmap=hmap,
linewidth=1.5,
ax=ax,
legend=legend,
legend_kwds=legend_kwds,
**kwargs
)
ax.set_axis_off()
ax.set_aspect("equal")
return fig, ax
[docs]def plot_local_autocorrelation(
moran_loc,
gdf,
attribute,
p=0.05,
region_column=None,
mask=None,
mask_color="#636363",
quadrant=None,
aspect_equal=True,
legend=True,
scheme="Quantiles",
cmap="YlGnBu",
figsize=(15, 4),
scatter_kwds=None,
fitline_kwds=None,
):
"""
Produce three-plot visualisation of Moran Scatteprlot, LISA cluster
and Choropleth maps, with Local Moran region and quadrant masking
Parameters
----------
moran_loc : esda.moran.Moran_Local or Moran_Local_BV instance
Values of Moran's Local Autocorrelation Statistic
gdf : geopandas dataframe
The Dataframe containing information to plot the two maps.
attribute : str
Column name of attribute which should be depicted in Choropleth map.
p : float, optional
The p-value threshold for significance. Points and polygons will
be colored by significance. Default = 0.05.
region_column: string, optional
Column name containing mask region of interest. Default = None
mask: str, float, int, optional
Identifier or name of the region to highlight. Default = None
Use the same dtype to specifiy as in original dataset.
mask_color: str, optional
Color of mask. Default = '#636363'
quadrant : int, optional
Quadrant 1-4 in scatterplot masking values in LISA cluster and
Choropleth maps. Default = None
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
figsize: tuple, optional
W, h of figure. Default = (15,4)
legend: boolean, optional
If True, legend for maps will be depicted. Default = True
scheme: str, optional
Name of PySAL classifier to be used. Default = 'Quantiles'
cmap: str, optional
Name of matplotlib colormap used for plotting the Choropleth.
Default = 'YlGnBu'
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline
in the scatterplot. Default =None.
Returns
-------
fig : Matplotlib figure instance
Moran Scatterplot, LISA cluster map and Choropleth.
axs : list of Matplotlib axes
Lisat of Matplotlib axes plotted.
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> import geopandas as gpd
>>> from esda.moran import Moran_Local
>>> from splot.esda import plot_local_autocorrelation
Data preparation and analysis
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
>>> moran_loc = Moran_Local(y, w)
Plotting with quadrant mask and region mask
>>> fig = plot_local_autocorrelation(moran_loc, gdf, 'Donatns', p=0.05,
... region_column='Dprtmnt',
... mask=['Ain'], quadrant=1)
>>> plt.show()
"""
fig, axs = plt.subplots(
1, 3, figsize=figsize, subplot_kw={"aspect": "equal", "adjustable": "datalim"}
)
# Moran Scatterplot
moran_scatterplot(
moran_loc, p=p, ax=axs[0], scatter_kwds=scatter_kwds, fitline_kwds=fitline_kwds
)
if aspect_equal is True:
axs[0].set_aspect("equal", "datalim")
else:
axs[0].set_aspect("auto")
# Lisa cluster map
# TODO: Fix legend_kwds: display boxes instead of points
lisa_cluster(
moran_loc,
gdf,
p=p,
ax=axs[1],
legend=legend,
legend_kwds={"loc": "upper left", "bbox_to_anchor": (0.92, 1.05)},
)
axs[1].set_aspect("equal")
# Choropleth for attribute
gdf.plot(
column=attribute,
scheme=scheme,
cmap=cmap,
legend=legend,
legend_kwds={"loc": "upper left", "bbox_to_anchor": (0.92, 1.05)},
ax=axs[2],
alpha=1,
)
axs[2].set_axis_off()
axs[2].set_aspect("equal")
# MASKING QUADRANT VALUES
if quadrant is not None:
# Quadrant masking in Scatterplot
mask_angles = {1: 0, 2: 90, 3: 180, 4: 270} # rectangle angles
# We don't want to change the axis data limits, so use the current ones
xmin, xmax = axs[0].get_xlim()
ymin, ymax = axs[0].get_ylim()
# We are rotating, so we start from 0 degrees and
# figured out the right dimensions for the rectangles for other angles
mask_width = {1: abs(xmax), 2: abs(ymax), 3: abs(xmin), 4: abs(ymin)}
mask_height = {1: abs(ymax), 2: abs(xmin), 3: abs(ymin), 4: abs(xmax)}
axs[0].add_patch(
patches.Rectangle(
(0, 0),
width=mask_width[quadrant],
height=mask_height[quadrant],
angle=mask_angles[quadrant],
color="#E5E5E5",
zorder=-1,
alpha=0.8,
)
)
# quadrant selection in maps
non_quadrant = ~(moran_loc.q == quadrant)
mask_quadrant = gdf[non_quadrant]
df_quadrant = gdf.iloc[~non_quadrant]
union2 = df_quadrant.unary_union.boundary
# LISA Cluster mask and cluster boundary
with warnings.catch_warnings(): # temorarily surpress geopandas warning
warnings.filterwarnings("ignore", category=UserWarning)
mask_quadrant.plot(
column=attribute,
scheme=scheme,
color="white",
ax=axs[1],
alpha=0.7,
zorder=1,
)
gpd.GeoSeries([union2]).plot(linewidth=1, ax=axs[1], color="#E5E5E5")
# CHOROPLETH MASK
with warnings.catch_warnings(): # temorarily surpress geopandas warning
warnings.filterwarnings("ignore", category=UserWarning)
mask_quadrant.plot(
column=attribute,
scheme=scheme,
color="white",
ax=axs[2],
alpha=0.7,
zorder=1,
)
gpd.GeoSeries([union2]).plot(linewidth=1, ax=axs[2], color="#E5E5E5")
# REGION MASKING
if region_column is not None:
# masking inside axs[0] or Moran Scatterplot
# enforce the same dtype of list and mask
if not isinstance(mask[0], type(gdf[region_column].iloc[0])):
warnings.warn(
"Values in `mask` are not the same dtype as"
+ " values in `region_column`. Converting `mask` values"
+ " to dtype of first observation in region_column."
)
data_type = type(gdf[region_column][0].item())
mask = list(map(data_type, mask))
ix = gdf[region_column].isin(mask)
if not ix.any():
raise ValueError(
"Specified values {} in `mask` not in `region_column`".format(mask)
)
df_mask = gdf[ix]
x_mask = moran_loc.z[ix]
y_mask = lag_spatial(moran_loc.w, moran_loc.z)[ix]
axs[0].plot(
x_mask,
y_mask,
color=mask_color,
marker="o",
markersize=14,
alpha=0.8,
linestyle="None",
zorder=-1,
)
# masking inside axs[1] or Lisa cluster map
union = df_mask.unary_union.boundary
gpd.GeoSeries([union]).plot(linewidth=2, ax=axs[1], color=mask_color)
# masking inside axs[2] or Chloropleth
gpd.GeoSeries([union]).plot(linewidth=2, ax=axs[2], color=mask_color)
return fig, axs
def _moran_loc_bv_scatterplot(
moran_loc_bv,
p=None,
aspect_equal=True,
ax=None,
scatter_kwds=None,
fitline_kwds=None,
):
"""
Moran Bivariate Scatterplot with option of coloring of Local Moran Statistics
Parameters
----------
moran_loc : esda.moran.Moran_Local_BV instance
Values of Moran's I Local Autocorrelation Statistics
p : float, optional
If given, the p-value threshold for significance. Points will
be colored by significance. By default it will not be colored.
Default =None.
aspect_equal : bool, optional
If True, Axes of Moran Scatterplot will show the same
aspect or visual proportions.
ax : Matplotlib Axes instance, optional
If given, the Moran plot will be created inside this axis.
Default =None.
scatter_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points.
Default =None.
fitline_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline.
Default =None.
Returns
-------
fig : Matplotlib Figure instance
Bivariate Moran Local scatterplot figure
ax : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> import geopandas as gpd
>>> from libpysal.weights.contiguity import Queen
>>> from libpysal import examples
>>> from esda.moran import Moran_Local_BV
>>> from splot.esda import moran_scatterplot
Load data and calculate Moran Local statistics
>>> guerry = examples.load_example('Guerry')
>>> link_to_data = guerry.get_path('guerry.shp')
>>> gdf = gpd.read_file(link_to_data)
>>> x = gdf['Suicids'].values
>>> y = gdf['Donatns'].values
>>> w = Queen.from_dataframe(gdf)
>>> w.transform = 'r'
>>> m = Moran_Local_BV(x, y, w)
Plot
>>> moran_scatterplot(m)
>>> plt.show()
Customize plot
>>> moran_scatterplot(m, p=0.05,
... fitline_kwds=dict(color='#4393c3')))
>>> plt.show()
"""
# to set default as an empty dictionary that is later filled with defaults
if scatter_kwds is None:
scatter_kwds = dict()
if fitline_kwds is None:
fitline_kwds = dict()
if p is not None:
if not isinstance(moran_loc_bv, Moran_Local_BV):
raise ValueError(
"`moran_loc_bv` is not a\n" + "esda.moran.Moran_Local_BV instance"
)
if "color" in scatter_kwds or "c" in scatter_kwds or "cmap" in scatter_kwds:
warnings.warn(
"To change the color use cmap with a colormap of 5,\n"
+ "c defines the LISA category, color will interfere with c"
)
# colors
spots_bv = moran_hot_cold_spots(moran_loc_bv, p)
hmap = colors.ListedColormap(
["#bababa", "#d7191c", "#abd9e9", "#2c7bb6", "#fdae61"]
)
# define customization
scatter_kwds.setdefault("alpha", 0.6)
scatter_kwds.setdefault("s", 40)
fitline_kwds.setdefault("alpha", 0.9)
# get fig and ax
fig, ax = _create_moran_fig_ax(ax, figsize=(7, 7), aspect_equal=aspect_equal)
# set labels
ax.set_xlabel("Attribute")
ax.set_ylabel("Spatial Lag")
ax.set_title("Moran BV Local Scatterplot")
# plot and set standards
lag = lag_spatial(moran_loc_bv.w, moran_loc_bv.zy)
fit = OLS(moran_loc_bv.zy[:, None], lag[:, None])
# v- and hlines
ax.axvline(0, alpha=0.5, color="k", linestyle="--")
ax.axhline(0, alpha=0.5, color="k", linestyle="--")
if p is not None:
fitline_kwds.setdefault("color", "k")
scatter_kwds.setdefault("cmap", hmap)
scatter_kwds.setdefault("c", spots_bv)
ax.plot(lag, fit.predy, **fitline_kwds)
ax.scatter(moran_loc_bv.zx, fit.predy, **scatter_kwds)
else:
scatter_kwds.setdefault("color", splot_colors["moran_base"])
fitline_kwds.setdefault("color", splot_colors["moran_fit"])
ax.plot(lag, fit.predy, **fitline_kwds)
ax.scatter(moran_loc_bv.zy, fit.predy, **scatter_kwds)
return fig, ax
[docs]def moran_facet(
moran_matrix,
figsize=(16, 12),
scatter_bv_kwds=None,
fitline_bv_kwds=None,
scatter_glob_kwds=dict(color="#737373"),
fitline_glob_kwds=None,
):
"""
Moran Facet visualization.
Includes BV Morans and Global Morans on the diagonal.
Parameters
----------
moran_matrix : esda.moran.Moran_BV_matrix instance
Dictionary of Moran_BV objects
figsize : tuple, optional
W, h of figure. Default =(16,12)
scatter_bv_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points of
off-diagonal Moran_BV plots.
Default =None.
fitline_bv_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline of
off-diagonal Moran_BV plots.
Default =None.
scatter_glob_kwds : keyword arguments, optional
Keywords used for creating and designing the scatter points of
diagonal Moran plots.
Default =None.
fitline_glob_kwds : keyword arguments, optional
Keywords used for creating and designing the moran fitline of
diagonal Moran plots.
Default =None.
Returns
-------
fig : Matplotlib Figure instance
Bivariate Moran Local scatterplot figure
axarr : matplotlib Axes instance
Axes in which the figure is plotted
Examples
--------
Imports
>>> import matplotlib.pyplot as plt
>>> import libpysal as lp
>>> import numpy as np
>>> import geopandas as gpd
>>> from esda.moran import Moran_BV_matrix
>>> from splot.esda import moran_facet
Load data and calculate Moran Local statistics
>>> f = gpd.read_file(lp.examples.get_path("sids2.dbf"))
>>> varnames = ['SIDR74', 'SIDR79', 'NWR74', 'NWR79']
>>> vars = [numpy.array(f[var]) for var in varnames]
>>> w = lp.io.open(lp.examples.get_path("sids2.gal")).read()
>>> moran_matrix = Moran_BV_matrix(vars, w, varnames = varnames)
Plot
>>> fig, axarr = moran_facet(moran_matrix)
>>> plt.show()
Customize plot
>>> fig, axarr = moran_facet(moran_matrix,
... fitline_bv_kwds=dict(color='#4393c3'))
>>> plt.show()
"""
nrows = int(numpy.sqrt(len(moran_matrix))) + 1
ncols = nrows
fig, axarr = plt.subplots(nrows, ncols, figsize=figsize, sharey=True, sharex=True)
fig.suptitle("Moran Facet")
for row in range(nrows):
for col in range(ncols):
if row == col:
global_m = Moran(
moran_matrix[row, (row + 1) % 4].zy,
moran_matrix[row, (row + 1) % 4].w,
)
_moran_global_scatterplot(
global_m,
ax=axarr[row, col],
scatter_kwds=scatter_glob_kwds,
fitline_kwds=fitline_glob_kwds,
)
axarr[row, col].set_facecolor("#d9d9d9")
else:
_moran_bv_scatterplot(
moran_matrix[row, col],
ax=axarr[row, col],
scatter_kwds=scatter_bv_kwds,
fitline_kwds=fitline_bv_kwds,
)
axarr[row, col].spines["bottom"].set_visible(False)
axarr[row, col].spines["left"].set_visible(False)
if row == nrows - 1:
axarr[row, col].set_xlabel(
str(moran_matrix[(col + 1) % 4, col].varnames["x"]).format(col)
)
axarr[row, col].spines["bottom"].set_visible(True)
else:
axarr[row, col].set_xlabel("")
if col == 0:
axarr[row, col].set_ylabel(
(
"Spatial Lag of "
+ str(moran_matrix[row, (row + 1) % 4].varnames["y"])
).format(row)
)
axarr[row, col].spines["left"].set_visible(True)
else:
axarr[row, col].set_ylabel("")
axarr[row, col].set_title("")
plt.tight_layout()
return fig, axarr