Indexing¶
tsecon.lookup(t, keys) is the vectorised "gather a list of MITs (or integer offsets)
from a TSeries" entry point. Routes through a Cython gather kernel when the compiled
extension is importable; introspect with lookup_is_cython(). See
design/cython_strategy.md for why the kernel speedup
is small here (~1.1× over NumPy) — np.take already runs in C.
tsecon.indexing ¶
Vectorised lookup over a TSeries — the array-of-keys companion to t[k].
TimeSeriesEcon.jl users get fast per-element access for free: Julia's
for k in keys; s += t[k]; end compiles to inlined getindex calls
and a tight numerical loop, so the per-element pattern is already
competitive. The Python equivalent — for k in keys: s += t[k] —
pays per-iteration __getitem__ dispatch, MIT object allocation,
and numpy-scalar boxing; the session-16 benchmark clocked this at
935x slower than Julia for 100 MIT lookups.
This module provides a public vectorised lookup ::
>>> from tsecon import TSeries, qq, lookup
>>> import numpy as np
>>> t = TSeries(qq(2020, 1), np.arange(100.0))
>>> keys = [qq(2020, 1) + i for i in range(100)]
>>> values = lookup(t, keys)
>>> values.shape
(100,)
so the user-facing pattern becomes lookup(t, keys) (returns an
ndarray of values at the requested positions) instead of a per-element
Python loop. The vectorised path runs entirely in NumPy / Cython C
code; the 935x gap is an API-shape gap, not a Python-speed gap.
Dispatch
:func:lookup validates the keys (frequency-checks MITs, bounds-checks
integer offsets), translates everything to int64 offsets into the
TSeries' underlying float64 buffer, then dispatches to the
:mod:~tsecon._indexing_kernels_cy Cython kernel when importable or
:mod:~tsecon._indexing_kernels's NumPy reference otherwise. Both
kernels share the contract gather(values, indices) -> out and
return identical bit-for-bit output (see the kernels' module
docstrings).
The two kernels' relative speed differs from the rec_linear story:
the gather is vectorisable, so the NumPy reference is already a tight
C loop via :func:numpy.take, and the Cython kernel only buys a
marginal further win (~1.5-3x, mostly from removing NumPy's per-call
dispatch overhead). The big win here is exposing the vectorised API at
all — that's where the 935x → ~1x recovery happens.
lookup_is_cython ¶
Return True iff the Cython-compiled gather kernel was importable.
Useful for tests, benchmarks, and diagnostic prints — the public
:func:lookup itself is implementation-agnostic. When this returns
False the same calls go through the pure-NumPy kernel in
_indexing_kernels.py; behaviour is identical, only speed
differs.
Source code in src/tsecon/indexing.py
lookup ¶
Return t's values at every position in keys, vectorised.
The array-of-keys companion to t[k]: while t[k] reads a
single MIT- or int-keyed position, :func:lookup reads many at
once with a single NumPy/Cython gather, avoiding the per-element
__getitem__ dispatch tax that makes the Python loop pattern
935x slower than the Julia equivalent on the M1.5 benchmark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
TSeries
|
The series to gather values from. Not mutated. |
required |
keys
|
sequence of MIT, sequence of int, or 1-D array-like
|
The positions to read. MIT keys are frequency-checked against
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Freshly allocated 1-D array of the same length as |
Raises:
| Type | Description |
|---|---|
TypeError
|
If any MIT key has a frequency different from |
IndexError
|
If any translated offset falls outside |
Examples:
Pick four MIT positions::
>>> from tsecon import TSeries, qq, lookup
>>> import numpy as np
>>> t = TSeries(qq(2020, 1), np.arange(100.0))
>>> lookup(t, [qq(2020, 1), qq(2020, 3), qq(2022, 1), qq(2024, 4)])
array([ 0., 2., 8., 19.])
Or pick four integer-offset positions (positional indexing,
matching t[i])::
>>> lookup(t, [0, 5, 10, 99])
array([ 0., 5., 10., 99.])
Notes
For a single key, prefer the direct indexer t[k] — it returns
a scalar without an array allocation and dispatches through the
same code path. :func:lookup is for the bulk case (e.g. 10+
keys) where vectorisation amortises the per-call setup cost.
See Also
lookup_is_cython : Check whether the compiled kernel is active.
Source code in src/tsecon/indexing.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | |