Collection Producers

The collection producer operators maintain collections of events over the data lifecycle.

Unary collection producers

The unary collection producers maintain a collection based on static parameters.

window

The window operator collects Events over a duration and re-emits them as a Collection as they enter and leave a fixed-width window of time.

By default the window is set to the authoritative domain's temporal width.

  • A <RealTimeDomain window={30_000} /> will cause the window operator to have a window length of 30 seconds.
  • A <StaticDomain /> will cause the window operator to have an infinite window length.

The window length can be changed with the windowLength argument. The windowLength is exclusive of the oldest point. A 30 second window does not include events exactly 30 seconds away from 'now'.

The example below produces a rolling 2 second average of the data source.

import { useDataTransformer } from '@electricui/timeseries-react'
import { DataTransformer, window, mean } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const rollingAverage = mean(window(dataSource, 2_000))
 
return rollingAverage
})

buffer

The buffer operator collects up to N Events and emits a Collection each time a new Event comes in.

If more than N events are added, old ones are evicted.

The optional third argument, onlyEmitAtCapacity specifies if the operator should wait until the capacity is reached before emitting the first collection.

The example below waits until 64 items have been buffered, before maintaining the latest 64 items as a Collection.

import { useDataTransformer } from '@electricui/timeseries-react'
import { buffer } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const buffered = buffer(dataSource, 64, true)
 
return buffered
})

batch

The batch operator collects events and re-emits a fixed size Collection of them once the capacity is reached.

Prefer the buffer or window operators unless you need non-overlapping slices of input data with relatively high latency.

The example below collects 512 events, then emits them all at once.

import { useDataTransformer } from '@electricui/timeseries-react'
import { batch } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const batchOf512 = batch(dataSource, 512)
 
return batchOf512
})

The head operator collects events and maintains a Collection of the top-N items as defined by an arbitrary order.

By default the ordering is by time, and the top 1 item is maintained, producing the 'earliest' event.

The operator can consume a regular Queryable or another Collection Queryable. If a non-Collection queryable is passed, it is implicitly wrapped with a window operator.

The example below maintains a Collection of the top 10 events by 'voltage' in the past 30 seconds.

import { useDataTransformer } from '@electricui/timeseries-react'
import { head, window } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const thirtySecondWindow = window(dataSource, 30_000)
 
const top10ByVoltage = head(thirtySecondWindow, { N: 10, orderBy: (data, time, tags) => data.voltage })
 
return top10ByVoltage
})

tail

The tail operator collects events and maintains a Collection of the bottom-N items as defined by an arbitrary order.

By default the ordering is by time, and the bottom 1 item is maintained, producing the 'latest' event.

The operator can consume a regular Queryable or another Collection Queryable. If a non-Collection queryable is passed, it is implicitly wrapped with a window operator.

The example below maintains a Collection of the 'rightmost' 10 events by their x coordinate in the past 30 seconds.

import { useDataTransformer } from '@electricui/timeseries-react'
import { tail, window } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const thirtySecondWindow = window(dataSource, 30_000)
 
const rightMost10 = tail(thirtySecondWindow, { N: 10, orderBy: (data, time, tags) => data.x })
 
return rightMost10
})

Binary collection producers

The binary collection producers utilise an external source of data to drive the 'selection' of events. If a non-collection queryable is passed as the input, the operator implicitly wraps it with a window operator, selecting the authoritative domain's window length of data.

Each operator comes in a singular and plural variant. The valueAccessor and searchRegionAccessor names are specialised to the type of data the operator indexes on. They each follow closely to the following signature format, with minor variations based on the data type.

The singular variant maintains a selection based on a singular input search source.

  • It can discard inputs by passing a null to the valueAccessor. Otherwise, this is the data used to index the event.
  • It can discard searches by passing a null to the searchRegionAccessor. Otherwise, this defines the search region.
  • It can discard matches based on the result of the predicate function.
  • It can remap the resulting event data and tags with the mapResult and tagResult functions.
  • Only the differences between the SearchRegions are emitted. If a search region is 'extended' for example, only the 'new' events will be emitted.
declare type CollectValueSingularStructure = (
queryable,
searchSource,
{
valueAccessor?: (data, time, tags) => Value | null,
searchRegionAccessor?: (data, time, tags) => SearchRegion,
predicate?: (data, time, tags, value, searchData, searchTags, searchRegion) => boolean,
mapResult?: (data, time, tags, value, searchData, searchTags, searchRegion) => Result,
tagResult?: (data, time, tags, value, searchData, searchTags, searchRegion) => ResultTags,
}
)

