tdfs4ds.dataset.offsets¶
tdfs4ds.dataset.offsets
¶
Feature offsets (lags and leads) in datasets. Pure module (no database access).
A dataset row describes an entity at one instant t (now, an as_of date, a
TimeManager's business date, or each instant of a historical window). An offset
reads a feature at another instant:
- a lag
-Lreads the feature att - L(autoregressive inputs:amountyesterday, the day before, last month); - a lead
+Lreads it att + L(forecast targets:churnone month later).
Equivalently, a value valid over [s, e) is, as a lag of L, valid over
[s + L, e + L): the period is shifted, then the usual temporal logic applies.
Two rules shape the SQL (verified on Vantage 20 by reading EXPLAIN plans):
- the shift is always applied to the CONSTANT side of a comparison
(
ValidStart <= t - L), never to the column (ValidStart + L <= t); Teradata folds the constant arithmetic into a literal, and each storage table is still read "from a single partition"; - a lead never reads past the database's current time:
t + Lbeyond now is not an observation, so the value is NULL there (an open stored period, "until changed", is not a value of the future).
Offsets are whole seconds or calendar months: valid periods are stored as
TIMESTAMP(0) WITH TIME ZONE whatever the source's own timestamp type, so a
sub-second offset could not change any result.
_HINT
module-attribute
¶
_HINT = "Write an offset with its sign: '-1 day' (the value one day BEFORE the dataset's instant, a lag) or '+1 month' (one month AFTER, a lead, e.g. a forecast target); '0' is the instant itself. Units: year, month, week, day, hour, minute, second (ISO 8601 also works: '-P1D', '+P1M', '-PT6H')."
_SUBSECOND
module-attribute
¶
_SUBSECOND = "offsets are whole seconds: tdfs4ds stores every valid period as TIMESTAMP(0) WITH TIME ZONE (whatever the source's timestamp type), so a fraction of a second cannot change a result. Milliseconds and microseconds are not accepted."
_SUBSECOND_RE
module-attribute
¶
_SUBSECOND_RE = compile('\\b(ms|msec|millisecond|milliseconds|us|usec|microsecond|microseconds)\\b', I)
_ADD_MONTHS_RE
module-attribute
¶
_INTERVAL_RE
module-attribute
¶
_INTERVAL_RE = compile("^\\((?P<expr>.*)\\s(?P<sign>[+-])\\s+INTERVAL\\s*'(?P<d>\\d+)\\s+(?P<h>\\d{2}):(?P<mi>\\d{2}):(?P<s>\\d{2})'\\s+DAY\\s+TO\\s+SECOND\\s*\\)$", IGNORECASE | DOTALL)
__all__
module-attribute
¶
__all__ = ['Offset', 'parse_offset', 'resolve_offsets', 'offset_alias', 'split_shift', 'offset_from', 'LAG', 'LEAD', 'PIVOT', 'PER_OFFSET', 'OFFSET_STRATEGIES', 'MAX_DAYS']
_FR_UNITS
module-attribute
¶
_FR_UNITS = {'year': 'an', 'month': 'mois', 'day': 'jour', 'hour': 'heure', 'minute': 'minute', 'second': 'seconde'}
Duration
¶
A non-negative length of time: a calendar part (months) and an exact part (seconds).
The two are kept apart because a month has no fixed length: months are
applied with ADD_MONTHS (which clamps month-ends — 31 May minus three
months is 29 Feb, verified live; - INTERVAL 'n' MONTH would raise
Error 2665 instead), the rest with a DAY TO SECOND interval.
interval_literal
¶
INTERVAL 'd hh:mi:ss' DAY TO SECOND for the exact part.
No precision on DAY: DAY(4) TO SECOND inside a literal is a
syntax error (Error 3706, verified live); the precision is taken from
the literal itself, and 9999 days is accepted.
apply
¶
SQL for expr ± self; sign is +1 or -1.
Months first (innermost), then the exact part — "back one month, then back two days". The order is fixed so the text round-trips.
Offset
¶
A signed offset: sign (-1 lag, +1 lead) and a non-zero Duration.
Offset.zero() (duration zero) is the dataset's own instant.
parse_duration
¶
Read value into a Duration. None stays None.
Accepted: '3 months', '90 days', '1 year 2 months', '2 days 6
hours', ISO 8601 ('P1Y2M', 'PT12H'), (3, 'months'), a
datetime.timedelta (whole seconds) or a Duration.
parse_offset
¶
Read value into an Offset.
Accepted: '0' / 0 (the instant), '-1 day', '+1 month',
'-1 month 2 days', signed ISO 8601 ('-P1D', '+PT6H'), a signed
(n, unit) tuple, a datetime.timedelta (its sign) or an Offset.
A non-zero value without a sign is refused: lag or lead must be explicit.
resolve_offsets
¶
{FEATURE_NAME_UPPER: [Offset, ...]} from the user's feature_offsets.
Every key must be a selected feature (case-insensitive). A single value is
accepted for a list of one; duplicates are dropped, the order kept. None
or {} gives {} (no offset at all: the historical output).
offset_alias
¶
The column name of base read at offset (base itself at zero).
split_shift
¶
(base_text, months, seconds): expr = base moved by that signed amount.
Reads the shapes Offset.apply writes (ADD_MONTHS(x, n) innermost,
then (x ± INTERVAL 'd hh:mi:ss' DAY TO SECOND)); anything else is its own
base with no shift.
offset_from
¶
The Offset of a signed (months, seconds) shift; refused when the signs disagree.
base_alias
¶
The column name alias had before its offset suffix (itself at zero / None).
offset_columns
¶
The columns of spec that read a feature at an offset.
One dict per column: alias, base (the feature's catalog name when
base_names maps FEATURE_ID to it, else the alias without its suffix),
feature_id, version, table, offset.
renamed_columns
¶
Columns whose name is not their feature's catalog name: offsets and aliases.
Like :func:offset_columns, plus the columns without offset whose name
differs from the feature's name in base_names ({FEATURE_ID: name}; a
feature whose name is unknown is left out). views {PROCESS_ID: view}
adds view, the process computing that version.
describe_offset_column
¶
Fixed sentence documenting an offset or aliased column (no LLM).
offset None: a column named by an alias, read at the dataset's instant; view names the process computing that version.