///|
/// A rectangular two-dimensional floating-point array with an explicit mask.
pub struct MaskedMatrix {
priv values : Array[Array[Double]]
priv mask : Array[Array[Bool]]
}
///|
fn validate_matrix_values(
values : Array[Array[Double]],
) -> Int32 raise MaskError {
if values.length() == 0 {
0
} else {
let columns = values[0].length()
for row in values {
if row.length() != columns {
raise MaskError::InvalidShape(
message="masked matrix rows must have equal length",
)
}
}
columns
}
}
///|
fn copy_bool_matrix(mask : Array[Array[Bool]]) -> Array[Array[Bool]] {
let result = Array(capacity=mask.length())
for row in mask {
result.push(row.copy())
}
result
}
///|
fn empty_mask_matrix(rows : Int32, columns : Int32) -> Array[Array[Bool]] {
let result = Array(capacity=rows)
for _ in 0.. MaskedMatrix raise MaskError {
let columns = validate_matrix_values(values)
let actual_mask = match mask {
None => empty_mask_matrix(values.length(), columns)
Some(mask) => {
if mask.length() != values.length() {
raise MaskError::InvalidShape(
message="masked matrix mask must have the same row count as values",
)
}
for row in mask {
if row.length() != columns {
raise MaskError::InvalidShape(
message="masked matrix mask rows must match values",
)
}
}
copy_bool_matrix(mask)
}
}
let copied_values = Array(capacity=values.length())
for row in values {
copied_values.push(row.copy())
}
{ values: copied_values, mask: actual_mask, }
}
///|
/// Number of matrix rows.
pub fn MaskedMatrix::rows(self : MaskedMatrix) -> Int32 {
self.values.length()
}
///|
/// Number of matrix columns.
pub fn MaskedMatrix::columns(self : MaskedMatrix) -> Int32 {
if self.values.length() == 0 {
0
} else {
self.values[0].length()
}
}
///|
/// Return a deep copy of the source matrix.
pub fn MaskedMatrix::values(self : MaskedMatrix) -> Array[Array[Double]] {
let result = Array(capacity=self.rows())
for row in self.values {
result.push(row.copy())
}
result
}
///|
/// Return a deep copy of the explicit mask matrix.
pub fn MaskedMatrix::mask(self : MaskedMatrix) -> Array[Array[Bool]] {
copy_bool_matrix(self.mask)
}
///|
/// Whether one matrix cell is explicitly masked.
pub fn MaskedMatrix::is_masked(
self : MaskedMatrix,
row : Int32,
column : Int32,
) -> Bool {
self.mask[row][column]
}
///|
/// Number of unmasked matrix cells.
pub fn MaskedMatrix::count(self : MaskedMatrix) -> Int32 {
let mut result = 0
for row in self.mask {
for masked in row {
if !masked {
result += 1
}
}
}
result
}
///|
/// Replace masked cells with `fill_value` and return a deep copy.
pub fn MaskedMatrix::filled(
self : MaskedMatrix,
fill_value : Double,
) -> Array[Array[Double]] {
let result = Array(capacity=self.rows())
for row in 0.. Array[Array[Double]] {
self.filled(nan_value())
}
///|
/// Return finite and unmasked cells in row-major order.
pub fn MaskedMatrix::finite_values(self : MaskedMatrix) -> Array[Double] {
let result = Array(capacity=self.count())
for row in 0.. Double,
) -> MaskedMatrix {
let values = Array(capacity=self.rows())
let mask = Array(capacity=self.rows())
for row in 0..