The plural variant maintains multiple selections based on a Collection of input search source. It has similar features to the singular variant:

  • It can discard inputs by passing a null to the valueAccessor. Otherwise, this is the data used to index the event.
  • It can discard searches by passing a null to the searchRegionAccessor. Otherwise, this defines the search region.
  • It can discard matches based on the result of the predicate function.
  • It can remap the resulting event data and tags with the mapResult and tagResult functions.

The notable differences are that:

  • The final Collection is only maintained at the granularity of the SearchRegion. If a SearchRegion is 'extended' for example, all the events in the old region are removed, then the events in the new region are added back. For this reason, if only one search region is maintained, prefer the singular variant of the operator.
  • The resulting event data can also be remapped in time, using the retimeResult function.
declare type CollectValuesPluralStructure = collectValues(
queryable,
searchSource,
{
valueAccessor?: (data, time, tags) => Value,
searchRegionAccessor?: (data, time, tags) => SearchRegion,
predicate?: (data, time, tags, value, searchData, searchTags, searchRegion) => boolean,
mapResult?: (data, time, tags, value, searchData, searchTags, searchRegion) => Result,
tagResult?: (data, time, tags, value, searchData, searchTags, searchRegion) => ResultTags,
retimeResult?: (data, time, tags, value, searchData, searchTags, searchRegion) => Time,
}
)

collectTimeSpan

The singular form collectTimeSpan operator maintains a Collection of events within a TimeSpan of { min: Time, max: Time }.

There is no valueAccessor, as it automatically selects the Event times.

The example below selects the region of time defined by the mouse drag minimum and maximum x coordinates. If the mouse hasn't completed, or has cancelled the drag, then it selects nothing.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal } from '@electricui/components-desktop-charts'
import { collectTimeSpan } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const selected = collectTimeSpan(dataSource, mouseSignal, {
// If the mouse has dragged, return the drag x min and max,
// otherwise return null, selecting nothing
searchRegionAccessor: (data, time, tags) => {
if (data.hasDragged) {
return ({ min: data.dragXMin, max: data.dragXMax })
}
return null
},
// just select the raw data (the default)
mapResult: (data, time, tags, searchData, searchTags, searchRegion) => data
})
 
return selected
})

collectTimeSpans

The plural form collectTimeSpans operator maintains a Collection of events within a Collection of TimeSpans of { min: Time, max: Time }.

There is no valueAccessor, as it automatically selects the Event times.

The example below selects the 1 second surrounding each of the 'peak' events within the chart window. It tags the resulting events with the peak that caused its collection.

import { useDataTransformer } from '@electricui/timeseries-react'
import { collectTimeSpans, window } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const chartPeaks = window(peakDS)
 
const selected = collectTimeSpans(dataSource, chartPeaks, {
// Pass through the peak time via an additional parameter
searchRegionAccessor: (data, time, tags) => ({ min: time - 500, max: time + 500, peakTime: time}),
// Tag each result with the time of the peak
tagResult: (data, time, tags, searchData, searchTags, searchRegion) => ({ peakTime: searchRegion.peakTime })
})
 
return selected
})

collectBoundingBox

The singular form collectBoundingBox operator maintains a Collection of events within a BoundingBox of { xMin: number, yMin: number, xMax: number, yMax: number }.

The positionAccessor is used to define the position of an Event. The searchBoundingBoxAccessor is used to define the search region.

The example below selects the region of time defined by the mouse drag coordinates. If the mouse hasn't completed, or has cancelled the drag, then it selects nothing.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { collectBoundingBox } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const selected = collectBoundingBox(dataSource, mouseSignal, {
// Select the x and y position from the event. This is the default.
positionAccessor: (data, time, tags) => ({ x: data.x, y: data.y }),
// If the mouse has dragged, return the drag bounding box,
// otherwise return null, selecting nothing
searchBoundingBoxAccessor: (data, time, tags) => {
if (data.hasDragged) {
return ({ xMin: data.dragXMin, xMax: data.dragXMax, yMin: data.dragYMin, yMax: data.dragYMax })
}
return null
},
// just select the raw data (the default)
mapResult: (data, time, tags, searchData, searchTags, searchRegion) => data,
})
 
