Skip to content

Commit

Permalink
ENH: add Moran_Local.plot (#355)
Browse files Browse the repository at this point in the history
* static plot

* simplify

* add tests

* fix docstrings
  • Loading branch information
martinfleis authored Jan 1, 2025
1 parent 95fc21c commit e095b21
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 14 deletions.
54 changes: 40 additions & 14 deletions esda/moran.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Moran:
w : W | Graph
original w object
z : array
zero-mean, unit standard deviation normalized y
zero-mean, unit standard deviation normalized y
permutations : int
number of permutations
I : float
Expand Down Expand Up @@ -673,7 +673,7 @@ class Moran_Rate(Moran): # noqa: N801
if adjusted is True, y is standardized rates
otherwise, y is raw rates
z : array
zero-mean, unit standard deviation normalized y
zero-mean, unit standard deviation normalized y
w : W | Graph
original w object
permutations : int
Expand Down Expand Up @@ -928,7 +928,7 @@ class Moran_Local: # noqa: N801
w : W | Graph
original w object
z : array
zero-mean, unit standard deviation normalized y
zero-mean, unit standard deviation normalized y
permutations : int
number of random permutations for calculation of pseudo p_values
Is : array
Expand Down Expand Up @@ -1249,7 +1249,28 @@ def explore(self, gdf, crit_value=0.05, **kwargs):
"""
gdf = gdf.copy()
gdf["Moran Cluster"] = self.get_cluster_labels(crit_value)
return _explore_local_moran(self, gdf, crit_value, **kwargs)
return _viz_local_moran(self, gdf, crit_value, "explore", **kwargs)

def plot(self, gdf, crit_value=0.05, **kwargs):
"""Create static map of LISA indicators
Parameters
----------
gdf : geopandas.GeoDataFrame
geodataframe used to conduct the local Moran analysis
crit_value : float, optional
critical value to determine statistical significance, by default 0.05
kwargs : dict, optional
additional keyword arguments passed to the geopandas `explore` method
Returns
-------
ax
matplotlib axis
"""
gdf = gdf.copy()
gdf["Moran Cluster"] = self.get_cluster_labels(crit_value)
return _viz_local_moran(self, gdf, crit_value, "plot", **kwargs)


class Moran_Local_BV: # noqa: N801
Expand Down Expand Up @@ -1571,7 +1592,7 @@ class Moran_Local_Rate(Moran_Local): # noqa: N801
if adjusted is True, y is standardized rates
otherwise, y is raw rates
z : array
zero-mean, unit standard deviation normalized y
zero-mean, unit standard deviation normalized y
w : W | Graph
original w object
permutations : int
Expand Down Expand Up @@ -1773,8 +1794,8 @@ def by_col(
df[col] = rate_df[col]


def _explore_local_moran(moran_local, gdf, crit_value, **kwargs):
"""Plot local Moran values as an interactive map
def _viz_local_moran(moran_local, gdf, crit_value, method, **kwargs):
"""Common helper for local Moran's I vizualization
Parameters
----------
Expand All @@ -1784,20 +1805,24 @@ def _explore_local_moran(moran_local, gdf, crit_value, **kwargs):
geodataframe used to create the Moran_Local class
crit_value : float, optional
critical value for determining statistical significance, by default 0.05
method : str {"explore", "plot"}
GeoDataFrame method to be used
kwargs : dict, optional
additional keyword arguments are passed directly
to geopandas.explore, by default None
to the plotting method, by default None
Returns
-------
m
folium.Map
m | ax
either folium.Map or maptlotlib.Axes
"""

try:
from matplotlib import colors
except ImportError:
raise ImportError("matplotlib library must be installed to use the explore feature") from None
except ImportError as err:
raise ImportError(
"matplotlib library must be installed to use the vizualization feature"
) from err

gdf = gdf.copy()
gdf["Moran Cluster"] = moran_local.get_cluster_labels(crit_value)
Expand All @@ -1817,8 +1842,9 @@ def _explore_local_moran(moran_local, gdf, crit_value, **kwargs):
if "cmap" not in kwargs:
kwargs["cmap"] = hmap

m = gdf[["Moran Cluster", "p-value", "geometry"]].explore("Moran Cluster", **kwargs)
return m
return getattr(gdf[["Moran Cluster", "p-value", "geometry"]], method)(
"Moran Cluster", **kwargs
)


def _get_cluster_labels(moran_local, crit_value):
Expand Down
25 changes: 25 additions & 0 deletions esda/tests/test_moran.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,31 @@ def test_Moran_Local_explore(self, w):
assert out_str.count("#fdae61") == 6
assert out_str.count("#d3d3d3") == 280

@parametrize_sac
def test_Moran_Local_plot(self, w):
import matplotlib

matplotlib.use("Agg")

lm = moran.Moran_Local(
sac1.HSG_VAL.values,
w,
transformation="r",
permutations=99,
keep_simulations=True,
seed=SEED,
)
ax = lm.plot(sac1)
unique, counts = np.unique(ax.collections[0].get_facecolors(), axis=0, return_counts=True)
np.testing.assert_array_almost_equal(unique, np.array([
[0.17254902, 0.48235294, 0.71372549, 1.],
[0.5372549 , 0.81176471, 0.94117647, 1.],
[0.82745098, 0.82745098, 0.82745098, 1.],
[0.84313725, 0.09803922, 0.10980392, 1.],
[0.99215686, 0.68235294, 0.38039216, 1.]]
))
np.testing.assert_array_equal(counts, np.array([86,3, 298,38, 3]))

@parametrize_desmith
def test_Moran_Local_parallel(self, w):
lm = moran.Moran_Local(
Expand Down

0 comments on commit e095b21

Please sign in to comment.