Skip to content

Commit

Permalink
Merge pull request #71 from ks905383/fix_silent
Browse files Browse the repository at this point in the history
Add ability to set module-wide defaults for silencing, backend aggregation algorithm
  • Loading branch information
ks905383 authored Jun 3, 2024
2 parents db8df95 + d531fd1 commit cf23817
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 58 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ __pycache__/
# xagg /wm/ directories created during docs processing
wm/
docs/notebooks/wm/
wm_export_test/


# C extensions
*.so
Expand Down
2 changes: 1 addition & 1 deletion docs/source/notebooks/base_run.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@
"metadata": {},
"source": [
"## Aggregate\n",
"Now, aggregate the gridded variable in `ds` onto the polygons in `gdf`. Use the option `silent=True` if you'd like to suppress the status updates. "
"Now, aggregate the gridded variable in `ds` onto the polygons in `gdf`. Use the option `silent=True` if you'd like to suppress the status updates, or set it as a default using `xa.set_defaults(silent=True)`. "
]
},
{
Expand Down
30 changes: 28 additions & 2 deletions docs/source/notebooks/full_run.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,28 @@
"weightmap = xa.pixel_overlaps(ds,gdf,weights=ds_pop.pop)"
]
},
{
"cell_type": "markdown",
"id": "d2e7bd60-7859-45e2-9c2a-de73d0a44833",
"metadata": {},
"source": [
"In general, we can suppress these status updates using:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6855fba5-e6d4-4410-8072-37b091c67b24",
"metadata": {},
"outputs": [],
"source": [
"with xa.set_options(silent=True):\n",
" weightmap = xa.pixel_overlaps(ds,gdf,weights=ds_pop.pop)\n",
"\n",
"# Or, to set as a default for the whole session, \n",
"xa.set_options(silent=True)"
]
},
{
"cell_type": "markdown",
"id": "2e6c1fd7-a2d6-4ea8-8c38-441b62247a3e",
Expand Down Expand Up @@ -354,7 +376,9 @@
],
"source": [
"# Aggregate\n",
"aggregated = xa.aggregate(ds,weightmap)"
"aggregated = xa.aggregate(ds,weightmap)\n",
"\n",
"# as before, use with xa.set_defaults(silent=True) to silence output. "
]
},
{
Expand Down Expand Up @@ -1033,7 +1057,9 @@
"\n",
"Other dimensions (e.g. `time`) are kept as they were originally in the grid variable.\n",
"\n",
"Fields in the inputted polygons (e.g., FIPS codes for the US Counties shapefile used here) are saved as additional variables. Attributes from the original `xarray` structure are kept. "
"Fields in the inputted polygons (e.g., FIPS codes for the US Counties shapefile used here) are saved as additional variables. Attributes from the original `xarray` structure are kept. \n",
"\n",
"Note that in all exports, if `xa.set_defaults(silent=False)`, a status update will be given when the file has successfully been exported. "
]
},
{
Expand Down
25 changes: 16 additions & 9 deletions docs/source/tips.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ Tips
Silencing status updates
---------------------------------------

To silence status updates to standard out, use the ``silent=True`` flag::
To silence status updates to standard out, set ``silent=True``::

import xagg as xa
xa.set_defaults(silent=True)

# Get overlap between pixels and polygons
weightmap = xa.pixel_overlaps(ds,gdf,silent=True)
You can also silence a single operation using ``with``::

# Calculate weightmaps without status updates
with xa.set_defaults(silent=True):
weightmap = xa.pixel_overlaps(ds,gdf)

# Aggregate data in [ds] onto polygons
aggregated = xa.aggregate(ds,weightmap,silent=True)
Note that the `silent=True` option in individual functions will be
slowly deprecated over the next few versions in favor of using one of
the two options above.

Saving weights file
---------------------------------------
Expand All @@ -38,11 +43,13 @@ Speed up overlap calculation
---------------------------------------
At the expense of increased memory usage, processing may be sped up using an alternate calculation method (``impl='dot_product'``) ::

# Get overlap between pixels and polygons
weightmap = xa.pixel_overlaps(ds,gdf,impl='dot_product')
import xagg as xa
# Set dot_product as default backend implementation
xa.set_defaults(impl='dot_product')

# Aggregate data in [ds] onto polygons
aggregated = xa.aggregate(ds,weightmap,impl='dot_product')
Note that the `impl=dot_product` option in individual functions will be
slowly deprecated over the next few versions in favor of using the option
setting approach above (or using ``with xa.set_defaults(impl='dot_product'):`` ).

Create diagnostic figure to inspect raster/polygon overlaps
------------------------------------------------------------
Expand Down
5 changes: 0 additions & 5 deletions readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ build:
conda:
environment: docs/docenvironment.yml

#python:
# version: 3.12
# install:
# - method: setuptools
# path: package
sphinx:
fail_on_warning: False
configuration: docs/source/conf.py
32 changes: 31 additions & 1 deletion tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from geopandas import testing as gpdt
from unittest import TestCase
from shapely.geometry import Polygon
from unittest.mock import patch
from io import StringIO
try:
import xesmf as xe
_has_xesmf=True
Expand All @@ -14,7 +16,7 @@
_has_xesmf=False

from xagg.core import (process_weights,create_raster_polygons,get_pixel_overlaps,aggregate,NoOverlapError)

from xagg.options import set_options

##### process_weights() tests #####
def test_process_weights_null():
Expand Down Expand Up @@ -568,7 +570,35 @@ def test_aggregate_with_some_nans():



##### aggregate() silencing tests #####
# Create polygon covering multiple pixels
gdf = {'name':['test'],
'geometry':[Polygon([(0,0),(0,1),(1,1),(1,0),(0,0)])]}
gdf = gpd.GeoDataFrame(gdf,crs="EPSG:4326")

# Get pixel overlaps
with set_options(silent=True):
wm = get_pixel_overlaps(gdf,pix_agg)


@patch('sys.stdout', new_callable=StringIO)
def test_aggregate_silent_true(mock_stdout):
with set_options(silent=True):
# Get aggregate
agg = aggregate(ds,wm)


# Check that nothing was printed
assert mock_stdout.getvalue() == ''

@patch('sys.stdout', new_callable=StringIO)
def test_aggregate_silent_false(mock_stdout):
with set_options(silent=False):
# Get aggregate
agg = aggregate(ds,wm)


# Check that nothing was printed
assert mock_stdout.getvalue() != ''


52 changes: 50 additions & 2 deletions tests/test_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
from geopandas import testing as gpdt
from unittest import TestCase
from shapely.geometry import Polygon
from unittest.mock import patch
from io import StringIO

from xagg.wrappers import (pixel_overlaps)
from xagg.wrappers import pixel_overlaps
from xagg.options import set_options


##### pixel_overlaps() tests #####
Expand Down Expand Up @@ -91,4 +94,49 @@ def test_pixel_overlaps_dataarray_wname():

assert np.allclose([v for v in df0.rel_area],[v for v in df_compare.rel_area])
assert np.allclose([v for v in df0.pix_idxs],[v for v in df_compare.pix_idxs])
assert np.allclose([v for v in df0.coords],[v for v in df_compare.coords])
assert np.allclose([v for v in df0.coords],[v for v in df_compare.coords])

##### pixel_overlaps() silent tests #####
@patch('sys.stdout', new_callable=StringIO)
def test_pixel_overlaps_silent_true(mock_stdout):

# Create dataarray
da = xr.DataArray(data=np.array([[0,1],[2,3]]),
coords={'lat':(['lat'],np.array([0,1])),
'lon':(['lon'],np.array([0,1]))},
dims=['lon','lat'],
name='tas')

# Create polygon covering one pixel
gdf_test = {'name':['test'],
'geometry':[Polygon([(-0.5,-0.5),(-0.5,0.5),(0.5,0.5),(0.5,-0.5),(-0.5,-0.5)])]}
gdf_test = gpd.GeoDataFrame(gdf_test,crs="EPSG:4326")

# Calculate pixel_overlaps through the wrapper function
with set_options(silent=True):
wm = pixel_overlaps(da,gdf_test)

# Check that nothing was printed
assert mock_stdout.getvalue() == ''


@patch('sys.stdout', new_callable=StringIO)
def test_pixel_overlaps_silent_false(mock_stdout):
# Create dataarray
da = xr.DataArray(data=np.array([[0,1],[2,3]]),
coords={'lat':(['lat'],np.array([0,1])),
'lon':(['lon'],np.array([0,1]))},
dims=['lon','lat'],
name='tas')

# Create polygon covering one pixel
gdf_test = {'name':['test'],
'geometry':[Polygon([(-0.5,-0.5),(-0.5,0.5),(0.5,0.5),(0.5,-0.5),(-0.5,-0.5)])]}
gdf_test = gpd.GeoDataFrame(gdf_test,crs="EPSG:4326")

# Calculate pixel_overlaps through the wrapper function
with set_options(silent=False):
wm = pixel_overlaps(da,gdf_test)

# Check that nothing was printed
assert mock_stdout.getvalue() != ''
3 changes: 2 additions & 1 deletion xagg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
# two functions)
from .wrappers import pixel_overlaps
from .auxfuncs import (normalize,fix_ds,get_bnds,subset_find)
from .core import (aggregate,read_wm)
from .core import (aggregate,read_wm)
from .options import get_options, set_options
28 changes: 18 additions & 10 deletions xagg/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import warnings
import os
import re
from .options import get_options

try:
import cartopy
Expand Down Expand Up @@ -91,56 +92,63 @@ def to_dataframe(self,loc_dim='poly_idx'):
return df_out

# Export functions
def to_netcdf(self,fn,loc_dim='poly_idx',silent=False):
def to_netcdf(self,fn,loc_dim='poly_idx',silent=None):
""" Save as netcdf
Parameters
-----------------
fn : str
fn : :py:class:`str`
The target filename
loc_dim : str, by default `'poly_idx'`
loc_dim : :py:class:`str`, by default `'poly_idx'`
What to name the polygon dimension
silent : bool, by default False
silent : :py:class:`bool`, by default False
If `True`, silences standard out
"""
if silent is None:
silent = get_options()['silent']

output_data(self,
output_format = 'netcdf',
output_fn = fn,
loc_dim = loc_dim,
silent = silent)

def to_csv(self,fn,silent=False):
def to_csv(self,fn,silent=None):
""" Save as csv
Parameters
-----------------
fn : str
fn : :py:class:`str`
The target filename
silent : bool, by default False
silent : :py:class:`bool`, by default False
If `True`, silences standard out
"""
if silent is None:
silent = get_options()['silent']
output_data(self,
output_format = 'csv',
output_fn = fn,
silent=silent)

def to_shp(self,fn,silent=False):
def to_shp(self,fn,silent=None):
""" Save as shapefile
fn : str
fn : :py:class:`str`
The target filename
silent : bool, by default False
silent : :py:class:`bool`, by default False
If `True`, silences standard out
"""
if silent is None:
silent = get_options()['silent']
output_data(self,
output_format = 'shp',
output_fn = fn,
Expand Down
Loading

0 comments on commit cf23817

Please sign in to comment.