ticoi 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ticoi might be problematic. Click here for more details.
- ticoi/__about__.py +1 -0
- ticoi/__init__.py +0 -0
- ticoi/core.py +1500 -0
- ticoi/cube_data_classxr.py +2204 -0
- ticoi/cube_writer.py +741 -0
- ticoi/example.py +81 -0
- ticoi/filtering_functions.py +676 -0
- ticoi/interpolation_functions.py +236 -0
- ticoi/inversion_functions.py +1015 -0
- ticoi/mjd2date.py +31 -0
- ticoi/optimize_coefficient_functions.py +264 -0
- ticoi/pixel_class.py +1830 -0
- ticoi/seasonality_functions.py +209 -0
- ticoi/utils.py +725 -0
- ticoi-0.0.1.dist-info/METADATA +152 -0
- ticoi-0.0.1.dist-info/RECORD +18 -0
- ticoi-0.0.1.dist-info/WHEEL +4 -0
- ticoi-0.0.1.dist-info/licenses/LICENSE +165 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import scipy.fft as fft
|
|
4
|
+
import scipy.signal as signal
|
|
5
|
+
from scipy.optimize import curve_fit
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def match_sine(
|
|
9
|
+
d: pd.DataFrame,
|
|
10
|
+
filt: str | None = None,
|
|
11
|
+
impose_frequency: bool = True,
|
|
12
|
+
several_freq: int | None = None,
|
|
13
|
+
raw_seasonality: bool = False,
|
|
14
|
+
d_raw: pd.DataFrame | None = None,
|
|
15
|
+
variable: str = "vv",
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Match a sine curve to TICOI results to look for a periodicity among the velocities. The period can either
|
|
19
|
+
be set to 365.25 days, or estimated along with the other parameters (amplitude, phase, offset).
|
|
20
|
+
|
|
21
|
+
:param d: [pd dataframe] --- pandas dataframe of the data at the considered pixel (TICOI results)
|
|
22
|
+
:param filt: which filter to use before processing the sinus ('highpass', 'lowpass' or None, default None)
|
|
23
|
+
:param impose_frequency: [bool] [default is True] --- Whether we should impose the frequency to 1/365.25 or not (default True)
|
|
24
|
+
:param several_freq: [int | None] [default is None] --- If > 1, a signal made of several frequencies (at n*f) is matched to the data
|
|
25
|
+
:param raw_seasonality: [bool] [default is False] --- If True, we also match a sinus to the raw data
|
|
26
|
+
:param d_raw: [pd dataframe | None] [default is None] --- If raw_seasonality is True, must be the dataframe of the raw velocity data (not displacements)
|
|
27
|
+
:param variable: [str] [default is 'vv'] --- variable used to fit the sinus (vx, vy or vv). vv correspond to the velocity magnitude
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
d = d.dropna()
|
|
31
|
+
dates = (d["date1"] + (d["date2"] - d["date1"]) // 2 - d["date1"].min()).dt.days.to_numpy()
|
|
32
|
+
|
|
33
|
+
N = len(dates)
|
|
34
|
+
if N <= 4: # do not compute anything
|
|
35
|
+
if raw_seasonality:
|
|
36
|
+
return np.nan, np.nan, np.nan, np.nan, np.nan
|
|
37
|
+
return np.nan, np.nan, np.nan
|
|
38
|
+
if variable == "vv":
|
|
39
|
+
vv = np.sqrt(d["vx"] ** 2 + d["vy"] ** 2).to_numpy()
|
|
40
|
+
elif variable == "direction":
|
|
41
|
+
vv = np.arctan2(d["vy"], d["vx"]).to_numpy()
|
|
42
|
+
else:
|
|
43
|
+
vv = d[variable]
|
|
44
|
+
|
|
45
|
+
Ts = dates[1] - dates[0]
|
|
46
|
+
|
|
47
|
+
# Filtering to remove inter-annual variations
|
|
48
|
+
if filt == "highpass":
|
|
49
|
+
b, a = signal.butter(4, [1 / (1.5 * 365), 1 / (2.001 * Ts)], "bandpass", fs=1 / Ts, output="ba")
|
|
50
|
+
vv_filt = signal.filtfilt(b, a, vv - np.mean(vv))
|
|
51
|
+
elif filt == "lowpass":
|
|
52
|
+
sos = signal.butter(4, 1 / (2.001 * Ts), "lowpass", fs=1 / Ts, output="sos")
|
|
53
|
+
vv_filt = signal.sosfilt(sos, vv - np.mean(vv))
|
|
54
|
+
else:
|
|
55
|
+
vv_filt = vv
|
|
56
|
+
|
|
57
|
+
# Frequency is set to 1/365.25 (one year)
|
|
58
|
+
if impose_frequency:
|
|
59
|
+
|
|
60
|
+
def sine_fconst(t, *args, freqs=1, f=1 / 365.25):
|
|
61
|
+
sine = args[0] * np.sin(
|
|
62
|
+
2 * np.pi * f * t + args[1]
|
|
63
|
+
) # args[0] amplitude of the signal, and args[1] the phase of the signal
|
|
64
|
+
for freq in range(1, freqs):
|
|
65
|
+
sine += args[2 * freq] * np.sin(2 * np.pi * (freq + 1) * f * t + args[2 * freq + 1])
|
|
66
|
+
return sine + args[-1]
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
# Find the best matching sinus to TICOI results
|
|
70
|
+
if several_freq is None:
|
|
71
|
+
several_freq = 1
|
|
72
|
+
guess = np.concatenate(
|
|
73
|
+
[np.concatenate([[np.max(vv_filt) - np.min(vv_filt), 0] for _ in range(several_freq)]), [0]]
|
|
74
|
+
)
|
|
75
|
+
popt, pcov = curve_fit(lambda t, *args: sine_fconst(t, *args, freqs=several_freq), dates, vv_filt, p0=guess)
|
|
76
|
+
|
|
77
|
+
sine_year = sine_fconst(np.linspace(1, 365, 365), *popt, freqs=several_freq)
|
|
78
|
+
A = np.max(sine_year) - popt[-1]
|
|
79
|
+
f = 1 / 365.25
|
|
80
|
+
first_max_day = pd.Timedelta(np.argmax(sine_year), "D") + d["date1"].min() # date of the maximum
|
|
81
|
+
max_day = (first_max_day - pd.Timestamp(year=first_max_day.year, month=1, day=1)).days # day of the year
|
|
82
|
+
del sine_year
|
|
83
|
+
|
|
84
|
+
if raw_seasonality:
|
|
85
|
+
# Find the best matching sinus to raw data
|
|
86
|
+
dates_raw = (d_raw.index - d["date1"].min()).days.to_numpy()
|
|
87
|
+
raw_c = d_raw["vv"] - d_raw["vv"].mean()
|
|
88
|
+
guess_raw = np.concatenate(
|
|
89
|
+
[np.concatenate([[np.max(raw_c) - np.min(raw_c), 0] for _ in range(several_freq)]), [0]]
|
|
90
|
+
)
|
|
91
|
+
popt_raw, pcov_raw = curve_fit(
|
|
92
|
+
lambda t, *args: sine_fconst(t, *args, freqs=several_freq), dates_raw, raw_c, p0=guess_raw
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
sine_raw_year = sine_fconst(np.linspace(1, 365, 365), *popt_raw, freqs=several_freq)
|
|
96
|
+
A_raw = np.max(sine_raw_year) - popt_raw[-1]
|
|
97
|
+
first_max_day_raw = pd.Timedelta(np.argmax(sine_raw_year), "D") + d["date1"].min()
|
|
98
|
+
max_day_raw = (first_max_day_raw - pd.Timestamp(year=first_max_day.year, month=1, day=1)).days
|
|
99
|
+
del sine_raw_year
|
|
100
|
+
|
|
101
|
+
except RuntimeError:
|
|
102
|
+
A, f, max_day = np.nan, np.nan, np.nan
|
|
103
|
+
if raw_seasonality:
|
|
104
|
+
A_raw, max_day_raw = np.nan, np.nan
|
|
105
|
+
|
|
106
|
+
# Frequency is to be found too
|
|
107
|
+
else: # use fft, with an hanning window
|
|
108
|
+
n = 64 * N
|
|
109
|
+
window = signal.windows.hann(N)
|
|
110
|
+
vv_win_tf = fft.rfft(vv_filt * window, n=n)
|
|
111
|
+
freq = fft.rfftfreq(n, d=Ts)
|
|
112
|
+
|
|
113
|
+
# Match a sinus to the data
|
|
114
|
+
def sine_fvar(t, A, f, phi, off):
|
|
115
|
+
return A * np.sin(2 * np.pi * f * t + phi) + off
|
|
116
|
+
|
|
117
|
+
# Initial guess of the best matching sinus parameters
|
|
118
|
+
guess = np.array(
|
|
119
|
+
[
|
|
120
|
+
np.max(2 / N * np.abs(vv_win_tf)),
|
|
121
|
+
freq[np.argmax(np.abs(vv_win_tf))],
|
|
122
|
+
np.angle(vv_win_tf)[np.argmax(np.abs(vv_win_tf))],
|
|
123
|
+
np.mean(vv),
|
|
124
|
+
],
|
|
125
|
+
dtype="float",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
popt, pcov = curve_fit(sine_fvar, dates, vv, p0=guess)
|
|
130
|
+
|
|
131
|
+
sine_year = sine_fvar(np.linspace(1, 365, 365), *popt, freqs=several_freq)
|
|
132
|
+
A = np.max(sine_year) - popt[-1]
|
|
133
|
+
first_max_day = pd.Timedelta(np.argmax(sine_year), "D") + d["date1"].min()
|
|
134
|
+
max_day = (first_max_day - pd.Timestamp(year=first_max_day.year, month=1, day=1)).days
|
|
135
|
+
del sine_year
|
|
136
|
+
|
|
137
|
+
except RuntimeError:
|
|
138
|
+
A, f, max_day = np.nan, np.nan, np.nan
|
|
139
|
+
|
|
140
|
+
# Return Period, amplitude and phase of the periodicity
|
|
141
|
+
if impose_frequency and raw_seasonality:
|
|
142
|
+
return 1 / f, A, max_day, A_raw, max_day_raw
|
|
143
|
+
else:
|
|
144
|
+
return 1 / f, A, max_day
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def rolling_std(raw, dataf_lp, local_var_method="uniform_7d"):
|
|
148
|
+
"""
|
|
149
|
+
Compute Amplitude to local VARiations index, which compares the amplitude of the best matching sinus to the standard
|
|
150
|
+
deviation of the noise using one of the four given methods.
|
|
151
|
+
|
|
152
|
+
:param A: float, amplitude of the best matchning sinus
|
|
153
|
+
:param raw: list, raw data
|
|
154
|
+
:param dataf_lp: list of pandas dataframes, TICOI results
|
|
155
|
+
:param local_var_method: str, method to be used to process the local variations
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
# Compute local variations
|
|
159
|
+
if local_var_method == "rolling_7d":
|
|
160
|
+
var = raw["vv"].rolling(window="7D", center=True).std(ddof=0).drop_duplicates().dropna().median().item()
|
|
161
|
+
|
|
162
|
+
elif local_var_method.split("_")[0] == "uniform":
|
|
163
|
+
period_between_dates = (
|
|
164
|
+
np.diff(np.sort(np.concatenate([raw["date1"], raw["date2"]]))).astype("timedelta64[D]").astype("int")
|
|
165
|
+
)
|
|
166
|
+
min_period = np.min(period_between_dates[period_between_dates > 0])
|
|
167
|
+
var_dates = pd.date_range(start=raw["date1"].min(), end=raw["date2"].max(), freq=f"{min_period}D")
|
|
168
|
+
local_var = pd.Series(index=var_dates)
|
|
169
|
+
|
|
170
|
+
if local_var_method == "uniform_7d":
|
|
171
|
+
for date in var_dates:
|
|
172
|
+
local_var[date] = raw.loc[
|
|
173
|
+
(raw.index > date - pd.Timedelta("3D")) & (raw.index < date + pd.Timedelta("3D")), "vv"
|
|
174
|
+
].std(ddof=0)
|
|
175
|
+
|
|
176
|
+
elif local_var_method == "uniform_all":
|
|
177
|
+
for date in var_dates:
|
|
178
|
+
local_var[date] = raw.loc[(raw["date1"] < date) & (raw["date2"] > date), "vv"].std(ddof=0)
|
|
179
|
+
|
|
180
|
+
var = local_var[local_var > 0].dropna().median()
|
|
181
|
+
|
|
182
|
+
elif local_var_method == "residu":
|
|
183
|
+
dataf_lp.index = dataf_lp["First_date"] + (dataf_lp["Second_date"] - dataf_lp["First_date"]) // 2
|
|
184
|
+
dataf_lp["vv"] = np.sqrt(dataf_lp["vx"] ** 2 + dataf_lp["vy"] ** 2)
|
|
185
|
+
dataf_lp = dataf_lp.reindex(index=np.unique(raw.index)).interpolate().dropna()
|
|
186
|
+
dataf = raw[raw.index >= dataf_lp.index[0]]
|
|
187
|
+
dataff_vv_c = dataf["vv"] - dataf_lp["vv"]
|
|
188
|
+
var = dataff_vv_c.std(ddof=0)
|
|
189
|
+
|
|
190
|
+
return var
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def AtoVar(A, raw, dataf_lp, local_var_method="uniform_7d"):
|
|
194
|
+
"""
|
|
195
|
+
Compute Amplitude to local VARiations index, which compares the amplitude of the best matching sinus to the standard
|
|
196
|
+
deviation of the noise using one of the four given methods.
|
|
197
|
+
|
|
198
|
+
:param A: float, amplitude of the best matchning sinus
|
|
199
|
+
:param raw: list, raw data
|
|
200
|
+
:param dataf_lp: list of pandas dataframes, TICOI results
|
|
201
|
+
:param local_var_method: str, method to be used to process the local variations
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
if A == np.nan:
|
|
205
|
+
return np.nan
|
|
206
|
+
|
|
207
|
+
var = rolling_std(raw, dataf_lp, local_var_method)
|
|
208
|
+
|
|
209
|
+
return max(0, 1 - var / abs(A))
|