compression-safeguards
  • Home
  • Try the safeguards

API Reference

  • Overview
  • compression_safeguards
    • api
    • safeguards
      • abc
      • combinators
        • all
        • any
        • assume_safe
        • select
      • eb
      • pointwise
        • abc
        • eb
        • lossless
        • qoi
          • eb
        • same
        • sign
      • qois
      • stencil
        • abc
        • qoi
          • eb
    • utils
      • bindings
      • cast
      • error
      • intervals
      • typing
  • numcodecs_safeguards
    • checksum
    • compute
    • lossless
  • xarray_safeguards

Examples

  • Datasets
  • Special values
    • NaN values
    • Missing values
      • Lossless compression
      • Compressing energy release with lossy compressors and safeguards
      • Compressing energy release using the safeguarded lossy compressors
      • Compressing energy release with masked lossy compressors
      • Compressing energy release with OptZConfig
      • Visual comparison of the decompressed missing values
  • Regions of Interest (RoIs)
    • Latent Heat Flux
  • Quantities of Interest (QoIs)
    • Pointwise: Log-scale Specific Humidity
    • Semi-Pointwise: Kinetic energy
    • Higher-order derivatives
    • Neighbourhood: Relative Vorticity
    • Bounding the dSSIM metric
  • Topology
    • Precipitation extrema
    • Isosurfaces
  • Error bound distributions
    • Absolute error bound
  • Safeguards on chunked data
    • xarray-safeguards
  • Preview
    • Incremental safeguards

Links

  • GitHub
  • PyPI
    • compression-safeguards
    • numcodecs-safeguards
    • xarray-safeguards
compression-safeguards
  • Examples
  • Special values
  • Missing values
  • Try     View Source

Preserving (non-NaN) missing values with safeguards¶

In this example, we compare how three lossy compressors (ZFP, SZ3, and SPERR) handle missing values that are not encoded as NaNs. We compress a single time step of the energy release component of the fire danger index. The data is on an unstructured grid and contains missing values over sea. We then apply safeguards to guarantee that special missing values are preserved. Next, we show how a masking meta-compressor can also be used to preserve missing values. We also compare the safeguards with the compressor configuration auto-tuner OptZConfig.

Missing values are often encoded in GRIB files as the value 9999. Since tools like cfgrib hide this detail and replace missing values with NaNs when loading the data, we manually read in the GRIB file using eccodes to observe the non-NaN missing values.

Copied!
import ssl

ssl._create_default_https_context = ssl._create_stdlib_context
import ssl ssl._create_default_https_context = ssl._create_stdlib_context
Copied!
import copy
from pathlib import Path

import earthkit.plots
import eccodes
import humanize
import matplotlib as mpl
import numpy as np
import pandas as pd
from earthkit.plots.resample import Interpolate
from matplotlib import patheffects as PathEffects
from matplotlib import pyplot as plt
from numcodecs_safeguards import SafeguardedCodec
from numcodecs_wasm_pressio import Pressio
from numcodecs_wasm_sperr import Sperr
from numcodecs_wasm_sz3 import Sz3
from numcodecs_wasm_zfp import Zfp
from numcodecs_wasm_zstd import Zstd
from numcodecs_zero import ZeroCodec
import copy from pathlib import Path import earthkit.plots import eccodes import humanize import matplotlib as mpl import numpy as np import pandas as pd from earthkit.plots.resample import Interpolate from matplotlib import patheffects as PathEffects from matplotlib import pyplot as plt from numcodecs_safeguards import SafeguardedCodec from numcodecs_wasm_pressio import Pressio from numcodecs_wasm_sperr import Sperr from numcodecs_wasm_sz3 import Sz3 from numcodecs_wasm_zfp import Zfp from numcodecs_wasm_zstd import Zstd from numcodecs_zero import ZeroCodec
Copied!
data = Path("data") / "cems-ercnfdr" / "data.grib"
data = Path("data") / "cems-ercnfdr" / "data.grib"
Copied!
with data.open("rb") as f:
    gid = eccodes.codes_grib_new_from_file(f)
    grib_keys = []
    it = eccodes.codes_keys_iterator_new(gid)
    while eccodes.codes_keys_iterator_next(it):
        grib_keys.append(eccodes.codes_keys_iterator_get_name(it))
    eccodes.codes_keys_iterator_delete(it)
    eccodes.codes_release(gid)
with data.open("rb") as f: gid = eccodes.codes_grib_new_from_file(f) grib_keys = [] it = eccodes.codes_keys_iterator_new(gid) while eccodes.codes_keys_iterator_next(it): grib_keys.append(eccodes.codes_keys_iterator_get_name(it)) eccodes.codes_keys_iterator_delete(it) eccodes.codes_release(gid)
Copied!
with data.open("rb") as f:
    gid = eccodes.codes_grib_new_from_file(f)
    (long_name,) = eccodes.codes_get_array(gid, "name", ktype=str)
    (missing_value,) = eccodes.codes_get_array(gid, "missingValue")
    latitudes = eccodes.codes_get_array(gid, "latitudes")
    longitudes = eccodes.codes_get_array(gid, "longitudes")
    ercnfdr = eccodes.codes_get_array(gid, "values")
    eccodes.codes_release(gid)
with data.open("rb") as f: gid = eccodes.codes_grib_new_from_file(f) (long_name,) = eccodes.codes_get_array(gid, "name", ktype=str) (missing_value,) = eccodes.codes_get_array(gid, "missingValue") latitudes = eccodes.codes_get_array(gid, "latitudes") longitudes = eccodes.codes_get_array(gid, "longitudes") ercnfdr = eccodes.codes_get_array(gid, "values") eccodes.codes_release(gid)
Copied!
missing_value
missing_value
np.int64(9999)
Copied!
def compute_corrections_percentage(
    my_ercnfdr: np.ndarray, orig_ercnfdr: np.ndarray
) -> float:
    return np.mean(
        ~(
            (my_ercnfdr == orig_ercnfdr)
            | (np.isnan(my_ercnfdr) & np.isnan(orig_ercnfdr))
        )
    )
def compute_corrections_percentage( my_ercnfdr: np.ndarray, orig_ercnfdr: np.ndarray ) -> float: return np.mean( ~( (my_ercnfdr == orig_ercnfdr) | (np.isnan(my_ercnfdr) & np.isnan(orig_ercnfdr)) ) )
Copied!
import observe

observations = []
import observe observations = []

Lossless compression¶

We first compress the data losslessly with ZStandard at level 22, which gives maximum compression, to provide a baseline.

Copied!
zstd = Zstd(level=22)

with observe.observe(zstd, observations):
    ercnfdr_zstd_enc = zstd.encode(ercnfdr)
    ercnfdr_zstd = zstd.decode(ercnfdr_zstd_enc)
ercnfdr_zstd_cr = ercnfdr.nbytes / ercnfdr_zstd_enc.nbytes
zstd = Zstd(level=22) with observe.observe(zstd, observations): ercnfdr_zstd_enc = zstd.encode(ercnfdr) ercnfdr_zstd = zstd.decode(ercnfdr_zstd_enc) ercnfdr_zstd_cr = ercnfdr.nbytes / ercnfdr_zstd_enc.nbytes

Compressing energy release with lossy compressors and safeguards¶

We configure each compressor with an absolute error bound of 1, the step size between the discrete metric values.

Copied!
eb_abs = 1
eb_abs = 1
Copied!
zfp = Zfp(mode="fixed-accuracy", tolerance=eb_abs)

with observe.observe(zfp, observations):
    ercnfdr_zfp_enc = zfp.encode(ercnfdr)
    ercnfdr_zfp = zfp.decode(ercnfdr_zfp_enc)
ercnfdr_zfp_cr = ercnfdr.nbytes / ercnfdr_zfp_enc.nbytes
zfp = Zfp(mode="fixed-accuracy", tolerance=eb_abs) with observe.observe(zfp, observations): ercnfdr_zfp_enc = zfp.encode(ercnfdr) ercnfdr_zfp = zfp.decode(ercnfdr_zfp_enc) ercnfdr_zfp_cr = ercnfdr.nbytes / ercnfdr_zfp_enc.nbytes
Copied!
sz3 = Sz3(eb_mode="abs", eb_abs=eb_abs)

with observe.observe(sz3, observations):
    ercnfdr_sz3_enc = sz3.encode(ercnfdr)
    ercnfdr_sz3 = sz3.decode(ercnfdr_sz3_enc)
ercnfdr_sz3_cr = ercnfdr.nbytes / ercnfdr_sz3_enc.nbytes
sz3 = Sz3(eb_mode="abs", eb_abs=eb_abs) with observe.observe(sz3, observations): ercnfdr_sz3_enc = sz3.encode(ercnfdr) ercnfdr_sz3 = sz3.decode(ercnfdr_sz3_enc) ercnfdr_sz3_cr = ercnfdr.nbytes / ercnfdr_sz3_enc.nbytes
Copied!
sperr = Sperr(mode="pwe", pwe=eb_abs)

with observe.observe(sperr, observations):
    ercnfdr_sperr_enc = sperr.encode(ercnfdr)
    ercnfdr_sperr = sperr.decode(ercnfdr_sperr_enc)
ercnfdr_sperr_cr = ercnfdr.nbytes / ercnfdr_sperr_enc.nbytes
sperr = Sperr(mode="pwe", pwe=eb_abs) with observe.observe(sperr, observations): ercnfdr_sperr_enc = sperr.encode(ercnfdr) ercnfdr_sperr = sperr.decode(ercnfdr_sperr_enc) ercnfdr_sperr_cr = ercnfdr.nbytes / ercnfdr_sperr_enc.nbytes
Copied!
zero = ZeroCodec()

