From 9d5cd73115149ab6b048c6fc3ddd14a704ecb5b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20M=C3=BCller?= <mueller.seb@posteo.de>
Date: Tue, 29 Aug 2023 13:39:04 +0200
Subject: [PATCH] data: add tools for masked arrays

---
 src/finam/data/__init__.py |  8 +++++
 src/finam/data/tools.py    | 66 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git a/src/finam/data/__init__.py b/src/finam/data/__init__.py
index 9edf5a82..836f98ea 100644
--- a/src/finam/data/__init__.py
+++ b/src/finam/data/__init__.py
@@ -88,12 +88,15 @@ from .tools import (
     assert_type,
     check,
     check_quantified,
+    filled,
     full,
     full_like,
     get_dimensionality,
     get_magnitude,
     get_units,
+    has_masked_values,
     has_time_axis,
+    is_masked_array,
     is_quantified,
     prepare,
     quantify,
@@ -141,3 +144,8 @@ __all__ += [
     "to_datetime",
     "to_units",
 ]
+__all__ += [
+    "is_masked_array",
+    "has_masked_values",
+    "filled",
+]
diff --git a/src/finam/data/tools.py b/src/finam/data/tools.py
index 9aee2a61..9484f685 100644
--- a/src/finam/data/tools.py
+++ b/src/finam/data/tools.py
@@ -433,6 +433,72 @@ def is_quantified(xdata):
     return isinstance(xdata, pint.Quantity)
 
 
+def is_masked_array(xdata):
+    """
+    Check if data is a masked array.
+
+    Parameters
+    ----------
+    xdata : Any
+        The given data array.
+
+    Returns
+    -------
+    bool
+        Whether the data is a MaskedArray.
+    """
+    if is_quantified(xdata):
+        return np.ma.isMaskedArray(xdata.magnitude)
+    return np.ma.isMaskedArray(xdata)
+
+
+def has_masked_values(xdata):
+    """
+    Determine whether the data has masked values.
+
+    Parameters
+    ----------
+    xdata : Any
+        The given data array.
+
+    Returns
+    -------
+    bool
+        Whether the data is a MaskedArray and has any masked values.
+    """
+    if is_quantified(xdata):
+        return np.ma.is_masked(xdata.magnitude)
+    return np.ma.is_masked(xdata)
+
+
+def filled(xdata, fill_value=None):
+    """
+    Return a filled array if the data is masked.
+
+    Parameters
+    ----------
+    xdata : :class:`pint.Quantity` or :class:`numpy.ndarray` or :class:`numpy.ma.MaskedArray`
+        The reference object input.
+    fill_value : array_like, optional
+        The value to use for invalid entries. Can be scalar or non-scalar.
+        If non-scalar, the resulting ndarray must be broadcastable over
+        input array. Default is None, in which case, the `fill_value`
+        attribute of the array is used instead.
+
+    Returns
+    -------
+    pint.Quantity or numpy.ndarray
+        New object with the same shape and type as other,
+        with the data filled with fill_value.
+        Units will be taken from the input if present.
+    """
+    if not is_masked_array(xdata):
+        return xdata
+    if is_quantified(xdata):
+        return UNITS.Quantity(xdata.magnitude.filled(fill_value), xdata.units)
+    return xdata.filled(fill_value)
+
+
 def quantify(xdata, units=None):
     """
     Quantifies data.
-- 
GitLab