tobac 1.6.2__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.
Files changed (53) hide show
  1. tobac/__init__.py +112 -0
  2. tobac/analysis/__init__.py +31 -0
  3. tobac/analysis/cell_analysis.py +628 -0
  4. tobac/analysis/feature_analysis.py +212 -0
  5. tobac/analysis/spatial.py +619 -0
  6. tobac/centerofgravity.py +226 -0
  7. tobac/feature_detection.py +1758 -0
  8. tobac/merge_split.py +324 -0
  9. tobac/plotting.py +2321 -0
  10. tobac/segmentation/__init__.py +10 -0
  11. tobac/segmentation/watershed_segmentation.py +1316 -0
  12. tobac/testing.py +1179 -0
  13. tobac/tests/segmentation_tests/test_iris_xarray_segmentation.py +0 -0
  14. tobac/tests/segmentation_tests/test_segmentation.py +1183 -0
  15. tobac/tests/segmentation_tests/test_segmentation_time_pad.py +104 -0
  16. tobac/tests/test_analysis_spatial.py +1109 -0
  17. tobac/tests/test_convert.py +265 -0
  18. tobac/tests/test_datetime.py +216 -0
  19. tobac/tests/test_decorators.py +148 -0
  20. tobac/tests/test_feature_detection.py +1321 -0
  21. tobac/tests/test_generators.py +273 -0
  22. tobac/tests/test_import.py +24 -0
  23. tobac/tests/test_iris_xarray_match_utils.py +244 -0
  24. tobac/tests/test_merge_split.py +351 -0
  25. tobac/tests/test_pbc_utils.py +497 -0
  26. tobac/tests/test_sample_data.py +197 -0
  27. tobac/tests/test_testing.py +747 -0
  28. tobac/tests/test_tracking.py +714 -0
  29. tobac/tests/test_utils.py +650 -0
  30. tobac/tests/test_utils_bulk_statistics.py +789 -0
  31. tobac/tests/test_utils_coordinates.py +328 -0
  32. tobac/tests/test_utils_internal.py +97 -0
  33. tobac/tests/test_xarray_utils.py +232 -0
  34. tobac/tracking.py +613 -0
  35. tobac/utils/__init__.py +27 -0
  36. tobac/utils/bulk_statistics.py +360 -0
  37. tobac/utils/datetime.py +184 -0
  38. tobac/utils/decorators.py +540 -0
  39. tobac/utils/general.py +753 -0
  40. tobac/utils/generators.py +87 -0
  41. tobac/utils/internal/__init__.py +2 -0
  42. tobac/utils/internal/coordinates.py +430 -0
  43. tobac/utils/internal/iris_utils.py +462 -0
  44. tobac/utils/internal/label_props.py +82 -0
  45. tobac/utils/internal/xarray_utils.py +439 -0
  46. tobac/utils/mask.py +364 -0
  47. tobac/utils/periodic_boundaries.py +419 -0
  48. tobac/wrapper.py +244 -0
  49. tobac-1.6.2.dist-info/METADATA +154 -0
  50. tobac-1.6.2.dist-info/RECORD +53 -0
  51. tobac-1.6.2.dist-info/WHEEL +5 -0
  52. tobac-1.6.2.dist-info/licenses/LICENSE +29 -0
  53. tobac-1.6.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,351 @@
1
+ """Module to test tobac.merge_split"""
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ import pytest
6
+
7
+ import datetime
8
+
9
+ import tobac.testing as tbtest
10
+ import tobac.tracking as tbtrack
11
+ import tobac.merge_split as mergesplit
12
+
13
+
14
+ def test_merge_split_MEST():
15
+ """Tests tobac.merge_split.merge_split_cells by
16
+ generating two cells, colliding them into one another,
17
+ and merging them.
18
+ """
19
+
20
+ cell_1 = tbtest.generate_single_feature(
21
+ 1,
22
+ 1,
23
+ min_h1=0,
24
+ max_h1=100,
25
+ min_h2=0,
26
+ max_h2=100,
27
+ frame_start=0,
28
+ num_frames=2,
29
+ spd_h1=20,
30
+ spd_h2=20,
31
+ start_date=datetime.datetime(2022, 1, 1, 0),
32
+ )
33
+ cell_1["feature"] = [1, 3]
34
+
35
+ cell_2 = tbtest.generate_single_feature(
36
+ 1,
37
+ 100,
38
+ min_h1=0,
39
+ max_h1=101,
40
+ min_h2=0,
41
+ max_h2=101,
42
+ frame_start=0,
43
+ num_frames=2,
44
+ spd_h1=20,
45
+ spd_h2=-20,
46
+ start_date=datetime.datetime(2022, 1, 1, 0),
47
+ )
48
+ cell_2["feature"] = [2, 4]
49
+ cell_3 = tbtest.generate_single_feature(
50
+ 30,
51
+ 50,
52
+ min_h1=0,
53
+ max_h1=100,
54
+ min_h2=0,
55
+ max_h2=100,
56
+ frame_start=2,
57
+ num_frames=2,
58
+ spd_h1=20,
59
+ spd_h2=0,
60
+ start_date=datetime.datetime(2022, 1, 1, 0, 10),
61
+ )
62
+ cell_3["feature"] = [5, 6]
63
+ features = pd.concat([cell_1, cell_2, cell_3])
64
+ output = tbtrack.linking_trackpy(features, None, 1, 1, v_max=40)
65
+
66
+ dist_between = np.sqrt(
67
+ np.power(
68
+ output[output["frame"] == 1].iloc[0]["hdim_1"]
69
+ - output[output["frame"] == 1].iloc[1]["hdim_1"],
70
+ 2,
71
+ )
72
+ + np.power(
73
+ output[output["frame"] == 1].iloc[0]["hdim_2"]
74
+ - output[output["frame"] == 1].iloc[1]["hdim_2"],
75
+ 2,
76
+ )
77
+ )
78
+
79
+ # Test a successful merger
80
+ mergesplit_output_merged = mergesplit.merge_split_MEST(
81
+ output, dxy=10, distance=(dist_between + 50) * 10
82
+ )
83
+
84
+ # These cells should have merged together.
85
+ assert len(mergesplit_output_merged["track"]) == 1
86
+
87
+ # Test an unsuccessful merger.
88
+ mergesplit_output_unmerged = mergesplit.merge_split_MEST(
89
+ output, dxy=10, distance=(dist_between - 50) * 10
90
+ )
91
+
92
+ # These cells should NOT have merged together.
93
+ print(mergesplit_output_unmerged["track"])
94
+ assert len(mergesplit_output_unmerged["track"]) == 2
95
+
96
+
97
+ def test_merge_split_MEST_PBC():
98
+ """
99
+ Test PBC handling for merge_split_MEST
100
+ """
101
+ test_features = pd.DataFrame(
102
+ {
103
+ "feature": [1, 2, 3, 4],
104
+ "hdim_1": [1, 89, 1, 99],
105
+ "hdim_2": [50, 50, 50, 50],
106
+ "cell": [1, 2, 1, 2],
107
+ "frame": [0, 0, 1, 1],
108
+ "time": [
109
+ datetime.datetime(2000, 1, 1),
110
+ datetime.datetime(2000, 1, 1),
111
+ datetime.datetime(2000, 1, 1, 0, 5),
112
+ datetime.datetime(2000, 1, 1, 0, 5),
113
+ ],
114
+ }
115
+ )
116
+ # Test without PBCs
117
+ mergesplit_output_no_pbc = mergesplit.merge_split_MEST(
118
+ test_features,
119
+ dxy=1,
120
+ distance=25,
121
+ )
122
+
123
+ assert len(mergesplit_output_no_pbc["track"]) == 2
124
+
125
+ # Test with PBC in hdim_1, cells should merge
126
+ mergesplit_output_hdim_1_pbc = mergesplit.merge_split_MEST(
127
+ test_features,
128
+ dxy=1,
129
+ distance=25,
130
+ PBC_flag="hdim_1",
131
+ min_h1=0,
132
+ max_h1=100,
133
+ )
134
+
135
+ assert len(mergesplit_output_hdim_1_pbc["track"]) == 1
136
+
137
+ # Test with PBC in hdim_2, cells should not merge
138
+ mergesplit_output_hdim_2_pbc = mergesplit.merge_split_MEST(
139
+ test_features,
140
+ dxy=1,
141
+ distance=25,
142
+ PBC_flag="hdim_2",
143
+ min_h2=0,
144
+ max_h2=100,
145
+ )
146
+
147
+ assert len(mergesplit_output_hdim_2_pbc["track"]) == 2
148
+
149
+ # Test with PBC in both dimensions, cells should merge
150
+ mergesplit_output_both_pbc = mergesplit.merge_split_MEST(
151
+ test_features,
152
+ dxy=1,
153
+ distance=25,
154
+ PBC_flag="both",
155
+ min_h1=0,
156
+ max_h1=100,
157
+ min_h2=0,
158
+ max_h2=100,
159
+ )
160
+
161
+ assert len(mergesplit_output_both_pbc["track"]) == 1
162
+
163
+
164
+ def test_merge_split_MEST_frame_len():
165
+ """
166
+ Test the frame_len parameter of merge_split_MEST
167
+ """
168
+ test_features = pd.DataFrame(
169
+ {
170
+ "feature": [1, 2, 3, 4],
171
+ "hdim_1": [50, 50, 50, 50],
172
+ "hdim_2": [50, 50, 50, 50],
173
+ "cell": [1, 1, 2, 2],
174
+ "frame": [0, 1, 3, 4],
175
+ "time": [
176
+ datetime.datetime(2000, 1, 1),
177
+ datetime.datetime(2000, 1, 1),
178
+ datetime.datetime(2000, 1, 1, 0, 5),
179
+ datetime.datetime(2000, 1, 1, 0, 5),
180
+ ],
181
+ }
182
+ )
183
+
184
+ # Test with short frame_len, expect no link
185
+ mergesplit_output = mergesplit.merge_split_MEST(
186
+ test_features,
187
+ dxy=1,
188
+ distance=25,
189
+ frame_len=1,
190
+ )
191
+ assert len(mergesplit_output["track"]) == 2
192
+
193
+ # Test with longer frame_len, expect link
194
+ mergesplit_output = mergesplit.merge_split_MEST(
195
+ test_features,
196
+ dxy=1,
197
+ distance=25,
198
+ frame_len=2,
199
+ )
200
+ assert len(mergesplit_output["track"]) == 1
201
+
202
+
203
+ def test_merge_split_MEST_no_cell():
204
+ """
205
+ Test merge/split in cases with features with no cell
206
+ """
207
+ test_features = pd.DataFrame(
208
+ {
209
+ "feature": [1, 2, 3],
210
+ "hdim_1": [25, 30, 50],
211
+ "hdim_2": [25, 30, 50],
212
+ "cell": [1, -1, 1],
213
+ "frame": [0, 0, 1],
214
+ "time": [
215
+ datetime.datetime(2000, 1, 1),
216
+ datetime.datetime(2000, 1, 1),
217
+ datetime.datetime(2000, 1, 1, 0, 5),
218
+ ],
219
+ }
220
+ )
221
+
222
+ mergesplit_output = mergesplit.merge_split_MEST(
223
+ test_features,
224
+ dxy=1,
225
+ distance=25,
226
+ )
227
+
228
+ assert len(mergesplit_output["track"]) == 1
229
+
230
+ assert mergesplit_output["feature_parent_cell_id"].values[1] == -1
231
+
232
+
233
+ def test_merge_split_MEST_3D():
234
+ """
235
+ Test merge/split support for 3D tracks and dz input
236
+ """
237
+
238
+ test_features = pd.DataFrame(
239
+ {
240
+ "feature": [1, 2, 3, 4],
241
+ "vdim": [1, 2, 1, 2],
242
+ "hdim_1": [50, 40, 50, 40],
243
+ "hdim_2": [50, 50, 50, 50],
244
+ "cell": [1, 2, 1, 2],
245
+ "frame": [0, 0, 1, 1],
246
+ "altitude": [500, 750, 1500, 2000],
247
+ "time": [
248
+ datetime.datetime(2000, 1, 1),
249
+ datetime.datetime(2000, 1, 1),
250
+ datetime.datetime(2000, 1, 1, 0, 5),
251
+ datetime.datetime(2000, 1, 1, 0, 5),
252
+ ],
253
+ }
254
+ )
255
+
256
+ # Test with dz=10, expect merge
257
+ mergesplit_output_3d_merge = mergesplit.merge_split_MEST(
258
+ test_features,
259
+ dxy=1,
260
+ dz=10,
261
+ distance=20,
262
+ )
263
+
264
+ assert len(mergesplit_output_3d_merge["track"]) == 1
265
+
266
+ # Test with dz=25, expect no merge
267
+ mergesplit_output_3d_nomerge = mergesplit.merge_split_MEST(
268
+ test_features,
269
+ dxy=1,
270
+ dz=25,
271
+ distance=20,
272
+ )
273
+
274
+ assert len(mergesplit_output_3d_nomerge["track"]) == 2
275
+
276
+ # Test providing vertical_coord
277
+ mergesplit_output_3d_coord_merge = mergesplit.merge_split_MEST(
278
+ test_features,
279
+ dxy=1,
280
+ vertical_coord="altitude",
281
+ distance=1100,
282
+ )
283
+ mergesplit_output_3d_coord_nomerge = mergesplit.merge_split_MEST(
284
+ test_features,
285
+ dxy=1,
286
+ vertical_coord="altitude",
287
+ distance=20,
288
+ )
289
+
290
+ assert len(mergesplit_output_3d_coord_merge["track"]) == 1
291
+ assert len(mergesplit_output_3d_coord_nomerge["track"]) == 2
292
+
293
+ # Test auto find vertical_coord
294
+ mergesplit_output_3d_coord_merge = mergesplit.merge_split_MEST(
295
+ test_features,
296
+ dxy=1,
297
+ distance=1100,
298
+ )
299
+ mergesplit_output_3d_coord_nomerge = mergesplit.merge_split_MEST(
300
+ test_features,
301
+ dxy=1,
302
+ distance=20,
303
+ )
304
+
305
+ assert len(mergesplit_output_3d_coord_merge["track"]) == 1
306
+ assert len(mergesplit_output_3d_coord_nomerge["track"]) == 2
307
+
308
+ # Test error if both dz and coord are provided
309
+ with pytest.raises(ValueError):
310
+ mergesplit.merge_split_MEST(
311
+ test_features,
312
+ dxy=1,
313
+ dz=1,
314
+ vertical_coord="auto",
315
+ distance=20,
316
+ )
317
+
318
+ # Test that wrong vertical coord name causes an error
319
+ with pytest.raises(ValueError):
320
+ mergesplit.merge_split_MEST(
321
+ test_features,
322
+ dxy=1,
323
+ vertical_coord="invalid_coord_name",
324
+ distance=20,
325
+ )
326
+
327
+ # Test that auto search fails if coordinate name is not in default list
328
+ test_features = pd.DataFrame(
329
+ {
330
+ "feature": [1, 2, 3, 4],
331
+ "vdim": [1, 2, 1, 2],
332
+ "hdim_1": [50, 40, 50, 40],
333
+ "hdim_2": [50, 50, 50, 50],
334
+ "cell": [1, 2, 1, 2],
335
+ "frame": [0, 0, 1, 1],
336
+ "invalid_coord_name": [500, 1500, 1000, 2000],
337
+ "time": [
338
+ datetime.datetime(2000, 1, 1),
339
+ datetime.datetime(2000, 1, 1),
340
+ datetime.datetime(2000, 1, 1, 0, 5),
341
+ datetime.datetime(2000, 1, 1, 0, 5),
342
+ ],
343
+ }
344
+ )
345
+
346
+ with pytest.raises(ValueError):
347
+ mergesplit.merge_split_MEST(
348
+ test_features,
349
+ dxy=1,
350
+ distance=20,
351
+ )