series_iir
You can use series_iir when you need to apply digital signal processing techniques to time-series data. This is particularly useful for smoothing noisy data, removing high-frequency components, implementing custom filters, or applying frequency-selective transformations to time-series measurements.
Usage#
Syntax#
series_iir(array, numerator, denominator)Parameters#
| Parameter | Type | Description |
|---|---|---|
array |
dynamic | A dynamic array of numeric values (input series). |
numerator |
dynamic | A dynamic array of numerator (feedforward) coefficients. |
denominator |
dynamic | A dynamic array of denominator (feedback) coefficients. |
Returns#
A dynamic array containing the filtered output series after applying the IIR filter defined by the numerator and denominator coefficients.
Use case examples#
In log analysis, you can use series_iir to smooth noisy request duration measurements, making trends and patterns more visible.
Query
['sample-http-logs']
| summarize durations = make_list(req_duration_ms) by id
| extend smoothed = series_iir(durations, dynamic([0.2, 0.6, 0.2]), dynamic([1.0]))
| take 5Output
| id | durations | smoothed |
|---|---|---|
| u123 | [50, 120, 45, 200, 60] | [50, 91, 62, 128, 88] |
| u456 | [30, 35, 80, 40, 45] | [30, 33, 54, 46, 45] |
This query applies an IIR filter to smooth request duration measurements, reducing noise while preserving the underlying trend.
In OpenTelemetry traces, you can use series_iir to filter span duration data, removing high-frequency noise to better identify sustained performance trends.
Query
['otel-demo-traces']
| extend duration_ms = duration / 1ms
| summarize durations = make_list(duration_ms) by ['service.name']
| extend filtered = series_iir(durations, dynamic([0.1, 0.8, 0.1]), dynamic([1.0, -0.3]))
| take 5Output
| service.name | durations | filtered |
|---|---|---|
| frontend | [100, 150, 95, 200, 120] | [100, 130, 108, 152, 133] |
| checkout | [200, 250, 180, 300, 220] | [200, 230, 202, 248, 232] |
This query applies an IIR filter with feedback to span durations, smoothing out transient spikes while maintaining sensitivity to sustained changes.
In security logs, you can use series_iir to filter request rate data, separating sustained traffic changes from brief anomalies.
Query
['sample-http-logs']
| summarize request_counts = make_list(req_duration_ms) by status
| extend filtered = series_iir(request_counts, dynamic([0.15, 0.7, 0.15]), dynamic([1.0, -0.4]))
| take 5Output
| status | request_counts | filtered |
|---|---|---|
| 200 | [100, 105, 300, 110, 95] | [100, 103, 180, 142, 120] |
| 401 | [10, 12, 50, 15, 11] | [10, 11, 27, 20, 16] |
This query uses IIR filtering to smooth security event patterns, helping distinguish between brief anomalies and sustained attack patterns.
List of related functions#
- series_sum: Returns the sum of series elements. Use for simple aggregation instead of filtering.
- series_stats: Returns statistical measures. Use for statistical analysis instead of signal processing.
- series_abs: Returns absolute values. Often used after IIR filtering to analyze magnitude.
- make_series: Creates time-series from tabular data. Often used before applying
series_iirfor signal processing.
Other query languages#
Splunk SPL users
In Splunk SPL, signal processing typically requires external tools or complex manual calculations with streamstats. In APL, series_iir provides built-in digital filtering capabilities for array data.
Splunk example
... | streamstats window=5 avg(value) as smoothed_value
... (limited to basic moving averages)APL equivalent
datatable(values: dynamic)
[
dynamic([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
]
| extend filtered = series_iir(values, dynamic([0.25, 0.5, 0.25]), dynamic([1.0, -0.5]))ANSI SQL users
In SQL, implementing IIR filters requires complex recursive queries or user-defined functions. In APL, series_iir provides this functionality as a built-in operation on array data.
SQL example
-- Complex recursive CTE required for IIR filtering
WITH RECURSIVE filtered AS (...)
SELECT * FROM filtered;APL equivalent
datatable(values: dynamic)
[
dynamic([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
]
| extend filtered = series_iir(values, dynamic([0.25, 0.5, 0.25]), dynamic([1.0, -0.5]))