pysal / momepy

Urban Morphology Measuring Toolkit
https://docs.momepy.org
BSD 3-Clause "New" or "Revised" License
496 stars 59 forks source link

all equivalent `"face_artifact_index"` leads to `LinAlgError` in `preprocessing.FaceArtifacts` #665

Closed jGaboardi closed 2 weeks ago

jGaboardi commented 2 weeks ago
import spaghetti, momepy
lattice = spaghetti.regular_lattice((0,0,4,4), 3, exterior=True)
ntw = spaghetti.Network(in_data=lattice)
edges = spaghetti.element_as_gdf(ntw, vertices=False, arcs=True)
momepy.FaceArtifacts(edges)
---------------------------------------------------------------------------
LinAlgError                               Traceback (most recent call last)
File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py:223](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py#line=222), in gaussian_kde.__init__(self, dataset, bw_method, weights)
    222 try:
--> 223     self.set_bandwidth(bw_method=bw_method)
    224 except linalg.LinAlgError as e:

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py:571](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py#line=570), in gaussian_kde.set_bandwidth(self, bw_method)
    569     raise ValueError(msg)
--> 571 self._compute_covariance()

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py:583](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py#line=582), in gaussian_kde._compute_covariance(self)
    580     self._data_covariance = atleast_2d(cov(self.dataset, rowvar=1,
    581                                        bias=False,
    582                                        aweights=self.weights))
--> 583     self._data_cho_cov = linalg.cholesky(self._data_covariance,
    584                                          lower=True)
    586 self.covariance = self._data_covariance * self.factor**2

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/linalg/_decomp_cholesky.py:90](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/linalg/_decomp_cholesky.py#line=89), in cholesky(a, lower, overwrite_a, check_finite)
     47 """
     48 Compute the Cholesky decomposition of a matrix.
     49 
   (...)
     88 
     89 """
---> 90 c, lower = _cholesky(a, lower=lower, overwrite_a=overwrite_a, clean=True,
     91                      check_finite=check_finite)
     92 return c

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/linalg/_decomp_cholesky.py:38](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/linalg/_decomp_cholesky.py#line=37), in _cholesky(a, lower, overwrite_a, clean, check_finite)
     37 if info > 0:
---> 38     raise LinAlgError("%d-th leading minor of the array is not positive "
     39                       "definite" % info)
     40 if info < 0:

LinAlgError: 1-th leading minor of the array is not positive definite

The above exception was the direct cause of the following exception:

LinAlgError                               Traceback (most recent call last)
Cell In[34], line 4
      2 ntw = spaghetti.Network(in_data=lattice)
      3 edges = spaghetti.element_as_gdf(ntw, vertices=False, arcs=True)
----> 4 fac = momepy.FaceArtifacts(edges)

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/momepy/preprocessing.py:1637](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/momepy/preprocessing.py#line=1636), in FaceArtifacts.__init__(self, gdf, index, height_mins, height_maxs, prominence)
   1626 peak_parameters = {
   1627     "height_mins": height_mins,
   1628     "height_maxs": height_maxs,
   1629     "prominence": prominence,
   1630 }
   1631 mylinspace = np.linspace(
   1632     self.polygons["face_artifact_index"].min(),
   1633     self.polygons["face_artifact_index"].max(),
   1634     1000,
   1635 )
-> 1637 self.kde = gaussian_kde(
   1638     self.polygons["face_artifact_index"], bw_method="silverman"
   1639 )
   1640 self.pdf = self.kde.pdf(mylinspace)
   1642 # find peaks

File [~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py:232](http://localhost:8888/~/miniforge3/envs/py312_sgeop-latest/lib/python3.12/site-packages/scipy/stats/_kde.py#line=231), in gaussian_kde.__init__(self, dataset, bw_method, weights)
    224 except linalg.LinAlgError as e:
    225     msg = ("The data appears to lie in a lower-dimensional subspace "
    226            "of the space in which it is expressed. This has resulted "
    227            "in a singular data covariance matrix, which cannot be "
   (...)
    230            "analysis [/](http://localhost:8888/) dimensionality reduction and using "
    231            "`gaussian_kde` with the transformed data.")
--> 232     raise linalg.LinAlgError(msg) from e

LinAlgError: The data appears to lie in a lower-dimensional subspace of the space in which it is expressed. This has resulted in a singular data covariance matrix, which cannot be treated using the algorithms implemented in `gaussian_kde`. Consider performing principle component analysis [/](http://localhost:8888/) dimensionality reduction and using `gaussian_kde` with the transformed data.

@martinfleis I guess the question here is should we provide a more graceful failure with a more user-friendly error message?

xref #664

martinfleis commented 2 weeks ago

I even think that having this issue documenting the cause of the error is enough. This is impossible to encounter in practice.