Generated reference › Rolling Statistics — Machine Learning/Feature Engineering
kind: generated#block#machine-learning-feature-engineering

Rolling Statistics — Machine Learning/Feature Engineering

Machine_Learning/Feature_Engineering/Rolling_Statistics · 1 input / 1 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

Rolling Statistics

Machine Learning / Feature Engineering

Reduces the last N samples of every channel to one number, elementwise: y[c] = stat( u[k][c], u[k−1][c], …, u[k−N+1][c] ), with the statistic chosen by configuration – mean, standard deviation, minimum, maximum, RMS, peak-to-peak, peak-to-RMS, root-sum-square or zero-crossing rate.

This is the feature front end a deployed model needs: a classifier is almost never trained on a raw sample, but on a handful of windowed statistics of it. Feed several of these blocks from one signal and concatenate them to build the feature vector a Dense Layer or a Decision Tree expects.

Stateful and discrete by nature. The window advances one sample per step and has no derivative to integrate, so the block declares itself discrete-only and always steps at its own rate.

Ports

  • u – the input sample, any size [m,1]. Each of the m channels carries its own independent window; nothing is mixed across channels.
  • y – the statistic, [m,1]: the same size as the input, one reduced value per channel. The reduction is over TIME, never over the vector – for a statistic across the entries of one sample use Sum Of Elements or MinMax instead.

Parameters

  • Statistic – which reduction the window gets. Each is a different code path rather than a retuning of one, so each is verified as its own mode:
    • Mean – the arithmetic average, emitted as a sum times the constant 1/N.
    • Standard Deviation – the population form, dividing the squared deviations by N and not by N−1. That is what numpy.std gives by default and what Layer Normalization uses in this same library; a model fitted with pandas.rolling().std() (which defaults to N−1) expects the other one, and at small N the two differ materially – at N = 5 by 12%.
    • Minimum / Maximum – the extreme over the window, a chain of comparisons.
    • RMS – √(mean of the squares). Distinct from the standard deviation in that the mean is not removed first, so it carries the signal's DC level.
    • Peak To Peak – max − min over the window: the signal's swing, ignoring where it sits. Two comparison chains and one subtraction, and the only one of the last three that stays synthesizable.
    • Peak To RMS – max(|u|) ÷ RMS, the crest factor: how peaky the window is relative to its own energy. A pure sine gives √2 ≈ 1.414 whatever its amplitude, so the number is a SHAPE and not a level. ⚠ On a window that is entirely zero this answers 0, where MATLAB's peak2rms answers NaN (it computes 0÷0). That is the one deliberate difference: the denominator is floored at 10−12 rather than tested, because the exported VHDL has no conditional to test it with, and handing ten generated cores a NaN is worse than answering zero for a signal that is not there. The floor is seven orders below the resolution these signals cross a port at, so it cannot change a real answer.
    • Root Sum Square – √(sum of the squares), the window's Euclidean length. This is the RMS without the averaging, so the two differ by exactly √N – at N = 5 by a factor of 2.236. Pick this one when you want total energy over the window and RMS when you want energy per sample.
    • Zero Crossing Rate – how often the signal changes sign across the window, as a fraction between 0 and 1. It is the only mode that looks at PAIRS rather than at values: each of the N−1 adjacent pairs inside the window contributes 1 for a sign change, ½ when one of the two is exactly zero, and 0 otherwise, and the total is divided by N−1. A rough signal scores near 1, a signal that keeps its sign scores 0, and N = 1 answers 0 because a single sample forms no pair. ⚠ It is a rate per sample, not per second: multiply by 1/Ts with a Gain if you want crossings per second. ⚠ It also differs from MATLAB's zerocrossrate at the window edge, deliberately: that function gives the first element a fictitious predecessor of 0 and divides by N, which is why zerocrossrate([1 1 1 1]) is 0.125 and not 0. A streaming window's predecessor is a real sample that has just left it, so this block takes the N−1 interior pairs and needs no fiction.
  • Window Length – N, the number of samples the statistic is taken over, one or more. The window is a whole number of samples, so at a sampling time of Ts it spans N·Ts seconds. N = 1 makes every statistic the sample itself (and the standard deviation zero), which is legal and useless.
  • Initial Window Value – what every slot of the window holds before the first sample arrives. The window is full from the very first step: this block does not produce a shorter average over the first N−1 samples the way pandas.rolling does, because a partial window costs a sample counter and a divide-by-count branch in all ten exported languages for a startup transient a feature pipeline discards anyway. Set it to the signal's resting level if the first N steps matter to you.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period. Being discrete-only, a non-positive value falls back to the model's global sampling time rather than to the surrounding rate.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The window is unrolled at export time – N and m are both known then – so no backend contains a loop bound, a modulo or a ring-buffer index, and each carries the N−1 stored samples in its own persistent form seeded with Initial Window Value.

