///|
/// A one-dimensional floating-point array with an explicit boolean mask.
///
/// Masked values remain in `values` so callers can preserve source data, while
/// plotting adapters use `to_array` to replace masked entries with NaN.
pub struct MaskedArray {
priv values : Array[Double]
priv mask : Array[Bool]
}
///|
fn nan_value() -> Double {
0.0 / 0.0
}
///|
fn mask_or_invalid(value : Double, masked : Bool) -> Bool {
masked || value.is_nan() || value.is_inf()
}
///|
/// Construct a masked array. An omitted mask means that every value is
/// initially unmasked; an explicit mask must have the same length as `values`.
pub fn masked_array(
values : Array[Double],
mask? : Array[Bool],
) -> MaskedArray raise MaskError {
let actual_mask = match mask {
None => {
let result = Array(capacity=values.length())
for _ in values {
result.push(false)
}
result
}
Some(mask) => {
if mask.length() != values.length() {
raise MaskError::InvalidShape(
message="masked array mask must have the same length as values",
)
}
mask.copy()
}
}
{ values: values.copy(), mask: actual_mask, }
}
///|
/// Number of entries in a masked array.
pub fn MaskedArray::length(self : MaskedArray) -> Int32 {
self.values.length()
}
///|
/// Return a copy of the source values, including values that are masked.
pub fn MaskedArray::values(self : MaskedArray) -> Array[Double] {
self.values.copy()
}
///|
/// Return a copy of the explicit boolean mask.
pub fn MaskedArray::mask(self : MaskedArray) -> Array[Bool] {
self.mask.copy()
}
///|
/// Whether the entry at `index` is explicitly masked.
pub fn MaskedArray::is_masked(self : MaskedArray, index : Int32) -> Bool {
self.mask[index]
}
///|
/// Number of entries that are not explicitly masked.
pub fn MaskedArray::count(self : MaskedArray) -> Int32 {
let mut result = 0
for masked in self.mask {
if !masked {
result += 1
}
}
result
}
///|
/// Whether every entry is explicitly masked.
pub fn MaskedArray::all_masked(self : MaskedArray) -> Bool {
self.count() == 0
}
///|
/// Replace masked entries with `fill_value` and return an independent array.
pub fn MaskedArray::filled(
self : MaskedArray,
fill_value : Double,
) -> Array[Double] {
let result = Array(capacity=self.length())
for index in 0.. Array[Double] {
self.filled(nan_value())
}
///|
/// Return unmasked entries in their original order.
pub fn MaskedArray::compressed(self : MaskedArray) -> Array[Double] {
let result = Array(capacity=self.count())
for index in 0.. Array[Double] {
let result = Array(capacity=self.count())
for index in 0.. Double,
) -> MaskedArray {
let values = Array(capacity=self.length())
let mask = Array(capacity=self.length())
for index in 0.. Double,
) -> MaskedArray raise DataError {
if self.length() != other.length() {
raise DataError::LengthMismatch(
expected=self.length(),
actual=other.length(),
)
}
let values = Array(capacity=self.length())
let mask = Array(capacity=self.length())
for index in 0..