rbed

Aggregates

Every Event carries Aggregates: for each Channel its Rule aggregates, the minimum, maximum and mean over the Samples of the Event, and more. They tell you what the data looked like while the Event ran, without keeping the Samples yourself.

Which Channels

An Event carries the Aggregates of the Channels its Rule's Conditions use, plus those named with aggregate(...) (see Aggregates for more Channels). Read them with event.aggregates(x).

Reading a Channel the Rule does not aggregate throws an IllegalArgumentException that names the missing .aggregate(...), so a forgotten declaration is never mistaken for "no data".

A vehicle's drives

Drive is an Event while speed is above a threshold. Its Condition uses speed, so speed is aggregated anyway; .aggregate(temp) adds the engine temperature. The log is irregular: a Sample every 2 seconds while the speed changes, every 10 seconds otherwise.

The live chart and its controls need JavaScript. The tables show the Events at the default parameters.

The site cannot run the default parameters: (JavaScript) TypeError: Cannot read properties of undefined (reading 'length')

import io.github.romanhaendler.rbed.*;
import java.util.Map;

Channel speed = Channel.named("speed");   // m/s
Channel temp = Channel.named("temp");     // engine temperature, °C

Rule drive = Rule.named("Drive")
    .when(speed.above(1))
    .aggregate(temp);                     // speed is aggregated anyway
Detector detector = Detector.builder()
    .rules(drive)
    .listener(new EventListener() {
      @Override public void onEnded(Event e) {
        Aggregates s = e.aggregates(speed);
        System.out.printf("avg %.1f m/s, max %.1f m/s, %.0f m driven%n",
            s.timeWeightedMean(), s.max(), s.integral());
      }
    })
    .build();

double[][] log = {   // time (s), speed (m/s), engine temperature (°C)
  {0, 0, 60}, {10, 0, 60}, {20, 0, 60}, {22, 3, 61}, {24, 6, 62}, {26, 9, 63}, {28, 12, 64},
  {30, 14, 65}, {40, 14, 70}, {50, 14, 74}, {60, 14, 77}, {62, 11, 77}, {64, 8, 77},
  {66, 5, 77}, {68, 2, 76}, {70, 0, 76}, {80, 0, 74}, {90, 0, 72}, {100, 0, 70},
  {102, 3, 70}, {104, 6, 71}, {106, 8, 71}, {116, 8, 73}, {126, 8, 75}, {128, 5, 75},
  {130, 2, 75}, {132, 0, 75}, {140, 0, 74}, {150, 0, 73},
};
for (double[] row : log) {
  detector.push((long) row[0] * 1_000, Map.of(speed, row[1], temp, row[2]));
}
detector.close();

At the default threshold of 1 m/s there are two Drives, from 22 s to 70 s and from 102 s to 132 s.

Things to try

  • Raise Driving above to 10 m/s: the second drive, never faster than 8 m/s, is no Drive any more, and the first one is shorter, from 28 s to 64 s. Its Aggregates change with its span: its mean speed rises.

The Event table on every page of this site shows each Event's minimum, maximum and mean per Channel under the Event.

The components

  • min, max, mean: over the Samples of the Event in which the Channel had a value.
  • count: the number of those Samples. If it is 0, min, max and mean are NaN: the site's Event tables then say "no value", and the full Aggregates show a dash.
  • timeWeightedMean: the mean with each value weighted by how long it held, until the next Sample, time advance or the Event's end.
  • integral: value × seconds, time-weighted like the mean: distance from speed, energy from power, dose from rate.

The plain mean treats every Sample alike, which is right for regularly sampled data. With irregular sampling, as in the log above, timeWeightedMean gives the true average over time. At the default threshold, the first Drive's Samples average 9.33 m/s, but the vehicle spent most of the Drive at 14 m/s: its time-weighted mean is 11.67 m/s, and its integral of 560 is the distance it covered, in metres.

The time-weighted values are NaN if the Channel had no value during the Event, and the time-weighted mean also for an Event of zero length.

While an Event runs

The listener receives the Event as it stands at each stage of its lifecycle. Aggregates of the Pending and the Confirmed Event cover the Event so far; those of the Ended Event cover it from its start to its end.

Another Rule can use them in its Conditions: latest(rule).min(x), .max(x) and .mean(x) are the Aggregates of the most recent Confirmed Event of rule (see The latest Event).

Reference

Methods used: Channel.named, Rule.named, Rule.when, Expression.above, Rule.aggregate, Detector.builder, Detector.Builder.rules, Detector.Builder.listener, EventListener.onEnded, Event.aggregates, Aggregates.timeWeightedMean, Aggregates.max, Aggregates.integral, Detector.Builder.build, Detector.push, Detector.close.