@@ -360,6 +360,7 @@ template hmean(string summation)
360
360
}
361
361
362
362
// / Harmonic mean of vector
363
+ version (mir_test)
363
364
pure @safe nothrow @nogc
364
365
unittest
365
366
{
@@ -371,6 +372,7 @@ unittest
371
372
}
372
373
373
374
// / Harmonic mean of matrix
375
+ version (mir_test)
374
376
pure @safe
375
377
unittest
376
378
{
@@ -383,6 +385,7 @@ unittest
383
385
}
384
386
385
387
// / Column harmonic mean of matrix
388
+ version (mir_test)
386
389
pure @safe
387
390
unittest
388
391
{
@@ -404,14 +407,15 @@ unittest
404
407
}
405
408
406
409
// / Can also pass arguments to hmean
407
- pure @safe
410
+ version (mir_test)
411
+ pure @safe nothrow @nogc
408
412
unittest
409
413
{
410
- import mir.ndslice.topology: map, repeat;
414
+ import mir.ndslice.topology: repeat;
411
415
import mir.math.common: approxEqual;
412
416
413
417
// Set sum algorithm or output type
414
- auto x = [1 , 1e-100 , 1 , - 1e-100 ];
418
+ static immutable x = [1 , 1e-100 , 1 , - 1e-100 ];
415
419
416
420
assert (x.hmean! " kb2" .approxEqual(2 ));
417
421
assert (x.hmean! " precise" .approxEqual(2 ));
@@ -420,6 +424,149 @@ unittest
420
424
assert (float .max.repeat(3 ).hmean! (double , " fast" ).approxEqual(float .max));
421
425
}
422
426
427
+ /+ +
428
+ Centers `slice`, which must be a finite iterable.
429
+
430
+ By default, `slice` is centered by the mean. A custom function may also be provided
431
+ using `centralTendency`.
432
+
433
+ Returns:
434
+ The elements in the slice with the average subtracted from them.
435
+ +/
436
+ template center (alias centralTendency = mean! (Summation.appropriate))
437
+ {
438
+ import mir.ndslice.slice: Slice, SliceKind, sliced, hasAsSlice;
439
+ /+ +
440
+ Params:
441
+ slice = slice
442
+ +/
443
+ auto center (Iterator, size_t N, SliceKind kind)(
444
+ Slice! (Iterator, N, kind) slice)
445
+ {
446
+ import core.lifetime : move;
447
+ import mir.ndslice.topology: vmap;
448
+ import mir.ndslice.internal: LeftOp, ImplicitlyUnqual;
449
+
450
+ auto m = centralTendency(slice.lightScope);
451
+ alias T = typeof (m);
452
+ return slice.move.vmap(LeftOp! (" -" , ImplicitlyUnqual! T)(m));
453
+ }
454
+
455
+ // / ditto
456
+ auto center (T)(T[] array)
457
+ {
458
+ return center (array.sliced);
459
+ }
460
+
461
+ // / ditto
462
+ auto center (T)(T withAsSlice)
463
+ if (hasAsSlice! T)
464
+ {
465
+ return center (withAsSlice.asSlice);
466
+ }
467
+ }
468
+
469
+ // / Center vector
470
+ version (mir_test)
471
+ @safe pure nothrow
472
+ unittest
473
+ {
474
+ import mir.ndslice.slice: sliced;
475
+ import mir.algorithm.iteration: all;
476
+ import mir.math.common: approxEqual;
477
+
478
+ auto x = [1.0 , 2 , 3 , 4 , 5 , 6 ].sliced;
479
+ assert (x.center.all! approxEqual([- 2.5 , - 1.5 , - 0.5 , 0.5 , 1.5 , 2.5 ]));
480
+
481
+ // Can center using different functions
482
+ assert (x.center! hmean.all! approxEqual([- 1.44898 , - 0.44898 , 0.55102 , 1.55102 , 2.55102 , 3.55102 ]));
483
+ }
484
+
485
+ // / Center dynamic array
486
+ version (mir_test)
487
+ @safe pure nothrow
488
+ unittest
489
+ {
490
+ import mir.algorithm.iteration: all;
491
+ import mir.math.common: approxEqual;
492
+
493
+ auto x = [1.0 , 2 , 3 , 4 , 5 , 6 ];
494
+ assert (x.center.all! approxEqual([- 2.5 , - 1.5 , - 0.5 , 0.5 , 1.5 , 2.5 ]));
495
+ }
496
+
497
+ // / Center matrix
498
+ version (mir_test)
499
+ @safe pure
500
+ unittest
501
+ {
502
+ import mir.ndslice: fuse;
503
+ import mir.algorithm.iteration: all;
504
+ import mir.math.common: approxEqual;
505
+
506
+ auto x = [
507
+ [0.0 , 1 , 2 ],
508
+ [3.0 , 4 , 5 ]
509
+ ].fuse;
510
+
511
+ auto y = [
512
+ [- 2.5 , - 1.5 , - 0.5 ],
513
+ [ 0.5 , 1.5 , 2.5 ]
514
+ ].fuse;
515
+
516
+ assert (x.center.all! approxEqual(y));
517
+ }
518
+
519
+ // / Column center matrix
520
+ version (mir_test)
521
+ pure @safe
522
+ unittest
523
+ {
524
+ import mir.algorithm.iteration: all, equal;
525
+ import mir.math.common: approxEqual;
526
+ import mir.ndslice: fuse;
527
+ import mir.ndslice.topology: alongDim, byDim, map;
528
+
529
+ auto x = [
530
+ [20.0 , 100.0 , 2000.0 ],
531
+ [10.0 , 5.0 , 2.0 ]
532
+ ].fuse;
533
+
534
+ auto result = [
535
+ [ 5.0 , 47.5 , 999 ],
536
+ [- 5.0 , - 47.5 , - 999 ]
537
+ ].fuse;
538
+
539
+ // Use byDim with map to compute average of row/column.
540
+ auto xCenterByDim = x.byDim! 1. map! center;
541
+ auto resultByDim = result.byDim! 1 ;
542
+ assert (xCenterByDim.equal! (equal! approxEqual)(resultByDim));
543
+
544
+ auto xCenterAlongDim = x.alongDim! 0. map! center;
545
+ auto resultAlongDim = result.alongDim! 0 ;
546
+ assert (xCenterByDim.equal! (equal! approxEqual)(resultAlongDim));
547
+ }
548
+
549
+ // / Can also pass arguments to average function used by center
550
+ version (mir_test)
551
+ pure @safe nothrow
552
+ unittest
553
+ {
554
+ import mir.ndslice.slice: sliced;
555
+ import mir.algorithm.iteration: all;
556
+ import mir.ndslice.topology: repeat;
557
+ import mir.math.common: approxEqual;
558
+
559
+ // Set sum algorithm or output type
560
+ auto a = [1 , 1e100 , 1 , - 1e100 ];
561
+
562
+ auto x = a.sliced * 10_000;
563
+ auto result = [5000 , 1e104 - 5000 , 5000 , - 1e104 - 5000 ].sliced;
564
+
565
+ assert (x.center! (mean! " kbn" ).all! approxEqual(result));
566
+ assert (x.center! (mean! " kb2" ).all! approxEqual(result));
567
+ assert (x.center! (mean! " precise" ).all! approxEqual(result));
568
+ }
569
+
423
570
/+ +
424
571
A linear regression model with a single explanatory variable.
425
572
+/
0 commit comments