1pub struct CustomStandardScaler {
10 mean: f64,
11 scale: f64,
12}
13
14impl CustomStandardScaler {
15 const EPSILON: f64 = 1e-6;
16 const CLIP: f64 = 100.0;
17
18 pub fn fit(x: &[f64]) -> Self {
19 let mean = mean(x);
20 let scale = std_dev(x, mean, 0) + Self::EPSILON;
21 CustomStandardScaler { mean, scale }
22 }
23
24 pub fn transform(&self, x: &[f64]) -> Vec<f64> {
25 x.iter()
26 .map(|&v| ((v - self.mean) / self.scale).clamp(-Self::CLIP, Self::CLIP))
27 .collect()
28 }
29}
30
31pub struct OutlierRemover {
34 lower_bound: f64,
35 upper_bound: f64,
36}
37
38impl OutlierRemover {
39 pub fn fit(x: &[f64], threshold: f64) -> Self {
40 let mean1 = mean(x);
41 let std1 = std_dev(x, mean1, 1).max(1e-6);
42 let lower1 = mean1 - threshold * std1;
43 let upper1 = mean1 + threshold * std1;
44
45 let clean: Vec<f64> = x
46 .iter()
47 .copied()
48 .filter(|&v| v >= lower1 && v <= upper1)
49 .collect();
50 let (mean2, std2) = if clean.is_empty() {
51 (mean1, std1)
52 } else {
53 let m = mean(&clean);
54 (m, std_dev(&clean, m, 1).max(1e-6))
55 };
56
57 OutlierRemover {
58 lower_bound: mean2 - threshold * std2,
59 upper_bound: mean2 + threshold * std2,
60 }
61 }
62
63 pub fn transform(&self, x: &[f64]) -> Vec<f64> {
64 x.iter()
65 .map(|&v| {
66 let v = (-((v.abs()).ln_1p()) + self.lower_bound).max(v);
67 (v.abs().ln_1p() + self.upper_bound).min(v)
68 })
69 .collect()
70 }
71}
72
73pub struct StandardScaler {
77 mean: f64,
78 scale: f64,
79}
80
81impl StandardScaler {
82 pub fn fit(x: &[f64]) -> Self {
83 let mean = mean(x);
84 let std = std_dev(x, mean, 0);
85 StandardScaler {
86 mean,
87 scale: if std == 0.0 { 1.0 } else { std },
88 }
89 }
90
91 pub fn transform(&self, x: &[f64]) -> Vec<f64> {
92 x.iter().map(|&v| (v - self.mean) / self.scale).collect()
93 }
94
95 pub fn inverse_transform(&self, x: &[f64]) -> Vec<f64> {
96 x.iter().map(|&v| v * self.scale + self.mean).collect()
97 }
98
99 pub fn inverse_transform_scalar(&self, v: f64) -> f64 {
100 v * self.scale + self.mean
101 }
102}
103
104pub struct RobustScaler {
107 median: f64,
108 scale: f64,
109}
110
111impl RobustScaler {
112 pub fn fit(x: &[f64]) -> Self {
113 let mut sorted = x.to_vec();
114 sorted.sort_by(|a, b| a.total_cmp(b));
115 let median = percentile_linear(&sorted, 50.0);
116 let q25 = percentile_linear(&sorted, 25.0);
117 let q75 = percentile_linear(&sorted, 75.0);
118 let iqr = q75 - q25;
119 let norm_factor = norm_ppf(0.75) - norm_ppf(0.25); let scale = if iqr == 0.0 { 1.0 } else { iqr / norm_factor };
121 RobustScaler { median, scale }
122 }
123
124 pub fn transform(&self, x: &[f64]) -> Vec<f64> {
125 x.iter().map(|&v| (v - self.median) / self.scale).collect()
126 }
127}
128
129pub struct QuantileTransformer {
133 references: Vec<f64>, quantiles: Vec<f64>, }
136
137impl QuantileTransformer {
138 const BOUNDS_THRESHOLD: f64 = 1e-7;
139
140 pub fn fit(x: &[f64]) -> Self {
141 let n_quantiles = 1000usize.min(x.len().max(1));
142 let mut sorted = x.to_vec();
143 sorted.sort_by(|a, b| a.total_cmp(b));
144
145 let references: Vec<f64> = (0..n_quantiles)
146 .map(|i| i as f64 / (n_quantiles - 1).max(1) as f64)
147 .collect();
148 let mut quantiles: Vec<f64> = references
149 .iter()
150 .map(|&r| percentile_linear(&sorted, r * 100.0))
151 .collect();
152 for i in 1..quantiles.len() {
154 if quantiles[i] < quantiles[i - 1] {
155 quantiles[i] = quantiles[i - 1];
156 }
157 }
158 QuantileTransformer {
159 references,
160 quantiles,
161 }
162 }
163
164 pub fn transform(&self, x: &[f64]) -> Vec<f64> {
165 x.iter()
166 .map(|&v| {
167 let u = interp_monotonic(v, &self.quantiles, &self.references)
168 .clamp(Self::BOUNDS_THRESHOLD, 1.0 - Self::BOUNDS_THRESHOLD);
169 norm_ppf(u)
170 })
171 .collect()
172 }
173}
174
175pub struct PowerTransformer {
178 lambda: f64,
179 post_mean: f64,
180 post_std: f64,
181}
182
183impl PowerTransformer {
184 pub fn fit(x: &[f64]) -> Self {
185 let lambda = yeo_johnson_optimize(x);
186 let transformed: Vec<f64> = x
187 .iter()
188 .map(|&v| yeo_johnson_transform(v, lambda))
189 .collect();
190 let post_mean = mean(&transformed);
191 let post_std = std_dev(&transformed, post_mean, 0).max(1e-300);
192 PowerTransformer {
193 lambda,
194 post_mean,
195 post_std,
196 }
197 }
198
199 pub fn transform(&self, x: &[f64]) -> Vec<f64> {
200 x.iter()
201 .map(|&v| (yeo_johnson_transform(v, self.lambda) - self.post_mean) / self.post_std)
202 .collect()
203 }
204}
205
206pub fn apply_pipeline(
210 train_col: &[f64],
211 test_col: &[f64],
212 norm_method: super::config_gen::NormMethod,
213 outlier_threshold: f64,
214) -> (Vec<f64>, Vec<f64>) {
215 use super::config_gen::NormMethod;
216
217 let scaler = CustomStandardScaler::fit(train_col);
218 let mut train = scaler.transform(train_col);
219 let mut test = scaler.transform(test_col);
220
221 match norm_method {
222 NormMethod::None => {}
223 NormMethod::Power => {
224 let n = PowerTransformer::fit(&train);
225 train = n.transform(&train);
226 test = n.transform(&test);
227 }
228 NormMethod::Quantile => {
229 let n = QuantileTransformer::fit(&train);
230 train = n.transform(&train);
231 test = n.transform(&test);
232 }
233 NormMethod::QuantileRtdl => {
234 let noisy = inject_rtdl_noise(&train);
238 let n = QuantileTransformer::fit(&noisy);
239 let train_q = n.transform(&noisy);
240 let test_q = n.transform(&test);
241 let std = StandardScaler::fit(&train_q);
242 train = std.transform(&train_q);
243 test = std.transform(&test_q);
244 }
245 NormMethod::Robust => {
246 let n = RobustScaler::fit(&train);
247 train = n.transform(&train);
248 test = n.transform(&test);
249 }
250 }
251
252 let outlier = OutlierRemover::fit(&train, outlier_threshold);
253 (outlier.transform(&train), outlier.transform(&test))
254}
255
256fn inject_rtdl_noise(x: &[f64]) -> Vec<f64> {
260 let std = std_dev(x, mean(x), 0);
261 let noise = 1e-3;
262 let noise_std = noise / std.max(noise);
263 let mut state = 0x2545F4914F6CDD1Du64;
264 let mut next_u64 = move || {
265 state ^= state << 13;
266 state ^= state >> 7;
267 state ^= state << 17;
268 state
269 };
270 x.iter()
271 .map(|&v| {
272 let u1 = (next_u64() >> 11) as f64 / (1u64 << 53) as f64;
273 let u2 = (next_u64() >> 11) as f64 / (1u64 << 53) as f64;
274 let z = (-2.0 * u1.max(1e-300).ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
275 v + noise_std * z
276 })
277 .collect()
278}
279
280fn yeo_johnson_transform(x: f64, lambda: f64) -> f64 {
281 const EPS: f64 = 1e-6;
282 if x >= 0.0 {
283 if lambda.abs() > EPS {
284 ((x + 1.0).powf(lambda) - 1.0) / lambda
285 } else {
286 (x + 1.0).ln()
287 }
288 } else if (lambda - 2.0).abs() > EPS {
289 -((-x + 1.0).powf(2.0 - lambda) - 1.0) / (2.0 - lambda)
290 } else {
291 -(-x + 1.0).ln()
292 }
293}
294
295fn yeo_johnson_neg_log_likelihood(x: &[f64], lambda: f64) -> f64 {
298 let n = x.len() as f64;
299 let transformed: Vec<f64> = x
300 .iter()
301 .map(|&v| yeo_johnson_transform(v, lambda))
302 .collect();
303 let m = mean(&transformed);
304 let var = variance(&transformed, m, 0);
305 if var < f64::MIN_POSITIVE {
306 return f64::INFINITY;
307 }
308 let mut loglike = -n / 2.0 * var.ln();
309 let sum_term: f64 = x.iter().map(|&v| v.signum() * v.abs().ln_1p()).sum();
310 loglike += (lambda - 1.0) * sum_term;
311 -loglike
312}
313
314fn yeo_johnson_optimize(x: &[f64]) -> f64 {
315 brent_minimize(
316 |lambda| yeo_johnson_neg_log_likelihood(x, lambda),
317 -2.0,
318 2.0,
319 )
320}
321
322fn brent_minimize(f: impl Fn(f64) -> f64, xa0: f64, xb0: f64) -> f64 {
326 const GOLD: f64 = 1.618_034;
327 const GLIMIT: f64 = 100.0;
328 const TINY: f64 = 1e-21;
329
330 let (mut ax, mut bx) = (xa0, xb0);
331 let (mut fa, mut fb) = (f(ax), f(bx));
332 if fa < fb {
333 std::mem::swap(&mut ax, &mut bx);
334 std::mem::swap(&mut fa, &mut fb);
335 }
336 let mut cx = bx + GOLD * (bx - ax);
337 let mut fc = f(cx);
338
339 while fc < fb {
340 let r = (bx - ax) * (fb - fc);
341 let q = (bx - cx) * (fb - fa);
342 let denom = 2.0 * (q - r).abs().max(TINY) * (q - r).signum();
343 let mut u = bx - ((bx - cx) * q - (bx - ax) * r) / denom;
344 let ulim = bx + GLIMIT * (cx - bx);
345
346 let fu;
347 if (bx - u) * (u - cx) > 0.0 {
348 fu = f(u);
349 if fu < fc {
350 ax = bx;
351 bx = u;
352 fa = fb;
353 fb = fu;
354 break;
355 } else if fu > fb {
356 cx = u;
357 fc = fu;
358 break;
359 }
360 u = cx + GOLD * (cx - bx);
361 let fu2 = f(u);
362 ax = bx;
363 bx = cx;
364 cx = u;
365 fa = fb;
366 fb = fc;
367 fc = fu2;
368 } else if (cx - u) * (u - ulim) > 0.0 {
369 fu = f(u);
370 if fu < fc {
371 bx = cx;
372 cx = u;
373 let u2 = cx + GOLD * (cx - bx);
374 fb = fc;
375 fc = fu;
376 let fu2 = f(u2);
377 ax = bx;
378 bx = cx;
379 cx = u2;
380 fa = fb;
381 fb = fc;
382 fc = fu2;
383 } else {
384 ax = bx;
385 bx = cx;
386 cx = u;
387 fa = fb;
388 fb = fc;
389 fc = fu;
390 }
391 } else if (u - ulim) * (ulim - cx) >= 0.0 {
392 u = ulim;
393 fu = f(u);
394 ax = bx;
395 bx = cx;
396 cx = u;
397 fa = fb;
398 fb = fc;
399 fc = fu;
400 } else {
401 u = cx + GOLD * (cx - bx);
402 fu = f(u);
403 ax = bx;
404 bx = cx;
405 cx = u;
406 fa = fb;
407 fb = fc;
408 fc = fu;
409 }
410 if (cx - ax).abs() > 1e6 {
411 break; }
413 }
414
415 let (mut a, mut c) = if ax < cx { (ax, cx) } else { (cx, ax) };
417 let mut x = bx;
418 let mut w = bx;
419 let mut v = bx;
420 let mut fx = f(x);
421 let mut fw = fx;
422 let mut fv = fx;
423 let mut d = 0.0f64;
424 let mut e = 0.0f64;
425 const CGOLD: f64 = 0.381_966;
426 const ZEPS: f64 = 1e-12;
427 const TOL: f64 = 1e-8;
428
429 for _ in 0..100 {
430 let xm = 0.5 * (a + c);
431 let tol1 = TOL * x.abs() + ZEPS;
432 let tol2 = 2.0 * tol1;
433 if (x - xm).abs() <= tol2 - 0.5 * (c - a) {
434 break;
435 }
436 let mut use_golden = true;
437 if e.abs() > tol1 {
438 let r = (x - w) * (fx - fv);
439 let mut q = (x - v) * (fx - fw);
440 let mut p = (x - v) * q - (x - w) * r;
441 q = 2.0 * (q - r);
442 if q > 0.0 {
443 p = -p;
444 }
445 q = q.abs();
446 let etemp = e;
447 e = d;
448 if p.abs() < (0.5 * q * etemp).abs() && p > q * (a - x) && p < q * (c - x) {
449 d = p / q;
450 let u = x + d;
451 if u - a < tol2 || c - u < tol2 {
452 d = if xm - x >= 0.0 { tol1 } else { -tol1 };
453 }
454 use_golden = false;
455 }
456 }
457 if use_golden {
458 e = if x >= xm { a - x } else { c - x };
459 d = CGOLD * e;
460 }
461 let u = if d.abs() >= tol1 {
462 x + d
463 } else {
464 x + if d >= 0.0 { tol1 } else { -tol1 }
465 };
466 let fu = f(u);
467 if fu <= fx {
468 if u >= x {
469 a = x;
470 } else {
471 c = x;
472 }
473 v = w;
474 fv = fw;
475 w = x;
476 fw = fx;
477 x = u;
478 fx = fu;
479 } else {
480 if u < x {
481 a = u;
482 } else {
483 c = u;
484 }
485 if fu <= fw || w == x {
486 v = w;
487 fv = fw;
488 w = u;
489 fw = fu;
490 } else if fu <= fv || v == x || v == w {
491 v = u;
492 fv = fu;
493 }
494 }
495 }
496 x
497}
498
499fn mean(x: &[f64]) -> f64 {
504 if x.is_empty() {
505 0.0
506 } else {
507 x.iter().sum::<f64>() / x.len() as f64
508 }
509}
510
511fn variance(x: &[f64], mean: f64, ddof: usize) -> f64 {
512 let n = x.len();
513 if n <= ddof {
514 return 0.0;
515 }
516 let sum_sq: f64 = x.iter().map(|&v| (v - mean).powi(2)).sum();
517 sum_sq / (n - ddof) as f64
518}
519
520fn std_dev(x: &[f64], mean: f64, ddof: usize) -> f64 {
521 variance(x, mean, ddof).sqrt()
522}
523
524pub fn percentile_linear(sorted: &[f64], p: f64) -> f64 {
526 let n = sorted.len();
527 if n == 0 {
528 return f64::NAN;
529 }
530 if n == 1 {
531 return sorted[0];
532 }
533 let rank = (p / 100.0) * (n - 1) as f64;
534 let lo = rank.floor() as usize;
535 let hi = rank.ceil() as usize;
536 if lo == hi {
537 sorted[lo]
538 } else {
539 let frac = rank - lo as f64;
540 sorted[lo] * (1.0 - frac) + sorted[hi] * frac
541 }
542}
543
544fn interp_monotonic(x: f64, xp: &[f64], fp: &[f64]) -> f64 {
547 let n = xp.len();
548 if x <= xp[0] {
549 return fp[0];
550 }
551 if x >= xp[n - 1] {
552 return fp[n - 1];
553 }
554 let idx = xp.partition_point(|&v| v <= x);
556 let (x0, x1) = (xp[idx - 1], xp[idx]);
557 let (y0, y1) = (fp[idx - 1], fp[idx]);
558 if x1 == x0 {
559 y0
560 } else {
561 y0 + (y1 - y0) * (x - x0) / (x1 - x0)
562 }
563}
564
565pub fn norm_ppf(p: f64) -> f64 {
568 if p <= 0.0 {
569 return f64::NEG_INFINITY;
570 }
571 if p >= 1.0 {
572 return f64::INFINITY;
573 }
574 const A: [f64; 6] = [
575 -3.969_683_028_665_376e+01,
576 2.209_460_984_245_205e+02,
577 -2.759_285_104_469_687e+02,
578 1.383_577_518_672_690e+02,
579 -3.066_479_806_614_716e+01,
580 2.506_628_277_459_239e+00,
581 ];
582 const B: [f64; 5] = [
583 -5.447_609_879_822_406e+01,
584 1.615_858_368_580_409e+02,
585 -1.556_989_798_598_866e+02,
586 6.680_131_188_771_972e+01,
587 -1.328_068_155_288_572e+01,
588 ];
589 const C: [f64; 6] = [
590 -7.784_894_002_430_293e-03,
591 -3.223_964_580_411_365e-01,
592 -2.400_758_277_161_838e+00,
593 -2.549_732_539_343_734e+00,
594 4.374_664_141_464_968e+00,
595 2.938_163_982_698_783e+00,
596 ];
597 const D: [f64; 4] = [
598 7.784_695_709_041_462e-03,
599 3.224_671_290_700_398e-01,
600 2.445_134_137_142_996e+00,
601 3.754_408_661_907_416e+00,
602 ];
603 const P_LOW: f64 = 0.02425;
604 let p_high = 1.0 - P_LOW;
605
606 let mut x = if p < P_LOW {
607 let q = (-2.0 * p.ln()).sqrt();
608 (((((C[0] * q + C[1]) * q + C[2]) * q + C[3]) * q + C[4]) * q + C[5])
609 / ((((D[0] * q + D[1]) * q + D[2]) * q + D[3]) * q + 1.0)
610 } else if p <= p_high {
611 let q = p - 0.5;
612 let r = q * q;
613 (((((A[0] * r + A[1]) * r + A[2]) * r + A[3]) * r + A[4]) * r + A[5]) * q
614 / (((((B[0] * r + B[1]) * r + B[2]) * r + B[3]) * r + B[4]) * r + 1.0)
615 } else {
616 let q = (-2.0 * (1.0 - p).ln()).sqrt();
617 -(((((C[0] * q + C[1]) * q + C[2]) * q + C[3]) * q + C[4]) * q + C[5])
618 / ((((D[0] * q + D[1]) * q + D[2]) * q + D[3]) * q + 1.0)
619 };
620
621 let e = 0.5 * erfc(-x / std::f64::consts::SQRT_2) - p;
623 let u = e * (2.0 * std::f64::consts::PI).sqrt() * (x * x / 2.0).exp();
624 x -= u / (1.0 + x * u / 2.0);
625 x
626}
627
628fn erfc(x: f64) -> f64 {
629 1.0 - erf(x)
630}
631
632fn erf(x: f64) -> f64 {
635 let sign = if x < 0.0 { -1.0 } else { 1.0 };
636 let x = x.abs();
637 const A1: f64 = 0.254_829_592;
638 const A2: f64 = -0.284_496_736;
639 const A3: f64 = 1.421_413_741;
640 const A4: f64 = -1.453_152_027;
641 const A5: f64 = 1.061_405_429;
642 const P: f64 = 0.327_591_1;
643 let t = 1.0 / (1.0 + P * x);
644 let y = 1.0 - (((((A5 * t + A4) * t) + A3) * t + A2) * t + A1) * t * (-x * x).exp();
645 sign * y
646}
647
648#[cfg(test)]
649mod tests {
650 use super::*;
651
652 #[test]
653 fn test_custom_standard_scaler() {
654 let x = vec![1.0, 2.0, 3.0, 4.0, 5.0];
655 let s = CustomStandardScaler::fit(&x);
656 let t = s.transform(&x);
657 assert!((t[0] - (1.0 - 3.0) / (2f64.sqrt() + 1e-6)).abs() < 1e-9);
659 assert!((t[2]).abs() < 1e-9); }
661
662 #[test]
663 fn test_robust_scaler_matches_scipy_norm_factor() {
664 let x: Vec<f64> = (0..101).map(|i| i as f64).collect();
665 let s = RobustScaler::fit(&x);
666 assert!((s.median - 50.0).abs() < 1e-9);
668 }
669
670 #[test]
671 fn test_norm_ppf_known_values() {
672 assert!((norm_ppf(0.5)).abs() < 1e-6);
673 assert!((norm_ppf(0.975) - 1.959_963_985).abs() < 1e-4);
674 assert!((norm_ppf(0.025) + 1.959_963_985).abs() < 1e-4);
675 }
676
677 #[test]
678 fn test_yeo_johnson_identity_at_lambda_1() {
679 assert!((yeo_johnson_transform(5.0, 1.0) - 5.0).abs() < 1e-9);
680 assert!((yeo_johnson_transform(-3.0, 1.0) - (-3.0)).abs() < 1e-9);
681 }
682
683 #[test]
684 fn test_power_transformer_matches_sklearn() {
685 let x = vec![
688 0.5478467492858172,
689 -0.9208531449445188,
690 -1.8361059042552212,
691 -1.9338894578858836,
692 1.2530809568010897,
693 1.6510223091108869,
694 0.42654310306871945,
695 0.9179862439359936,
696 0.17449996586169148,
697 1.740289695151073,
698 1.2634142164861286,
699 -1.9890459993194076,
700 1.4296171063502774,
701 -1.8656576987781426,
702 0.9186217857197763,
703 -1.297377517589764,
704 1.4527156893995463,
705 0.16584488099636685,
706 -0.8011524378504609,
707 -0.3092511152093662,
708 ];
709 let expected_lambda = 1.345222766603304;
710 let expected_transformed = [
711 0.2719479783598148,
712 -0.8318522439585222,
713 -1.3650247006836644,
714 -1.4181615227275621,
715 0.9606112117567647,
716 1.3853857310641091,
717 0.16297351213481462,
718 0.6223628894658091,
719 -0.05314385725391679,
720 1.483863071047962,
721 0.9713347980371447,
722 -1.4478643497510573,
723 1.1461058305187737,
724 -1.3811492793940139,
725 0.6229863176690302,
726 -1.0599639970952783,
727 1.1707302052556954,
728 -0.06030208166470946,
729 -0.7561721820272899,
730 -0.42466733075390517,
731 ];
732 let pt = PowerTransformer::fit(&x);
733 assert!(
734 (pt.lambda - expected_lambda).abs() < 1e-4,
735 "lambda {} vs expected {}",
736 pt.lambda,
737 expected_lambda
738 );
739 let got = pt.transform(&x);
740 for (g, e) in got.iter().zip(expected_transformed.iter()) {
741 assert!((g - e).abs() < 1e-3, "got {g} vs expected {e}");
742 }
743 }
744
745 #[test]
746 fn test_percentile_linear() {
747 let sorted = vec![1.0, 2.0, 3.0, 4.0, 5.0];
748 assert!((percentile_linear(&sorted, 50.0) - 3.0).abs() < 1e-9);
749 assert!((percentile_linear(&sorted, 0.0) - 1.0).abs() < 1e-9);
750 assert!((percentile_linear(&sorted, 100.0) - 5.0).abs() < 1e-9);
751 }
752
753 #[test]
754 fn test_quantile_transformer_matches_sklearn() {
755 let x = vec![
759 0.5478467492858172,
760 -0.9208531449445188,
761 -1.8361059042552212,
762 -1.9338894578858836,
763 1.2530809568010897,
764 1.6510223091108869,
765 0.42654310306871945,
766 0.9179862439359936,
767 0.17449996586169148,
768 1.740289695151073,
769 1.2634142164861286,
770 -1.9890459993194076,
771 1.4296171063502774,
772 -1.8656576987781426,
773 0.9186217857197763,
774 -1.297377517589764,
775 1.4527156893995463,
776 0.16584488099636685,
777 -0.8011524378504609,
778 -0.3092511152093662,
779 ];
780 let expected = [
781 0.199201324789267,
782 -0.6336400007797011,
783 -1.003147967662534,
784 -1.6198562586382699,
785 0.633640000779701,
786 1.6198562586382697,
787 0.0660118123758406,
788 0.33603814037182306,
789 -0.06601181237584074,
790 5.19933758270342,
791 0.8045963803603002,
792 -5.199337582605575,
793 1.0031479676625337,
794 -1.2521195202652193,
795 0.47950565333094985,
796 -0.8045963803603002,
797 1.2521195202652189,
798 -0.199201324789267,
799 -0.47950565333095013,
800 -0.33603814037182317,
801 ];
802 let qt = QuantileTransformer::fit(&x);
803 let got = qt.transform(&x);
804 for (g, e) in got.iter().zip(expected.iter()) {
805 assert!((g - e).abs() < 1e-3, "got {g} vs expected {e}");
806 }
807 }
808}