38
38
zeros ,
39
39
array ,
40
40
median ,
41
- histogram
41
+ histogram ,
42
42
)
43
43
from datetime import datetime , timedelta
44
44
from numba import njit
45
45
from Polygon import Polygon
46
46
from .observation import EddiesObservations
47
47
from .. import VAR_DESCR_inv
48
- from ..generic import split_line , wrap_longitude , build_index
48
+ from ..generic import split_line , wrap_longitude , build_index , distance , cumsum_by_track
49
49
from ..poly import polygon_overlap , create_vertice_from_2darray
50
50
51
51
@@ -56,7 +56,7 @@ class TrackEddiesObservations(EddiesObservations):
56
56
"""Class to practice Tracking on observations
57
57
"""
58
58
59
- __slots__ = ("__obs_by_track" , "__first_index_of_track" )
59
+ __slots__ = ("__obs_by_track" , "__first_index_of_track" , "__nb_track" )
60
60
61
61
ELEMENTS = [
62
62
"lon" ,
@@ -85,6 +85,16 @@ def __init__(self, *args, **kwargs):
85
85
super ().__init__ (* args , ** kwargs )
86
86
self .__first_index_of_track = None
87
87
self .__obs_by_track = None
88
+ self .__nb_track = None
89
+
90
+ @property
91
+ def nb_tracks (self ):
92
+ """
93
+ Will count and send number of track
94
+ """
95
+ if self .__nb_track is None :
96
+ self .__nb_track = (self .nb_obs_by_track != 0 ).sum ()
97
+ return self .__nb_track
88
98
89
99
def __repr__ (self ):
90
100
content = super ().__repr__ ()
@@ -94,23 +104,56 @@ def __repr__(self):
94
104
nb_obs = self .observations .shape [0 ]
95
105
m = self ["virtual" ].astype ("bool" )
96
106
nb_m = m .sum ()
97
- bins_t = (0 , 20 , 50 , 100 , 200 , 1000 , 10000 )
107
+ bins_t = (1 , 20 , 50 , 100 , 200 , 1000 , 10000 )
98
108
nb_tracks_by_t = histogram (nb , bins = bins_t )[0 ]
99
109
nb_obs_by_t = histogram (nb , bins = bins_t , weights = nb )[0 ]
100
- pct_tracks_by_t = nb_tracks_by_t / nb_tracks_by_t .sum () * 100.
101
- pct_obs_by_t = nb_obs_by_t / nb_obs_by_t .sum () * 100.
110
+ pct_tracks_by_t = nb_tracks_by_t / nb_tracks_by_t .sum () * 100.0
111
+ pct_obs_by_t = nb_obs_by_t / nb_obs_by_t .sum () * 100.0
112
+ d = self .distance_to_next () / 1000.0
113
+ cum_d = cumsum_by_track (d , self .tracks )
114
+ m_last = ones (d .shape , dtype = "bool" )
115
+ m_last [- 1 ] = False
116
+ m_last [self .index_from_track [1 :] - 1 ] = False
102
117
content += f"""
103
- | { nb .shape [0 ]} tracks ({ nb .mean ():.2f} obs/tracks, shorter { nb .min ()} obs, longer { nb .max ()} obs)
104
- | { nb_m } filled observations ({ nb_m / nb .shape [0 ]:.2f} obs/tracks, { nb_m / nb_obs * 100 :.2f} % of total)
105
- | Intepolated speed area : { self ["speed_area" ][m ].sum () / period / 1e12 :.2f} Mkm²/day
106
- | Intepolated effective area : { self ["effective_area" ][m ].sum () / period / 1e12 :.2f} Mkm²/day
118
+ | { self .nb_tracks } tracks ({
119
+ nb_obs / self .nb_tracks :.2f} obs/tracks, shorter { nb [nb != 0 ].min ()} obs, longer { nb .max ()} obs)
120
+ | { nb_m } filled observations ({ nb_m / self .nb_tracks :.2f} obs/tracks, { nb_m / nb_obs * 100 :.2f} % of total)
121
+ | Intepolated speed area : { self ["speed_area" ][m ].sum () / period / 1e12 :.2f} Mkm²/day
122
+ | Intepolated effective area : { self ["effective_area" ][m ].sum () / period / 1e12 :.2f} Mkm²/day
123
+ | Distance by day : Mean { d [m_last ].mean ():.2f} , Median { median (d [m_last ]):.2f} km/day
124
+ | Distance by track : Mean { cum_d [~ m_last ].mean ():.2f} , Median { median (cum_d [~ m_last ]):.2f} km/track
107
125
----Distribution in lifetime:
108
126
| Lifetime (days ) { self .box_display (bins_t )}
109
127
| Percent of tracks : { self .box_display (pct_tracks_by_t )}
110
- | Percent of eddies : { self .box_display (pct_obs_by_t )}
111
- """
128
+ | Percent of eddies : { self .box_display (pct_obs_by_t )} """
112
129
return content
113
130
131
+ def add_distance (self ):
132
+ """Add a field of distance (m) between to consecutive observation, 0 for the last observation of each track
133
+ """
134
+ if "distance_next" in self .observations .dtype .descr :
135
+ return self
136
+ new = self .add_fields (("distance_next" ,))
137
+ new ["distance_next" ][:1 ] = self .distance_to_next ()
138
+ return new
139
+
140
+ def distance_to_next (self ):
141
+ """
142
+ :return: array of distance in m, 0 when next obs if from another track
143
+ :rtype: array
144
+ """
145
+ d = distance (
146
+ self .longitude [:- 1 ],
147
+ self .latitude [:- 1 ],
148
+ self .longitude [1 :],
149
+ self .latitude [1 :],
150
+ )
151
+ d [self .index_from_track [1 :] - 1 ] = 0
152
+ d_ = empty (d .shape [0 ] + 1 , dtype = d .dtype )
153
+ d_ [:- 1 ] = d
154
+ d_ [- 1 ] = 0
155
+ return d_
156
+
114
157
def filled_by_interpolation (self , mask ):
115
158
"""Filled selected values by interpolation
116
159
"""
@@ -329,15 +372,14 @@ def __extract_with_mask(
329
372
):
330
373
"""
331
374
Extract a subset of observations
332
- Args:
333
- mask: mask to select observations
334
- full_path: extract full path if only one part is selected
335
- remove_incomplete: delete path which are not fully selected
336
- compress_id: resample track number to use a little range
337
- reject_virtual: if track are only virtual in selection we remove track
338
-
339
- Returns:
340
- same object with selected observations
375
+
376
+ :param array(bool) mask: mask to select observations
377
+ :param full_path: extract full path if only one part is selected
378
+ :param remove_incomplete: delete path which are not fully selected
379
+ :param compress_id: resample track number to use a little range
380
+ :param reject_virtual: if track are only virtual in selection we remove track
381
+ :return: same object with selected observations
382
+ :rtype: self
341
383
"""
342
384
if full_path and remove_incomplete :
343
385
logger .warning (
@@ -373,6 +415,14 @@ def __extract_with_mask(
373
415
return new
374
416
375
417
def plot (self , ax , ref = None , ** kwargs ):
418
+ """
419
+ This function will draw path of each track
420
+
421
+ :param matplotlib.axes.Axes ax: ax where drawed
422
+ :param float,int ref: if defined all coordinates will be wrapped with ref like west boundary
423
+ :param dict kwargs: keyword arguments for Axes.plot
424
+ :return: matplotlib mappable
425
+ """
376
426
if "label" in kwargs :
377
427
kwargs ["label" ] += " (%s eddies)" % (self .nb_obs_by_track != 0 ).sum ()
378
428
x , y = split_line (self .longitude , self .latitude , self .tracks )
@@ -436,7 +486,15 @@ def split_network(self, intern=True, **kwargs):
436
486
# )
437
487
438
488
def set_tracks (self , x , y , ids , window ):
439
- # Will split one group in tracks
489
+ """
490
+ Will split one group in tracks
491
+
492
+ :param array x: coordinates of group
493
+ :param array y: coordinates of group
494
+ :param ndarray ids: several fields like time, group, ...
495
+ :param int windows: number of days where observations could missed
496
+ """
497
+
440
498
time_index = build_index (ids ["time" ])
441
499
nb = x .shape [0 ]
442
500
used = zeros (nb , dtype = "bool" )
0 commit comments