From 8021fb6754b27fb6f44d0f10394cc99516de3e57 Mon Sep 17 00:00:00 2001 From: Frederic Nouguier Date: Tue, 17 Jan 2023 17:43:15 +0000 Subject: [PATCH 1/7] check if coords are numerical --- xrft/xrft.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/xrft/xrft.py b/xrft/xrft.py index 30d738e8..95ff3e01 100644 --- a/xrft/xrft.py +++ b/xrft/xrft.py @@ -330,6 +330,7 @@ def fft( daft : `xarray.DataArray` The output of the Fourier transformation, with appropriate dimensions. """ + from pandas.api.types import is_numeric_dtype if dim is None: dim = list(da.dims) @@ -352,6 +353,11 @@ def fft( real_dim ] # real dim has to be moved or added at the end ! + if not np.all([is_numeric_dtype(da.coords[d]) for d in dim]): # checking if coodinates are numerical + raise ValueError( + "All transformed dimensions must have numerical coordinates." + ) + if chunks_to_segments: da = _stack_chunks(da, dim) @@ -520,6 +526,7 @@ def ifft( da : `xarray.DataArray` The output of the Inverse Fourier transformation, with appropriate dimensions. """ + from pandas.api.types import is_numeric_dtype if dim is None: dim = list(daft.dims) @@ -540,6 +547,12 @@ def ifft( dim = [d for d in dim if d != real_dim] + [ real_dim ] # real dim has to be moved or added at the end ! + + if not np.all([is_numeric_dtype(da.coords[d]) for d in dim]): # checking if coodinates are numerical + raise ValueError( + "All transformed dimensions must have numerical coordinates." + ) + if lag is None: lag = [daft[d].attrs.get("direct_lag", 0.0) for d in dim] msg = "Default ifft's behaviour (lag=None) changed! Default value of lag was zero (centered output coordinates) and is now set to transformed coordinate's attribute: 'direct_lag'." From 89757c8e85e8132b351aa54993587f1bb5eed83c Mon Sep 17 00:00:00 2001 From: Frederic Nouguier Date: Tue, 17 Jan 2023 17:50:57 +0000 Subject: [PATCH 2/7] black formatting --- xrft/xrft.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/xrft/xrft.py b/xrft/xrft.py index 95ff3e01..a86a34d1 100644 --- a/xrft/xrft.py +++ b/xrft/xrft.py @@ -353,10 +353,10 @@ def fft( real_dim ] # real dim has to be moved or added at the end ! - if not np.all([is_numeric_dtype(da.coords[d]) for d in dim]): # checking if coodinates are numerical - raise ValueError( - "All transformed dimensions must have numerical coordinates." - ) + if not np.all( + [is_numeric_dtype(da.coords[d]) for d in dim] + ): # checking if coodinates are numerical + raise ValueError("All transformed dimensions must have numerical coordinates.") if chunks_to_segments: da = _stack_chunks(da, dim) @@ -548,10 +548,10 @@ def ifft( real_dim ] # real dim has to be moved or added at the end ! - if not np.all([is_numeric_dtype(da.coords[d]) for d in dim]): # checking if coodinates are numerical - raise ValueError( - "All transformed dimensions must have numerical coordinates." - ) + if not np.all( + [is_numeric_dtype(da.coords[d]) for d in dim] + ): # checking if coodinates are numerical + raise ValueError("All transformed dimensions must have numerical coordinates.") if lag is None: lag = [daft[d].attrs.get("direct_lag", 0.0) for d in dim] From 37e36e20381f99a96e64214733dcc5188fcc3add Mon Sep 17 00:00:00 2001 From: Frederic Nouguier Date: Tue, 17 Jan 2023 17:56:00 +0000 Subject: [PATCH 3/7] da to daft in ifft --- xrft/xrft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xrft/xrft.py b/xrft/xrft.py index a86a34d1..17f0b4d8 100644 --- a/xrft/xrft.py +++ b/xrft/xrft.py @@ -549,7 +549,7 @@ def ifft( ] # real dim has to be moved or added at the end ! if not np.all( - [is_numeric_dtype(da.coords[d]) for d in dim] + [is_numeric_dtype(daft.coords[d]) for d in dim] ): # checking if coodinates are numerical raise ValueError("All transformed dimensions must have numerical coordinates.") From 522e1800c8493164afea98d287ff071afc216232 Mon Sep 17 00:00:00 2001 From: Frederic Nouguier Date: Fri, 20 Jan 2023 13:46:24 +0000 Subject: [PATCH 4/7] moce import is_numeric to begining of file --- xrft/xrft.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/xrft/xrft.py b/xrft/xrft.py index 17f0b4d8..18027a9d 100644 --- a/xrft/xrft.py +++ b/xrft/xrft.py @@ -15,7 +15,7 @@ import scipy.linalg as spl from .detrend import detrend as _detrend - +from pandas.api.types import is_numeric_dtype __all__ = [ "fft", @@ -330,8 +330,6 @@ def fft( daft : `xarray.DataArray` The output of the Fourier transformation, with appropriate dimensions. """ - from pandas.api.types import is_numeric_dtype - if dim is None: dim = list(da.dims) else: @@ -526,8 +524,6 @@ def ifft( da : `xarray.DataArray` The output of the Inverse Fourier transformation, with appropriate dimensions. """ - from pandas.api.types import is_numeric_dtype - if dim is None: dim = list(daft.dims) else: From ed9219a887896c59383b6e2c2bb69ca8aa59ca46 Mon Sep 17 00:00:00 2001 From: Frederic Nouguier Date: Wed, 8 Feb 2023 12:06:34 +0000 Subject: [PATCH 5/7] adding ufuncs removal in this PR --- xrft/xrft.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xrft/xrft.py b/xrft/xrft.py index 18027a9d..af62b7b4 100644 --- a/xrft/xrft.py +++ b/xrft/xrft.py @@ -907,8 +907,10 @@ def cross_phase(da1, da2, dim=None, true_phase=True, **kwargs): kwargs : dict : see xrft.fft for argument list """ - cp = xr.ufuncs.angle( - cross_spectrum(da1, da2, dim=dim, true_phase=true_phase, **kwargs) + cp = xr.apply_ufunc( + np.angle, + cross_spectrum(da1, da2, dim=dim, true_phase=true_phase, **kwargs), + dask="allowed", ) if da1.name and da2.name: From 5688129d4a99b444c02bd6f83c8800c7fa8f0017 Mon Sep 17 00:00:00 2001 From: Frederic Nouguier Date: Wed, 8 Feb 2023 14:32:05 +0000 Subject: [PATCH 6/7] adding datetime in correct coords dtype + test --- xrft/tests/test_xrft.py | 18 ++++++++++++++++++ xrft/xrft.py | 27 ++++++++++++++++++++------- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/xrft/tests/test_xrft.py b/xrft/tests/test_xrft.py index f63144c9..ae248deb 100644 --- a/xrft/tests/test_xrft.py +++ b/xrft/tests/test_xrft.py @@ -1344,3 +1344,21 @@ def test_nondim_coords(): xrft.power_spectrum(da) xrft.power_spectrum(da, dim=["time", "y"]) + + +def test_non_numerical_or_datetime_coords(): + """Error should be raised if there are non-numerical or non-datetime coordinate""" + da = xr.DataArray( + np.random.rand(2, 5, 3), + dims=["time", "x", "y"], + coords={ + "time": np.array(["2019-04-18", "2019-04-19"], dtype="datetime64"), + "x": range(5), + "y": ["a", "b", "c"], + }, + ) + + with pytest.raises(ValueError): + xrft.power_spectrum(da) + + xrft.power_spectrum(da, dim=["time", "x"]) diff --git a/xrft/xrft.py b/xrft/xrft.py index af62b7b4..9717c5d9 100644 --- a/xrft/xrft.py +++ b/xrft/xrft.py @@ -15,7 +15,7 @@ import scipy.linalg as spl from .detrend import detrend as _detrend -from pandas.api.types import is_numeric_dtype +from pandas.api.types import is_numeric_dtype, is_datetime64_any_dtype __all__ = [ "fft", @@ -352,9 +352,14 @@ def fft( ] # real dim has to be moved or added at the end ! if not np.all( - [is_numeric_dtype(da.coords[d]) for d in dim] - ): # checking if coodinates are numerical - raise ValueError("All transformed dimensions must have numerical coordinates.") + [ + (is_numeric_dtype(da.coords[d]) or is_datetime64_any_dtype(da.coords[d])) + for d in dim + ] + ): # checking if coodinates are numerical or datetime + raise ValueError( + "All transformed dimensions coordinates must be numerical or datetime." + ) if chunks_to_segments: da = _stack_chunks(da, dim) @@ -545,9 +550,17 @@ def ifft( ] # real dim has to be moved or added at the end ! if not np.all( - [is_numeric_dtype(daft.coords[d]) for d in dim] - ): # checking if coodinates are numerical - raise ValueError("All transformed dimensions must have numerical coordinates.") + [ + ( + is_numeric_dtype(daft.coords[d]) + or is_datetime64_any_dtype(daft.coords[d]) + ) + for d in dim + ] + ): # checking if coodinates are numerical or datetime + raise ValueError( + "All transformed dimensions coordinates must be numerical or datetime." + ) if lag is None: lag = [daft[d].attrs.get("direct_lag", 0.0) for d in dim] From 74730fc6af0c1b6517d3ed2b6580d03d6a89d4b5 Mon Sep 17 00:00:00 2001 From: Frederic Nouguier Date: Wed, 8 Feb 2023 15:25:50 +0000 Subject: [PATCH 7/7] coords with calendar accepted + removing obj call --- xrft/xrft.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/xrft/xrft.py b/xrft/xrft.py index 9717c5d9..9ddb3c19 100644 --- a/xrft/xrft.py +++ b/xrft/xrft.py @@ -230,9 +230,9 @@ def _lag_coord(coord): decoded_time = cftime.date2num(lag, ref_units, calendar) return decoded_time elif pd.api.types.is_datetime64_dtype(v0): - return lag.astype("timedelta64[s]").astype("f8").data + return lag.astype("timedelta64[s]").astype("f8") else: - return lag.data + return lag def dft( @@ -353,7 +353,11 @@ def fft( if not np.all( [ - (is_numeric_dtype(da.coords[d]) or is_datetime64_any_dtype(da.coords[d])) + ( + is_numeric_dtype(da.coords[d]) + or is_datetime64_any_dtype(da.coords[d]) + or bool(getattr(da.coords[d][0].item(), "calendar", False)) + ) for d in dim ] ): # checking if coodinates are numerical or datetime @@ -461,7 +465,7 @@ def fft( dims=up_dim, coords={up_dim: newcoords[up_dim]}, ) # taking advantage of xarray broadcasting and ordered coordinates - daft[up_dim].attrs.update({"direct_lag": lag.obj}) + daft[up_dim].attrs.update({"direct_lag": lag}) if true_amplitude: daft = daft * np.prod(delta_x) @@ -554,6 +558,7 @@ def ifft( ( is_numeric_dtype(daft.coords[d]) or is_datetime64_any_dtype(daft.coords[d]) + or bool(getattr(daft.coords[d][0].item(), "calendar", False)) ) for d in dim ]