Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functional #189

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions esda/functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from .moran import (

Check warning on line 1 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L1

Added line #L1 was not covered by tests
Moran,
Moran_Local,
Moran_BV,
Moran_Local_BV,
Moran_Rate,
Moran_Local_Rate,
)
from .geary import Geary
from .gamma import Gamma
from .geary_local import Geary_Local
from .geary_local_mv import Geary_Local_MV
from .getisord import G, G_Local
from .join_counts import Join_Counts
from .join_counts_local import Join_Counts_Local
from .join_counts_local_bv import Join_Counts_Local_BV
from .join_counts_local_mv import Join_Counts_Local_MV

Check warning on line 17 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L9-L17

Added lines #L9 - L17 were not covered by tests

# from .lee import Spatial_Pearson # no solution yet for sklearn style classes
# from .losh import LOSH
import inspect

Check warning on line 21 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L21

Added line #L21 was not covered by tests

for klass in (

Check warning on line 23 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L23

Added line #L23 was not covered by tests
Moran,
Moran_Local,
Moran_BV,
Moran_Local_BV,
Moran_Rate,
Moran_Local_Rate,
Geary,
Gamma,
Geary_Local,
Geary_Local_MV,
G,
G_Local,
Join_Counts,
Join_Counts_Local,
Join_Counts_Local_BV,
Join_Counts_Local_MV,
):
assert hasattr(klass, "_statistic"), f"{klass} has no _statistic"
assert not callable(klass._statistic), f"{klass}._statistic is callable"
klassname = klass.__name__
name = klass.__name__.lower()
if klassname == "LOSH":
defn = f"def {name}(*args, **kwargs):\n\tobj = {klassname}(*args, **kwargs)\n\treturn obj._statistic, obj.pval"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These have to separate the init and fit phases, so I need to figure out how to re-write the signature so that the options all go through.

elif klassname == "Spatial_Pearson":
defn = f"def {name}(*args, **kwargs):\n\tobj = {klassname}(*args, **kwargs)\n\treturn obj._statistic, obj.significance_"

Check warning on line 48 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L41-L48

Added lines #L41 - L48 were not covered by tests
else:
defn = f"def {name}(*args, **kwargs):\n\tobj = {klassname}(*args, **kwargs)\n\treturn obj._statistic, obj.p_sim"
exec(defn)
exec(f"{name}.__doc__ = {klassname}.__doc__")
init_sig = inspect.signature(klass)
globals()[name].__signature__ = init_sig
del globals()[klassname]

Check warning on line 55 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L50-L55

Added lines #L50 - L55 were not covered by tests

for klass in (LOSH, Spatial_Pearson):

Check warning on line 57 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L57

Added line #L57 was not covered by tests
# sklearn style...
pass

Check warning on line 59 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L59

Added line #L59 was not covered by tests

del klassname
del klass
del name
del init_sig
del defn
del inspect

Check warning on line 66 in esda/functions.py

View check run for this annotation

Codecov / codecov/patch

esda/functions.py#L61-L66

Added lines #L61 - L66 were not covered by tests
8 changes: 6 additions & 2 deletions esda/geary_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
n_jobs = self.n_jobs
seed = self.seed

self.localG = self._statistic(x, w)
self.localG = self._stat_func(x, w)

