///|
/// Observations are rows, features are columns. Public accessors return copies.
pub struct Dataset {
features : Array[String]
samples : Array[String]
cells : Array[Array[Double]]
}
///|
fn labels(names : Array[String]) -> Unit raise CodaError {
for i, name in names {
if name.trim().length() == 0 || name.length() > 128 {
raise Invalid("labels must be nonblank and at most 128 code units")
}
for j in 0.. Dataset raise CodaError {
vector_size(features.length())
if rows.length() < 1 || rows.length() > 1024 {
raise Budget("dataset row count must be 1..1024")
}
if samples.length() != rows.length() {
raise Shape("sample label count differs from rows")
}
labels(features)
labels(samples)
let cells = []
for row in rows {
if row.length() != features.length() {
raise Shape("ragged dataset or incorrect feature count")
}
positive(row)
cells.push(closure(row))
}
{ features: features.copy(), samples: samples.copy(), cells, }
}
///|
pub fn Dataset::feature_names(self : Dataset) -> Array[String] {
self.features.copy()
}
///|
pub fn Dataset::sample_names(self : Dataset) -> Array[String] {
self.samples.copy()
}
///|
pub fn Dataset::rows(self : Dataset) -> Array[Array[Double]] {
self.cells.map(r => r.copy())
}
///|
pub fn Dataset::row_count(self : Dataset) -> Int {
self.cells.length()
}
///|
pub fn Dataset::part_count(self : Dataset) -> Int {
self.features.length()
}
///|
pub fn Dataset::clr_rows(
self : Dataset,
) -> Array[Array[Double]] raise CodaError {
self.cells.map(r => clr(r))
}
///|
pub fn Dataset::ilr_rows(
self : Dataset,
b : Basis,
) -> Array[Array[Double]] raise CodaError {
self.cells.map(r => ilr(r, b))
}
///|
/// Explicit name alignment prevents silently comparing different column orders.
pub fn Dataset::select_features(
self : Dataset,
order : Array[String],
) -> Dataset raise CodaError {
labels(order)
vector_size(order.length())
let indices = []
for name in order {
let mut found = -1
for i, existing in self.features {
if existing == name {
found = i
}
}
if found < 0 {
raise Invalid("requested feature name is absent")
}
indices.push(found)
}
dataset(order, self.samples, self.cells.map(r => subcomposition(r, indices)))
}
///|
pub fn Dataset::select_samples(
self : Dataset,
indices : Array[Int],
) -> Dataset raise CodaError {
if indices.length() == 0 {
raise Shape("empty sample selection")
}
unique_indices(indices, self.cells.length())
dataset(
self.features,
indices.map(i => self.samples[i]),
indices.map(i => self.cells[i]),
)
}