Files
k-mood/crates/domain/tests/correlation/strategy_test.rs
Gabriel Kaszewski 23d052278a
All checks were successful
CI / ci (push) Successful in 19m38s
changes
2026-08-26 20:58:14 +02:00

181 lines
4.7 KiB
Rust

use domain::activity::ActivityId;
use domain::correlation::{CorrelationInput, CorrelationStrategy, Observation, SeriesShape};
use domain::entry::{DayMood, Mood};
use domain::metric::MetricKind;
fn day(mood: Mood) -> DayMood {
DayMood::of(&[mood]).unwrap()
}
fn observations(pairs: &[(f64, Mood)]) -> Vec<Observation> {
pairs
.iter()
.map(|(value, mood)| Observation::new(*value, day(*mood)))
.collect()
}
fn score(strategy: CorrelationStrategy, pairs: &[(f64, Mood)]) -> Option<f64> {
strategy
.score(&observations(pairs))
.map(|coefficient| coefficient.value())
}
const TIED: [(f64, Mood); 6] = [
(1_000.0, Mood::Meh),
(2_000.0, Mood::Meh),
(3_000.0, Mood::Meh),
(4_000.0, Mood::Good),
(5_000.0, Mood::Good),
(6_000.0, Mood::Rad),
];
#[test]
fn kendall_corrects_for_ties_rather_than_counting_pairs_alone() {
let score = score(CorrelationStrategy::Kendall, &TIED).unwrap();
let tau_b = 0.856_348_838_6;
let tau_a = 0.733_333_333_3;
assert!((score - tau_b).abs() < 1e-9, "expected tau-b, got {score}");
assert!(
(score - tau_a).abs() > 1e-9,
"this is tau-a, which overstates disagreement when ranks are tied"
);
}
#[test]
fn kendall_reaches_both_extremes() {
let rising = score(
CorrelationStrategy::Kendall,
&[
(1.0, Mood::Awful),
(2.0, Mood::Bad),
(3.0, Mood::Meh),
(4.0, Mood::Good),
],
)
.unwrap();
let falling = score(
CorrelationStrategy::Kendall,
&[
(1.0, Mood::Good),
(2.0, Mood::Meh),
(3.0, Mood::Bad),
(4.0, Mood::Awful),
],
)
.unwrap();
assert!((rising - 1.0).abs() < 1e-12);
assert!((falling + 1.0).abs() < 1e-12);
}
#[test]
fn pearson_measures_the_line_where_the_rank_methods_measure_the_order() {
let curved = [
(1.0, Mood::Awful),
(2.0, Mood::Bad),
(3.0, Mood::Meh),
(100.0, Mood::Good),
];
let pearson = score(CorrelationStrategy::Pearson, &curved).unwrap();
let spearman = score(CorrelationStrategy::Spearman, &curved).unwrap();
assert!((spearman - 1.0).abs() < 1e-12, "the order is perfect");
assert!(
(pearson - 0.785_026_421).abs() < 1e-9,
"the line is not, got {pearson}"
);
}
#[test]
fn a_mean_difference_is_told_as_a_share_of_the_mood_scale() {
let score = score(
CorrelationStrategy::MeanDifference,
&[
(1.0, Mood::Good),
(1.0, Mood::Rad),
(1.0, Mood::Good),
(1.0, Mood::Rad),
(0.0, Mood::Bad),
(0.0, Mood::Meh),
(0.0, Mood::Bad),
(0.0, Mood::Awful),
],
)
.unwrap();
assert!(
(score - 0.625).abs() < 1e-12,
"two and a half mood points out of four, got {score}"
);
}
#[test]
fn a_mean_difference_needs_days_on_both_sides() {
let only_present = score(
CorrelationStrategy::MeanDifference,
&[(1.0, Mood::Good), (1.0, Mood::Rad)],
);
let only_absent = score(
CorrelationStrategy::MeanDifference,
&[(0.0, Mood::Good), (0.0, Mood::Rad)],
);
assert!(only_present.is_none());
assert!(only_absent.is_none());
}
#[test]
fn a_strategy_scores_one_shape_of_series_and_an_input_has_one() {
assert_eq!(
CorrelationStrategy::MeanDifference.scores(),
SeriesShape::Presence
);
assert_eq!(
CorrelationInput::Activity(ActivityId::generate()).series(),
SeriesShape::Presence
);
for continuous in [
CorrelationStrategy::Pearson,
CorrelationStrategy::Spearman,
CorrelationStrategy::Kendall,
] {
assert_eq!(continuous.scores(), SeriesShape::Continuous);
}
assert_eq!(
CorrelationInput::Metric(MetricKind::Steps).series(),
SeriesShape::Continuous
);
assert_eq!(
CorrelationInput::MoonPhase.series(),
SeriesShape::Continuous
);
}
#[test]
fn only_matching_strategies_are_offered_for_an_input() {
let steps = CorrelationInput::Metric(MetricKind::Steps);
let exercise = CorrelationInput::Activity(ActivityId::generate());
let for_steps: Vec<&str> = CorrelationStrategy::ALL
.into_iter()
.filter(|strategy| strategy.can_score(&steps))
.map(|strategy| strategy.name())
.collect();
let for_exercise: Vec<&str> = CorrelationStrategy::ALL
.into_iter()
.filter(|strategy| strategy.can_score(&exercise))
.map(|strategy| strategy.name())
.collect();
assert_eq!(for_steps, ["pearson", "spearman", "kendall"]);
assert_eq!(for_exercise, ["meanDifference"]);
}