///|
/// Stores validated dense numeric rows and integer class labels.
pub(all) struct ClassificationDataset {
rows : Array[Array[Double]]
labels : Array[Int]
feature_total : Int
class_total : Int
} derive(Eq, Debug)
///|
/// Stores validated dense numeric rows and continuous targets.
pub(all) struct RegressionDataset {
rows : Array[Array[Double]]
targets : Array[Double]
feature_total : Int
} derive(Eq, Debug)
///|
fn finite_number(value : Double) -> Bool {
!value.is_nan() && value != @double.infinity && value != @double.neg_infinity
}
///|
fn validate_rows(
rows : Array[Array[Double]],
) -> Result[(Array[Array[Double]], Int), TreeError] {
if rows.is_empty() {
return Err(EmptyDataset)
}
let expected = rows[0].length()
if expected == 0 {
return Err(EmptyFeatureRow(0))
}
let copied : Array[Array[Double]] = []
for row_index, row in rows {
if row.length() == 0 {
return Err(EmptyFeatureRow(row_index))
}
if row.length() != expected {
return Err(InconsistentFeatureCount(row_index, expected, row.length()))
}
for feature_index, value in row {
if !finite_number(value) {
return Err(NonFiniteFeature(row_index, feature_index))
}
}
copied.push(row.copy())
}
Ok((copied, expected))
}
///|
/// Validates and copies a classification dataset.
pub fn classification_dataset(
rows : Array[Array[Double]],
labels : Array[Int],
class_count : Int,
) -> Result[ClassificationDataset, TreeError] {
if class_count <= 0 {
return Err(InvalidClassCount(class_count))
}
let (copied_rows, feature_count) = match validate_rows(rows) {
Ok(value) => value
Err(error) => return Err(error)
}
if labels.length() != copied_rows.length() {
return Err(TargetCountMismatch(copied_rows.length(), labels.length()))
}
for row_index, label in labels {
if label < 0 || label >= class_count {
return Err(InvalidClassLabel(row_index, label, class_count))
}
}
Ok({
rows: copied_rows,
labels: labels.copy(),
feature_total: feature_count,
class_total: class_count,
})
}
///|
/// Validates and copies a regression dataset.
pub fn regression_dataset(
rows : Array[Array[Double]],
targets : Array[Double],
) -> Result[RegressionDataset, TreeError] {
let (copied_rows, feature_count) = match validate_rows(rows) {
Ok(value) => value
Err(error) => return Err(error)
}
if targets.length() != copied_rows.length() {
return Err(TargetCountMismatch(copied_rows.length(), targets.length()))
}
for row_index, target in targets {
if !finite_number(target) {
return Err(NonFiniteTarget(row_index))
}
}
Ok({
rows: copied_rows,
targets: targets.copy(),
feature_total: feature_count,
})
}
///|
pub fn ClassificationDataset::row_count(self : ClassificationDataset) -> Int {
self.rows.length()
}
///|
pub fn ClassificationDataset::feature_count(
self : ClassificationDataset,
) -> Int {
self.feature_total
}
///|
pub fn ClassificationDataset::class_count(self : ClassificationDataset) -> Int {
self.class_total
}
///|
pub fn ClassificationDataset::row(
self : ClassificationDataset,
index : Int,
) -> Array[Double] {
self.rows[index].copy()
}
///|
pub fn ClassificationDataset::label(
self : ClassificationDataset,
index : Int,
) -> Int {
self.labels[index]
}
///|
pub fn RegressionDataset::row_count(self : RegressionDataset) -> Int {
self.rows.length()
}
///|
pub fn RegressionDataset::feature_count(self : RegressionDataset) -> Int {
self.feature_total
}
///|
pub fn RegressionDataset::row(
self : RegressionDataset,
index : Int,
) -> Array[Double] {
self.rows[index].copy()
}
///|
pub fn RegressionDataset::target(
self : RegressionDataset,
index : Int,
) -> Double {
self.targets[index]
}