-
Notifications
You must be signed in to change notification settings - Fork 0
/
shoreline_timeseries_sandbox_synthetic_1d_generation.py
214 lines (195 loc) · 6.94 KB
/
shoreline_timeseries_sandbox_synthetic_1d_generation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
"""
Mark Lundine
This is an experimental script to investigate how much information can be pulled
from satellite shoreline timeseries.
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime
import random
import pandas as pd
import shoreline_timeseries_analysis_single as stsa
import os
def time_array_to_years(datetimes):
"""
Converts array of datetimes to years since earliest datetime
inputs:
datetimes (array): array of datetimes
returns:
datetimes_years (array): the time array but in years from the earliest datetime
"""
initial_time = datetimes[0]
datetimes_seconds = [None]*len(datetimes)
for i in range(len(datetimes)):
t = datetimes[i]
dt = t-initial_time
dt_sec = dt.total_seconds()
datetimes_seconds[i] = dt_sec
datetimes_seconds = np.array(datetimes_seconds)
datetimes_years = datetimes_seconds/(60*60*24*365)
return datetimes_years
def linear_trend(t, trend_val):
"""
Applies linear trend to trace
y = mx+b
inputs:
t (array or float): array of time (in years) or just a single float of time in years
trend_val (float): trend value in m/year
returns:
y (float or array): the results of this linear trend function (in m)
"""
y = trend_val*t
return y
def sine_pattern(t, A, period_years):
"""
Applies seasonal pattern of random magnitude to trace
y = A*sin((2pi/L)*x)
inputs:
t (array or float): array of time (in years) or just a single float of time in years
A (float): amplitude of sine wave (in m)
period_years (float): period of sine wave in years
returns:
y (array or float): result of this sine function (in m)
"""
y = A*np.sin(((2*np.pi*t)/period_years))
return y
def enso_pattern(t, amplitudes, periods):
"""
Enso-esque pattern mean of a bunch of sine waves with periods between 3 and 7 years
Lenght of amplitudes should be equal to length of periods
inputs:
t (float): single time in years or array of times in years
amplitudes (array): array of amplitudes (in m), ex: np.random.uniform(low,high,size=100)
periods (array): array of periods in years, ex: np.arange(3, 7, 100)
outputs:
mean_y (float): mean of the distribution of sine waves (in m)
"""
y = [None]*len(amplitudes)
for i in range(len(y)):
y[i] = amplitudes[i]*np.sin(2*np.pi*t/periods[i])
y = np.array(y)
mean_y = np.mean(y)
return mean_y
def noise(y, noise_val):
"""
Applies random noise to trace
inputs:
y (float): shoreline_position at a specific time (in m)
noise_val (float): amount of noise to add (in m)
outputs:
y (float): shoreline position with noise added (in m)
"""
##Let's make the noise between -20 and 20 m to sort of simulate the uncertainty of satellite shorelines
noise = np.random.normal(-1*noise_val,noise_val,1)
y = y+noise
return y
def apply_NANs(y,
nan_idxes):
"""
Randomly throws NANs into a shoreline trace
inputs:
y (array): the shoreline positions over time
nan_idxes (array): the indices to thrown nans in
outputs:
y (array): the shoreline positions with nans thrown in at specified indices
"""
y[nan_idxes] = np.nan
return y
def make_matrix(dt):
"""
Just hardcoding a start and end datetime with a timestep of dt days
Start = Jan 1st 1984
End = Jan 1st 2024
Make a matrix to hold cross-shore position values
inputs:
dt (int): time spacing in days
outputs:
shoreline_matrix (array): an array of zeroes with length of the datetimes
datetimes (array): the datetimes
"""
datetimes = np.arange(datetime.datetime(1984,1,1),
datetime.datetime(2024,1,1),
datetime.timedelta(days=int(dt))
).astype(datetime.datetime)
num_transects = len(datetimes)
shoreline_matrix = np.zeros((len(datetimes)))
return shoreline_matrix, datetimes
def make_data(noise_val,
trend_val,
yearly_amplitude,
dt,
t_gap_frac,
save_name):
"""
Makes synthetic shoreline data, will save a csv and png plot of data
Csv will have columns 'date' and 'position',
corresponding to the timestamp and cross-shore position, respectively
y(t) = trend(t) + yearly_pattern(t) + noise(t) + nan(t)
inputs:
noise_val (float): amount of noise to add to each position in m
trend_val (float): linear trend value in m/year
yearly_amplitude (float): amplitude for sine wave with period of 1 year in m
dt (int): time spacing in days
t_gap_frac (float): fraction of timesteps to throw NaNs in
save_name (str): name to give this
outputs:
save_name (str): the save name
"""
##Initialize stuff
#random.seed(0) #uncomment if you want to keep the randomness the same and play with hard-coded values
matrix, datetimes = make_matrix(dt)
num_timesteps = matrix.shape[0]
t = time_array_to_years(datetimes)
##randomly selecting a percent of the time periods to throw gaps in
max_nans = int(t_gap_frac*len(t))
num_nans = random.randint(0, max_nans)
nan_idxes = random.sample(range(len(t)), num_nans)
##Building matrix
for i in range(num_timesteps):
##Linear trend + yearly cycle + noise
matrix[i] = sum([linear_trend(t[i], trend_val),
sine_pattern(t[i], yearly_amplitude, 1),
noise(matrix[i], noise_val)
]
)
matrix = apply_NANs(matrix, nan_idxes)
df = pd.DataFrame({'date':datetimes,
'position':matrix})
df.to_csv(save_name+'.csv', index=False)
plt.rcParams["figure.figsize"] = (16,4)
plt.title(r'Trend value = ' +str(np.round(trend_val,2)) +
r'm/year Yearly Amplitude = ' +
str(np.round(yearly_amplitude,2)) + r'm dt = ' +
str(dt) + r' days Missing timestamps = ' +
str(t_gap_frac*100)+'%'+
' Noise amount = ' + str(noise_val) + 'm')
plt.plot(df['date'], df['position'], '--o', color='k', markersize=1, linewidth=1)
plt.xlim(min(df['date']), max(df['date']))
plt.ylim(min(df['position']), max(df['position']))
plt.xlabel('Time (UTC)')
plt.ylabel('Cross-Shore Position (m)')
plt.minorticks_on()
plt.tight_layout()
plt.savefig(save_name+'.png', dpi=300)
return save_name
##Example call, setting the random seed
random.seed(0)
##Noise value in meters
noise_val = 20
##Trend value in m/year
trend_val = random.uniform(-20, 20)
##Amplitude for yearly pattern in m
yearly_amplitude = random.uniform(0, 20)
##Revisit time in days
dt = 12
##Fraction of missing time periods
t_gap_frac = 0.15
##give it a save_name
save_name = 'test1'
make_data(noise_val,
trend_val,
yearly_amplitude,
dt,
t_gap_frac,
save_name)