Skip to content

Commit d66bbd1

Browse files
committed
update version and extend data ingestion functions to accept new format
1 parent 8cabf12 commit d66bbd1

2 files changed

Lines changed: 158 additions & 54 deletions

File tree

resonate/filters.py

Lines changed: 157 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,42 @@ def get_distance_matrix(detections: pd.DataFrame):
1616
Returns:
1717
pd.DataFrame: A Pandas DataFrame matrix of station to station distances
1818
"""
19-
stn_grouped = detections.groupby('station', dropna=False)
20-
stn_locs = stn_grouped[['longitude', 'latitude']].mean()
19+
# what we expect the input data format to be
20+
data_format_guess = 'otn_old'
21+
22+
if 'catalogNumber' in detections.columns: # the new version of otn det extracts uses camel case
23+
data_format_guess = 'otn_2025'
24+
25+
26+
# set the stations locations to their deployments' mean lat and lon
27+
28+
if data_format_guess == 'otn_old':
29+
stn_grouped = detections.groupby('station', dropna=False)
30+
stn_locs = stn_grouped[['longitude', 'latitude']].mean()
31+
elif: data_format_guess == 'otn_2025':
32+
stn_grouped = detections.groupby('station', dropna=False)
33+
stn_locs = stn_grouped[['decimalLongitude', 'decimalLatitude']].mean()
2134

2235
dist_mtx = pd.DataFrame(
2336
np.zeros(len(stn_locs) ** 2).reshape(len(stn_locs), len(stn_locs)),
2437
index=stn_locs.index, columns=stn_locs.index)
2538

26-
for cstation in dist_mtx.columns:
27-
for rstation in dist_mtx.index:
28-
cpoint = (stn_locs.loc[cstation, 'latitude'],
29-
stn_locs.loc[cstation, 'longitude'])
30-
rpoint = (stn_locs.loc[rstation, 'latitude'],
31-
stn_locs.loc[rstation, 'longitude'])
32-
dist_mtx.loc[rstation, cstation] = geodesic(cpoint, rpoint).m
39+
if data_format_guess == 'otn_old':
40+
for cstation in dist_mtx.columns:
41+
for rstation in dist_mtx.index:
42+
cpoint = (stn_locs.loc[cstation, 'latitude'],
43+
stn_locs.loc[cstation, 'longitude'])
44+
rpoint = (stn_locs.loc[rstation, 'latitude'],
45+
stn_locs.loc[rstation, 'longitude'])
46+
dist_mtx.loc[rstation, cstation] = geodesic(cpoint, rpoint).m
47+
elif data_format_guess == 'otn_2025':
48+
for cstation in dist_mtx.columns:
49+
for rstation in dist_mtx.index:
50+
cpoint = (stn_locs.loc[cstation, 'decimalLatitude'],
51+
stn_locs.loc[cstation, 'decimalLongitude'])
52+
rpoint = (stn_locs.loc[rstation, 'decimalLatitude'],
53+
stn_locs.loc[rstation, 'decimalLongitude'])
54+
dist_mtx.loc[rstation, cstation] = geodesic(cpoint, rpoint).m
3355
dist_mtx.index.name = None
3456
return dist_mtx
3557

@@ -66,7 +88,18 @@ def filter_detections(detections: pd.DataFrame, suspect_file=None,
6688
detections.
6789
"""
6890

