///|
/// Streaming k-means with bounded state and optional exponential adaptation.
pub struct OnlineKMeans {
centroids : Array[Array[Double]]
counts : Array[Double]
learning_rate : Double
mut assignments : Int
}
///|
pub fn OnlineKMeans::new(
clusters : Int,
dimension : Int,
learning_rate? : Double = 0.05,
) -> OnlineKMeans {
let cluster_count = if clusters < 0 { 0 } else { clusters }
let size = if dimension < 0 { 0 } else { dimension }
{
centroids: Array::makei(cluster_count, class_index => {
Array::makei(size, feature_index => {
if class_index == feature_index % (cluster_count + 1) {
1.0
} else {
0.0
}
})
}),
counts: Array::make(cluster_count, 0.0),
learning_rate: clamp(learning_rate, 1.0e-6, 1.0),
assignments: 0,
}
}
///|
pub fn OnlineKMeans::clusters(self : OnlineKMeans) -> Int {
self.centroids.length()
}
///|
pub fn OnlineKMeans::dimension(self : OnlineKMeans) -> Int {
if self.centroids.is_empty() {
0
} else {
self.centroids[0].length()
}
}
///|
pub fn OnlineKMeans::centroids(self : OnlineKMeans) -> Array[Array[Double]] {
self.centroids.map(row => copy_vector(row))
}
///|
pub fn OnlineKMeans::nearest(
self : OnlineKMeans,
features : Array[Double],
) -> Int? {
if self.centroids.is_empty() {
None
} else {
let mut best = 0
let mut best_distance = squared_distance_for_cluster(
features,
self.centroids[0],
)
for cluster in 1.. Double {
let size = if left.length() < right.length() {
left.length()
} else {
right.length()
}
let mut total = 0.0
for i in 0.. Int? {
match self.nearest(features) {
None => None
Some(cluster) => {
let centroid = self.centroids[cluster]
let rate = self.learning_rate / (1.0 + 0.01 * self.counts[cluster])
let limit = if features.length() < centroid.length() {
features.length()
} else {
centroid.length()
}
for i in 0.. Double {
match self.nearest(features) {
None => 0.0
Some(cluster) =>
squared_distance_for_cluster(features, self.centroids[cluster]).sqrt()
}
}
///|
pub fn OnlineKMeans::cluster_counts(self : OnlineKMeans) -> Array[Double] {
copy_vector(self.counts)
}
///|
pub fn OnlineKMeans::assignments(self : OnlineKMeans) -> Int {
self.assignments
}
///|
pub fn OnlineKMeans::reset(self : OnlineKMeans) -> Unit {
self.counts.fill(0.0)
self.assignments = 0
}
///|
pub struct OnlineMedoid {
center : Array[Double]
mut count : Double
learning_rate : Double
mut cost : Double
}
///|
pub fn OnlineMedoid::new(
dimension : Int,
learning_rate? : Double = 0.05,
) -> OnlineMedoid {
{
center: Array::make(if dimension < 0 { 0 } else { dimension }, 0.0),
count: 0.0,
learning_rate: clamp(learning_rate, 1.0e-6, 1.0),
cost: 0.0,
}
}
///|
pub fn OnlineMedoid::update(
self : OnlineMedoid,
features : Array[Double],
) -> Double {
let distance = squared_distance_for_cluster(features, self.center).sqrt()
self.count += 1.0
let rate = self.learning_rate / (1.0 + self.count * 0.01)
let limit = if features.length() < self.center.length() {
features.length()
} else {
self.center.length()
}
for i in 0.. Array[Double] {
copy_vector(self.center)
}
///|
pub fn OnlineMedoid::count(self : OnlineMedoid) -> Double {
self.count
}
///|
pub fn OnlineMedoid::mean_cost(self : OnlineMedoid) -> Double {
if self.count <= 0.0 {
0.0
} else {
self.cost / self.count
}
}
///|
pub fn OnlineMedoid::reset(self : OnlineMedoid) -> Unit {
self.center.fill(0.0)
self.count = 0.0
self.cost = 0.0
}