f12479646e13460483bf203dff13d93f

Continuous Comparisons

[1]:
import numpy as np
import xarray as xr
import rioxarray as rxr
import gval

Load Datasets

In this example, the comparisons output of Variable Infiltration Capacity Model total annual CONUS precipitation in 2011 with that of the model output of PRISM, also total annual CONUS precipitation in 2011.

[2]:
candidate = rxr.open_rasterio(
    './livneh_2011_precip.tif', mask_and_scale=True
) # VIC
benchmark = rxr.open_rasterio(
    './prism_2011_precip.tif', mask_and_scale=True
) # PRISM

Run GVAL Continuous Compare

[3]:
agreement, metric_table = candidate.gval.continuous_compare(benchmark)

Output

Agreement Map

The agreement map in this case will be simply the difference between the two modeling outputs.

[4]:
agreement.gval.cont_plot(title="Agreement Map", figsize=(6, 3))
[4]:
<matplotlib.collections.QuadMesh at 0x7f01f38bba60>
_images/SphinxContinuousTutorial_11_1.png

In this case it is a bit difficult to see the variability of the precipitation difference, but if extreme values are masked out the map will look like the following:

[5]:
agreement.data = xr.where(
    (agreement < np.nanquantile(agreement.values, 0.0001)) |
    (agreement > np.nanquantile(agreement.values, 0.9999)),
    np.nan,
    agreement
)
agreement.gval.cont_plot(title="Agreement Map", figsize=(6, 3))
[5]:
<matplotlib.collections.QuadMesh at 0x7f01f385b430>
_images/SphinxContinuousTutorial_13_1.png

Metric Table

A metric table contains information about the unit of analysis, (a single band in this case), and selected continuous metrics. Since we did not provide the metrics argument GVAL computed all of the available continuous statistics.

[6]:
metric_table.transpose()
[6]:
0
band 1
coefficient_of_determination 0.685261
mean_absolute_error 216.089706
mean_absolute_percentage_error 0.319234
mean_normalized_mean_absolute_error 0.267845
mean_normalized_root_mean_squared_error 0.372578
mean_percentage_error 0.010022
mean_signed_error 8.085411
mean_squared_error 90351.664062
range_normalized_mean_absolute_error 0.033065
range_normalized_root_mean_squared_error 0.045995
root_mean_squared_error 300.585541
symmetric_mean_absolute_percentage_error 0.269394

Alternative Uses of GVAL Continuous Operations

Aside form running the entire process, it is possible to run each of the following steps individually: homogenizing maps, computing an agreement map. This allows for flexibility in workflows so that a user may use as much or as little functionality as needed.

Just like in continuous comparisons, homogenizing can be done as follows:

[7]:
candidate, benchmark = candidate.gval.homogenize(
    benchmark_map=benchmark,
    target_map = "candidate"
)

The “difference” comparison function is the default used for the comparison_function argument in gval.continuous_compare and is the only continuous comparison function available by default. It would be advised not to use a categorical comparison function such as ‘cantor’, ‘szudzik’, or a pairing dicitonary because it could result in a very large number of classes.

Using difference in comparison:

[8]:
agreement_map = candidate.gval.compute_agreement_map(
    benchmark_map=benchmark,
    comparison_function="difference",
    continuous=True
)

agreement_map.gval.cont_plot(title="Agreement Map", figsize=(6, 3))
[8]:
<matplotlib.collections.QuadMesh at 0x7f01f37525c0>
_images/SphinxContinuousTutorial_25_1.png

The following uses an abritrary custom registered function for use in a continuous agreement map:

[9]:
from gval import Comparison
from numbers import Number

@Comparison.register_function(name='divide', vectorize_func=True)
def multiply(c: Number, b: Number) -> Number:
    return c / b

agreement_map = candidate.gval.compute_agreement_map(
    benchmark_map=benchmark,
    comparison_function="divide",
    continuous=True
)

agreement_map.gval.cont_plot(title="Agreement Map", figsize=(6, 3))
[9]:
<matplotlib.collections.QuadMesh at 0x7f01f3635cc0>
_images/SphinxContinuousTutorial_27_1.png

Like in cateogrical compare, all metrics are computed by default if no argument is provided, metrics can also take a list of the desired metrics and will only return metrics in this list.

[10]:
_, metric_table = candidate.gval.continuous_compare(
    benchmark,
    metrics=['mean_absolute_error', 'mean_squared_error']
)

metric_table
[10]:
band mean_absolute_error mean_squared_error
0 1 215.106232 89814.117188

Just like registering comparison functions, you are able to register a metric function on both a method and a class of functions. Below is registering a metric function:

[11]:
from typing import Union
import numpy as np
import xarray as xr
from gval import ContStats

@ContStats.register_function(name="min_error")
def min_error(error: Union[xr.Dataset, xr.DataArray]) -> float:
    return error.min().values

The following is registering a class of metric functions. In this case, the names associated with each function will respond to each method’s name.

[12]:
@ContStats.register_function_class()
class MetricFunctions:

    @staticmethod
    def median_error(error: Union[xr.Dataset, xr.DataArray]) -> float:
        return error.median().values

    @staticmethod
    def max_error(error: Union[xr.Dataset, xr.DataArray]) -> float:
        return error.max().values
[13]:
_, metric_table = candidate.gval.continuous_compare(
    benchmark,
    metrics=['min_error', 'median_error', 'max_error']
)

metric_table
[13]:
band min_error median_error max_error
0 1 -3035.655273 25.858208 4263.23291

Save Output

Finally, a user can take the results and save them to a directory of their choice. The following is an example of saving the agreement map and then the metric table:

[14]:
# output agreement map
agreement_file = 'continuous_agreement_map.tif'
metric_file = 'continuous_metric_file.csv'

agreement_map.rio.to_raster(agreement_file)
metric_table.to_csv(metric_file)