1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use crate::data::Value;
/// Scale transformation types.
#[derive(Clone, Debug)]
pub enum ScaleTransform {
Identity,
Log10,
Log2,
Ln,
Sqrt,
Reverse,
}
impl ScaleTransform {
/// Apply the forward transformation.
pub fn apply(&self, value: f64) -> f64 {
match self {
ScaleTransform::Identity => value,
ScaleTransform::Log10 => {
if value > 0.0 {
value.log10()
} else {
f64::NEG_INFINITY
}
}
ScaleTransform::Log2 => {
if value > 0.0 {
value.log2()
} else {
f64::NEG_INFINITY
}
}
ScaleTransform::Ln => {
if value > 0.0 {
value.ln()
} else {
f64::NEG_INFINITY
}
}
ScaleTransform::Sqrt => {
if value >= 0.0 {
value.sqrt()
} else {
f64::NAN
}
}
ScaleTransform::Reverse => -value,
}
}
/// Apply the inverse transformation.
pub fn inverse(&self, value: f64) -> f64 {
match self {
ScaleTransform::Identity => value,
ScaleTransform::Log10 => 10f64.powf(value),
ScaleTransform::Log2 => 2f64.powf(value),
ScaleTransform::Ln => value.exp(),
ScaleTransform::Sqrt => value * value,
ScaleTransform::Reverse => -value,
}
}
/// Transform a Value.
pub fn transform_value(&self, value: &Value) -> Value {
match value.as_f64() {
Some(f) => {
let t = self.apply(f);
if t.is_finite() {
Value::Float(t)
} else {
Value::Na
}
}
None => value.clone(),
}
}
pub fn is_identity(&self) -> bool {
matches!(self, ScaleTransform::Identity)
}
}