-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwindstorms.py
195 lines (166 loc) · 6.43 KB
/
windstorms.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
'''
Creates data classes for windstorm events, lull events (breaks in meeting threshold wind speed), and windstorm events with lulls.
'''
import copy
from dataclasses import dataclass
import pandas as pd
SPEED_COL10 = 'Avg Wind Speed @ 10m [m/s]'
DATE_COL = 'Date'
# DATE_COL = 'DOY'
TIME_COL = 'MST'
DIR_COL10 = 'Avg Wind Direction @ 10m [deg]'
class Windstorm:
'''Can create windstorm objects using start and stop indices. Properties are speed and duration'''
def __init__(self,
df: pd.DataFrame,
start_index: int,
stop_index: int,
speed_col_name=SPEED_COL10,
direction_col_name=DIR_COL10):
self._df = df
self.start_index = start_index
self.stop_index = stop_index
self.speed_col_name = speed_col_name
self.direction_col_name = direction_col_name
@property
def data(self):
return self._df[self.start_index: self.stop_index]
@property
def speed(self):
return self.data[self.speed_col_name]
@property
def direction(self):
return self.data[self.direction_col_name]
@property
def time(self):
return self.data[TIME_COL]
@property
def duration(self):
return self.stop_index - self.start_index
def merge(self, storm2):
self.start_index = min(self.start_index, storm2.start_index)
self.stop_index = max(self.stop_index, storm2.stop_index)
return self
def __repr__(self):
return ''.join([
f'Storm start:\t{self._df[DATE_COL][self.start_index]}',
f' {self._df[TIME_COL][self.start_index]}',
f'\tend:\t{self._df[DATE_COL][self.stop_index]}',
f' {self._df[TIME_COL][self.stop_index]}',
f'\t\tduration: {self.duration} minutes\t'
])
def __str__(self):
return self.__repr__() + '\n' + str(self.speed)
@staticmethod
def storms_by_threshold(df, threshold: float, direction_thresholds: list, speed_col_name=SPEED_COL10, direction_col_name=DIR_COL10):
above = False
speeds = df[speed_col_name]
directions = df[direction_col_name]
for index, (speed, direction) in enumerate(zip(speeds, directions)):
if not above and (speed >= threshold) and (direction_thresholds[0] <= direction <= direction_thresholds[1]):
above = True
start = index
if above and (speed < threshold or (direction < direction_thresholds[0]) or (direction > direction_thresholds[1])):
above = False
yield Windstorm(df, start, index,
speed_col_name=speed_col_name,
direction_col_name=direction_col_name)
if above:
yield Windstorm(df, start, index, speed_col_name=speed_col_name, direction_col_name=direction_col_name)
@dataclass
class Lull:
start: int
stop: int
df: pd.DataFrame
@property
def duration(self):
return self.stop - self.start
def __repr__(self):
return f'Lull(start={self.start}, stop={self.stop})'
class WindstormWithLulls(Windstorm):
'''Can create windstorm objects using start and stop indices. Properties are speed and duration'''
def __init__(self,
df,
start_index,
stop_index,
lulls=[],
speed_col_name=SPEED_COL10,
direction_col_name=DIR_COL10):
self._df = df
self.start_index = start_index
self.stop_index = stop_index
self.speed_col_name = speed_col_name
self.direction_col_name = direction_col_name
self.lulls = lulls
def __repr__(self):
return super().__repr__() + f'with {len(self.lulls)} lulls'
def merge(self, storm2: Windstorm):
if (
min(self.stop_index, storm2.stop_index) <
max(self.start_index, storm2.start_index)
):
# we have a lull between them
self.lulls.append(
Lull(min(self.stop_index, storm2.stop_index),
max(self.start_index, storm2.start_index),
self._df))
self.lulls += storm2.lulls
super().merge(storm2)
return self
@staticmethod
def from_windstorm(storm: Windstorm):
return WindstormWithLulls(
storm._df,
storm.start_index,
storm.stop_index,
[],
speed_col_name=storm.speed_col_name,
direction_col_name=storm.direction_col_name)
@staticmethod
def storms_by_threshold(
df,
speed_threshold: float,
lull_threshold: float,
direction_thresholds: list,
speed_col_name=SPEED_COL10,
direction_col_name=DIR_COL10):
storms = (WindstormWithLulls.from_windstorm(storm)
for storm in
Windstorm.storms_by_threshold(
df,
speed_threshold,
direction_thresholds,
speed_col_name,
direction_col_name))
current_storm = next(storms)
returned = False
for next_storm in storms:
if next_storm.start_index - current_storm.stop_index < lull_threshold:
# combine the two
current_storm.merge(next_storm)
returned = False
else:
yield current_storm
returned = True
current_storm = next_storm
if not returned:
yield current_storm
def __add__(self, storm2):
return copy.copy(self).merge(storm2)
def __radd__(self, num):
if num == 0:
return self
else:
raise ValueError(f'Cannot add {self.__repr__()} and {num.__repr__()}')
if __name__ == '__main__':
df = pd.read_csv('2022April_data.txt')
storm = Windstorm(df, 0, 150)
storm2 = WindstormWithLulls(df, 100, 200, lulls=[(120, 130), (140, 160)])
storm3 = WindstormWithLulls(df, 250, 280, lulls=[(252, 255)])
storms = list(Windstorm.storms_by_threshold(df, 10))
storms_with_lulls = [storm
for storm in
WindstormWithLulls.storms_by_threshold(df, 10, 720)
if storm.duration >= 60]
for storm in storms_with_lulls:
print(storm)