1use nalgebra::DMatrix;
7use num_traits::Float;
8use r2rs_base::traits::StatisticalSlice;
9
10#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
11pub enum NAMethod {
12 #[default]
13 Everything,
14 AllObs,
15 CompleteObs,
16 NAOrComplete,
17 PairwiseCompleteObs,
18}
19
20#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
21pub enum Method {
22 #[default]
23 Pearson,
24 Kendall,
25 Spearman,
26}
27
28fn clamp(x: f64) -> f64 {
29 if x > 1.0 {
30 1.0
31 } else if x < -1.0 {
32 -1.0
33 } else {
34 x
35 }
36}
37
38fn sign(x: f64) -> f64 {
39 if x == 0.0 {
40 0.0
41 } else {
42 x.signum()
43 }
44}
45
46pub fn sd(x: &[f64]) -> f64 {
47 var(
48 &DMatrix::from_column_slice(x.len(), 1, x),
49 NAMethod::Everything,
50 )
51 .sqrt()
52}
53
54pub fn var(x: &DMatrix<f64>, na_method: NAMethod) -> f64 {
55 let x = DMatrix::from_column_slice(x.as_slice().len(), 1, x.as_slice());
56 c_cov(&x, Some(&x), na_method, false)[(0, 0)]
57}
58
59pub fn cov(
60 x: &DMatrix<f64>,
61 y: &DMatrix<f64>,
62 method: Method,
63 na_method: NAMethod,
64) -> DMatrix<f64> {
65 if let Method::Pearson = method {
66 c_cov(x, Some(y), na_method, false)
67 } else if na_method == NAMethod::CompleteObs || na_method == NAMethod::NAOrComplete {
68 let nas = x
69 .row_iter()
70 .zip(y.row_iter())
71 .enumerate()
72 .filter_map(|(i, (x, y))| {
73 if x.iter().any(|x_i| x_i.is_nan()) || y.iter().any(|y_i| y_i.is_nan()) {
74 Some(i)
75 } else {
76 None
77 }
78 })
79 .collect::<Vec<_>>();
80
81 let mut r_x = x.clone().remove_rows_at(&nas);
82 for mut column in r_x.column_iter_mut() {
83 for (&rank, x_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
84 *x_i = rank;
85 }
86 }
87
88 let mut r_y = y.clone().remove_rows_at(&nas);
89 for mut column in r_y.column_iter_mut() {
90 for (&rank, y_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
91 *y_i = rank;
92 }
93 }
94
95 c_cov(&r_x, Some(&r_y), na_method, method == Method::Kendall)
96 } else if na_method != NAMethod::PairwiseCompleteObs {
97 let mut r_x = x.clone();
98 for mut column in r_x.column_iter_mut() {
99 for (&rank, x_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
100 *x_i = rank;
101 }
102 }
103
104 let mut r_y = y.clone();
105 for mut column in r_y.column_iter_mut() {
106 for (&rank, y_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
107 *y_i = rank;
108 }
109 }
110
111 c_cov(&r_x, Some(&r_y), na_method, method == Method::Kendall)
112 } else {
113 panic!("Cannot handle Pairwise Complete Obs")
114 }
115}
116
117pub fn cor(
118 x: &DMatrix<f64>,
119 y: &DMatrix<f64>,
120 na_method: NAMethod,
121 method: Method,
122) -> DMatrix<f64> {
123 if method == Method::Pearson {
124 c_cor(x, Some(y), na_method, false)
125 } else if na_method == NAMethod::CompleteObs || na_method == NAMethod::NAOrComplete {
126 let nas = x
127 .row_iter()
128 .zip(y.row_iter())
129 .enumerate()
130 .filter_map(|(i, (x, y))| {
131 if x.iter().any(|x_i| x_i.is_nan()) || y.iter().any(|y_i| y_i.is_nan()) {
132 Some(i)
133 } else {
134 None
135 }
136 })
137 .collect::<Vec<_>>();
138
139 let mut r_x = x.clone().remove_rows_at(&nas);
140 for mut column in r_x.column_iter_mut() {
141 for (&rank, x_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
142 *x_i = rank;
143 }
144 }
145
146 let mut r_y = y.clone().remove_rows_at(&nas);
147 for mut column in r_y.column_iter_mut() {
148 for (&rank, y_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
149 *y_i = rank;
150 }
151 }
152
153 c_cor(&r_x, Some(&r_y), na_method, method == Method::Kendall)
154 } else if na_method != NAMethod::PairwiseCompleteObs {
155 let mut r_x = x.clone();
156 for mut column in r_x.column_iter_mut() {
157 for (&rank, x_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
158 *x_i = rank;
159 }
160 }
161
162 let mut r_y = y.clone();
163 for mut column in r_y.column_iter_mut() {
164 for (&rank, y_i) in column.as_slice().rank().iter().zip(column.iter_mut()) {
165 *y_i = rank;
166 }
167 }
168
169 c_cor(&r_x, Some(&r_y), na_method, method == Method::Kendall)
170 } else {
171 let mut r = DMatrix::zeros(x.ncols(), y.ncols());
172 for i in 0..x.ncols() {
173 for j in 0..y.ncols() {
174 let mut x2 = x.column(i).iter().cloned().collect::<Vec<_>>();
175 let mut y2 = y.column(j).iter().cloned().collect::<Vec<_>>();
176 let nas = x2
177 .iter()
178 .zip(x2.iter())
179 .enumerate()
180 .filter_map(|(i, (x_i, y_i))| {
181 if x_i.is_nan() || y_i.is_nan() {
182 Some(i)
183 } else {
184 None
185 }
186 })
187 .collect::<Vec<_>>();
188 x2 = x2
189 .into_iter()
190 .enumerate()
191 .filter_map(|(i, x_i)| if nas.contains(&i) { None } else { Some(x_i) })
192 .collect::<Vec<_>>()
193 .rank();
194 y2 = y2
195 .into_iter()
196 .enumerate()
197 .filter_map(|(i, y_i)| if nas.contains(&i) { None } else { Some(y_i) })
198 .collect::<Vec<_>>()
199 .rank();
200 r[(i, j)] = if !x2.is_empty() && !y2.is_empty() {
201 c_cor(
202 &DMatrix::from_vec(x2.len(), 1, x2),
203 Some(&DMatrix::from_vec(y2.len(), 1, y2)),
204 NAMethod::AllObs,
205 method == Method::Kendall,
206 )[(0, 0)]
207 } else {
208 f64::nan()
209 };
210 }
211 }
212 r
213 }
214}
215
216fn c_cov(
217 x: &DMatrix<f64>,
218 y: Option<&DMatrix<f64>>,
219 na_method: NAMethod,
220 kendall: bool,
221) -> DMatrix<f64> {
222 corcov(x, y, na_method, kendall, false)
223}
224
225fn c_cor(
226 x: &DMatrix<f64>,
227 y: Option<&DMatrix<f64>>,
228 na_method: NAMethod,
229 kendall: bool,
230) -> DMatrix<f64> {
231 corcov(x, y, na_method, kendall, true)
232}
233
234fn corcov(
235 x: &DMatrix<f64>,
236 y: Option<&DMatrix<f64>>,
237 na_method: NAMethod,
238 kendall: bool,
239 cor: bool,
240) -> DMatrix<f64> {
241 let mut na_fail = false;
242 let mut everything = false;
243 let mut empty_err = true;
244 let mut pair = false;
245 match na_method {
246 NAMethod::AllObs => na_fail = true,
247 NAMethod::CompleteObs => {}
248 NAMethod::PairwiseCompleteObs => pair = true,
249 NAMethod::Everything => {
250 everything = true;
251 empty_err = false;
252 }
253 NAMethod::NAOrComplete => empty_err = false,
254 };
255
256 if let Some(y) = y {
257 if everything {
258 cov_na_2(x, y, cor, kendall)
259 } else if !pair {
260 let ind = complete2(x, y, na_fail);
261 if empty_err && !ind.iter().any(|&i| i == 1.0) {
262 panic!("No complete element pairs");
263 }
264 cov_complete2(x, y, &ind, cor, kendall)
265 } else {
266 cov_pairwise2(x, y, cor, kendall)
267 }
268 } else if everything {
269 cov_na_2(x, x, cor, kendall)
270 } else if !pair {
271 let ind = complete2(x, x, na_fail);
272 if empty_err && !ind.iter().any(|&i| i == 1.0) {
273 panic!("No complete element pairs");
274 }
275 cov_complete2(x, x, &ind, cor, kendall)
276 } else {
277 cov_pairwise2(x, x, cor, kendall)
278 }
279}
280
281fn mean_2(x: &DMatrix<f64>, has_na: &[bool]) -> Vec<f64> {
282 let mut xm = vec![0.0; x.nrows()];
283 for i in 0..x.ncols() {
284 let mut tmp;
285 if has_na[i] {
286 tmp = f64::nan();
287 } else {
288 let xx = &x.column(i);
289 let mut sum = 0.0;
290 for k in 0..x.nrows() {
291 sum += xx[k];
292 }
293 tmp = sum / x.nrows() as f64;
294 if tmp.is_finite() {
295 sum = 0.;
296 for k in 0..x.nrows() {
297 sum += xx[k] - tmp;
298 }
299 tmp += sum / x.nrows() as f64;
300 }
301 }
302 xm[i] = tmp;
303 }
304 xm
305}
306
307fn cov_na_2(x: &DMatrix<f64>, y: &DMatrix<f64>, cor: bool, kendall: bool) -> DMatrix<f64> {
308 let mut ans = DMatrix::repeat(x.ncols(), y.ncols(), f64::nan());
309
310 let has_na_x = x
311 .column_iter()
312 .map(|c| c.iter().any(|c_i| c_i.is_nan()))
313 .collect::<Vec<_>>();
314 let has_na_y = y
315 .column_iter()
316 .map(|c| c.iter().any(|c_i| c_i.is_nan()))
317 .collect::<Vec<_>>();
318
319 let n = x.nrows();
320 let n1 = n - 1;
321
322 let mut xm = if kendall {
323 vec![0.0; x.ncols()]
324 } else {
325 mean_2(x, &has_na_x)
326 };
327 let mut ym = if kendall {
328 vec![0.0; x.ncols()]
329 } else {
330 mean_2(y, &has_na_y)
331 };
332
333 for i in 0..x.ncols() {
334 if has_na_x[i] {
335 for j in 0..y.ncols() {
336 ans[(i, j)] = f64::nan();
337 }
338 } else {
339 let xx = x.column(i);
340 if !kendall {
341 let xxm = xm[i];
342 for j in 0..y.ncols() {
343 if has_na_y[j] {
344 ans[(i, j)] = f64::nan();
345 } else {
346 let yy = y.column(j);
347 let yym = ym[j];
348 let mut sum = 0.0;
349 for k in 0..n {
350 sum += (xx[k] - xxm) * (yy[k] - yym);
351 }
352 ans[(i, j)] = sum / n1 as f64;
353 }
354 }
355 } else {
356 for j in 0..y.ncols() {
357 if has_na_y[j] {
358 ans[(i, j)] = f64::nan();
359 } else {
360 let yy = y.column(j);
361 let mut sum = 0.0;
362 for k in 0..n {
363 for n1 in 0..n {
364 sum += sign(xx[k] - xx[n1]) * sign(yy[k] - yy[n1]);
365 }
366 }
367 ans[(i, j)] = sum;
368 }
369 }
370 }
371 }
372 }
373
374 if cor {
375 let cov_sdev = |x: &DMatrix<f64>, has_na_x: &[bool], xm: &mut [f64]| {
376 for i in 0..x.ncols() {
377 if !has_na_x[i] {
378 let xx = x.column(i);
379 let mut sum = 0.0;
380 if !kendall {
381 let xxm = xm[i];
382 for k in 0..n {
383 sum += (xx[k] - xxm) * (xx[k] - xxm);
384 }
385 sum /= n1 as f64;
386 } else {
387 for k in 0..n {
388 for n1 in 0..n {
389 if xx[k] != xx[n1] {
390 sum += 1.0;
391 }
392 }
393 }
394 }
395 xm[i] = sum.sqrt();
396 }
397 }
398 };
399
400 cov_sdev(x, &has_na_x, &mut xm);
401 cov_sdev(y, &has_na_y, &mut ym);
402
403 let mut _sd_0 = false;
404 for i in 0..x.ncols() {
405 if !has_na_x[i] {
406 for j in 0..y.ncols() {
407 if !has_na_y[j] {
408 if xm[i] == 0.0 || ym[j] == 0.0 {
409 _sd_0 = true;
410 ans[(i, j)] = f64::nan();
411 } else {
412 ans[(i, j)] /= xm[i] * ym[j];
413 ans[(i, j)] = clamp(ans[(i, j)]);
414 }
415 }
416 }
417 }
418 }
419 }
420
421 ans
422}
423
424fn complete2(x: &DMatrix<f64>, y: &DMatrix<f64>, na_fail: bool) -> Vec<f64> {
425 let mut ind = vec![1.0; x.nrows()];
426 for j in 0..x.ncols() {
427 let z = x.column(j);
428 for i in 0..x.nrows() {
429 if z[i].is_nan() {
430 if na_fail {
431 panic!("Missing observations in cov/cor")
432 } else {
433 ind[i] = 0.0;
434 }
435 }
436 }
437 }
438 for j in 0..y.ncols() {
439 let z = y.column(j);
440 for i in 0..x.nrows() {
441 if z[i].is_nan() {
442 if na_fail {
443 panic!("Missing observations in cov/cor")
444 } else {
445 ind[i] = 0.0;
446 }
447 }
448 }
449 }
450 ind
451}
452
453fn cov_complete2(
454 x: &DMatrix<f64>,
455 y: &DMatrix<f64>,
456 ind: &[f64],
457 cor: bool,
458 kendall: bool,
459) -> DMatrix<f64> {
460 let mut ans = DMatrix::repeat(x.ncols(), y.ncols(), f64::nan());
461
462 let n = x.nrows();
463 let n1 = n - 1;
464 let mut xm = x
465 .column_iter()
466 .map(|c| c.as_slice().mean())
467 .collect::<Vec<_>>();
468 let mut ym = y
469 .column_iter()
470 .map(|c| c.as_slice().mean())
471 .collect::<Vec<_>>();
472
473 for i in 0..x.ncols() {
474 let xx = x.column(i);
475 let xxm = xm[i];
476 for j in 0..y.ncols() {
477 let yy = y.column(j);
478 let mut sum = 0.0;
479 if !kendall {
480 let yym = ym[j];
481 for k in 0..n {
482 if ind[k] != 0.0 {
483 sum += (xx[k] - xxm) * (yy[k] - yym);
484 }
485 }
486 ans[(i, j)] = sum / n1 as f64;
487 } else {
488 for k in 0..n {
489 if ind[k] != 0.0 {
490 for n1 in 0..n {
491 if ind[n1] != 0.0 {
492 sum += sign(xx[k] - xx[n1]) * sign(yy[k] - yy[n1]);
493 }
494 }
495 }
496 }
497 ans[(i, j)] = sum;
498 }
499 }
500 }
501
502 if cor {
503 let cov_sdev = |x: &DMatrix<f64>, xm: &mut [f64], ind: &[f64]| {
504 for (i, xx) in x.column_iter().enumerate() {
505 let mut sum = 0.0;
506 if !kendall {
507 let xxm = xm[i];
508 for k in 0..n {
509 if ind[k] != 0.0 {
510 sum += (xx[k] - xxm) * (xx[k] - xxm);
511 }
512 }
513 sum /= n1 as f64;
514 } else {
515 for k in 0..n {
516 if ind[k] != 0.0 {
517 for n1 in 0..n {
518 if ind[n1] != 0.0 && xx[k] != xx[n1] {
519 sum += 1.0;
520 }
521 }
522 }
523 }
524 }
525 xm[i] = sum.sqrt();
526 }
527 };
528
529 cov_sdev(x, &mut xm, ind);
530 cov_sdev(y, &mut ym, ind);
531
532 let mut _sd_0 = false;
533 for i in 0..x.ncols() {
534 for j in 0..y.ncols() {
535 if xm[i] == 0.0 || ym[j] == 0.0 {
536 _sd_0 = true;
537 ans[(i, j)] = f64::nan();
538 } else {
539 ans[(i, j)] /= xm[i] * ym[j];
540 ans[(i, j)] = clamp(ans[(i, j)]);
541 }
542 }
543 }
544 }
545
546 ans
547}
548
549fn cov_pairwise2(x: &DMatrix<f64>, y: &DMatrix<f64>, cor: bool, kendall: bool) -> DMatrix<f64> {
550 let mut ans = DMatrix::repeat(x.ncols(), y.ncols(), f64::nan());
551 let mut _sd_0 = false;
552
553 let n = x.nrows();
554 let mut n1 = n - 1;
555
556 for i in 0..x.ncols() {
557 let xx = x.column(i);
558 for j in 0..y.ncols() {
559 let yy = y.column(j);
560 let mut xmean = 0.0;
561 let mut ymean = 0.0;
562
563 let mut nobs = 0;
564 for k in 0..n {
565 if !xx[k].is_nan() && !yy[k].is_nan() {
566 nobs += 1;
567 if !kendall {
568 xmean += xx[k];
569 ymean += yy[k];
570 }
571 }
572 }
573
574 if nobs >= 2 {
575 let mut xsd = 0.0;
576 let mut ysd = 0.0;
577 let mut sum = 0.0;
578
579 if !kendall {
580 xmean /= nobs as f64;
581 ymean /= nobs as f64;
582 n1 = nobs - 1;
583 }
584
585 for k in 0..n {
586 if !xx[k].is_nan() && !yy[k].is_nan() {
587 if !kendall {
588 let xm = xx[k] - xmean;
589 let ym = yy[k] - ymean;
590
591 sum += xm * ym;
592 if cor {
593 xsd += xm * xm;
594 ysd += ym * ym;
595 }
596 } else {
597 for n1 in 0..k {
598 if !xx[n1].is_nan() && !yy[n1].is_nan() {
599 let xm = sign(xx[k] - xx[n1]);
600 let ym = sign(yy[k] - yy[n1]);
601
602 sum += xm * ym;
603 if cor {
604 xsd += xm * xm;
605 ysd += ym * ym;
606 }
607 }
608 }
609 }
610 }
611 }
612
613 if cor {
614 if xsd == 0.0 || ysd == 0.0 {
615 _sd_0 = true;
616 sum = f64::nan();
617 } else {
618 if !kendall {
619 xsd /= n1 as f64;
620 ysd /= n1 as f64;
621 sum /= n1 as f64;
622 }
623 sum /= xsd.sqrt() * ysd.sqrt();
624 sum = clamp(sum);
625 }
626 } else if !kendall {
627 sum /= n1 as f64;
628 }
629
630 ans[(i, j)] = sum;
631 } else {
632 ans[(i, j)] = f64::nan();
633 }
634 }
635 }
636
637 ans
638}
639
640pub fn cov2cor(v: &DMatrix<f64>) -> DMatrix<f64> {
641 let is = v
642 .diagonal()
643 .iter()
644 .map(|i| (1.0 / i).sqrt())
645 .collect::<Vec<_>>();
646
647 let mut ret = v.clone();
648 ret.column_iter_mut()
649 .for_each(|mut c| c.iter_mut().zip(is.iter()).for_each(|(c_i, i)| *c_i *= i));
650 ret.row_iter_mut()
651 .for_each(|mut r| r.iter_mut().zip(is.iter()).for_each(|(r_i, i)| *r_i *= i));
652
653 ret
654}