nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
value_stats.hpp
1#pragma once
2//
3// value_stats.hpp — display statistics over a numeric Value.
4//
5// Single source of truth for the optional Min/Max/Range/Mean/Median/Mode/
6// Var/Std columns shown in the IDE's Variable / struct viewer. Used by both
7// the core workspace serializer (Engine::workspaceJSON) and the WASM
8// inspect-cell serializer (repl_bindings emitInspectCell), so the numbers
9// match wherever a value is displayed.
10//
11// Non-finite elements are skipped (omitnan-style) so a stray NaN doesn't
12// blank the whole row. complex → magnitude, logical → 0/1. Variance is the
13// sample (N-1) form; mode is the smallest most-frequent value (MATLAB).
14
15#include <nk/value/value.hpp>
16#include <vector>
17#include <algorithm>
18#include <cmath>
19
20namespace nk {
21
22struct ValueStats {
23 double min, max, mean, median, mode, var, std;
24};
25
26// Stats over the half-open element range [start, start+count) in
27// column-major linear order. `count == SIZE_MAX` means "to the end".
28// The range form drives per-page stats for 3-D / N-D arrays: page p is the
29// contiguous block [p*rows*cols, (p+1)*rows*cols). Returns false for
30// non-numeric types or when no finite element remains in the range.
31inline bool computeValueStatsRange(const Value &val, std::size_t start,
32 std::size_t count, ValueStats &s)
33{
34 const std::size_t numel = val.numel();
35 if (start > numel) start = numel;
36 const std::size_t end =
37 (count > numel - start) ? numel : start + count; // clamp to numel
38 std::vector<double> v;
39 v.reserve(end - start);
40 if (val.type() == ValueType::DOUBLE) {
41 const double *p = val.doubleData();
42 for (std::size_t i = start; i < end; ++i)
43 if (std::isfinite(p[i])) v.push_back(p[i]);
44 } else if (val.type() == ValueType::LOGICAL) {
45 const std::uint8_t *p = val.logicalData();
46 for (std::size_t i = start; i < end; ++i) v.push_back(p[i] ? 1.0 : 0.0);
47 } else if (val.type() == ValueType::COMPLEX) {
48 const Complex *p = val.complexData();
49 for (std::size_t i = start; i < end; ++i) {
50 double m = std::hypot(p[i].real(), p[i].imag());
51 if (std::isfinite(m)) v.push_back(m);
52 }
53 } else if (isFloatType(val.type()) || isIntegerType(val.type())) {
54 // SINGLE + INT8..UINT64 — read each element as double. (DOUBLE is
55 // handled by the fast path above; integers are always finite, so
56 // the isfinite filter only ever skips a single's NaN/Inf.)
57 for (std::size_t i = start; i < end; ++i) {
58 double x = val.elemAsDouble(i);
59 if (std::isfinite(x)) v.push_back(x);
60 }
61 } else {
62 return false;
63 }
64 const std::size_t n = v.size();
65 if (n == 0) return false;
66
67 double sum = 0.0, mn = v[0], mx = v[0];
68 for (double x : v) { sum += x; if (x < mn) mn = x; if (x > mx) mx = x; }
69 s.min = mn; s.max = mx; s.mean = sum / static_cast<double>(n);
70
71 std::vector<double> sorted = v;
72 std::sort(sorted.begin(), sorted.end());
73 s.median = (n % 2) ? sorted[n / 2]
74 : 0.5 * (sorted[n / 2 - 1] + sorted[n / 2]);
75
76 double acc = 0.0;
77 for (double x : v) { double d = x - s.mean; acc += d * d; }
78 s.var = (n >= 2) ? acc / static_cast<double>(n - 1) : 0.0;
79 s.std = std::sqrt(s.var);
80
81 // Mode: longest run in the sorted data; ties resolve to the smaller
82 // value (ascending sort → keep the first run that reaches the max).
83 double bestVal = sorted[0];
84 std::size_t bestCnt = 1, curCnt = 1;
85 for (std::size_t i = 1; i < n; ++i) {
86 curCnt = (sorted[i] == sorted[i - 1]) ? curCnt + 1 : 1;
87 if (curCnt > bestCnt) { bestCnt = curCnt; bestVal = sorted[i]; }
88 }
89 s.mode = bestVal;
90 return true;
91}
92
93// Whole-array convenience overload — stats over every element.
94inline bool computeValueStats(const Value &val, ValueStats &s)
95{
96 return computeValueStatsRange(val, 0, val.numel(), s);
97}
98
99} // namespace nk
Core MATLAB-compatible multidimensional array and scalar value type.
Definition value.hpp:61
Value sum(const Value &x, std::pmr::memory_resource *mr=nullptr)
Sum of array elements along first non-singleton dimension (sum(x)).
Value imag(const Value &x, std::pmr::memory_resource *mr=nullptr)
Imaginary part of complex array (y = imag(x)).
Value real(const Value &x, std::pmr::memory_resource *mr=nullptr)
Real part of complex array (y = real(x)).