with observe.observe(zero, observations):
    ercnfdr_zero_enc = zero.encode(ercnfdr)
    ercnfdr_zero = zero.decode(ercnfdr_zero_enc)
zero = ZeroCodec() with observe.observe(zero, observations): ercnfdr_zero_enc = zero.encode(ercnfdr) ercnfdr_zero = zero.decode(ercnfdr_zero_enc)

Compressing energy release using the safeguarded lossy compressors¶

We configure the safeguards to preserve the special missing value, so that missing values are preserved and no non-missing values take on the special value, and to preserve an absolute error bound of 1.

Copied!
ercnfdr_sg = dict()
ercnfdr_sg_cr = dict()

for codec in [
    zero,
    zfp,
    sz3,
    sperr,
]:
    sg = SafeguardedCodec(
        codec=codec,
        safeguards=[
            dict(kind="eb", type="abs", eb=eb_abs),
            dict(kind="same", value="missing_value", exclusive=True),
        ],
        fixed_constants=dict(missing_value=missing_value),
    )

    with observe.observe(sg, observations):
        ercnfdr_sg_enc = sg.encode(ercnfdr)
        ercnfdr_sg[codec.codec_id] = sg.decode(ercnfdr_sg_enc)
    ercnfdr_sg_cr[codec.codec_id] = ercnfdr.nbytes / np.asarray(ercnfdr_sg_enc).nbytes
ercnfdr_sg = dict() ercnfdr_sg_cr = dict() for codec in [ zero, zfp, sz3, sperr, ]: sg = SafeguardedCodec( codec=codec, safeguards=[ dict(kind="eb", type="abs", eb=eb_abs), dict(kind="same", value="missing_value", exclusive=True), ], fixed_constants=dict(missing_value=missing_value), ) with observe.observe(sg, observations): ercnfdr_sg_enc = sg.encode(ercnfdr) ercnfdr_sg[codec.codec_id] = sg.decode(ercnfdr_sg_enc) ercnfdr_sg_cr[codec.codec_id] = ercnfdr.nbytes / np.asarray(ercnfdr_sg_enc).nbytes
Copied!
ercnfdr_sg_lossless = dict()
ercnfdr_sg_lossless_cr = dict()

for codec in [
    zero,
    zfp,
    sz3,
    sperr,
]:
    sg = SafeguardedCodec(
        codec=codec,
        safeguards=[
            dict(kind="eb", type="abs", eb=eb_abs),
            dict(kind="same", value="missing_value", exclusive=True),
        ],
        fixed_constants=dict(missing_value=missing_value),
        # produce lossless corrections and refine them with iteration
        compute=dict(unstable_iterative=True, unstable_lossless_corrections=True),
    )

    with observe.observe(sg, observations):
        ercnfdr_sg_lossless_enc = sg.encode(ercnfdr)
        ercnfdr_sg_lossless[codec.codec_id] = sg.decode(ercnfdr_sg_lossless_enc)
    ercnfdr_sg_lossless_cr[codec.codec_id] = (
        ercnfdr.nbytes / np.asarray(ercnfdr_sg_lossless_enc).nbytes
    )
