Improved datetime handling in utils to avoid type mixing
@@ -928,21 +928,42 @@ def find_range(values, soft_range=[]):
def max_range(ranges, combined=True):
"""
Computes the maximal lower and upper bounds from a list bounds.
+
+ Args:
+ ranges (list of tuples): A list of range tuples
+ combined (boolean, optional): Whether to combine bounds
+ Whether range should be computed on lower and upper bound
+ independently or both at once
+
+ Returns:
+ The maximum range as a single tuple
"""
try:
with warnings.catch_warnings():
warnings.filterwarnings('ignore', r'All-NaN (slice|axis) encountered')
values = [tuple(np.NaN if v is None else v for v in r) for r in ranges]
- if pd and all(isinstance(v, pd.Timestamp) for r in values for v in r):
- values = [(v1.to_datetime64(), v2.to_datetime64()) for v1, v2 in values]
+ if pd and any(isinstance(v, datetime_types) and not isinstance(v, cftime_types)
+ for r in values for v in r):
+ converted = []
+ for l, h in values:
+ if isinstance(l, datetime_types) and isinstance(h, datetime_types):
+ l, h = (pd.Timestamp(l).to_datetime64(),
+ pd.Timestamp(h).to_datetime64())
+ converted.append((l, h))
+ values = converted
+
arr = np.array(values)
if not len(arr):
return np.NaN, np.NaN
elif arr.dtype.kind in 'OSU':
- arr = list(python2sort([v for r in values for v in r if not is_nan(v) and v is not None]))
+ arr = list(python2sort([
+ v for r in values for v in r
+ if not is_nan(v) and v is not None]))
return arr[0], arr[-1]
elif arr.dtype.kind in 'M':
- return (arr.min(), arr.max()) if combined else (arr[:, 0].min(), arr[:, 1].min())
+ return ((arr.min(), arr.max()) if combined else
+ (arr[:, 0].min(), arr[:, 1].min()))
+
if combined:
return (np.nanmin(arr), np.nanmax(arr))
else:
@@ -1085,12 +1106,28 @@ def unique_iterator(seq):
def unique_array(arr):
"""
- Returns an array of unique values in the input order
+ Returns an array of unique values in the input order.
+
+ Args:
+ arr (np.ndarray or list): The array to compute unique values on
+
+ Returns:
+ A new array of unique values
"""
if not len(arr):
- return arr
+ return np.asarray(arr)
elif pd:
+ if isinstance(arr, np.ndarray) and arr.dtype.kind not in 'MO':
+ # Avoid expensive unpacking if not potentially datetime
return pd.unique(arr)
+
+ values = []
+ for v in arr:
+ if (isinstance(v, datetime_types) and
+ not isinstance(v, cftime_types)):
+ v = pd.Timestamp(v).to_datetime64()
+ values.append(v)
+ return pd.unique(values)
else:
arr = np.asarray(arr)
_, uniq_inds = np.unique(arr, return_index=True)
