Source code for perda.utils.resampling
import numpy as np
from numpy.typing import NDArray
from ..core_data_structures.data_instance import DataInstance
from ..core_data_structures.resampling_helpers import ResampleMethod, _interpolate
from ..units import Timescale, _to_seconds
[docs]
def apply_time_offset(
di: DataInstance | list[DataInstance],
offset_s: float,
source_time_unit: Timescale = Timescale.MS,
) -> DataInstance | list[DataInstance]:
"""Shift signals in time by a fixed offset, but keeping the same timestamp list.
Shifts the values, not the timestamps. Create a new time series with the same values offset_s away,
then re-interpolate the original timestamps onto this series.
Parameters
----------
di : DataInstance | list[DataInstance]
Input signal(s). Lists are processed independently.
offset_s : float
Time shift in seconds (positive shifts the signal later).
source_time_unit : Timescale, optional
Timestamp unit of ``di.timestamp_np``. Default is ``Timescale.MS``.
Returns
-------
DataInstance | list[DataInstance]
New DataInstance(s) with time-shifted ``value_np`` on the original timestamp grid.
Examples
--------
>>> di_aligned = apply_time_offset(gps_speed_di, offset_s=-0.08)
>>> a, b = apply_time_offset([di_a, di_b], offset_s=0.05)
"""
di_list = [di] if isinstance(di, DataInstance) else di
results: list[DataInstance] = []
for instance in di_list:
if offset_s == 0.0:
results.append(instance)
continue
t_s: NDArray = _to_seconds(
instance.timestamp_np.astype(np.float64), source_time_unit
)
signal = instance.value_np.astype(np.float64)
valid = ~np.isnan(signal)
if valid.sum() < 2:
print("Too few valid points to interpolate, skipping time offset")
results.append(instance)
continue
src_t: NDArray = t_s[valid] + offset_s
src_v: NDArray = signal[valid]
shifted = np.interp(t_s, src_t, src_v, left=src_v[0], right=src_v[-1])
results.append(
DataInstance(
timestamp_np=instance.timestamp_np,
value_np=shifted,
label=instance.label or f"var_id={instance.var_id}",
var_id=instance.var_id,
cpp_name=instance.cpp_name,
)
)
return results[0] if len(results) == 1 else results
[docs]
def resample_to_freq(
di: DataInstance,
freq_hz: float,
timestamp_divisor: float,
method: ResampleMethod = ResampleMethod.LINEAR,
) -> DataInstance:
"""
Resample a DataInstance onto a uniform frequency grid.
Parameters
----------
di : DataInstance
Source DataInstance
freq_hz : float
Target sampling frequency in Hz
timestamp_divisor : float
Raw timestamp units per second (e.g. 1e6 for microseconds)
method : ResampleMethod, optional
Interpolation method. Default is LINEAR.
Returns
-------
DataInstance
New DataInstance with values resampled onto a uniform timestamp grid
"""
dt = timestamp_divisor / freq_hz
target_ts = np.arange(
di.timestamp_np[0], di.timestamp_np[-1], dt, dtype=np.float64
).astype(np.int64)
resampled_val = _interpolate(
target_ts.astype(np.float64),
di.timestamp_np.astype(np.float64),
di.value_np,
method,
)
return DataInstance(
timestamp_np=target_ts,
value_np=resampled_val,
label=di.label,
var_id=di.var_id,
cpp_name=di.cpp_name,
)