From da42cfc4b65d9cb8b54d1e68ab7822c5c6043585 Mon Sep 17 00:00:00 2001 From: BaptisteDE Date: Fri, 25 Sep 2026 15:30:35 +0200 Subject: [PATCH] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Support=20numpy=202=20and?= =?UTF-8?q?=20pandas=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relax dependency pins (numpy<2, pandas<3) so tide installs on Python 3.14, where numpy 1.x has no wheels. Fix pandas 3 incompatibilities: - time_integrate / TimeIntegrate assumed a nanosecond DatetimeIndex (index.view("int64") * 1e-9). pandas 3 defaults to microseconds, which made integrals 1000x off. Convert with a resolution-agnostic timedelta division instead. - AddFourierPairs wrote into a read-only array under Copy-on-Write; copy it. - Tests hardcoded datetime64[ns] expected indexes; follow the input unit. Test suite passes on numpy 2.5 with pandas 2.3 and pandas 3.0 (Python 3.14). Co-Authored-By: Claude Opus 5.5 (1M context) --- pyproject.toml | 4 ++-- tests/test_processing.py | 14 +++++++------- tide/math.py | 2 +- tide/processing.py | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 60e02cc..1e77f12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,8 +21,8 @@ classifiers = [ ] requires-python = ">=3.10" dependencies = [ - "numpy>=1.22.4, <2.0", - "pandas>=2.0.0, <3.0", + "numpy>=1.22.4", + "pandas>=2.0.0, <4.0", "scipy>=1.9.1, <2.0", "bigtree>=0.21.3", "scikit-learn>=1.2.2, <2.0", diff --git a/tests/test_processing.py b/tests/test_processing.py index 5dd07c3..88b4ba5 100644 --- a/tests/test_processing.py +++ b/tests/test_processing.py @@ -511,8 +511,8 @@ def test_pd_add_time_lag(self): "1:00:00_col1": [0.0], }, index=pd.DatetimeIndex( - ["2009-01-01 01:00:00"], dtype="datetime64[ns, UTC]", freq="h", tz="UTC" - ), + ["2009-01-01 01:00:00"], freq="h", tz="UTC" + ).as_unit(df.index.unit), ) lager = AddTimeLag(time_lag=dt.timedelta(hours=1), drop_resulting_nan=True) @@ -541,8 +541,8 @@ def test_pd_add_time_lag_string_time_lag(self): "1h_col1": [0.0], }, index=pd.DatetimeIndex( - ["2009-01-01 01:00:00"], dtype="datetime64[ns, UTC]", freq="h", tz="UTC" - ), + ["2009-01-01 01:00:00"], freq="h", tz="UTC" + ).as_unit(df.index.unit), ) lager = AddTimeLag(time_lag="1h", drop_resulting_nan=True) @@ -1399,7 +1399,7 @@ def test_drop_quantile(self): filtered_noise["Noise_1"][filtered_noise["Noise_1"].isna()].index, pd.DatetimeIndex( ["2009-01-01 10:00:00+00:00", "2009-01-01 15:30:00+00:00"], - dtype="datetime64[ns, UTC]", + dtype=f"datetime64[{toy_df.index.unit}, UTC]", freq=None, ), ) @@ -1407,7 +1407,7 @@ def test_drop_quantile(self): pd.testing.assert_index_equal( filtered_noise["Noise_2"][filtered_noise["Noise_2"].isna()].index, pd.DatetimeIndex( - ["2009-01-01 03:30:00+00:00"], dtype="datetime64[ns, UTC]", freq=None + ["2009-01-01 03:30:00+00:00"], dtype=f"datetime64[{toy_df.index.unit}, UTC]", freq=None ), ) @@ -1440,7 +1440,7 @@ def test_drop_quantile(self): pd.testing.assert_index_equal( filtered["Temp_2"][filtered["Temp_2"].isna()].index, pd.DatetimeIndex( - ["2009-01-01 11:30:00+00:00"], dtype="datetime64[ns, UTC]", freq=None + ["2009-01-01 11:30:00+00:00"], dtype=f"datetime64[{toy_df.index.unit}, UTC]", freq=None ), ) diff --git a/tide/math.py b/tide/math.py index 369e5b7..697c731 100644 --- a/tide/math.py +++ b/tide/math.py @@ -95,7 +95,7 @@ def time_integrate(data: pd.DataFrame | pd.Series) -> pd.Series: if data.empty: return pd.Series(index=data.columns, dtype=float) - t = (data.index.view("int64") - data.index[0].value) * 1e-9 # seconds + t = ((data.index - data.index[0]) / pd.Timedelta(seconds=1)).to_numpy() # seconds y = data.to_numpy() result = trapezoid(y, t, axis=0) diff --git a/tide/processing.py b/tide/processing.py index ab70d17..5cdc688 100644 --- a/tide/processing.py +++ b/tide/processing.py @@ -1050,7 +1050,7 @@ def _transform_implementation(self, X: pd.Series | pd.DataFrame): if len(X) == 0: return X - time_diffs = np.diff(X.index.view("int64")) * 1e-9 + time_diffs = np.diff(X.index) / np.timedelta64(1, "s") # Create DataFrame for integrated values integrated = pd.DataFrame(index=X.index, columns=X.columns, dtype=float) @@ -3846,7 +3846,7 @@ def _transform_implementation(self, X: pd.Series | pd.DataFrame): new_index = X.index.to_frame().diff().squeeze() sec_dt = [element.total_seconds() for element in new_index] - increasing_seconds = pd.Series(sec_dt).cumsum().to_numpy() + increasing_seconds = pd.Series(sec_dt).cumsum().to_numpy(copy=True) increasing_seconds[0] = 0 omega = 2 * np.pi * frequency