The three HDL targets split by statistic, which is the one thing to know before exporting to hardware:

  • Mean, Minimum, Maximum and Peak To Peak are genuine synthesizable Q16.16. The mean's 1/N is folded into a constant at export time and multiplied, so no divider appears in the datapath; the extremes are a chain of comparisons, and peak-to-peak is that chain twice with one subtraction.
  • Standard Deviation, RMS, Root Sum Square, Peak To RMS and Zero Crossing Rate are simulation-only real arithmetic: the first four need a square root, and the last two divide – neither has a place in a Q16.16 datapath. They simulate correctly and quantize at the port boundary, and are not offered as synthesizable.

The reduction multiplies by the constant 1/N in every target, including the in-app solver, so the exported core and the simulation agree to the last bit rather than to within a division's rounding.

Simulink bridge

None, and here for a structural reason rather than a missing block. All six counterparts do exist in the installed DSP System Toolbox – dspstat3/Moving Average, Moving RMS, Moving Standard Deviation, Moving Maximum, Moving Minimum – but a catalog entry carries exactly one library path, while this block's statistics are that many different Simulink blocks. Registering any one of them would export the rest as that one, silently, which is worse than reporting no equivalent. The three newest modes have no Simulink counterpart at all: peak2peak, peak2rms, rssq and zerocrossrate are Signal Processing Toolbox functions, and that toolbox ships no Simulink library. The bridge therefore reports the block, and it has no parity testbench, which is the documented consequence of that choice. Code export verification still covers it across all ten languages.

Notes

  • Stateful: the same input gives a different output depending on the N−1 samples before it. The window is re-seeded at the start of every run.
  • No state space, deliberately. The mean alone is linear, but the statistic is a configuration choice and the others are not, so the block declines one in every mode rather than being mergeable in one of nine.
  • The state is read before it is written. The output is computed from the window as it stood, and only then does the window shift. The exported code shifts from the far end downwards for that reason – except on the three HDL targets, where the state is a registered signal and pre-clock reads give it for free.
  • Not Tapped Delay, and not MinMax Running Resettable. The first emits the window itself and reduces nothing; the second runs since its last reset rather than over a window, so its answer never comes back down – that one, not this block, is where a cumulative maximum or minimum lives.
  • Two modes are dimensionless, and the other seven carry the input's unit. Peak To RMS is a ratio, so scaling the input does not change it, and Zero Crossing Rate is a count over a count. Neither is in the input's units, which matters when several of these feed one feature vector.

Code facts#

FactValue
registered typeMachine_Learning/Feature_Engineering/Rolling_Statistics
familyMachine_Learning/Feature_Engineering
solver environment classICoreBlock_0_Machine_Learning_1_Feature_Engineering_2_Rolling_Statistics
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Machine_Learning/Feature_Engineering/Rolling_Statistics/ICoreBlock_0_Machine_Learning_1_Feature_Engineering_2_Rolling_Statistics.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Machine_Learning/Feature_Engineering/Rolling_Statistics/ICoreBlock_0_Machine_Learning_1_Feature_Engineering_2_Rolling_Statistics.h
default size on canvas124 × 84 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoubley

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
StatisticMean%~%Standard Deviation%~%Minimum%~%Maximum%~%RMS%~%Pea…—
Window Length5—
Initial Window Value0—

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): no single Simulink equivalent: its windowed statistics are five SEPARATE blocks (dspstat3/Moving Average, Moving RMS, Moving Standard Deviation, Moving Maximum, Moving Minimum), while a bridge entry carries exactly one library path -- so any mapping would export four of the five statistics as the wrong block, silently

Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

Rolling Statistics — one statistic over the last N samples, elementwise per channel y[c] = stat( u[k][c], u[k-1][c], ... , u[k-N+1][c] )

Read the header before this file: it records the pre-filled window (and why a partial one was rejected), the config-seed / running-window split every generator depends on, the read-before-write shift, and the per-statistic HDL split.

⚠ EVERY BACKEND MULTIPLIES BY THE RECIPROCAL 1/N, including the C++ reference in reduce(). A backend that divided instead would differ in the last bit from one that multiplied, which is a difference export verification can see and nothing else would explain.

Sample results#

Rolling Statistics — Step: 0 -> 1 at t = 1 sRolling Statistics — Step: 0 -> 1 at t = 1 s00.51012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0.2
rampRamp: slope 1 from t = 00 … 5.7
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-0.9604 … 0.96
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-0.7 … 1.3

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit c01902987 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Machine_Learning__Feature_Engineering__Rolling_Statistics.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).