Time-series analysis
egms_client.analysis ¶
What the time series say beyond their mean velocity. Needs the tables extra.
Every EGMS CSV comes with one model fitted to each series over the whole
release: mean_velocity, acceleration, seasonality (the annual
amplitude) and rmse_ts. That model is::
displacement(t) = a + v t + acceleration / 2 * t**2 + A cos(2 pi t) + B sin(2 pi t)
with t in years from the middle of the window. :func:fit is that same
model, and over the full window it gives back the archive's columns (the
tests check it on real CSVs). What it adds:
- any window:
fit(frame, start=date(2024, 11, 1))is the velocity of the last year, where the archive only gives five years at once; - the seasonal phase: the day of the year the seasonal cycle peaks.
Two things the archive's columns cannot show:
- :func:
trend_change: a velocity that changes at one date, as on a slope that starts to creep. One acceleration over five years averages it away. - :func:
unwrapping_jumps: steps of half the radar wavelength (about 27.7 mm along the line of sight) between two acquisitions. They come from the processing (phase unwrapping), not from the ground. Only meaningful on L2A / L2B, whose series are along the line of sight.
Every function takes a table of :func:egms_client.tables.read_csv (one
row per point, one column per date) and returns one row per point, in the
same order, with its pid. Points with a missing value in the window get
NaN. Points are processed in blocks, so a whole L3 tile (~170,000 cells)
fits in memory.
Least squares treats the residuals as independent, which InSAR residuals are not: read the statistics as rankings, not as exact probabilities.
fit ¶
fit(frame: Any, *, start: date | None = None, end: date | None = None) -> Any
The archive's model fitted to each series, over start to end (inclusive).
Columns: pid, velocity (mm/year), acceleration (mm/year²),
seasonal_amplitude (mm), seasonal_peak_doy (the day of the year
the seasonal cycle peaks, 1-366), rmse (mm), dates (how many
were used). Over the full window, velocity, acceleration,
seasonal_amplitude and rmse are the archive's mean_velocity,
acceleration, seasonality and rmse_ts.
The acceleration needs two years or more to mean something, and the
season needs one full year: shorter windows raise ValueError.
trend_change ¶
trend_change(frame: Any, *, min_segment_days: int = 365, step_days: int = 30, start: date | None = None, end: date | None = None) -> Any
The date where each series changes velocity, if it does.
A continuous two-slope line (with the seasonal cycle) is fitted for
every candidate date, one every step_days, at least
min_segment_days from either end. The best one is kept.
Columns: pid, breakpoint (the date), velocity_before and
velocity_after (mm/year), change (after minus before),
delta_bic (how much better two slopes fit than one; above 10 is
strong evidence, and below 0 one slope is enough), rmse (mm).
unwrapping_jumps ¶
unwrapping_jumps(frame: Any, *, tolerance_mm: float = 5.0, window: int = 5, start: date | None = None, end: date | None = None) -> Any
Steps of a multiple of half a wavelength between two acquisitions.
A step counts when the difference between two consecutive dates is
within tolerance_mm of k times 27.7 mm (k ≥ 1), and the series stays
there: the median of the window dates after it, minus the median of
the window dates before, is the same step. A single outlier, which
goes up and comes back, is not a jump. Meant for L2A / L2B.
Columns: pid, jumps (how many), first_jump (its date, or
missing), total_step_mm (their sum, signed).