@@ -488,8 +488,7 @@ def iter_on(self, xname, bins=None):
488
488
489
489
:param str,array xname:
490
490
:param array bins: bounds of each bin ,
491
- :return: Group observations
492
- :rtype: self.__class__
491
+ :return: index or mask, bound low, bound up
493
492
"""
494
493
x = self [xname ] if isinstance (xname , str ) else xname
495
494
d = x [1 :] - x [:- 1 ]
@@ -498,19 +497,28 @@ def iter_on(self, xname, bins=None):
498
497
elif not isinstance (bins , ndarray ):
499
498
bins = array (bins )
500
499
nb_bins = len (bins ) - 1
501
- i = numba_digitize ( x , bins ) - 1
500
+
502
501
# Not monotonous
503
502
if (d < 0 ).any ():
503
+ # If bins cover a small part of value
504
+ test , translate , x = iter_mode_reduce (x , bins )
505
+ # convert value in bins number
506
+ i = numba_digitize (x , bins ) - 1
507
+ # Order by bins
504
508
i_sort = i .argsort ()
509
+ # If in reduce mode we will translate i_sort in full array index
510
+ i_sort_ = translate [i_sort ] if test else i_sort
511
+ # Bound for each bins in sorting view
505
512
i0 , i1 , _ = build_index (i [i_sort ])
506
513
m = ~ (i0 == i1 )
507
514
i0 , i1 = i0 [m ], i1 [m ]
508
515
for i0_ , i1_ in zip (i0 , i1 ):
509
516
i_bins = i [i_sort [i0_ ]]
510
517
if i_bins == - 1 or i_bins == nb_bins :
511
518
continue
512
- yield i_sort [i0_ :i1_ ], bins [i_bins ], bins [i_bins + 1 ]
519
+ yield i_sort_ [i0_ :i1_ ], bins [i_bins ], bins [i_bins + 1 ]
513
520
else :
521
+ i = numba_digitize (x , bins ) - 1
514
522
i0 , i1 , _ = build_index (i )
515
523
m = ~ (i0 == i1 )
516
524
i0 , i1 = i0 [m ], i1 [m ]
@@ -522,10 +530,8 @@ def align_on(self, other, var_name="time", **kwargs):
522
530
"""
523
531
Align the time indexes of two datasets.
524
532
"""
525
- iter_self , iter_other = (
526
- self .iter_on (var_name , ** kwargs ),
527
- other .iter_on (var_name , ** kwargs ),
528
- )
533
+ iter_self = self .iter_on (var_name , ** kwargs )
534
+ iter_other = other .iter_on (var_name , ** kwargs )
529
535
indexs_other , b0_other , b1_other = iter_other .__next__ ()
530
536
for indexs_self , b0_self , b1_self in iter_self :
531
537
if b0_self > b0_other :
@@ -1038,10 +1044,23 @@ def intern(flag, public_label=False):
1038
1044
labels = [VAR_DESCR [label ]["nc_name" ] for label in labels ]
1039
1045
return labels
1040
1046
1041
- def match (self , other , method = "overlap" , intern = False , cmin = 0 , ** kwargs ):
1047
+ def match (
1048
+ self ,
1049
+ other ,
1050
+ i_self = None ,
1051
+ i_other = None ,
1052
+ method = "overlap" ,
1053
+ intern = False ,
1054
+ cmin = 0 ,
1055
+ ** kwargs ,
1056
+ ):
1042
1057
"""Return index and score computed on the effective contour.
1043
1058
1044
1059
:param EddiesObservations other: Observations to compare
1060
+ :param array[bool,int],None i_self:
1061
+ Index or mask to subset observations, it could avoid to build a specific dataset.
1062
+ :param array[bool,int],None i_other:
1063
+ Index or mask to subset observations, it could avoid to build a specific dataset.
1045
1064
:param str method:
1046
1065
- "overlap": the score is computed with contours;
1047
1066
- "circle": circles are computed and used for score (TODO)
@@ -1054,25 +1073,20 @@ def match(self, other, method="overlap", intern=False, cmin=0, **kwargs):
1054
1073
1055
1074
.. minigallery:: py_eddy_tracker.EddiesObservations.match
1056
1075
"""
1057
- # if method is "overlap" method will use contour to compute score,
1058
- # if method is "circle" method will apply a formula of circle overlap
1059
1076
x_name , y_name = self .intern (intern )
1077
+ if i_self is None :
1078
+ i_self = slice (None )
1079
+ if i_other is None :
1080
+ i_other = slice (None )
1060
1081
if method == "overlap" :
1061
- i , j = bbox_intersection (
1062
- self [x_name ], self [y_name ], other [x_name ], other [y_name ]
1063
- )
1064
- c = vertice_overlap (
1065
- self [x_name ][i ],
1066
- self [y_name ][i ],
1067
- other [x_name ][j ],
1068
- other [y_name ][j ],
1069
- ** kwargs ,
1070
- )
1082
+ x0 , y0 = self [x_name ][i_self ], self [y_name ][i_self ]
1083
+ x1 , y1 = other [x_name ][i_other ], other [y_name ][i_other ]
1084
+ i , j = bbox_intersection (x0 , y0 , x1 , y1 )
1085
+ c = vertice_overlap (x0 [i ], y0 [i ], x1 [j ], y1 [j ], ** kwargs )
1071
1086
elif method == "close_center" :
1072
- i , j , c = close_center (
1073
- self .latitude , self .longitude , other .latitude , other .longitude , ** kwargs
1074
- )
1075
-
1087
+ x0 , y0 = self .longitude [i_self ], self .latitude [i_self ]
1088
+ x1 , y1 = other .longitude [i_other ], other .latitude [i_other ]
1089
+ i , j , c = close_center (x0 , y0 , x1 , y1 , ** kwargs )
1076
1090
m = c >= cmin # ajout >= pour garder la cmin dans la sélection
1077
1091
return i [m ], j [m ], c [m ]
1078
1092
@@ -2438,3 +2452,41 @@ def numba_digitize(values, bins):
2438
2452
continue
2439
2453
out [i ] = (v_ - bins [0 ]) / step + 1
2440
2454
return out
2455
+
2456
+
2457
+ @njit (cache = True )
2458
+ def iter_mode_reduce (x , bins ):
2459
+ """
2460
+ Test if we could use a reduce mode
2461
+
2462
+ :param array x: array to divide in group
2463
+ :param array bins: array which defined bounds between each group
2464
+ :return: If reduce mode, translator, and reduce x
2465
+ """
2466
+ nb = x .shape [0 ]
2467
+ # If we use less than half value
2468
+ limit = nb // 2
2469
+ # low and up
2470
+ x0 , x1 = bins [0 ], bins [- 1 ]
2471
+ m = empty (nb , dtype = numba_types .bool_ )
2472
+ # To count number of value cover by bins
2473
+ c = 0
2474
+ for i in range (nb ):
2475
+ x_ = x [i ]
2476
+ test = (x_ >= x0 ) * (x_ <= x1 )
2477
+ m [i ] = test
2478
+ if test :
2479
+ c += 1
2480
+ # If number value exceed limit
2481
+ if c > limit :
2482
+ return False , empty (0 , dtype = numba_types .int_ ), x
2483
+ # Indices to be able to translate in full index array
2484
+ indices = empty (c , dtype = numba_types .int_ )
2485
+ x_ = empty (c , dtype = x .dtype )
2486
+ j = 0
2487
+ for i in range (nb ):
2488
+ if m [i ]:
2489
+ indices [j ] = i
2490
+ x_ [j ] = x [i ]
2491
+ j += 1
2492
+ return True , indices , x_
0 commit comments