if permutations:
self.p_sim, self.rlocalG = _crand_plus(
Expand Down Expand Up @@ -150,8 +150,12 @@

return self

@property
def _statistic(self):
return self.localG

Check warning on line 155 in esda/geary_local.py

View check run for this annotation

Codecov / codecov/patch

esda/geary_local.py#L155

Added line #L155 was not covered by tests

@staticmethod
def _statistic(x, w):
def _stat_func(x, w):
# Caclulate z-scores for x
zscore_x = (x - np.mean(x)) / np.std(x)
# Create focal (xi) and neighbor (zi) values
Expand Down
39 changes: 22 additions & 17 deletions esda/geary_local_mv.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,21 @@
>>> lG_mv.localG[0:5]
>>> lG_mv.p_sim[0:5]
"""
self.variables = np.array(variables, dtype='float')
self.variables = np.array(variables, dtype="float")

w = self.connectivity
w.transform = 'r'
w.transform = "r"

self.n = len(variables[0])
self.w = w

permutations = self.permutations

# Caclulate z-scores for input variables
# to be used in _statistic and _crand
zvariables = stats.zscore(variables, axis=1)

self.localG = self._statistic(variables, zvariables, w)
self.localG = self._stat_func(variables, zvariables, w)

if permutations:
self._crand(zvariables)
Expand All @@ -95,27 +95,30 @@

return self

@property
def _statistic(self):
return self.localG

Check warning on line 100 in esda/geary_local_mv.py

View check run for this annotation

Codecov / codecov/patch

esda/geary_local_mv.py#L100

Added line #L100 was not covered by tests

@staticmethod
def _statistic(variables, zvariables, w):
def _stat_func(variables, zvariables, w):
# Define denominator adjustment
k = len(variables)
# Create focal and neighbor values
adj_list = w.to_adjlist(remove_symmetric=False)
zseries = [pd.Series(i, index=w.id_order) for i in zvariables]
focal = [zseries[i].loc[adj_list.focal].values
for i in range(len(variables))]
neighbor = [zseries[i].loc[adj_list.neighbor].values
for i in range(len(variables))]
focal = [zseries[i].loc[adj_list.focal].values for i in range(len(variables))]
neighbor = [
zseries[i].loc[adj_list.neighbor].values for i in range(len(variables))
]
# Carry out local Geary calculation
gs = adj_list.weight.values * \
(np.array(focal) - np.array(neighbor))**2
gs = adj_list.weight.values * (np.array(focal) - np.array(neighbor)) ** 2
# Reorganize data
temp = pd.DataFrame(gs).T
temp['ID'] = adj_list.focal.values
adj_list_gs = temp.groupby(by='ID').sum()
temp["ID"] = adj_list.focal.values
adj_list_gs = temp.groupby(by="ID").sum()
localG = np.array(adj_list_gs.sum(axis=1) / k)

return (localG)
return localG

def _crand(self, zvariables):
"""
Expand Down Expand Up @@ -146,13 +149,15 @@
np.random.shuffle(idsi)
vars_rand = []
for j in range(nvars):
vars_rand.append(zvariables[j][idsi[rids[:, 0:wc[i]]]])
vars_rand.append(zvariables[j][idsi[rids[:, 0 : wc[i]]]])
# vars rand as tmp
# Calculate diff
diff = []
for z in range(nvars):
diff.append((np.array((zvariables[z][i] - vars_rand[z])**2
* w[i])).sum(1) / nvars)
diff.append(
(np.array((zvariables[z][i] - vars_rand[z]) ** 2 * w[i])).sum(1)
/ nvars
)
# add up differences
temp = np.array([sum(x) for x in zip(*diff)])
# Assign to object to be returned
Expand Down
8 changes: 6 additions & 2 deletions esda/join_counts_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
self.n = len(y)
self.w = w

self.LJC = self._statistic(y, w)
self.LJC = self._stat_func(y, w)

if permutations:
self.p_sim, self.rjoins = _crand_plus(
Expand All @@ -132,8 +132,12 @@

return self

@property
def _statistic(self):
return self.LJC

Check warning on line 137 in esda/join_counts_local.py

View check run for this annotation

Codecov / codecov/patch

esda/join_counts_local.py#L137

Added line #L137 was not covered by tests

@staticmethod
def _statistic(y, w):
def _stat_func(y, w):
# Create adjacency list. Note that remove_symmetric=False - this is
# different from the esda.Join_Counts() function.
adj_list = w.to_adjlist(remove_symmetric=False)
Expand Down
8 changes: 6 additions & 2 deletions esda/join_counts_local_bv.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
n_jobs = self.n_jobs
seed = self.seed

self.LJC = self._statistic(x, z, w, case=case)
self.LJC = self._stat_func(x, z, w, case=case)

if permutations:
if case == "BJC":
Expand Down Expand Up @@ -168,8 +168,12 @@

return self

@property
def _statistic(self):
return self.LJC

Check warning on line 173 in esda/join_counts_local_bv.py

View check run for this annotation

Codecov / codecov/patch

esda/join_counts_local_bv.py#L173

Added line #L173 was not covered by tests

@staticmethod
def _statistic(x, z, w, case):
def _stat_func(x, z, w, case):
# Create adjacency list. Note that remove_symmetric=False - this is
# different from the esda.Join_Counts() function.
adj_list = w.to_adjlist(remove_symmetric=False)
Expand Down
8 changes: 6 additions & 2 deletions esda/join_counts_local_mv.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
# np.array() of dtype='float' for numba
self.ext = np.array(np.prod(np.vstack(variables), axis=0), dtype="float")

self.LJC = self._statistic(variables, w)
self.LJC = self._stat_func(variables, w)

if permutations:
self.p_sim, self.rjoins = _crand_plus(
Expand All @@ -132,8 +132,12 @@

return self

@property
def _statistic(self):
return self.LJC

Check warning on line 137 in esda/join_counts_local_mv.py

View check run for this annotation

Codecov / codecov/patch

esda/join_counts_local_mv.py#L137

Added line #L137 was not covered by tests

@staticmethod
def _statistic(variables, w):
def _stat_func(variables, w):
# Create adjacency list. Note that remove_symmetric=False -
# different from the esda.Join_Counts() function.
adj_list = w.to_adjlist(remove_symmetric=False)
Expand Down
38 changes: 21 additions & 17 deletions esda/lee.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
---------
connectivity: scipy.sparse matrix object
the connectivity structure describing the relationships
between observed units. Will be row-standardized.
between observed units. Will be row-standardized.
permutations: int
the number of permutations to conduct for inference.
if < 1, no permutational inference will be conducted.
if < 1, no permutational inference will be conducted.

Attributes
----------
Expand All @@ -36,10 +36,10 @@
smoothing factor"
reference_distribution_: numpy.ndarray (n_permutations, 2,2)
distribution of correlation matrices for randomly-shuffled
maps.
maps.
significance_: numpy.ndarray (2,2)
permutation-based p-values for the fraction of times the
observed correlation was more extreme than the simulated
observed correlation was more extreme than the simulated
correlations.
"""
self.connectivity = connectivity
Expand Down Expand Up @@ -77,7 +77,7 @@
)
if self.connectivity is None:
self.connectivity = sparse.eye(Z.shape[0])
self.association_ = self._statistic(Z, self.connectivity)
self.association_ = self._stat_func(Z, self.connectivity)

Check warning on line 80 in esda/lee.py

View check run for this annotation

Codecov / codecov/patch

esda/lee.py#L80

Added line #L80 was not covered by tests

standard_connectivity = sparse.csc_matrix(
self.connectivity / self.connectivity.sum(axis=1)
Expand All @@ -100,8 +100,12 @@
self.significance_ = (extreme + 1.0) / (self.permutations + 1.0)
return self

@property
def _statistic(self):
return self.association_

Check warning on line 105 in esda/lee.py

View check run for this annotation

Codecov / codecov/patch

esda/lee.py#L105

Added line #L105 was not covered by tests

@staticmethod
def _statistic(Z, W):
def _stat_func(Z, W):
ctc = W.T @ W
ones = numpy.ones(ctc.shape[0])
return (Z.T @ ctc @ Z) / (ones.T @ ctc @ ones)
Expand All @@ -118,13 +122,13 @@
---------
connectivity: scipy.sparse matrix object
the connectivity structure describing the relationships
between observed units. Will be row-standardized.
between observed units. Will be row-standardized.
permutations: int
the number of permutations to conduct for inference.
if < 1, no permutational inference will be conducted.
if < 1, no permutational inference will be conducted.
significance_: numpy.ndarray (2,2)
permutation-based p-values for the fraction of times the
observed correlation was more extreme than the simulated
observed correlation was more extreme than the simulated
correlations.
Attributes
----------
Expand All @@ -135,10 +139,10 @@
smoothing factor"
reference_distribution_: numpy.ndarray (n_permutations, n_samples)
distribution of correlation matrices for randomly-shuffled
maps.
maps.
significance_: numpy.ndarray (n_samples,)
permutation-based p-values for the fraction of times the
observed correlation was more extreme than the simulated
observed correlation was more extreme than the simulated
correlations.


Expand All @@ -151,28 +155,28 @@

def fit(self, x, y):
"""
bivariate local pearson's R based on Eq. 22 in Lee (2001), using
bivariate local pearson's R based on Eq. 22 in Lee (2001), using
site-wise conditional randomization from Moran_Local_BV.

L_i = \dfrac{
n \cdot
\Big[\big(\sum_i w_{ij}(x_j - \bar{x})\big)
\big(\sum_i w_{ij}(y_j - \bar{y})\big) \Big]
}
}
{
\sqrt{\sum_i (x_i - \bar{x})^2}
\sqrt{\sum_i (y_i - \bar{y})^2}}
= \dfrac{
n \cdot
(\tilde{x}_j - \bar{x})
(\tilde{y}_j - \bar{y})
}
}
{
\sqrt{\sum_i (x_i - \bar{x})^2}
\sqrt{\sum_i (y_i - \bar{y})^2}}

Lee, Sang Il. (2001), "Developing a bivariate spatial
association measure: An integration of Pearson's r and
Lee, Sang Il. (2001), "Developing a bivariate spatial
association measure: An integration of Pearson's r and
Moran's I." Journal of Geographical Systems, 3(4):369-385.

Arguments
Expand Down
Loading