return selected
})

collectBoundingBoxes

The plural form collectBoundingBoxes operator maintains a Collection of events within a Collection of BoundingBoxs of { xMin: number, yMin: number, xMax: number, yMax: number }.

The positionAccessor is used to define the position of an Event. The searchBoundingBoxAccessor is used to define the search region.

The example below selects all particle count events within 100 units of the 'impact sites', within the chart temporal range. It tags the resulting events with the impact site coordinates that caused its collection.

import { useDataTransformer } from '@electricui/timeseries-react'
import { collectBoundingBoxes, window } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const impactSitesWithinTemporalRange = window(impactSites)
 
const selected = collectBoundingBoxes(dataSource, impactSitesWithinTemporalRange, {
// Select the x and y position from the event. This is the default.
positionAccessor: (data, time, tags) => ({ x: data.x, y: data.y }),
// Initially search for the bounding box surrounding 100 units on each side
searchBoundingBoxAccessor: (data, time, tags) => ({
xMin: data.x - 100,
xMax: data.x + 100,
yMin: data.y - 100,
yMax: data.y + 100,
impactX: data.x,
impactY: data.y,
}),
// Use the predicate to filter to a 100 unit radius 'circle' instead of just the bounding box
predicate: (data, time, tags, position, searchData, searchTags, searchRegion) => {
const distance = Math.sqrt(
Math.pow(position.x + searchRegion.impactX, 2) + Math.pow(position.y + searchRegion.impactY, 2)
)
 
return distance <= 100
},
// Tag each result with the position of the impact site
tagResult: (data, time, tags, position, searchData, searchTags, searchRegion) =>
({ impactX: searchRegion.impactX, impactY: searchRegion.impactY }),
})
 
return selected
})

collectValueSpan

The singular form collectValueSpan operator maintains a Collection of events within a SearchRegion of { min: V, max: V }.

The valueAccessor is used to define the value of an Event. The searchRegionAccessor is used to define the search region. The order operator is used to define the ordering of values if they are non-number.

The example below selects the region of voltage values defined by the mouse drag coordinates on the y coordinate. If the mouse hasn't completed, or has cancelled the drag, then it selects nothing.

import { useDataTransformer } from '@electricui/timeseries-react'
import { useMouseSignal, MouseData } from '@electricui/components-desktop-charts'
import { collectValueSpan } from '@electricui/dataflow'
 
const [mouseSignal, captureRef] = useMouseSignal()
 
const dataTransformer = useDataTransformer(() => {
const selected = collectValueSpan(dataSource, mouseSignal, {
// Order by the voltage
valueAccessor: (data, time, tags) => data.voltage,
// If the mouse has dragged, return the drag bounding defined by the y coordinate,
// otherwise return null, selecting nothing
searchRegionAccessor: (data, time, tags) => {
if (data.hasDragged) {
return ({ min: data.dragYMin, max: data.dragYMax })
}
return null
},
// just select the raw data (the default)
mapResult: (data, time, tags, searchData, searchTags, searchRegion) => data,
})
 
return selected
})

collectValueSpans

The plural form collectValueSpans operator maintains a Collection of events within a Collection of SearchRegion of { min: V, max: V }.

The valueAccessor is used to define the value of an Event. The searchRegionAccessor is used to define the search region. The order operator is used to define the ordering of values if they are non-number.

The example below selects all events within 100 units of the trigger voltage, within the chart temporal range. It tags the resulting events with the trigger voltage that caused its collection.

import { useDataTransformer } from '@electricui/timeseries-react'
import { collectValueSpans, window } from '@electricui/dataflow'
 
const dataTransformer = useDataTransformer(() => {
const triggersWithinTemporalRange = window(triggers)
 
const selected = collectValueSpans(dataSource, triggersWithinTemporalRange, {
// Select the x and y position from the event. This is the default.
valueAccessor: (data, time, tags) => data.voltage,
// Search for values within 100 units of the trigger voltage
searchRegionAccessor: (data, time, tags) => ({
min: data - 100,
max: data + 100,
triggerVoltage: data,
}),
// Tag each result with the trigger voltage
tagResult: (data, time, tags, position, searchData, searchTags, searchRegion) =>
({ triggerVoltage: searchRegion.triggerVoltage }),
})
 
return selected
})