Skip to content

Merge Strategies

How overlapping chunk boundaries are reconciled in Streaming mode.

Streaming LOESS processes data in fixed-size chunks with a configurable overlap. Points inside the overlap zone are fitted twice — once by the left chunk and once by the right chunk. The merge_strategy decides how those two estimates are combined into a single output value.

Chunk A: [=========|=====]
Chunk B: [=====|=========]
Overlap: [=====]
↑
merge_strategy
applied here
StrategyMethodRobustnessSpeed
"average"Simple mean of both estimatesLowFastest
"take_first"Left-chunk estimate onlyLowFastest
"take_last"Right-chunk estimate onlyLowFastest
"weighted_average"Distance-weighted meanHighModerate

Merge Strategies


Takes the arithmetic mean of the left-chunk and right-chunk estimates in the overlap region. Fast and sufficient when both chunks have similar smoothing quality.

Use when: Chunks are large and the overlap region has uniform data density.

const { StreamingLoess } = require('fastloess-wasm');
const n = 100;
const xChunk = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const yChunk = Float64Array.from(xChunk, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const processor = new StreamingLoess(
{},
{ chunk_size: 60, overlap: 20, merge_strategy: "average" }
);
processor.process_chunk(xChunk.slice(0, 60), yChunk.slice(0, 60));
// The second chunk's overlap region (its first 20 points) is where
// merge_strategy actually blends the two chunks' estimates.
const result = processor.process_chunk(xChunk.slice(60), yChunk.slice(60));
console.log("Merged value in overlap region (average):", result.y[5].toFixed(4));
Merged value in overlap region (average): 0.2134

Keeps only the left-chunk estimate in the overlap zone and discards the right-chunk estimate. Produces a definitive, non-revised output as soon as the right boundary of each chunk is reached.

Use when: You need final output values immediately after each chunk (no look-ahead revision); left-chunk data quality is higher.

const { StreamingLoess } = require('fastloess-wasm');
const n = 100;
const xChunk = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const yChunk = Float64Array.from(xChunk, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const processor = new StreamingLoess({}, { chunk_size: 60, overlap: 20, merge_strategy: "take_first" });
processor.process_chunk(xChunk.slice(0, 60), yChunk.slice(0, 60));
const result = processor.process_chunk(xChunk.slice(60), yChunk.slice(60));
console.log("Merged value in overlap region (take_first):", result.y[5].toFixed(4));
Merged value in overlap region (take_first): 0.2280

Keeps only the right-chunk estimate in the overlap zone. The right chunk sees more of the surrounding data, so its fit can be more accurate near the left boundary of the new chunk.

Use when: Right-chunk context improves overlap quality; you are post-processing complete data rather than streaming live.

const { StreamingLoess } = require('fastloess-wasm');
const n = 100;
const xChunk = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const yChunk = Float64Array.from(xChunk, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const processor = new StreamingLoess({}, { chunk_size: 60, overlap: 20, merge_strategy: "take_last" });
processor.process_chunk(xChunk.slice(0, 60), yChunk.slice(0, 60));
const result = processor.process_chunk(xChunk.slice(60), yChunk.slice(60));
console.log("Merged value in overlap region (take_last):", result.y[5].toFixed(4));
Merged value in overlap region (take_last): 0.1988

Assigns each overlap point a weight proportional to its proximity to the centre of its respective chunk: points near the left-chunk centre get higher left weight; points near the right-chunk centre get higher right weight. This produces the smoothest transition across chunk boundaries.

y^=wLy^L+wRy^RwL+wR\hat{y} = \frac{w_L \hat{y}_L + w_R \hat{y}_R}{w_L + w_R}

where wLw_L and wRw_R are linear distance weights from the chunk centres.

Use when: Minimising boundary artefacts is more important than speed; moderate overlap (10–20 % of chunk size).

const { StreamingLoess } = require('fastloess-wasm');
const n = 100;
const xChunk = Float64Array.from({ length: n }, (_, i) => i * 2 * Math.PI / (n - 1));
const yChunk = Float64Array.from(xChunk, (xi, i) => Math.sin(xi) + (((i * 7 + 3) % 17) / 17 - 0.5) * 0.6);
const processor = new StreamingLoess(
{},
{ chunk_size: 60, overlap: 20, merge_strategy: "weighted_average" }
);
processor.process_chunk(xChunk.slice(0, 60), yChunk.slice(0, 60));
const result = processor.process_chunk(xChunk.slice(60), yChunk.slice(60));
console.log("Merged value in overlap region (weighted_average):", result.y[5].toFixed(4));
Merged value in overlap region (weighted_average): 0.2207

SituationRecommended Strategy
General purpose"weighted_average"
Maximum throughput"average"
Immediate finalised output"take_first"
Post-processing, right context better"take_last"
Minimising boundary artefacts"weighted_average"