TimeFReader¶
TimeFReader converts a committed TimeF version into an in-memory TimeFDataset.
It is the inverse of TimeFWriter. It uses only manifest.json and never runs
connector code. It reads through a DatasetVersion handle, which holds the manifest
and a view of the version's files on the file system. As a result, a read never opens the registry
again or parses the manifest again. TimeFReader is in the timenet.reader module.
from timenet.reader import TimeFReader
from timenet.registry import DatasetVersion, open_registry
# From a registry (reads in place, no download):
version = open_registry("~/timenet/registry").open_version("timenet/hello-world")
# ...or from a version directory already on disk:
version = DatasetVersion.open_local(version_dir)
with TimeFReader(version) as reader:
dataset = reader.read()
values = dataset.records[0].time_series[0].to_arrow()
You can use TimeFReader as a context manager. Its close() method, called by __exit__, releases
the open handles and decoded-chunk caches of the selected values backend.
What is eager vs lazy¶
__init__ reads nothing. The handle already has the parsed manifest. Every table resolves on first
use.
TimeFReader reads the time-series index and the annotations table one pruned row group at a time.
It decodes only the row groups that a lookup's key statistics cannot rule out. Tasks decode on first
access to .tasks. Per-series values and Record construction stay lazy. read() and
iter_records() build records with loader closures. When the code calls to_arrow(), to_numpy(),
or read_steps(), these closures pull data from storage.
iter_records(record_ids=...) filters on the stored id column. It streams records one at a time and
does not build a TimeFDataset.
TimeFReader keeps the index as Arrow data and searches it per lookup. It does not expand the index
into one Python object per row. As a result, when a large dataset opens, the memory it uses stays
proportional to the size of the index file. It does not grow to a multiple of that size. Each index
row uses about 180 bytes of memory. A row is one (record, series, chunk) tuple.
Type reconstruction¶
TimeFReader reads specs, data sources, and annotation metadata directly from the manifest's flat
descriptors. It does not create any classes at runtime. TimeSeries.spec is the TimeSeriesSpec
descriptor for its spec_type. TimeFReader rebuilds annotations as real Annotation instances. It
decodes the values from JSON and rebuilds the span as a TimePoint, TimeInterval, StepPoint, or
StepInterval. It resolves tasks against the built-in TASKS registry and links from_tasks.
Everything pickles and compares equal to the original data, field by field. This equality is why
multiprocessing DataLoader workers are safe.
Value reads¶
Each series' loader resolves its index rows, sorted by chunk_idx. The loader dispatches through the
manifest's values_backend. For Parquet, chunk_file, chunk_major_idx, and chunk_minor_idx
identify the shard, row group, and row offset. For Zarr, they identify the array path and the start
offset in time.
Scalars use a primitive Arrow array. N-D values use pa.FixedShapeTensorArray, whose length is the
number of timesteps. to_numpy() is the explicit conversion to the array framework and keeps the
spec's dtype and trailing shape. Range-aware reads select only the requested chunks in time.
TimeFReader caches Parquet handles and decoded Zarr chunks for the life of the reader. When the code
calls close(), it releases them.
API¶
| Member | Description |
|---|---|
read() |
Materialize the full TimeFDataset. |
iter_records() |
Yield each Record lazily. |
verify() |
Hash every manifest-listed artifact and reject missing or mismatched content. |
metadata / schema / tasks / values_backend |
Reconstructed metadata, schema, tasks, and selected values backend. |
Errors¶
DatasetVersion.open_local and a registry's open_version build the handle. If the version directory
has no manifest.json, they raise FileNotFoundError. If the manifest is malformed or is an
unsupported version, they raise TimeFFormatError (an TimeNetInvalidManifestError).
Opening the reader reads nothing else. As a result, the reader does not catch a missing or corrupt
file at open time. The error surfaces on the first access that needs the file. Tasks raise the error
on first access to .tasks. Records raise it on iteration. The index and annotations raise it on the
first read that needs them.
A corrupt control-plane table raises TimeFFormatError with its context. You can call verify() for
an integrity check at construction time. It reopens every listed file through the handle and raises
TimeFFormatError on a missing or mismatched file.
Round-trip guarantee¶
For a dataset that passes writer validation, TimeFReader(...).read() restores every record's
record_id, subject_ids, task_ids, and annotations. It also restores each series' spec,
signal, source_id, time_series_id, window, and values, with the exact dtype and shape
preserved. It restores each task's payload and resolved from_tasks. TimeSeries object identity is
not preserved. time_series_id is the durable handle.
The API reference for timenet.reader has the full symbol listing.