martibosch / pylandstats

Computing landscape metrics in the Python ecosystem
https://doi.org/10.1371/journal.pone.0225734
GNU General Public License v3.0
83 stars 16 forks source link

Error with compute_class_metrics_df #14

Open simon-tarr opened 3 years ago

simon-tarr commented 3 years ago

Description

I have a small .tif file and I'm trying to compute patch and class-level metrics with PyLandStats using the methods compute_patch_metrics_df and compute_class_metrics_df. I can successfully use compute_patch_metrics_df which returns:

          class_val          area  perimeter  perimeter_area_ratio  shape_index  fractal_dimension  euclidean_nearest_neighbor
patch_id                                                                                                                      
0                 2  1.607143e-08   0.122938          7.649463e+06     2.400000           0.797257                    0.002561
1                 2  1.639942e-10   0.005122          3.123531e+07     1.000000           1.000000                    0.024733
2                 2  1.013484e-07   0.274049          2.704027e+06     2.140000           0.777663                    0.032724
3                 2  3.279884e-10   0.007684          2.342648e+07     1.000000           0.990673                    0.002561
4                 2  1.459548e-08   0.117815          8.072046e+06     2.421053           0.798199                    0.010325
...             ...           ...        ...                   ...          ...                ...                         ...
169              43  6.559767e-10   0.012806          1.952207e+07     1.250000           0.962605                    0.002864
170              43  1.475948e-09   0.017928          1.214706e+07     1.166667           0.972284                    0.002561
171              43  3.279884e-10   0.007684          2.342648e+07     1.000000           0.990673                    0.004050
172              43  3.279884e-10   0.007684          2.342648e+07     1.000000           0.990673                    0.004050
173              44  2.469096e-06   0.952768          3.858772e+05     1.512195           0.775226                         NaN

[174 rows x 7 columns]

However I get an error with using compute_class_metrics_df (regardless of the input raster). The error is:

Traceback (most recent call last):
  File "/usr/src/app/ecomap/tests.py", line 278, in test_pylandstats
    y = ls.compute_class_metrics_df()
  File "/usr/local/lib/python3.9/site-packages/pylandstats/landscape.py", line 2616, in compute_class_metrics_df
    {
  File "/usr/local/lib/python3.9/site-packages/pylandstats/landscape.py", line 2617, in <dictcomp>
    class_val: getattr(self, metric)(
  File "/usr/local/lib/python3.9/site-packages/pylandstats/landscape.py", line 1240, in total_edge
    self._adjacency_df.groupby(
  File "/usr/local/lib/python3.9/site-packages/pandas/core/frame.py", line 4308, in drop
    return super().drop(
  File "/usr/local/lib/python3.9/site-packages/pandas/core/generic.py", line 4153, in drop
    obj = obj._drop_axis(labels, axis, level=level, errors=errors)
  File "/usr/local/lib/python3.9/site-packages/pandas/core/generic.py", line 4188, in _drop_axis
    new_axis = axis.drop(labels, errors=errors)
  File "/usr/local/lib/python3.9/site-packages/pandas/core/indexes/base.py", line 5591, in drop
    raise KeyError(f"{labels[mask]} not found in axis")
KeyError: '[None] not found in axis'

Is this error user error on my part or some other issue related to the code in PyLandStats? I've included one of the rasters which creates the above error.

from pylandstats import Landscape

ls = Landscape('/data/tests/testraster.tif')
x = ls.compute_patch_metrics_df() # Executes successfully
print(x)

y = ls.compute_class_metrics_df() # Described error here
print(y)

Thank you! testraster.tif.zip

simon-tarr commented 3 years ago

I have been able to successfully run compute_class_metrics_df() by loading in the raster via rioxarrary:

from pylandstats import Landscape
import rioxarray as rxr

raster = rxr.open_rasterio("/Users/simontarr/Downloads/testraster2.tif", masked=True).squeeze()
raster = raster.astype(int)

# Load in raster as rioxarray
ls=Landscape(raster.values, res=(len(raster.x),len(raster.y)))

x = ls.compute_patch_metrics_df()
print('PATCH METRICS \n', x)

y = ls.compute_class_metrics_df(['total_area', 'proportion_of_landscape', 'number_of_patches']) # Described error here
print('CLASS METRICS \n', y)
martibosch commented 3 years ago

Hello @simon-tarr,

thank you for using pylandstats and reporting this. It seems that the problem is that the raster that you have sent does not have a nodata value, hence rasterio assigns a Python None to such value, which is then problematic when pylandstats deals with the pixel adjacency matrix. From your second message, I suppose that rioxarray deals with it differently, also maybe because of the masked=True argument.

I will assess whether I should add a patch to pylandstats to avoid this situation, since although the raster's metadata is problematic, pylandstats should be able to at least give a more informative error. In the meantime, you can use the following workaround:

ls = pls.Landscape('testraster.tif', nodata=0)

Thank you again. Best, Martí

simon-tarr commented 3 years ago

Hey @martibosch, sorry about my very slow reply - I got caught up on another project and had to put my pylandstats work on the back burner. Thanks for your explanation about what was causing the error, makes sense! The workaround is appreciated!

CWen001 commented 3 years ago

Same errors there. The workaround is nice, and a patch to fix the metadata reading is also much appreciated, thanks.

Hossein-Madadi commented 1 year ago

Hello @simon-tarr,

thank you for using pylandstats and reporting this. It seems that the problem is that the raster that you have sent does not have a nodata value, hence rasterio assigns a Python None to such value, which is then problematic when pylandstats deals with the pixel adjacency matrix. From your second message, I suppose that rioxarray deals with it differently, also maybe because of the masked=True argument.

I will assess whether I should add a patch to pylandstats to avoid this situation, since although the raster's metadata is problematic, pylandstats should be able to at least give a more informative error. In the meantime, you can use the following workaround:

ls = pls.Landscape('testraster.tif', nodata=0)

Thank you again. Best, Martí

My problem solved with "nodata=0". Thanks.