(data: &DMatrix<f64>, labels: &[usize])
| 402 | } |
| 403 | |
| 404 | fn silhouette_samples(data: &DMatrix<f64>, labels: &[usize]) -> Vec<f64> { |
| 405 | let n = data.nrows(); |
| 406 | let mut by_cluster: BTreeMap<usize, Vec<usize>> = BTreeMap::new(); |
| 407 | for (i, lbl) in labels.iter().copied().enumerate() { |
| 408 | by_cluster.entry(lbl).or_default().push(i); |
| 409 | } |
| 410 | |
| 411 | let mut pairwise = DMatrix::zeros(n, n); |
| 412 | for i in 0..n { |
| 413 | for j in i..n { |
| 414 | let mut s = 0.0; |
| 415 | for c in 0..data.ncols() { |
| 416 | let diff = data[(i, c)] - data[(j, c)]; |
| 417 | s += diff * diff; |
| 418 | } |
| 419 | let d = s.sqrt(); |
| 420 | pairwise[(i, j)] = d; |
| 421 | pairwise[(j, i)] = d; |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | let mut scores = vec![0.0; n]; |
| 426 | for i in 0..n { |
| 427 | let own = labels[i]; |
| 428 | let own_members = &by_cluster[&own]; |
| 429 | |
| 430 | let a = if own_members.len() <= 1 { |
| 431 | 0.0 |
| 432 | } else { |
| 433 | let mut s = 0.0; |
| 434 | let mut cnt = 0usize; |
| 435 | for &j in own_members { |
| 436 | if j != i { |
| 437 | s += pairwise[(i, j)]; |
| 438 | cnt += 1; |
| 439 | } |
| 440 | } |
| 441 | if cnt == 0 { |
| 442 | 0.0 |
| 443 | } else { |
| 444 | s / cnt as f64 |
| 445 | } |
| 446 | }; |
| 447 | |
| 448 | let mut b = f64::INFINITY; |
| 449 | for (cluster, members) in &by_cluster { |
| 450 | if *cluster == own || members.is_empty() { |
| 451 | continue; |
| 452 | } |
| 453 | let mut s = 0.0; |
| 454 | for &j in members { |
| 455 | s += pairwise[(i, j)]; |
| 456 | } |
| 457 | let mean = s / members.len() as f64; |
| 458 | if mean < b { |
| 459 | b = mean; |
| 460 | } |
| 461 | } |
no test coverage detected