ercnfdr_sg_lossless = dict() ercnfdr_sg_lossless_cr = dict() for codec in [ zero, zfp, sz3, sperr, ]: sg = SafeguardedCodec( codec=codec, safeguards=[ dict(kind="eb", type="abs", eb=eb_abs), dict(kind="same", value="missing_value", exclusive=True), ], fixed_constants=dict(missing_value=missing_value), # produce lossless corrections and refine them with iteration compute=dict(unstable_iterative=True, unstable_lossless_corrections=True), ) with observe.observe(sg, observations): ercnfdr_sg_lossless_enc = sg.encode(ercnfdr) ercnfdr_sg_lossless[codec.codec_id] = sg.decode(ercnfdr_sg_lossless_enc) ercnfdr_sg_lossless_cr[codec.codec_id] = ( ercnfdr.nbytes / np.asarray(ercnfdr_sg_lossless_enc).nbytes )

Compressing energy release with masked lossy compressors¶

Missing values in the data can also be preserved using meta-compressors that store the location of the missing values to restore them during decompression. Such a metacompressor is implemented in libpressio as the mask_interpolation_compressor_plugin and in the numcodecs_mask.MaskMetaCodec.

Copied!
from numcodecs.packbits import PackBits
from numcodecs_combinators.stack import CodecStack
from numcodecs_mask import MaskMetaCodec

ercnfdr_mask = dict()
ercnfdr_mask_cr = dict()

for codec in [
    zfp,
    sz3,
    sperr,
]:
    mask = MaskMetaCodec(
        mask=int(missing_value),
        codec=codec,
        # bitpack the mask and compress it with default Zstd
        bitmap_codec=CodecStack(PackBits(), Zstd(level=3)),
    )

    with observe.observe(mask, observations):
        ercnfdr_mask_enc = mask.encode(ercnfdr)
        ercnfdr_mask[codec.codec_id] = mask.decode(ercnfdr_mask_enc)
    ercnfdr_mask_cr[codec.codec_id] = (
        ercnfdr.nbytes / np.asarray(ercnfdr_mask_enc).nbytes
    )
from numcodecs.packbits import PackBits from numcodecs_combinators.stack import CodecStack from numcodecs_mask import MaskMetaCodec ercnfdr_mask = dict() ercnfdr_mask_cr = dict() for codec in [ zfp, sz3, sperr, ]: mask = MaskMetaCodec( mask=int(missing_value), codec=codec, # bitpack the mask and compress it with default Zstd bitmap_codec=CodecStack(PackBits(), Zstd(level=3)), ) with observe.observe(mask, observations): ercnfdr_mask_enc = mask.encode(ercnfdr) ercnfdr_mask[codec.codec_id] = mask.decode(ercnfdr_mask_enc) ercnfdr_mask_cr[codec.codec_id] = ( ercnfdr.nbytes / np.asarray(ercnfdr_mask_enc).nbytes )
Copied!
ercnfdr_mask_sg = dict()
ercnfdr_mask_sg_cr = dict()

for codec in [
    zfp,
    sz3,
    sperr,
]:
    mask_sg = SafeguardedCodec(
        codec=MaskMetaCodec(
            mask=int(missing_value),
            codec=codec,
            # bitpack the mask and compress it with default Zstd
            bitmap_codec=CodecStack(PackBits(), Zstd(level=3)),
        ),
        safeguards=[
            dict(kind="eb", type="abs", eb=eb_abs),
            dict(kind="same", value="missing_value", exclusive=True),
        ],
        fixed_constants=dict(missing_value=missing_value),
    )

    with observe.observe(mask_sg, observations):
        ercnfdr_mask_sg_enc = mask_sg.encode(ercnfdr)
        ercnfdr_mask_sg[codec.codec_id] = mask_sg.decode(ercnfdr_mask_sg_enc)
    ercnfdr_mask_sg_cr[codec.codec_id] = (
        ercnfdr.nbytes / np.asarray(ercnfdr_mask_sg_enc).nbytes
    )
ercnfdr_mask_sg = dict() ercnfdr_mask_sg_cr = dict() for codec in [ zfp, sz3, sperr, ]: mask_sg = SafeguardedCodec( codec=MaskMetaCodec( mask=int(missing_value), codec=codec, # bitpack the mask and compress it with default Zstd bitmap_codec=CodecStack(PackBits(), Zstd(level=3)), ), safeguards=[ dict(kind="eb", type="abs", eb=eb_abs), dict(kind="same", value="missing_value", exclusive=True), ], fixed_constants=dict(missing_value=missing_value), ) with observe.observe(mask_sg, observations): ercnfdr_mask_sg_enc = mask_sg.encode(ercnfdr) ercnfdr_mask_sg[codec.codec_id] = mask_sg.decode(ercnfdr_mask_sg_enc) ercnfdr_mask_sg_cr[codec.codec_id] = ( ercnfdr.nbytes / np.asarray(ercnfdr_mask_sg_enc).nbytes )

Compressing energy release with OptZConfig¶

We configure OptZConfig with a custom safety violations metric, implemented in Python, that computes the percentage of violations $V$. We then maximise the score

$$ \textrm{score} = \begin{cases} -\textrm{V} \quad &\text{if } \textrm{V} > 0 \\ \textrm{CR} \quad &\text{otherwise} \end{cases} $$

using the FRAZ search algorithm with 25 iterations, where CR is the achieved compression ratio. Since FRAZ seems to struggle with finding sufficient absolute error bounds spread across several orders of magnitude, we search for bounds in logarithmic space by wrapping each codec in an Exponential<CODEC> meta-codec.

Copied!
import numcodecs


class SafetyViolationsMetric(numcodecs.abc.Codec):
    codec_id = "safety-violations-metric"

    def __init__(self):
        self._data = None

    def encode(self, buf):
        # store the original data for later
        self._data = np.array(buf, copy=True)
        # return no metric
        return np.empty(0, dtype=np.float64)

    def decode(self, buf, out=None):
        # compute the violations
        data_nan = np.where(self._data == missing_value, np.nan, self._data)
        buf_nan = np.where(buf == missing_value, np.nan, buf)
        violations = np.mean(
            ~(
                (np.abs(buf_nan - data_nan) <= eb_abs)
                | (np.isnan(buf_nan) & np.isnan(data_nan))
            )
        )
        self._data = None
        # return the violations score metric
        return numcodecs.compat.ndarray_copy(np.float64(violations), out)


numcodecs.registry.register_codec(SafetyViolationsMetric)
import numcodecs class SafetyViolationsMetric(numcodecs.abc.Codec): codec_id = "safety-violations-metric" def __init__(self): self._data = None def encode(self, buf): # store the original data for later self._data = np.array(buf, copy=True) # return no metric return np.empty(0, dtype=np.float64) def decode(self, buf, out=None): # compute the violations data_nan = np.where(self._data == missing_value, np.nan, self._data) buf_nan = np.where(buf == missing_value, np.nan, buf) violations = np.mean( ~( (np.abs(buf_nan - data_nan) <= eb_abs) | (np.isnan(buf_nan) & np.isnan(data_nan)) ) ) self._data = None # return the violations score metric return numcodecs.compat.ndarray_copy(np.float64(violations), out) numcodecs.registry.register_codec(SafetyViolationsMetric)
Copied!
class ExponentialZfp(Zfp):
    codec_id = "e-zfp.rs"

    def __new__(cls, tolerance: float, **kwargs):
        codec = super().__new__(cls, tolerance=np.exp(tolerance), **kwargs)
        codec._tolerance = tolerance
        return codec

    def get_config(self):
        return {
            **super().get_config(),
            "id": type(self).codec_id,
            "tolerance": self._tolerance,
        }


class ExponentialSz3(Sz3):
    codec_id = "e-sz3.rs"

    def __new__(cls, eb_abs: float, **kwargs):
        codec = super().__new__(cls, eb_abs=np.exp(eb_abs), **kwargs)
        codec._eb_abs = eb_abs
        return codec

    def get_config(self):
        return {
            **super().get_config(),
            "id": type(self).codec_id,
            "eb_abs": self._eb_abs,
        }


class ExponentialSperr(Sperr):
    codec_id = "e-sperr.rs"

    def __new__(cls, pwe: float, **kwargs):
        codec = super().__new__(cls, pwe=np.exp(pwe), **kwargs)
        codec._pwe = pwe
        return codec

    def get_config(self):
        return {**super().get_config(), "id": type(self).codec_id, "pwe": self._pwe}


numcodecs.registry.register_codec(ExponentialZfp)
numcodecs.registry.register_codec(ExponentialSz3)
numcodecs.registry.register_codec(ExponentialSperr)
class ExponentialZfp(Zfp): codec_id = "e-zfp.rs" def __new__(cls, tolerance: float, **kwargs): codec = super().__new__(cls, tolerance=np.exp(tolerance), **kwargs) codec._tolerance = tolerance return codec def get_config(self): return { **super().get_config(), "id": type(self).codec_id, "tolerance": self._tolerance, } class ExponentialSz3(Sz3): codec_id = "e-sz3.rs" def __new__(cls, eb_abs: float, **kwargs): codec = super().__new__(cls, eb_abs=np.exp(eb_abs), **kwargs) codec._eb_abs = eb_abs return codec def get_config(self): return { **super().get_config(), "id": type(self).codec_id, "eb_abs": self._eb_abs, } class ExponentialSperr(Sperr): codec_id = "e-sperr.rs" def __new__(cls, pwe: float, **kwargs): codec = super().__new__(cls, pwe=np.exp(pwe), **kwargs) codec._pwe = pwe return codec def get_config(self): return {**super().get_config(), "id": type(self).codec_id, "pwe": self._pwe} numcodecs.registry.register_codec(ExponentialZfp) numcodecs.registry.register_codec(ExponentialSz3) numcodecs.registry.register_codec(ExponentialSperr)
Copied!
ercnfdr_optzconfig = dict()
ercnfdr_optzconfig_cr = dict()

for codec, parameter, lower_bound in [
    (zfp, "tolerance", 1e-4),  # decent guess
    (sz3, "eb_abs", 1e-4),  # decent guess
    (sperr, "pwe", 1e-12),  # decent guess
]:
    optzconfig = Pressio(
        compressor_id="opt",
        compressor_config={
            "opt:output": ["composite:score"],
            "opt:inputs": [f"numcodecs.rs:{parameter}"],
            "opt:lower_bound": np.log(lower_bound),
            "opt:upper_bound": np.log(eb_abs),
            "opt:max_iterations": 25,
            "opt:objective_mode_name": "max",
        },
        early_config={
            "opt:compressor": "pressio",
            "pressio:compressor": "numcodecs.rs",
            **{
                f"numcodecs.rs:{k}": f"e-{v}" if k == "id" else v
                for k, v in codec.get_config().items()
            },
            "opt:search": "fraz",
            "pressio:metric": "composite",
            "composite:plugins": ["size", "numcodecs.rs-metric"],
            "composite:scripts": [
                """
                violations = metrics["numcodecs.rs-metric:decompression"]
                if violations > 0 then
                    return "score", -violations
                else
                    return "score", metrics["size:compression_ratio"]
                end
                """
            ],
            "numcodecs.rs-metric:id": "safety-violations-metric",
        },
    )

    with observe.observe(optzconfig, observations):
        ercnfdr_optzconfig_enc = optzconfig.encode(ercnfdr)
        ercnfdr_optzconfig[codec.codec_id] = optzconfig.decode(ercnfdr_optzconfig_enc)
    ercnfdr_optzconfig_cr[codec.codec_id] = (
        ercnfdr.nbytes / np.asarray(ercnfdr_optzconfig_enc).nbytes
    )
ercnfdr_optzconfig = dict() ercnfdr_optzconfig_cr = dict() for codec, parameter, lower_bound in [ (zfp, "tolerance", 1e-4), # decent guess (sz3, "eb_abs", 1e-4), # decent guess (sperr, "pwe", 1e-12), # decent guess ]: optzconfig = Pressio( compressor_id="opt", compressor_config={ "opt:output": ["composite:score"], "opt:inputs": [f"numcodecs.rs:{parameter}"], "opt:lower_bound": np.log(lower_bound), "opt:upper_bound": np.log(eb_abs), "opt:max_iterations": 25, "opt:objective_mode_name": "max", }, early_config={ "opt:compressor": "pressio", "pressio:compressor": "numcodecs.rs", **{ f"numcodecs.rs:{k}": f"e-{v}" if k == "id" else v for k, v in codec.get_config().items() }, "opt:search": "fraz", "pressio:metric": "composite", "composite:plugins": ["size", "numcodecs.rs-metric"], "composite:scripts": [ """ violations = metrics["numcodecs.rs-metric:decompression"] if violations > 0 then return "score", -violations else return "score", metrics["size:compression_ratio"] end """ ], "numcodecs.rs-metric:id": "safety-violations-metric", }, ) with observe.observe(optzconfig, observations): ercnfdr_optzconfig_enc = optzconfig.encode(ercnfdr) ercnfdr_optzconfig[codec.codec_id] = optzconfig.decode(ercnfdr_optzconfig_enc) ercnfdr_optzconfig_cr[codec.codec_id] = ( ercnfdr.nbytes / np.asarray(ercnfdr_optzconfig_enc).nbytes )
rank={0,1,} iter={0} input={-4.60517,} output={4.496,} objective={4.496}
rank={0,1,} iter={1} input={-7.06182,} output={3.8708,} objective={3.8708}
rank={0,1,} iter={2} input={-2.18607,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={3} input={0,} output={-0.0184899,} objective={-0.0184899}
rank={0,1,} iter={4} input={-9.20755,} output={3.50522,} objective={3.50522}
rank={0,1,} iter={5} input={-3.16842,} output={4.89099,} objective={4.89099}
rank={0,1,} iter={6} input={-5.70006,} output={4.16004,} objective={4.16004}
rank={0,1,} iter={7} input={-2.53616,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={8} input={-3.80305,} output={4.68521,} objective={4.68521}
rank={0,1,} iter={9} input={-2.36112,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={10} input={-8.05593,} output={3.74074,} objective={3.74074}
rank={0,1,} iter={11} input={-2.80431,} output={4.89099,} objective={4.89099}
rank={0,1,} iter={12} input={-6.3189,} output={4.01023,} objective={4.01023}
rank={0,1,} iter={13} input={-5.08077,} output={4.32152,} objective={4.32152}
rank={0,1,} iter={14} input={-3.43939,} output={4.89099,} objective={4.89099}
rank={0,1,} iter={15} input={-4.16385,} output={4.496,} objective={4.496}
rank={0,1,} iter={16} input={-2.27369,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={17} input={-2.62146,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={18} input={-2.44808,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={19} input={-2.98829,} output={4.89099,} objective={4.89099}
rank={0,1,} iter={20} input={-2.49227,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={21} input={-2.23002,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={22} input={-2.40451,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={23} input={-2.57823,} output={5.11573,} objective={5.11573}
rank={0,1,} iter={24} input={-2.3168,} output={5.11573,} objective={5.11573}
final_iter={25} inputs={-2.18607,} output={5.11573,}
rank={0,1,} iter={0} input={-4.60517,} output={57.2305,} objective={57.2305}
rank={0,1,} iter={1} input={-7.06182,} output={49.381,} objective={49.381}
rank={0,1,} iter={2} input={-2.18607,} output={49.8321,} objective={49.8321}
rank={0,1,} iter={3} input={-4.55271,} output={50.0362,} objective={50.0362}
rank={0,1,} iter={4} input={-6.93899,} output={50.0484,} objective={50.0484}
rank={0,1,} iter={5} input={-0.00161371,} output={-0.705348,} objective={-0.705348}
rank={0,1,} iter={6} input={-5.74589,} output={50.094,} objective={50.094}
rank={0,1,} iter={7} input={-9.20686,} output={43.8669,} objective={43.8669}
rank={0,1,} iter={8} input={-5.1495,} output={49.9377,} objective={49.9377}
rank={0,1,} iter={9} input={-3.37332,} output={49.6433,} objective={49.6433}
rank={0,1,} iter={10} input={-4.85078,} output={51.3479,} objective={51.3479}
rank={0,1,} iter={11} input={-8.08266,} output={49.0892,} objective={49.0892}
rank={0,1,} iter={12} input={-4.70582,} output={49.682,} objective={49.682}
rank={0,1,} iter={13} input={-1.44973,} output={50.9061,} objective={50.9061}
rank={0,1,} iter={14} input={-4.62843,} output={50.0374,} objective={50.0374}
rank={0,1,} iter={15} input={-6.34249,} output={50.2653,} objective={50.2653}
rank={0,1,} iter={16} input={-4.59057,} output={50.1172,} objective={50.1172}
rank={0,1,} iter={17} input={-2.78186,} output={51.5671,} objective={51.5671}
rank={0,1,} iter={18} input={-4.60945,} output={50.3447,} objective={50.3447}
rank={0,1,} iter={19} input={-3.96564,} output={49.4531,} objective={49.4531}
rank={0,1,} iter={20} input={-4.60007,} output={50.1508,} objective={50.1508}
rank={0,1,} iter={21} input={-8.61098,} output={46.2284,} objective={46.2284}
rank={0,1,} iter={22} input={-4.60479,} output={54.7881,} objective={54.7881}
rank={0,1,} iter={23} input={-7.57029,} output={49.3844,} objective={49.3844}
rank={0,1,} iter={24} input={-4.60684,} output={50.5807,} objective={50.5807}
final_iter={25} inputs={-4.60517,} output={57.2305,}
rank={0,1,} iter={0} input={-13.8155,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={1} input={-21.1855,} output={-0.50281,} objective={-0.50281}
rank={0,1,} iter={2} input={-6.55821,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={3} input={-21.7845,} output={-0.50147,} objective={-0.50147}
rank={0,1,} iter={4} input={-1.88648,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={5} input={-25.7671,} output={-0.393894,} objective={-0.393894}
rank={0,1,} iter={6} input={-2.75189,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={7} input={-2.20811,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={8} input={-14.0812,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={9} input={-23.2414,} output={-0.490516,} objective={-0.490516}
rank={0,1,} iter={10} input={-5.2064,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={11} input={-13.9117,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={12} input={-23.5603,} output={-0.492809,} objective={-0.492809}
rank={0,1,} iter={13} input={-4.43149,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={14} input={-25.3187,} output={-0.436805,} objective={-0.436805}
rank={0,1,} iter={15} input={-21.3734,} output={-0.503429,} objective={-0.503429}
rank={0,1,} iter={16} input={-17.6174,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={17} input={-0.375093,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={18} input={-13.3532,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={19} input={-2.37466,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={20} input={-27.4076,} output={-0.00373008,} objective={-0.00373008}
rank={0,1,} iter={21} input={-23.1945,} output={-0.495842,} objective={-0.495842}
rank={0,1,} iter={22} input={-5.1187,} output={-0.503931,} objective={-0.503931}
rank={0,1,} iter={23} input={-25.3987,} output={-0.417429,} objective={-0.417429}
rank={0,1,} iter={24} input={-0.821889,} output={-0.503931,} objective={-0.503931}
final_iter={25} inputs={-13.8155,} output={-0.503931,}

Visual comparison of the decompressed missing values¶

Since ZFP, SZ3, and SPERR have no knowledge of the special missing value, the large magnitude of 9999 bleeds into surrounding grid cells and some missing values have large non-missing-value values.

Copied!
old_scale_dashes = mpl.lines._scale_dashes


def scale_dashes(offset, dashes, lw):
    if lw == 0:
        return offset, dashes
    return old_scale_dashes(offset, dashes, lw)


mpl.lines._scale_dashes = scale_dashes
old_scale_dashes = mpl.lines._scale_dashes def scale_dashes(offset, dashes, lw): if lw == 0: return offset, dashes return old_scale_dashes(offset, dashes, lw) mpl.lines._scale_dashes = scale_dashes
Copied!
def plot_ercnfdr(
    my_ercnfdr: np.ndarray,
    cr,
    chart,
    title,
    eb_abs,
    error=False,
    corr=None,
    my_ercnfdr_mask=None,
    cr_mask=None,
    inset=True,
):
    x = longitudes
    y = latitudes
    z = np.where(my_ercnfdr == missing_value, np.nan, my_ercnfdr)

    source = earthkit.plots.sources.get_source(x=x, y=y, z=z)
    style = copy.deepcopy(earthkit.plots.styles.auto.guess_style(source))
    style._levels = earthkit.plots.styles.levels.Levels(np.linspace(-1, 81, 22))
    style._legend_kwargs["ticks"] = np.linspace(-1, 81, 5)

    extend_left = np.nanmin(z) < -1
    extend_right = np.nanmax(z) > 81

    extend = {
        (False, False): "neither",
        (True, False): "min",
        (False, True): "max",
        (True, True): "both",
    }[(extend_left, extend_right)]

    style._legend_kwargs["extend"] = extend

    interpolate = Interpolate()

    x, y, z = interpolate.apply(
        x=x,
        y=y,
        z=z,
        source_crs=source.crs,
        target_crs=chart.crs,
    )

    ercnfdr_x = longitudes
    ercnfdr_y = latitudes
    ercnfdr_z = np.where(ercnfdr == missing_value, np.nan, ercnfdr)

    ercnfdr_x, ercnfdr_y, ercnfdr_z = interpolate.apply(
        x=ercnfdr_x,
        y=ercnfdr_y,
        z=ercnfdr_z,
        source_crs=source.crs,
        target_crs=chart.crs,
    )

    chart.ax.set_global()
    chart.ax.fill_between(
        [0, 1],
        [1, 1],
        hatch="X",
        edgecolor="white",
        facecolor="lightgrey",
        transform=chart.ax.transAxes,
        zorder=-12,
    )

    chart.pcolormesh(
        x=x,
        y=y,
        z=z,
        style=style,
        norm=mpl.colors.BoundaryNorm(np.linspace(-1, 81, 22), ncolors=21, clip=True),
        zorder=-11,
        rasterized=True,
    )

    with plt.rc_context(
        {
            "hatch.color": "black",
            "hatch.linewidth": 2,
        }
    ):
        chart.contourf(
            x=ercnfdr_x,
            y=ercnfdr_y,
            z=np.isnan(ercnfdr_z) & ~np.isnan(z),
            colors=["none"],
            levels=[-0.5, 0.9, 1.5],
            hatches=[None, "X"],
            legend_style=None,
            zorder=-10,
        )
    with plt.rc_context(
        {
            "hatch.color": "white",
            "hatch.linewidth": 1,
        }
    ):
        chart.contourf(
            x=ercnfdr_x,
            y=ercnfdr_y,
            z=np.isnan(ercnfdr_z) & ~np.isnan(z),
            colors=["none"],
            levels=[-0.5, 0.9, 1.5],
            hatches=[None, "X"],
            legend_style=None,
            zorder=-10,
        )

    if error:
        if corr is not None:
            old_process_projection_requirements = (
                chart.ax.get_figure()._process_projection_requirements
            )

            def _process_projection_requirements(
                *, axes_class=None, polar=False, projection=None, **kwargs
            ):
                if axes_class is not None and projection is not None:
                    return axes_class, dict(projection=projection, **kwargs)
                return old_process_projection_requirements(
                    axes_class=axes_class, polar=polar, projection=projection, **kwargs
                )

            chart.ax.get_figure()._process_projection_requirements = (
                _process_projection_requirements
            )

            axin = chart.ax.inset_axes(
                [0.025, 0.05, 1 / 3, 1 / 3],
                xticklabels=[],
                yticklabels=[],
                axes_class=type(chart.ax),
                projection=chart.ax.projection,
            )
            axin.set_global()
            axin.scatter(
                longitudes,
                latitudes,
                s=1,
                c=~(my_ercnfdr == corr),
                cmap=mpl.colors.ListedColormap(["white", "green", "lawngreen"]),
                vmin=0,
                vmax=2,
                rasterized=True,
            )
            axin.coastlines(color="#555555")
            axin.spines["geo"].set_edgecolor("black")
            axin.set_title(
                "Corrections",
                path_effects=[PathEffects.withStroke(linewidth=3, foreground="white")],
            )
        elif inset:
            old_process_projection_requirements = (
                chart.ax.get_figure()._process_projection_requirements
            )

            def _process_projection_requirements(
                *, axes_class=None, polar=False, projection=None, **kwargs
            ):
                if axes_class is not None and projection is not None:
                    return axes_class, dict(projection=projection, **kwargs)
                return old_process_projection_requirements(
                    axes_class=axes_class, polar=polar, projection=projection, **kwargs
                )

            chart.ax.get_figure()._process_projection_requirements = (
                _process_projection_requirements
            )

            axin = chart.ax.inset_axes(
                [0.025, 0.05, 1 / 3, 1 / 3],
                xticklabels=[],
                yticklabels=[],
                axes_class=type(chart.ax),
                projection=chart.ax.projection,
            )
            axin.set_global()
            axin.scatter(
                longitudes,
                latitudes,
                s=1,
                c=~(
                    (np.abs(my_ercnfdr - ercnfdr) <= eb_abs)
                    & ((my_ercnfdr == missing_value) == (ercnfdr == missing_value))
                ),
                cmap=mpl.colors.ListedColormap(["white", "red"]),
                vmin=0,
                vmax=1,
                rasterized=True,
            )
            axin.coastlines(color="#555555")
            axin.spines["geo"].set_edgecolor("black")
            axin.set_title(
                "Violations",
                path_effects=[PathEffects.withStroke(linewidth=3, foreground="white")],
            )

    chart.title(title)

    if error:
        my_ercnfdr_nan = np.where(my_ercnfdr == missing_value, np.nan, my_ercnfdr)
        ercnfdr_nan = np.where(ercnfdr == missing_value, np.nan, ercnfdr)

        err_v = np.mean(
            ~(
                (np.abs(my_ercnfdr_nan - ercnfdr_nan) <= eb_abs)
                | (np.isnan(my_ercnfdr_nan) & np.isnan(ercnfdr_nan))
            )
        )
        err_v = (
            0
            if err_v == 0
            else np.format_float_positional(100 * err_v, precision=1, min_digits=1)
            + "%"
        )
        if err_v == "0.0%":
            err_v = "<0.05%"

        if my_ercnfdr_mask is not None:
            my_ercnfdr_mask_nan = np.where(
                my_ercnfdr_mask == missing_value, np.nan, my_ercnfdr_mask
            )

            err_mask_v = np.mean(
                ~(
                    (np.abs(my_ercnfdr_mask_nan - ercnfdr_nan) <= eb_abs)
                    | (np.isnan(my_ercnfdr_mask_nan) & np.isnan(ercnfdr_nan))
                )
            )
            err_mask_v = (
                0
                if err_mask_v == 0
                else np.format_float_positional(
                    100 * err_mask_v, precision=1, min_digits=1
                )
                + "%"
            )
            if err_mask_v == "0.0%":
                err_mask_v = "<0.05%"

        t = chart.ax.text(
            0.95,
            0.1,
            f"V={err_v}" + ("" if my_ercnfdr_mask is None else f" ({err_mask_v})"),
            ha="right",
            va="bottom",
            transform=chart.ax.transAxes,
        )
        t.set_bbox(dict(facecolor="white", alpha=0.75, edgecolor="black"))

    t = chart.ax.text(
        0.95,
        0.9,
        rf"$\times$ {np.round(cr, 2)}"
        + ("" if cr_mask is None else rf" ($\times$ {np.round(cr_mask, 2)})")
        if error
        else humanize.naturalsize(ercnfdr.nbytes, binary=True),
        ha="right",
        va="top",
        transform=chart.ax.transAxes,
    )
    t.set_bbox(dict(facecolor="white", alpha=0.75, edgecolor="black"))

    for m in earthkit.plots.schemas.schema.quickmap_subplot_workflow:
        if m != "title":
            getattr(chart, m)()

    for m in earthkit.plots.schemas.schema.quickmap_figure_workflow:
        if m != "legend":
            getattr(chart, m)()

    chart.legend(
        label=long_name.split(" (")[0] if error else long_name.replace(" (", "\n(")
    )

    counts, bins = np.histogram(z.flatten(), range=(-1, 81), bins=21)
    midpoints = bins[:-1] + np.diff(bins) / 2
    cb = chart.ax.collections[1].colorbar
    extend_width = (bins[-1] - bins[-2]) / (bins[-1] - bins[0])
    cax = cb.ax.inset_axes(
        [
            0.0 - extend_width * extend_left,
            1.25,
            1.0 + extend_width * (0 + extend_left + extend_right + 2),
            1.0,
        ]
    )
    cax.bar(
        midpoints,
        height=counts,
        width=(bins[-1] - bins[0]) / len(counts),
        color=cb.cmap(cb.norm(midpoints)),
    )
    if extend_left:
        cax.bar(
            bins[0] - (bins[1] - bins[0]) / 2,
            height=np.sum(z < -1),
            width=(bins[-1] - bins[0]) / len(counts),
            color=cb.cmap(cb.norm(midpoints[0])),
        )
    if extend_right:
        cax.bar(
            bins[-1] + (bins[-1] - bins[-2]) / 2,
            height=np.sum(z > 81),
            width=(bins[-1] - bins[0]) / len(counts),
            color=cb.cmap(cb.norm(midpoints[-1])),
        )
    cax.bar(
        bins[-1] + (bins[-1] - bins[-2]) * (extend_right * 2 + 2 + 1) / 2,
        height=np.sum(np.isnan(z)),
        width=(bins[-1] - bins[0]) / len(counts),
        color="lightgrey",
        edgecolor="white",
        lw=0,
        hatch="XXXX",
    )
    q1, q2, q3 = np.quantile(z.flatten(), [0.25, 0.5, 0.75])
    cax.axvline(z.mean().item(), ls=":", ymin=0.1, ymax=0.9, c="w", lw=2)
    cax.axvline(q1, ymin=0.25, ymax=0.75, c="w", lw=2)
    cax.axvline(q2, ymin=0.1, ymax=0.9, c="w", lw=2)
    cax.axvline(q3, ymin=0.25, ymax=0.75, c="w", lw=2)
    cax.axvline(z.mean().item(), ymin=0.1, ymax=0.9, ls=":", c="k", lw=1)
    cax.axvline(q1, ymin=0.25, ymax=0.75, c="k", lw=1)
    cax.axvline(q2, ymin=0.1, ymax=0.9, c="k", lw=1)
    cax.axvline(q3, ymin=0.25, ymax=0.75, c="k", lw=1)
    cax.set_xlim(
        -1 - (bins[-1] - bins[-2]) * extend_left,
        81 + (bins[-1] - bins[-2]) * (extend_right + 2),
    )
    cax.set_xticks([])
    cax.set_yticks([])
    cax.spines[:].set_visible(False)
def plot_ercnfdr( my_ercnfdr: np.ndarray, cr, chart, title, eb_abs, error=False, corr=None, my_ercnfdr_mask=None, cr_mask=None, inset=True, ): x = longitudes y = latitudes z = np.where(my_ercnfdr == missing_value, np.nan, my_ercnfdr) source = earthkit.plots.sources.get_source(x=x, y=y, z=z) style = copy.deepcopy(earthkit.plots.styles.auto.guess_style(source)) style._levels = earthkit.plots.styles.levels.Levels(np.linspace(-1, 81, 22)) style._legend_kwargs["ticks"] = np.linspace(-1, 81, 5) extend_left = np.nanmin(z) < -1 extend_right = np.nanmax(z) > 81 extend = { (False, False): "neither", (True, False): "min", (False, True): "max", (True, True): "both", }[(extend_left, extend_right)] style._legend_kwargs["extend"] = extend interpolate = Interpolate() x, y, z = interpolate.apply( x=x, y=y, z=z, source_crs=source.crs, target_crs=chart.crs, ) ercnfdr_x = longitudes ercnfdr_y = latitudes ercnfdr_z = np.where(ercnfdr == missing_value, np.nan, ercnfdr) ercnfdr_x, ercnfdr_y, ercnfdr_z = interpolate.apply( x=ercnfdr_x, y=ercnfdr_y, z=ercnfdr_z, source_crs=source.crs, target_crs=chart.crs, ) chart.ax.set_global() chart.ax.fill_between( [0, 1], [1, 1], hatch="X", edgecolor="white", facecolor="lightgrey", transform=chart.ax.transAxes, zorder=-12, ) chart.pcolormesh( x=x, y=y, z=z, style=style, norm=mpl.colors.BoundaryNorm(np.linspace(-1, 81, 22), ncolors=21, clip=True), zorder=-11, rasterized=True, ) with plt.rc_context( { "hatch.color": "black", "hatch.linewidth": 2, } ): chart.contourf( x=ercnfdr_x, y=ercnfdr_y, z=np.isnan(ercnfdr_z) & ~np.isnan(z), colors=["none"], levels=[-0.5, 0.9, 1.5], hatches=[None, "X"], legend_style=None, zorder=-10, ) with plt.rc_context( { "hatch.color": "white", "hatch.linewidth": 1, } ): chart.contourf( x=ercnfdr_x, y=ercnfdr_y, z=np.isnan(ercnfdr_z) & ~np.isnan(z), colors=["none"], levels=[-0.5, 0.9, 1.5], hatches=[None, "X"], legend_style=None, zorder=-10, ) if error: if corr is not None: old_process_projection_requirements = ( chart.ax.get_figure()._process_projection_requirements ) def _process_projection_requirements( *, axes_class=None, polar=False, projection=None, **kwargs ): if axes_class is not None and projection is not None: return axes_class, dict(projection=projection, **kwargs) return old_process_projection_requirements( axes_class=axes_class, polar=polar, projection=projection, **kwargs ) chart.ax.get_figure()._process_projection_requirements = ( _process_projection_requirements ) axin = chart.ax.inset_axes( [0.025, 0.05, 1 / 3, 1 / 3], xticklabels=[], yticklabels=[], axes_class=type(chart.ax), projection=chart.ax.projection, ) axin.set_global() axin.scatter( longitudes, latitudes, s=1, c=~(my_ercnfdr == corr), cmap=mpl.colors.ListedColormap(["white", "green", "lawngreen"]), vmin=0, vmax=2, rasterized=True, ) axin.coastlines(color="#555555") axin.spines["geo"].set_edgecolor("black") axin.set_title( "Corrections", path_effects=[PathEffects.withStroke(linewidth=3, foreground="white")], ) elif inset: old_process_projection_requirements = ( chart.ax.get_figure()._process_projection_requirements ) def _process_projection_requirements( *, axes_class=None, polar=False, projection=None, **kwargs ): if axes_class is not None and projection is not None: return axes_class, dict(projection=projection, **kwargs) return old_process_projection_requirements( axes_class=axes_class, polar=polar, projection=projection, **kwargs ) chart.ax.get_figure()._process_projection_requirements = ( _process_projection_requirements ) axin = chart.ax.inset_axes( [0.025, 0.05, 1 / 3, 1 / 3], xticklabels=[], yticklabels=[], axes_class=type(chart.ax), projection=chart.ax.projection, ) axin.set_global() axin.scatter( longitudes, latitudes, s=1, c=~( (np.abs(my_ercnfdr - ercnfdr) <= eb_abs) & ((my_ercnfdr == missing_value) == (ercnfdr == missing_value)) ), cmap=mpl.colors.ListedColormap(["white", "red"]), vmin=0, vmax=1, rasterized=True, ) axin.coastlines(color="#555555") axin.spines["geo"].set_edgecolor("black") axin.set_title( "Violations", path_effects=[PathEffects.withStroke(linewidth=3, foreground="white")], ) chart.title(title) if error: my_ercnfdr_nan = np.where(my_ercnfdr == missing_value, np.nan, my_ercnfdr) ercnfdr_nan = np.where(ercnfdr == missing_value, np.nan, ercnfdr) err_v = np.mean( ~( (np.abs(my_ercnfdr_nan - ercnfdr_nan) <= eb_abs) | (np.isnan(my_ercnfdr_nan) & np.isnan(ercnfdr_nan)) ) ) err_v = ( 0 if err_v == 0 else np.format_float_positional(100 * err_v, precision=1, min_digits=1) + "%" ) if err_v == "0.0%": err_v = "<0.05%" if my_ercnfdr_mask is not None: my_ercnfdr_mask_nan = np.where( my_ercnfdr_mask == missing_value, np.nan, my_ercnfdr_mask ) err_mask_v = np.mean( ~( (np.abs(my_ercnfdr_mask_nan - ercnfdr_nan) <= eb_abs) | (np.isnan(my_ercnfdr_mask_nan) & np.isnan(ercnfdr_nan)) ) ) err_mask_v = ( 0 if err_mask_v == 0 else np.format_float_positional( 100 * err_mask_v, precision=1, min_digits=1 ) + "%" ) if err_mask_v == "0.0%": err_mask_v = "<0.05%" t = chart.ax.text( 0.95, 0.1, f"V={err_v}" + ("" if my_ercnfdr_mask is None else f" ({err_mask_v})"), ha="right", va="bottom", transform=chart.ax.transAxes, ) t.set_bbox(dict(facecolor="white", alpha=0.75, edgecolor="black")) t = chart.ax.text( 0.95, 0.9, rf"$\times$ {np.round(cr, 2)}" + ("" if cr_mask is None else rf" ($\times$ {np.round(cr_mask, 2)})") if error else humanize.naturalsize(ercnfdr.nbytes, binary=True), ha="right", va="top", transform=chart.ax.transAxes, ) t.set_bbox(dict(facecolor="white", alpha=0.75, edgecolor="black")) for m in earthkit.plots.schemas.schema.quickmap_subplot_workflow: if m != "title": getattr(chart, m)() for m in earthkit.plots.schemas.schema.quickmap_figure_workflow: if m != "legend": getattr(chart, m)() chart.legend( label=long_name.split(" (")[0] if error else long_name.replace(" (", "\n(") ) counts, bins = np.histogram(z.flatten(), range=(-1, 81), bins=21) midpoints = bins[:-1] + np.diff(bins) / 2 cb = chart.ax.collections[1].colorbar extend_width = (bins[-1] - bins[-2]) / (bins[-1] - bins[0]) cax = cb.ax.inset_axes( [ 0.0 - extend_width * extend_left, 1.25, 1.0 + extend_width * (0 + extend_left + extend_right + 2), 1.0, ] ) cax.bar( midpoints, height=counts, width=(bins[-1] - bins[0]) / len(counts), color=cb.cmap(cb.norm(midpoints)), ) if extend_left: cax.bar( bins[0] - (bins[1] - bins[0]) / 2, height=np.sum(z < -1), width=(bins[-1] - bins[0]) / len(counts), color=cb.cmap(cb.norm(midpoints[0])), ) if extend_right: cax.bar( bins[-1] + (bins[-1] - bins[-2]) / 2, height=np.sum(z > 81), width=(bins[-1] - bins[0]) / len(counts), color=cb.cmap(cb.norm(midpoints[-1])), ) cax.bar( bins[-1] + (bins[-1] - bins[-2]) * (extend_right * 2 + 2 + 1) / 2, height=np.sum(np.isnan(z)), width=(bins[-1] - bins[0]) / len(counts), color="lightgrey", edgecolor="white", lw=0, hatch="XXXX", ) q1, q2, q3 = np.quantile(z.flatten(), [0.25, 0.5, 0.75]) cax.axvline(z.mean().item(), ls=":", ymin=0.1, ymax=0.9, c="w", lw=2) cax.axvline(q1, ymin=0.25, ymax=0.75, c="w", lw=2) cax.axvline(q2, ymin=0.1, ymax=0.9, c="w", lw=2) cax.axvline(q3, ymin=0.25, ymax=0.75, c="w", lw=2) cax.axvline(z.mean().item(), ymin=0.1, ymax=0.9, ls=":", c="k", lw=1) cax.axvline(q1, ymin=0.25, ymax=0.75, c="k", lw=1) cax.axvline(q2, ymin=0.1, ymax=0.9, c="k", lw=1) cax.axvline(q3, ymin=0.25, ymax=0.75, c="k", lw=1) cax.set_xlim( -1 - (bins[-1] - bins[-2]) * extend_left, 81 + (bins[-1] - bins[-2]) * (extend_right + 2), ) cax.set_xticks([]) cax.set_yticks([]) cax.spines[:].set_visible(False)
Copied!
def table_ercnfdr(
    my_ercnfdr: np.ndarray,
    cr,
    title,
    eb_abs,
    corr=None,
):
    err_inf = np.amax(np.abs(my_ercnfdr - ercnfdr))
    err_2 = np.sqrt(np.mean(np.square(my_ercnfdr - ercnfdr)))

    my_ercnfdr_nan = np.where(my_ercnfdr == missing_value, np.nan, my_ercnfdr)
    ercnfdr_nan = np.where(ercnfdr == missing_value, np.nan, ercnfdr)

    err_v = np.mean(
        ~(
            (np.abs(my_ercnfdr_nan - ercnfdr_nan) <= eb_abs)
            | (np.isnan(my_ercnfdr_nan) & np.isnan(ercnfdr_nan))
        )
    )
    err_v = (
        0
        if err_v == 0
        else np.format_float_positional(100 * err_v, precision=1, min_digits=1) + "%"
    )
    if err_v == "0.0%":
        err_v = "<0.05%"

    corr = None if corr is None else compute_corrections_percentage(my_ercnfdr, corr)
    corr = (
        ""
        if corr is None
        else (
            0
            if corr == 0
            else np.format_float_positional(100 * corr, precision=1, min_digits=1) + "%"
        )
    )
    if corr == "0.0%":
        corr = "<0.05%"

    return pd.DataFrame(
        {
            "Compressor": [title[0]],
            "Meta": [title[1]],
            "Safeguarded": [title[2]],
            "Corrections": [title[3]],
            r"$L_{\infty}$": [
                f"{err_inf:.02}",
            ],
            r"$L_{2}$": [
                f"{err_2:.02}",
            ],
            "V": [err_v],
            "C": [corr],
            "CR": [
                rf"$\times$ {np.round(cr, 2)}",
            ],
        }
    )
def table_ercnfdr( my_ercnfdr: np.ndarray, cr, title, eb_abs, corr=None, ): err_inf = np.amax(np.abs(my_ercnfdr - ercnfdr)) err_2 = np.sqrt(np.mean(np.square(my_ercnfdr - ercnfdr))) my_ercnfdr_nan = np.where(my_ercnfdr == missing_value, np.nan, my_ercnfdr) ercnfdr_nan = np.where(ercnfdr == missing_value, np.nan, ercnfdr) err_v = np.mean( ~( (np.abs(my_ercnfdr_nan - ercnfdr_nan) <= eb_abs) | (np.isnan(my_ercnfdr_nan) & np.isnan(ercnfdr_nan)) ) ) err_v = ( 0 if err_v == 0 else np.format_float_positional(100 * err_v, precision=1, min_digits=1) + "%" ) if err_v == "0.0%": err_v = "<0.05%" corr = None if corr is None else compute_corrections_percentage(my_ercnfdr, corr) corr = ( "" if corr is None else ( 0 if corr == 0 else np.format_float_positional(100 * corr, precision=1, min_digits=1) + "%" ) ) if corr == "0.0%": corr = "<0.05%" return pd.DataFrame( { "Compressor": [title[0]], "Meta": [title[1]], "Safeguarded": [title[2]], "Corrections": [title[3]], r"$L_{\infty}$": [ f"{err_inf:.02}", ], r"$L_{2}$": [ f"{err_2:.02}", ], "V": [err_v], "C": [corr], "CR": [ rf"$\times$ {np.round(cr, 2)}", ], } )
Copied!
fig = earthkit.plots.Figure(
    size=(10, 23),
    rows=6,
    columns=2,
)

plot_ercnfdr(
    ercnfdr,
    1.0,
    fig.add_map(0, 0),
    "Original",
    eb_abs=eb_abs,
)
plot_ercnfdr(
    ercnfdr_zfp,
    ercnfdr_zfp_cr,
    fig.add_map(1, 0),
    r"ZFP($\epsilon_{{abs}}$)",
    eb_abs=eb_abs,
    error=True,
    my_ercnfdr_mask=ercnfdr_mask["zfp.rs"],
    cr_mask=ercnfdr_mask_cr["zfp.rs"],
)
plot_ercnfdr(
    ercnfdr_sz3,
    ercnfdr_sz3_cr,
    fig.add_map(2, 0),
    r"SZ3($\epsilon_{{abs}}$)",
    eb_abs=eb_abs,
    error=True,
    my_ercnfdr_mask=ercnfdr_mask["sz3.rs"],
    cr_mask=ercnfdr_mask_cr["sz3.rs"],
)
plot_ercnfdr(
    ercnfdr_sperr,
    ercnfdr_sperr_cr,
    fig.add_map(3, 0),
    r"SPERR($\epsilon_{{abs}}$)",
    eb_abs=eb_abs,
    error=True,
    my_ercnfdr_mask=ercnfdr_mask["sperr.rs"],
    cr_mask=ercnfdr_mask_cr["sperr.rs"],
)

plot_ercnfdr(
    ercnfdr_sg["zero"],
    ercnfdr_sg_cr["zero"],
    fig.add_map(0, 1),
    rf"Safeguarded(0, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )",
    eb_abs=eb_abs,
    error=True,
    corr=ercnfdr_zero,
)
plot_ercnfdr(
    ercnfdr_sg["zfp.rs"],
    ercnfdr_sg_cr["zfp.rs"],
    fig.add_map(1, 1),
    rf"Safeguarded(ZFP, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )",
    eb_abs=eb_abs,
    error=True,
    corr=ercnfdr_zfp,
    my_ercnfdr_mask=ercnfdr_mask_sg["zfp.rs"],
    cr_mask=ercnfdr_mask_sg_cr["zfp.rs"],
)
plot_ercnfdr(
    ercnfdr_sg["sz3.rs"],
    ercnfdr_sg_cr["sz3.rs"],
    fig.add_map(2, 1),
    rf"Safeguarded(SZ3, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )",
    eb_abs=eb_abs,
    error=True,
    corr=ercnfdr_sz3,
    my_ercnfdr_mask=ercnfdr_mask_sg["sz3.rs"],
    cr_mask=ercnfdr_mask_sg_cr["sz3.rs"],
)
plot_ercnfdr(
    ercnfdr_sg["sperr.rs"],
    ercnfdr_sg_cr["sperr.rs"],
    fig.add_map(3, 1),
    rf"Safeguarded(SPERR, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )",
    eb_abs=eb_abs,
    error=True,
    corr=ercnfdr_sperr,
    my_ercnfdr_mask=ercnfdr_mask_sg["sperr.rs"],
    cr_mask=ercnfdr_mask_sg_cr["sperr.rs"],
)

plot_ercnfdr(
    ercnfdr_optzconfig["zfp.rs"],
    ercnfdr_optzconfig_cr["zfp.rs"],
    fig.add_map(4, 0),
    rf"OptZConfig(ZFP, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )",
    eb_abs=eb_abs,
    error=True,
    inset=False,
)
plot_ercnfdr(
    ercnfdr_optzconfig["sz3.rs"],
    ercnfdr_optzconfig_cr["sz3.rs"],
    fig.add_map(4, 1),
    rf"OptZConfig(SZ3, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )",
    eb_abs=eb_abs,
    error=True,
    inset=False,
)
plot_ercnfdr(
    ercnfdr_optzconfig["sperr.rs"],
    ercnfdr_optzconfig_cr["sperr.rs"],
    fig.add_map(5, 0),
    rf"OptZConfig(SPERR, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )",
    eb_abs=eb_abs,
    error=True,
    inset=True,
)

fig.save(Path("plots") / "missing.pdf")
fig = earthkit.plots.Figure( size=(10, 23), rows=6, columns=2, ) plot_ercnfdr( ercnfdr, 1.0, fig.add_map(0, 0), "Original", eb_abs=eb_abs, ) plot_ercnfdr( ercnfdr_zfp, ercnfdr_zfp_cr, fig.add_map(1, 0), r"ZFP($\epsilon_{{abs}}$)", eb_abs=eb_abs, error=True, my_ercnfdr_mask=ercnfdr_mask["zfp.rs"], cr_mask=ercnfdr_mask_cr["zfp.rs"], ) plot_ercnfdr( ercnfdr_sz3, ercnfdr_sz3_cr, fig.add_map(2, 0), r"SZ3($\epsilon_{{abs}}$)", eb_abs=eb_abs, error=True, my_ercnfdr_mask=ercnfdr_mask["sz3.rs"], cr_mask=ercnfdr_mask_cr["sz3.rs"], ) plot_ercnfdr( ercnfdr_sperr, ercnfdr_sperr_cr, fig.add_map(3, 0), r"SPERR($\epsilon_{{abs}}$)", eb_abs=eb_abs, error=True, my_ercnfdr_mask=ercnfdr_mask["sperr.rs"], cr_mask=ercnfdr_mask_cr["sperr.rs"], ) plot_ercnfdr( ercnfdr_sg["zero"], ercnfdr_sg_cr["zero"], fig.add_map(0, 1), rf"Safeguarded(0, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )", eb_abs=eb_abs, error=True, corr=ercnfdr_zero, ) plot_ercnfdr( ercnfdr_sg["zfp.rs"], ercnfdr_sg_cr["zfp.rs"], fig.add_map(1, 1), rf"Safeguarded(ZFP, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )", eb_abs=eb_abs, error=True, corr=ercnfdr_zfp, my_ercnfdr_mask=ercnfdr_mask_sg["zfp.rs"], cr_mask=ercnfdr_mask_sg_cr["zfp.rs"], ) plot_ercnfdr( ercnfdr_sg["sz3.rs"], ercnfdr_sg_cr["sz3.rs"], fig.add_map(2, 1), rf"Safeguarded(SZ3, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )", eb_abs=eb_abs, error=True, corr=ercnfdr_sz3, my_ercnfdr_mask=ercnfdr_mask_sg["sz3.rs"], cr_mask=ercnfdr_mask_sg_cr["sz3.rs"], ) plot_ercnfdr( ercnfdr_sg["sperr.rs"], ercnfdr_sg_cr["sperr.rs"], fig.add_map(3, 1), rf"Safeguarded(SPERR, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )", eb_abs=eb_abs, error=True, corr=ercnfdr_sperr, my_ercnfdr_mask=ercnfdr_mask_sg["sperr.rs"], cr_mask=ercnfdr_mask_sg_cr["sperr.rs"], ) plot_ercnfdr( ercnfdr_optzconfig["zfp.rs"], ercnfdr_optzconfig_cr["zfp.rs"], fig.add_map(4, 0), rf"OptZConfig(ZFP, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )", eb_abs=eb_abs, error=True, inset=False, ) plot_ercnfdr( ercnfdr_optzconfig["sz3.rs"], ercnfdr_optzconfig_cr["sz3.rs"], fig.add_map(4, 1), rf"OptZConfig(SZ3, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )", eb_abs=eb_abs, error=True, inset=False, ) plot_ercnfdr( ercnfdr_optzconfig["sperr.rs"], ercnfdr_optzconfig_cr["sperr.rs"], fig.add_map(5, 0), rf"OptZConfig(SPERR, $\epsilon_{{{{abs}}}} \cup \overset{{{{{missing_value}}}}}{{{{\leftrightarrow}}}}$ )", eb_abs=eb_abs, error=True, inset=True, ) fig.save(Path("plots") / "missing.pdf")
No description has been provided for this image
Copied!
ercnfdr_table = pd.concat(
    [
        table_ercnfdr(
            ercnfdr_sg_lossless["zero"],
            ercnfdr_sg_lossless_cr["zero"],
            [
                "0",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "lossless",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_zero,
        ),
        table_ercnfdr(
            ercnfdr_sg["zero"],
            ercnfdr_sg_cr["zero"],
            [
                "0",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "one-shot",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_zero,
        ),
        table_ercnfdr(
            ercnfdr_zfp,
            ercnfdr_zfp_cr,
            [r"ZFP($\epsilon_{abs}$)", "-", "-", ""],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_sg_lossless["zfp.rs"],
            ercnfdr_sg_lossless_cr["zfp.rs"],
            [
                r"ZFP($\epsilon_{abs}$)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "lossless",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_zfp,
        ),
        table_ercnfdr(
            ercnfdr_sg["zfp.rs"],
            ercnfdr_sg_cr["zfp.rs"],
            [
                r"ZFP($\epsilon_{abs}$)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "one-shot",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_zfp,
        ),
        table_ercnfdr(
            ercnfdr_mask["zfp.rs"],
            ercnfdr_mask_cr["zfp.rs"],
            [
                r"ZFP($\epsilon_{abs}$)",
                rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$",
                "-",
                "",
            ],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_mask_sg["zfp.rs"],
            ercnfdr_mask_sg_cr["zfp.rs"],
            [
                r"ZFP($\epsilon_{abs}$)",
                rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "one-shot",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_mask["zfp.rs"],
        ),
        table_ercnfdr(
            ercnfdr_optzconfig["zfp.rs"],
            ercnfdr_optzconfig_cr["zfp.rs"],
            [
                r"OptZConfig(ZFP)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "",
            ],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_sz3,
            ercnfdr_sz3_cr,
            [r"SZ3($\epsilon_{abs}$)", "-", "-", ""],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_sg_lossless["sz3.rs"],
            ercnfdr_sg_lossless_cr["sz3.rs"],
            [
                r"SZ3($\epsilon_{abs}$)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "lossless",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_sz3,
        ),
        table_ercnfdr(
            ercnfdr_sg["sz3.rs"],
            ercnfdr_sg_cr["sz3.rs"],
            [
                r"SZ3($\epsilon_{abs}$)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "one-shot",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_sz3,
        ),
        table_ercnfdr(
            ercnfdr_mask["sz3.rs"],
            ercnfdr_mask_cr["sz3.rs"],
            [
                r"SZ3($\epsilon_{abs}$)",
                rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$",
                "-",
                "",
            ],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_mask_sg["sz3.rs"],
            ercnfdr_mask_sg_cr["sz3.rs"],
            [
                r"SZ3($\epsilon_{abs}$)",
                rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "one-shot",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_mask["sz3.rs"],
        ),
        table_ercnfdr(
            ercnfdr_optzconfig["sz3.rs"],
            ercnfdr_optzconfig_cr["sz3.rs"],
            [
                r"OptZConfig(SZ3)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "",
            ],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_sperr,
            ercnfdr_sperr_cr,
            [r"SPERR($\epsilon_{abs}$)", "-", "-", ""],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_sg_lossless["sperr.rs"],
            ercnfdr_sg_lossless_cr["sperr.rs"],
            [
                r"SPERR($\epsilon_{abs}$)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "lossless",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_sperr,
        ),
        table_ercnfdr(
            ercnfdr_sg["sperr.rs"],
            ercnfdr_sg_cr["sperr.rs"],
            [
                r"SPERR($\epsilon_{abs}$)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "one-shot",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_sperr,
        ),
        table_ercnfdr(
            ercnfdr_mask["sperr.rs"],
            ercnfdr_mask_cr["sperr.rs"],
            [
                r"SPERR($\epsilon_{abs}$)",
                rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$",
                "-",
                "",
            ],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_mask_sg["sperr.rs"],
            ercnfdr_mask_sg_cr["sperr.rs"],
            [
                r"SPERR($\epsilon_{abs}$)",
                rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "one-shot",
            ],
            eb_abs=eb_abs,
            corr=ercnfdr_mask["sperr.rs"],
        ),
        table_ercnfdr(
            ercnfdr_optzconfig["sperr.rs"],
            ercnfdr_optzconfig_cr["sperr.rs"],
            [
                r"OptZConfig(SPERR)",
                "-",
                rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$",
                "",
            ],
            eb_abs=eb_abs,
        ),
        table_ercnfdr(
            ercnfdr_zstd,
            ercnfdr_zstd_cr,
            ["ZSTD(22)", "-", "-", ""],
            eb_abs=eb_abs,
        ),
    ]
).set_index(["Compressor", "Meta", "Safeguarded", "Corrections"])

Path("tables").joinpath("missing.tex").write_text(
    ercnfdr_table.to_latex(escape=False)
    .replace("%", r"\%")
    .replace("\\cline{1-9} \\cline{2-9} \\cline{3-9}\n\\bottomrule", "\\bottomrule")
)

ercnfdr_table
ercnfdr_table = pd.concat( [ table_ercnfdr( ercnfdr_sg_lossless["zero"], ercnfdr_sg_lossless_cr["zero"], [ "0", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "lossless", ], eb_abs=eb_abs, corr=ercnfdr_zero, ), table_ercnfdr( ercnfdr_sg["zero"], ercnfdr_sg_cr["zero"], [ "0", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "one-shot", ], eb_abs=eb_abs, corr=ercnfdr_zero, ), table_ercnfdr( ercnfdr_zfp, ercnfdr_zfp_cr, [r"ZFP($\epsilon_{abs}$)", "-", "-", ""], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_sg_lossless["zfp.rs"], ercnfdr_sg_lossless_cr["zfp.rs"], [ r"ZFP($\epsilon_{abs}$)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "lossless", ], eb_abs=eb_abs, corr=ercnfdr_zfp, ), table_ercnfdr( ercnfdr_sg["zfp.rs"], ercnfdr_sg_cr["zfp.rs"], [ r"ZFP($\epsilon_{abs}$)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "one-shot", ], eb_abs=eb_abs, corr=ercnfdr_zfp, ), table_ercnfdr( ercnfdr_mask["zfp.rs"], ercnfdr_mask_cr["zfp.rs"], [ r"ZFP($\epsilon_{abs}$)", rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$", "-", "", ], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_mask_sg["zfp.rs"], ercnfdr_mask_sg_cr["zfp.rs"], [ r"ZFP($\epsilon_{abs}$)", rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "one-shot", ], eb_abs=eb_abs, corr=ercnfdr_mask["zfp.rs"], ), table_ercnfdr( ercnfdr_optzconfig["zfp.rs"], ercnfdr_optzconfig_cr["zfp.rs"], [ r"OptZConfig(ZFP)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "", ], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_sz3, ercnfdr_sz3_cr, [r"SZ3($\epsilon_{abs}$)", "-", "-", ""], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_sg_lossless["sz3.rs"], ercnfdr_sg_lossless_cr["sz3.rs"], [ r"SZ3($\epsilon_{abs}$)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "lossless", ], eb_abs=eb_abs, corr=ercnfdr_sz3, ), table_ercnfdr( ercnfdr_sg["sz3.rs"], ercnfdr_sg_cr["sz3.rs"], [ r"SZ3($\epsilon_{abs}$)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "one-shot", ], eb_abs=eb_abs, corr=ercnfdr_sz3, ), table_ercnfdr( ercnfdr_mask["sz3.rs"], ercnfdr_mask_cr["sz3.rs"], [ r"SZ3($\epsilon_{abs}$)", rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$", "-", "", ], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_mask_sg["sz3.rs"], ercnfdr_mask_sg_cr["sz3.rs"], [ r"SZ3($\epsilon_{abs}$)", rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "one-shot", ], eb_abs=eb_abs, corr=ercnfdr_mask["sz3.rs"], ), table_ercnfdr( ercnfdr_optzconfig["sz3.rs"], ercnfdr_optzconfig_cr["sz3.rs"], [ r"OptZConfig(SZ3)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "", ], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_sperr, ercnfdr_sperr_cr, [r"SPERR($\epsilon_{abs}$)", "-", "-", ""], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_sg_lossless["sperr.rs"], ercnfdr_sg_lossless_cr["sperr.rs"], [ r"SPERR($\epsilon_{abs}$)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "lossless", ], eb_abs=eb_abs, corr=ercnfdr_sperr, ), table_ercnfdr( ercnfdr_sg["sperr.rs"], ercnfdr_sg_cr["sperr.rs"], [ r"SPERR($\epsilon_{abs}$)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "one-shot", ], eb_abs=eb_abs, corr=ercnfdr_sperr, ), table_ercnfdr( ercnfdr_mask["sperr.rs"], ercnfdr_mask_cr["sperr.rs"], [ r"SPERR($\epsilon_{abs}$)", rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$", "-", "", ], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_mask_sg["sperr.rs"], ercnfdr_mask_sg_cr["sperr.rs"], [ r"SPERR($\epsilon_{abs}$)", rf"$\overset{{{missing_value}}}{{\leftrightarrow}}$", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "one-shot", ], eb_abs=eb_abs, corr=ercnfdr_mask["sperr.rs"], ), table_ercnfdr( ercnfdr_optzconfig["sperr.rs"], ercnfdr_optzconfig_cr["sperr.rs"], [ r"OptZConfig(SPERR)", "-", rf"$\epsilon_{{abs}} \cup \overset{{{missing_value}}}{{\leftrightarrow}}$", "", ], eb_abs=eb_abs, ), table_ercnfdr( ercnfdr_zstd, ercnfdr_zstd_cr, ["ZSTD(22)", "-", "-", ""], eb_abs=eb_abs, ), ] ).set_index(["Compressor", "Meta", "Safeguarded", "Corrections"]) Path("tables").joinpath("missing.tex").write_text( ercnfdr_table.to_latex(escape=False) .replace("%", r"\%") .replace("\\cline{1-9} \\cline{2-9} \\cline{3-9}\n\\bottomrule", "\\bottomrule") ) ercnfdr_table
$L_{\infty}$ $L_{2}$ V C CR
Compressor Meta Safeguarded Corrections
0 - $\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ lossless 1.0 0.12 0 87.6% $\times$ 62.89
one-shot 1.0 0.31 0 87.6% $\times$ 72.72
ZFP($\epsilon_{abs}$) - - 0.75 0.16 1.8% $\times$ 6.27
$\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ lossless 0.75 0.15 0 1.8% $\times$ 6.16
one-shot 0.75 0.15 0 1.8% $\times$ 6.16
$\overset{9999}{\leftrightarrow}$ - 0.75 0.15 0 $\times$ 6.18
$\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ one-shot 0.75 0.15 0 0 $\times$ 6.18
OptZConfig(ZFP) - $\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ 0.0 0.0 0 $\times$ 5.12
SZ3($\epsilon_{abs}$) - - 1.0 0.89 70.5% $\times$ 59.96
$\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ lossless 1.0 0.31 0 70.5% $\times$ 46.0
one-shot 1.0 0.31 0 70.5% $\times$ 46.0
$\overset{9999}{\leftrightarrow}$ - 1.0 0.31 0 $\times$ 53.1
$\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ one-shot 1.0 0.31 0 0 $\times$ 53.1
OptZConfig(SZ3) - $\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ 6.4e-05 1.3e-06 0 $\times$ 57.23
SPERR($\epsilon_{abs}$) - - 0.75 0.38 50.3% $\times$ 5.63
$\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ lossless 0.75 0.24 0 50.3% $\times$ 5.5
one-shot 0.75 0.24 0 50.3% $\times$ 5.5
$\overset{9999}{\leftrightarrow}$ - 0.75 0.24 0 $\times$ 5.57
$\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ one-shot 0.75 0.24 0 0 $\times$ 5.57
OptZConfig(SPERR) - $\epsilon_{abs} \cup \overset{9999}{\leftrightarrow}$ 7.5e-07 3.9e-07 50.4% $\times$ 2.35
ZSTD(22) - - 0.0 0.0 0 $\times$ 49.94
Copied!
import json

with Path("observations").joinpath("missing.json").open("w") as f:
    json.dump(observations, f)
import json with Path("observations").joinpath("missing.json").open("w") as f: json.dump(observations, f)
Copied!

Next

Built with ProperDocs using a theme provided by Read the Docs.