///|
fn finite_double(value : Double) -> Bool {
!value.is_nan() && value != @double.infinity && value != @double.neg_infinity
}
///|
fn copy_matrix(rows : Array[Array[Double]]) -> Array[Array[Double]] {
let copied : Array[Array[Double]] = []
for row in rows {
copied.push(row.copy())
}
copied
}
///|
fn sorted_distinct_labels(labels : Array[Int]) -> Array[Int] {
let result : Array[Int] = []
for label in labels {
let mut position = 0
while position < result.length() && result[position] < label {
position = position + 1
}
if position == result.length() || result[position] != label {
result.push(label)
let mut index = result.length() - 1
while index > position {
result[index] = result[index - 1]
index = index - 1
}
result[position] = label
}
}
result
}
///|
/// Validates and defensively copies a dense numeric classification dataset.
pub fn dataset(
features : Array[Array[Double]],
labels : Array[Int],
name : String,
) -> Result[Dataset, SvmError] {
if features.is_empty() {
return Err(EmptyDataset)
}
if features.length() != labels.length() {
return Err(LabelLengthMismatch(features.length(), labels.length()))
}
let columns = features[0].length()
if columns == 0 {
return Err(EmptyFeatureSet)
}
for row_index, row in features {
if row.length() != columns {
return Err(RaggedRow(row_index, columns, row.length()))
}
for column_index, value in row {
if !finite_double(value) {
return Err(NonFiniteFeature(row_index, column_index))
}
}
}
Ok({
feature_rows: copy_matrix(features),
class_labels: labels.copy(),
dataset_name: name,
column_count: columns,
sorted_classes: sorted_distinct_labels(labels),
})
}
///|
pub fn Dataset::row_count(self : Dataset) -> Int {
self.feature_rows.length()
}
///|
pub fn Dataset::feature_count(self : Dataset) -> Int {
self.column_count
}
///|
pub fn Dataset::name(self : Dataset) -> String {
self.dataset_name
}
///|
pub fn Dataset::features(self : Dataset) -> Array[Array[Double]] {
copy_matrix(self.feature_rows)
}
///|
pub fn Dataset::labels(self : Dataset) -> Array[Int] {
self.class_labels.copy()
}
///|
pub fn Dataset::classes(self : Dataset) -> Array[Int] {
self.sorted_classes.copy()
}
///|
pub fn Dataset::class_count(self : Dataset, label : Int) -> Int {
let mut count = 0
for value in self.class_labels {
if value == label {
count = count + 1
}
}
count
}
///|
pub fn Dataset::row(
self : Dataset,
index : Int,
) -> Result[Array[Double], SvmError] {
if index < 0 || index >= self.row_count() {
return Err(InvalidSubsetIndex(index, self.row_count()))
}
Ok(self.feature_rows[index].copy())
}
///|
/// Selects unique rows in caller-provided order and gives the result a new name.
pub fn Dataset::subset(
self : Dataset,
indices : Array[Int],
name : String,
) -> Result[Dataset, SvmError] {
if indices.is_empty() {
return Err(EmptyDataset)
}
let selected_features : Array[Array[Double]] = []
let selected_labels : Array[Int] = []
for position, index in indices {
if index < 0 || index >= self.row_count() {
return Err(InvalidSubsetIndex(index, self.row_count()))
}
for previous = 0; previous < position; previous = previous + 1 {
if indices[previous] == index {
return Err(DuplicateSubsetIndex(index))
}
}
selected_features.push(self.feature_rows[index].copy())
selected_labels.push(self.class_labels[index])
}
dataset(selected_features, selected_labels, name)
}