91+
# what we expect the input data format to be
92+
data_format_guess = 'otn_old'
6993
# Set of mandatory column names for detection_file
94+
95+
if 'catalogNumber' in detections.columns: # the new version of otn det extracts uses camel case
96+
data_format_guess = 'otn_2025'
97+
mandatory_columns = set(['station',
98+
'unqDetecID',
99+
'dateCollectedUTC',
100+
'catalogNumber'])
101+
102+
else: # the original OTN detections did not use camel case.
70103
mandatory_columns = set(['station',
71104
'unqdetecid',
72105
'datecollected',
@@ -88,28 +121,47 @@ def filter_detections(detections: pd.DataFrame, suspect_file=None,
88121
# If the space before + after > min_time_buffer
89122
# Remove that detection row from the detections and add it to suspect detections.
90123
# SQL that does this is in load_to_postgresql under createSuspect
124+
91125
detections = detections.copy(deep=True)
92-
ind = detections['catalognumber'].unique()
93-
detections.loc[:, 'datecollected'] = pd.to_datetime(
94-
detections['datecollected'])
95-
user_int = timedelta(seconds=min_time_buffer)
96-
good_dets = pd.DataFrame()
97-
susp_dets = pd.DataFrame()
98-
grouped = detections.groupby('catalognumber', dropna=False)
99-
for anm in ind:
100-
anm_dets = grouped.get_group(anm).sort_values(
101-
'datecollected', ascending=True)
102-
intervals = anm_dets['datecollected'] - \
103-
anm_dets['datecollected'].shift(1)
104-
post_intervals = anm_dets['datecollected'].shift(
105-
-1) - anm_dets['datecollected']
106-
107-
good_dets = pd.concat([
108-
good_dets,
109-
anm_dets[
110-
(intervals <= user_int) | (post_intervals <= user_int)
111-
]
112-
])
126+
127+
if data_format_guess == 'otn_old':
128+
ind = detections['catalognumber'].unique()
129+
detections.loc[:, 'datecollected'] = pd.to_datetime(
130+
detections['datecollected'])
131+
user_int = timedelta(seconds=min_time_buffer)
132+
good_dets = pd.DataFrame()
133+
susp_dets = pd.DataFrame()
134+
grouped = detections.groupby('catalognumber', dropna=False)
135+
for anm in ind:
136+
anm_dets = grouped.get_group(anm).sort_values(
137+
'datecollected', ascending=True)
138+
intervals = anm_dets['datecollected'] - \
139+
anm_dets['datecollected'].shift(1)
140+
post_intervals = anm_dets['datecollected'].shift(
141+
-1) - anm_dets['datecollected']
142+
143+
elif data_format_guess == 'otn_2025':
144+
ind = detections['catalogNumber'].unique()
145+
detections.loc[:, 'dateCollectedUTC'] = pd.to_datetime(
146+
detections['dateCollectedUTC'])
147+
user_int = timedelta(seconds=min_time_buffer)
148+
good_dets = pd.DataFrame()
149+
susp_dets = pd.DataFrame()
150+
grouped = detections.groupby('catalogNumber', dropna=False)
151+
for anm in ind:
152+
anm_dets = grouped.get_group(anm).sort_values(
153+
'dateCollectedUTC', ascending=True)
154+
intervals = anm_dets['dateCollectedUTC'] - \
155+
anm_dets['dateCollectedUTC'].shift(1)
156+
post_intervals = anm_dets['dateCollectedUTC'].shift(
157+
-1) - anm_dets['dateCollectedUTC']
158+
159+
good_dets = pd.concat([
160+
good_dets,
161+
anm_dets[
162+
(intervals <= user_int) | (post_intervals <= user_int)
163+
]
164+
])
113165

114166
# If they aren't a good det, they're suspect!
115167
# TODO: Reporting: Decide if we want to report the big 'before/after'
@@ -118,8 +170,12 @@ def filter_detections(detections: pd.DataFrame, suspect_file=None,
118170
# append.
119171
# For now, just a matter of putting the complement of the good dets in
120172
# the susp_dets
121-
susp_dets = detections.loc[~detections['unqdetecid'].isin(
122-
good_dets['unqdetecid'])].copy(deep=True)
173+
if data_format_guess == 'otn_old':
174+
susp_dets = detections.loc[~detections['unqdetecid'].isin(
175+
good_dets['unqdetecid'])].copy(deep=True)
176+
elif data_format_guess == 'otn_2025':
177+
susp_dets = detections.loc[~detections['unqDetecID'].isin(
178+
good_dets['unqDetecID'])].copy(deep=True)
123179

124180
else:
125181
raise GenericException("Missing required input columns: {}".format(
@@ -132,9 +188,12 @@ def filter_detections(detections: pd.DataFrame, suspect_file=None,
132188
output_dict = {"filtered": good_dets, "suspect": susp_dets}
133189

134190
if distance_matrix:
135-
136191
# Must now have lat and long columns as well.
137-
dm_mandatory_columns = set(['latitude', 'longitude'])
192+
if data_format_guess == 'otn_old':
193+
dm_mandatory_columns = set(['latitude', 'longitude'])
194+
elif: data_format_guess == 'otn_2025':
195+
dm_mandatory_columns = set(['decimalLatitude', 'decimalLongitude'])
196+
138197
if dm_mandatory_columns.issubset(detections.columns):
139198
output_dict['dist_mtrx'] = get_distance_matrix(detections)
140199
print("There are {0} station locations in the distance \
@@ -175,20 +234,40 @@ def distance_filter(detections: pd.DataFrame, maximum_distance=100000, add_colum
175234
"""
176235
pd.options.mode.chained_assignment = None
177236

178-
mandatory_columns = set(['station',
237+
# what we expect the input data format to be
238+
data_format_guess = 'otn_old'
239+
# Set of mandatory column names for detection_file
240+
241+
if 'catalogNumber' in detections.columns: # the new version of otn det extracts uses camel case
242+
data_format_guess = 'otn_2025'
243+
244+
if data_format_guess == 'otn_old':
245+
mandatory_columns = set(['station',
179246
'unqdetecid',
180247
'datecollected',
181248
'catalognumber'])
249+
elif data_format_guess == 'otn_2025':
250+
mandatory_columns = set(['station',
251+
'unqDetecID',
252+
'dateCollectedUTC',
253+
'catalogNumber'])
182254

183255
if mandatory_columns.issubset(detections.columns):
184256
dm = get_distance_matrix(detections)
185257

186258
lead_lag_stn_df = pd.DataFrame()
187-
for _, group in detections.sort_values(['datecollected']).groupby(['catalognumber'], dropna=False):
188-
group['lag_station'] = group.station.shift(1).fillna(group.station)
189-
group['lead_station'] = group.station.shift(
190-
-1).fillna(group.station)
191-
lead_lag_stn_df = pd.concat([lead_lag_stn_df, group])
259+
if data_format_guess == 'otn_old':
260+
for _, group in detections.sort_values(['datecollected']).groupby(['catalognumber'], dropna=False):
261+
group['lag_station'] = group.station.shift(1).fillna(group.station)
262+
group['lead_station'] = group.station.shift(
263+
-1).fillna(group.station)
264+
lead_lag_stn_df = pd.concat([lead_lag_stn_df, group])
265+
elif data_format_guess == 'otn_2025':
266+
for _, group in detections.sort_values(['dateCollected']).groupby(['catalogNumber'], dropna=False):
267+
group['lag_station'] = group.station.shift(1).fillna(group.station)
268+
group['lead_station'] = group.station.shift(
269+
-1).fillna(group.station)
270+
lead_lag_stn_df = pd.concat([lead_lag_stn_df, group])
192271
del detections
193272

194273
distance_df = pd.DataFrame()
@@ -235,28 +314,53 @@ def velocity_filter(detections: pd.DataFrame, maximum_velocity=10, add_column:b
235314
detections.
236315
"""
237316
pd.options.mode.chained_assignment = None
317+
# what we expect the input data format to be
318+
data_format_guess = 'otn_old'
319+
# Set of mandatory column names for detection_file
238320

239-
mandatory_columns = set(['station',
321+
if 'catalogNumber' in detections.columns: # the new version of otn det extracts uses camel case
322+
data_format_guess = 'otn_2025'
323+
324+
if data_format_guess == 'otn_old':
325+
mandatory_columns = set(['station',
240326
'unqdetecid',
241327
'datecollected',
242328
'catalognumber'])
243-
329+
elif data_format_guess == 'otn_2025':
330+
mandatory_columns = set(['station',
331+
'unqDetecID',
332+
'dateCollected',
333+
'catalogNumber'])
334+
244335
if mandatory_columns.issubset(detections.columns):
245336

246337
dm = get_distance_matrix(detections)
247338

248339
lead_lag_df = pd.DataFrame()
249-
for _, group in detections.sort_values(['datecollected']).groupby(['catalognumber'], dropna=False):
250-
group['lag_station'] = group.station.shift(1).fillna(group.station)
251-
group['lead_station'] = group.station.shift(
252-
-1).fillna(group.station)
253-
254-
group.datecollected = pd.to_datetime(group.datecollected)
255-
group['lag_time_diff'] = group.datecollected.diff().fillna(
256-
timedelta(seconds=1))
257-
group['lead_time_diff'] = group.lag_time_diff.shift(
258-
-1).fillna(timedelta(seconds=1))
259-
lead_lag_df = pd.concat([lead_lag_df, group])
340+
if data_format_guess == 'otn_old':
341+
for _, group in detections.sort_values(['datecollected']).groupby(['catalognumber'], dropna=False):
342+
group['lag_station'] = group.station.shift(1).fillna(group.station)
343+
group['lead_station'] = group.station.shift(
344+
-1).fillna(group.station)
345+
346+
group.datecollected = pd.to_datetime(group.datecollected)
347+
group['lag_time_diff'] = group.datecollected.diff().fillna(
348+
timedelta(seconds=1))
349+
group['lead_time_diff'] = group.lag_time_diff.shift(
350+
-1).fillna(timedelta(seconds=1))
351+
lead_lag_df = pd.concat([lead_lag_df, group])
352+
elif data_format_guess == 'otn_2025':
353+
for _, group in detections.sort_values(['dateCollectedUTC']).groupby(['catalogNumber'], dropna=False):
354+
group['lag_station'] = group.station.shift(1).fillna(group.station)
355+
group['lead_station'] = group.station.shift(
356+
-1).fillna(group.station)
357+
358+
group.datecollected = pd.to_datetime(group.datecollected)
359+
group['lag_time_diff'] = group.datecollected.diff().fillna(
360+
timedelta(seconds=1))
361+
group['lead_time_diff'] = group.lag_time_diff.shift(
362+
-1).fillna(timedelta(seconds=1))
363+
lead_lag_df = pd.concat([lead_lag_df, group])
260364
del detections
261365

262366
vel_df = pd.DataFrame()

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
setup(
1414
name='resonATe',
15-
version='1.1',
15+
version='1.2',
1616
description='resonate data analysis package',
1717
long_description=readme,
1818
long_description_content_type='text/markdown',

0 commit comments

Comments
 (0)