The timeseries generator
Use it when you need values that move like a real signal over time — daily
sales, sensor readings, web traffic. Real series aren't flat noise or a single
distribution: they're layers — an overall trend (rising or falling), a
repeating season (weekly, yearly), and random noise on top. timeseries builds a
row's value exactly that way:
value(i) = base + trend·i + amplitude·sin(2π·i / period) + noise·random
where i is the row number (the time axis), counted from zero — so the first row
(i = 0) is exactly base.
Example outputs below are illustrative: exact digits can differ by core version and
seed, but the shape — the trend, the wave, the jitter — is what matters.
- Abase alone: a flat line
- Btrend added: the line starts climbing
- Cperiod and amplitude added: a wave rides on the trend
- Dnoise added: the wave stops being perfect
Why not just random numbers
A plain number generator produces white noise — values that
jump around a mean with no memory. Here's number with a normal distribution
centered on 1000:
<sequence name="Noise"><gen type="number" distribution="normal" mean="1000" sd="120"/></sequence>
Day 1 841 Day 2 1341 Day 3 1047 Day 4 1010 Day 5 1086 Day 6 1077 Day 7 862 Day 8 1072 Day 9 1114 Day 10 782 Day 11 979 Day 12 1014
No rise, no fall, no repeat — every day just churns around 1000. Real metrics don't
look like that: sales have a trend (the business grows), a weekly rhythm (weekends
differ from weekdays), and only on top of those some random deviation. Those three
layers are exactly what timeseries adds.
Attributes
<gen type="timeseries" base="1000" trend="20" period="7" amplitude="150" noise="30"/>
| Attribute | What it sets |
|---|---|
base | Starting level (default 0) |
trend | Slope: how much the value rises each step |
period | Length of the seasonal wave, in rows (e.g. 7 = a week) |
amplitude | Height of the seasonal wave |
noise | Strength of the random noise (standard deviation) |
decimals | Digits after the decimal point (default 0 — integer) |
Every layer is optional. The sections below take them one at a time — what each does, and when you'd reach for it.
base — the starting level
base fixes the value of the very first row (i = 0), and it's the level everything
else is measured from. On its own — no trend, no wave, no noise — it's just a
flat line.
<gen type="timeseries" base="500"/>
500 500 500 500 500
Use it to anchor a metric at a realistic level — a store that averages 500 orders a day, a sensor that idles at 20 degrees — before you add movement.
trend — direction
trend is the slope: each row adds trend to the last. Positive climbs, negative
falls. With only base + trend you get a dead-straight line.
<gen type="timeseries" base="1000" trend="20"/>
1000 1020 1040 1060 1080
Use it for growth or decay you want to be obvious at a glance — a subscriber count that gains 20 a day, a battery that drains a fixed amount each cycle.
period and amplitude — the seasonal wave
These two work as a pair, and neither does anything without the other. period is
how many rows one full cycle takes (7 = a weekly rhythm, 365 = a yearly one);
amplitude is how far the wave swings above and below the trend line. Together they
lay a repeating sin wave on whatever base + trend gives you.
<gen type="timeseries" base="1000" trend="20" period="7" amplitude="150"/>
1000 1137 1186 1125 1015 954 1003
Within each 7-row window the value rises to a peak and falls to a trough, then repeats. Because the trend keeps lifting the whole line, each cycle sits higher than the last — the wave rides up the slope. Use it for anything with a calendar rhythm: weekday-vs-weekend traffic, summer-vs-winter demand.
noise — real-world roughness
noise is the standard deviation of a random wobble added to every row. It's the
difference between a textbook curve and a real measurement.
<gen type="timeseries" base="1000" trend="20" period="7" amplitude="150" noise="30"/>
985 1087 1192 1107 936 966 1031
Compare with the clean wave above: the shape is the same, but each point jitters a
little (1000 → 985). Turn it up for a noisy sensor, down for a smooth aggregate.
The jitter is reproducible — see Details.
decimals — fractional values
By default the output is rounded to a whole number. decimals keeps that many
digits after the point — for temperatures, prices, or any measured quantity.
<gen type="timeseries" base="20" trend="0.5" noise="0.3" decimals="1"/>
20.0 20.4 21.2 21.4 22.1
Build it up one layer at a time
The clearest way to get a feel for the generator is to switch the layers on one at a
time. Below, three <sequence> columns run over the
same "days": trend (only trend), +season (add period + amplitude), and
+noise (add noise).
<sequence name="A"><gen type="timeseries" base="1000" trend="20"/></sequence>
<sequence name="B"><gen type="timeseries" base="1000" trend="20" period="7" amplitude="150"/></sequence>
<sequence name="C"><gen type="timeseries" base="1000" trend="20" period="7" amplitude="150" noise="30"/></sequence>
...
<data>Day ${{Day}} trend=${{A}} +season=${{B}} +noise=${{C}}</data>
Day trend +season +noise 01 1000 1000 985 02 1020 1137 1087 03 1040 1186 1192 04 1060 1125 1107 05 1080 1015 936 06 1100 954 966 07 1120 1003 1031 08 1140 1140 1087 09 1160 1277 1311 10 1180 1326 1347 11 1200 1265 1261 12 1220 1155 1126
Reading the columns:
- trend — a dead-straight line:
+20each day,1000, 1020, 1040 …. There's a direction, but no life to it. - +season — a weekly wave (
period="7") laid on the line. Within each week the value climbs to a peak and falls to a trough: peaks land on days 3 and 10 (1186 → 1326), the trough on day 6 (954). Peaks and troughs repeat every 7 rows, and each one sits higher than the last by exactlytrend · period = 20 · 7 = 140— the wave rides up the trend. - +noise — the same shape, but it jitters slightly (
1000 → 985), the way real measurements do.
Each column is the previous one plus one attribute — direction, then rhythm, then real-world roughness. That's how a realistic series is assembled.
Details
- Deterministic: the same
seedgives the same series. The noise is reproducible too — it's computed from the row number, not rolled on the fly. - Any size, either engine: a value is computed from its row number, so memory doesn't grow (see Large outputs). A billion points is no problem.
- The time axis is the row number. It pairs naturally with an
increment(a day counter) or adatecolumn beside it, so each value carries a real date.
Today it's one trend + one seasonal wave + noise. Planned: several seasonalities at once (weekly and yearly) and AR noise (correlated over time).