diff --git a/dataframe_api_compat/pandas_standard/pandas_standard.py b/dataframe_api_compat/pandas_standard/pandas_standard.py index 6dbdfcde..c71bd3fd 100644 --- a/dataframe_api_compat/pandas_standard/pandas_standard.py +++ b/dataframe_api_compat/pandas_standard/pandas_standard.py @@ -112,11 +112,9 @@ def __init__( def __column_namespace__(self) -> Any: return dataframe_api_compat.pandas_standard - @property def root_names(self): return sorted(set(self._root_names)) - @property def output_name(self): return self._output_name @@ -128,12 +126,12 @@ def _record_call( ) -> PandasColumn: calls = [*self._calls, (func, self, rhs)] if isinstance(rhs, PandasColumn): - root_names = self.root_names + rhs.root_names + root_names = self.root_names() + rhs.root_names() else: - root_names = self.root_names + root_names = self.root_names() return PandasColumn( root_names=root_names, - output_name=output_name or self.output_name, + output_name=output_name or self.output_name(), extra_calls=calls, ) @@ -320,12 +318,12 @@ def func(ser, _rhs): if ascending: return ( ser.sort_values() - .index.to_series(name=self.output_name) + .index.to_series(name=self.output_name()) .reset_index(drop=True) ) return ( ser.sort_values() - .index.to_series(name=self.output_name)[::-1] + .index.to_series(name=self.output_name())[::-1] .reset_index(drop=True) ) @@ -376,7 +374,7 @@ def func(ser, value): ser = num / other else: ser = ser.fillna(value) - return ser.rename(self.output_name) + return ser.rename(self.output_name()) return self._record_call( lambda ser, _rhs: func(ser, value), @@ -413,6 +411,66 @@ def rename(self, name: str) -> PandasColumn: ) return expr + @property + def dt(self) -> ColumnDatetimeAccessor: + """ + Return accessor with functions which work on temporal dtypes. + """ + return ColumnDatetimeAccessor(self) + + +class ColumnDatetimeAccessor: + def __init__(self, column: PandasColumn | PandasPermissiveColumn) -> None: + if isinstance(column, PandasPermissiveColumn): + self.eager = True + self.column = column._to_expression() + self._api_version = column._api_version + else: + self.eager = False + self.column = column + + def _return(self, expr: PandasColumn): + if not self.eager: + return expr + return ( + PandasDataFrame(pd.DataFrame(), api_version=self._api_version) + .select(expr) + .collect() + .get_column_by_name(self.column.output_name()) + ) + + def year(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.year, None) + return self._return(expr) + + def month(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.month, None) + return expr + + def day(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.day, None) + return expr + + def hour(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.hour, None) + return expr + + def minute(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.minute, None) + return expr + + def second(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.second, None) + return expr + + def microsecond(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.microsecond, None) + return expr + + def iso_weekday(self) -> Column: + expr = self.column._record_call(lambda ser, _rhs: ser.dt.weekday + 1, None) + return expr + class PandasGroupBy(GroupBy): def __init__(self, df: pd.DataFrame, keys: Sequence[str], api_version: str) -> None: @@ -518,6 +576,9 @@ def __init__(self, column: pd.Series[Any], api_version: str) -> None: "Try updating dataframe-api-compat?" ) + def __repr__(self) -> str: # pragma: no cover + return self.column.__repr__() + def _to_expression(self) -> PandasColumn: return PandasColumn( root_names=[], @@ -737,6 +798,13 @@ def to_array_object(self, dtype: str) -> Any: ) return self.column.to_numpy(dtype=dtype) + @property + def dt(self) -> ColumnDatetimeAccessor: + """ + Return accessor with functions which work on temporal dtypes. + """ + return ColumnDatetimeAccessor(self) + class PandasDataFrame(DataFrame): # Not technically part of the standard @@ -879,7 +947,7 @@ def _resolve_expression( return expression if not expression._calls: return expression._base_call(self.dataframe) - output_name = expression.output_name + output_name = expression.output_name() for func, lhs, rhs in expression._calls: lhs = self._resolve_expression(lhs) rhs = self._resolve_expression(rhs) diff --git a/dataframe_api_compat/polars_standard/polars_standard.py b/dataframe_api_compat/polars_standard/polars_standard.py index c2a296be..8cfc094c 100644 --- a/dataframe_api_compat/polars_standard/polars_standard.py +++ b/dataframe_api_compat/polars_standard/polars_standard.py @@ -116,6 +116,9 @@ def __init__( self._api_version = api_version self._dtype = column.dtype + def __repr__(self) -> str: # pragma: no cover + return self.column.__repr__() + # In the standard def __column_namespace__(self) -> Any: return dataframe_api_compat.polars_standard @@ -184,10 +187,10 @@ def unique_indices(self, *, skip_nulls: bool = True) -> PolarsPermissiveColumn[A raise NotImplementedError("not yet supported") def is_null(self) -> PolarsPermissiveColumn[Bool]: - return self._from_expression(self._to_expression().is_null()) + return self._from_expression(self._to_expression().is_null(), self._api_version) def is_nan(self) -> PolarsPermissiveColumn[Bool]: - return self._from_expression(self._to_expression().is_nan()) + return self._from_expression(self._to_expression().is_nan(), self._api_version) def any(self, *, skip_nulls: bool = True) -> bool | None: return self.column.any() @@ -224,99 +227,123 @@ def __eq__( # type: ignore[override] ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__eq__(other._to_expression()) + self._to_expression().__eq__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__eq__(other)) + return self._from_expression( + self._to_expression().__eq__(other), self._api_version + ) def __ne__( # type: ignore[override] self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__ne__(other._to_expression()) + self._to_expression().__ne__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__ne__(other)) + return self._from_expression( + self._to_expression().__ne__(other), self._api_version + ) def __ge__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__ge__(other._to_expression()) + self._to_expression().__ge__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__ge__(other)) + return self._from_expression( + self._to_expression().__ge__(other), self._api_version + ) def __gt__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__gt__(other._to_expression()) + self._to_expression().__gt__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__gt__(other)) + return self._from_expression( + self._to_expression().__gt__(other), self._api_version + ) def __le__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__le__(other._to_expression()) + self._to_expression().__le__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__le__(other)) + return self._from_expression( + self._to_expression().__le__(other), self._api_version + ) def __lt__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__lt__(other._to_expression()) + self._to_expression().__lt__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__lt__(other)) + return self._from_expression( + self._to_expression().__lt__(other), self._api_version + ) def __mul__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Any]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__mul__(other._to_expression()) + self._to_expression().__mul__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__mul__(other)) + return self._from_expression( + self._to_expression().__mul__(other), self._api_version + ) def __floordiv__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Any]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__floordiv__(other._to_expression()) + self._to_expression().__floordiv__(other._to_expression()), + self._api_version, ) - return self._from_expression(self._to_expression().__floordiv__(other)) + return self._from_expression( + self._to_expression().__floordiv__(other), self._api_version + ) def __truediv__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Any]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__truediv__(other._to_expression()) + self._to_expression().__truediv__(other._to_expression()), + self._api_version, ) - return self._from_expression(self._to_expression().__truediv__(other)) + return self._from_expression( + self._to_expression().__truediv__(other), self._api_version + ) def __pow__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Any]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__pow__(other._to_expression()) + self._to_expression().__pow__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__pow__(other)) + return self._from_expression( + self._to_expression().__pow__(other), self._api_version + ) def __mod__( self, other: PermissiveColumn[DType] | Any ) -> PolarsPermissiveColumn[Any]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__mod__(other._to_expression()) + self._to_expression().__mod__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__mod__(other)) + return self._from_expression( + self._to_expression().__mod__(other), self._api_version + ) def __divmod__( self, @@ -332,35 +359,45 @@ def __and__( ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__and__(other._to_expression()) + self._to_expression().__and__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__and__(other)) + return self._from_expression( + self._to_expression().__and__(other), self._api_version + ) def __or__( self, other: PermissiveColumn[Bool] | bool ) -> PolarsPermissiveColumn[Bool]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__or__(other._to_expression()) + self._to_expression().__or__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__or__(other)) + return self._from_expression( + self._to_expression().__or__(other), self._api_version + ) def __invert__(self) -> PolarsPermissiveColumn[Bool]: - return self._from_expression(self._to_expression().__invert__()) + return self._from_expression( + self._to_expression().__invert__(), self._api_version + ) def __add__(self, other: PermissiveColumn[Any] | Any) -> PolarsPermissiveColumn[Any]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__add__(other._to_expression()) + self._to_expression().__add__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__add__(other)) + return self._from_expression( + self._to_expression().__add__(other), self._api_version + ) def __sub__(self, other: PermissiveColumn[Any] | Any) -> PolarsPermissiveColumn[Any]: if isinstance(other, PermissiveColumn): return self._from_expression( - self._to_expression().__sub__(other._to_expression()) + self._to_expression().__sub__(other._to_expression()), self._api_version ) - return self._from_expression(self._to_expression().__sub__(other)) + return self._from_expression( + self._to_expression().__sub__(other), self._api_version + ) def sorted_indices( self, *, ascending: bool = True, nulls_position: Literal["first", "last"] = "last" @@ -381,29 +418,35 @@ def sort( ) def fill_nan(self, value: float | NullType) -> PolarsPermissiveColumn[DType]: - return self._from_expression(self._to_expression().fill_nan(value)) + return self._from_expression( + self._to_expression().fill_nan(value), self._api_version + ) def fill_null(self, value: Any) -> PolarsPermissiveColumn[DType]: - return self._from_expression(self._to_expression().fill_null(value)) + return self._from_expression( + self._to_expression().fill_null(value), self._api_version + ) def cumulative_sum(self, *, skip_nulls: bool = True) -> PolarsColumn: return self._from_expression( - self._to_expression().cumulative_sum(skip_nulls=skip_nulls) + self._to_expression().cumulative_sum(skip_nulls=skip_nulls), self._api_version ) def cumulative_prod(self, *, skip_nulls: bool = True) -> PolarsColumn: return self._from_expression( - self._to_expression().cumulative_prod(skip_nulls=skip_nulls) + self._to_expression().cumulative_prod(skip_nulls=skip_nulls), + self._api_version, ) def cumulative_max(self, *, skip_nulls: bool = True) -> PolarsColumn: return self._from_expression( - self._to_expression().cumulative_max(skip_nulls=skip_nulls) + self._to_expression().cumulative_max(skip_nulls=skip_nulls), self._api_version ) def cumulative_min(self, *, skip_nulls: bool = True) -> PolarsColumn: return self._from_expression( - self._to_expression().cumulative_min(skip_nulls=skip_nulls) + self._to_expression().cumulative_min(skip_nulls=skip_nulls), + self._api_version, ) def to_array_object(self, dtype: str) -> Any: @@ -422,12 +465,20 @@ def rename(self, name: str) -> PolarsPermissiveColumn[DType]: def _to_expression(self) -> PolarsColumn: return PolarsColumn(pl.lit(self.column), api_version=self._api_version) - def _from_expression(self, expression: PolarsColumn): + @classmethod + def _from_expression(cls, expression: PolarsColumn, api_version: str): df = pl.select(expression._expr) return PolarsPermissiveColumn( - df.get_column(df.columns[0]), api_version=self._api_version + df.get_column(df.columns[0]), api_version=api_version ) + @property + def dt(self) -> ColumnDatetimeAccessor: + """ + Return accessor with functions which work on temporal dtypes. + """ + return ColumnDatetimeAccessor(self) + class PolarsGroupBy(GroupBy): def __init__(self, df: pl.LazyFrame, keys: Sequence[str], api_version: str) -> None: @@ -525,11 +576,9 @@ def __init__( def __column_namespace__(self) -> Any: # pragma: no cover return dataframe_api_compat.polars_standard - @property def root_names(self) -> list[str]: return sorted(set(self._expr.meta.root_names())) - @property def output_name(self) -> list[str]: return self._expr.meta.output_name() @@ -852,6 +901,81 @@ def rename(self, name: str) -> PolarsColumn: api_version=self._api_version, ) + @property + def dt(self) -> ColumnDatetimeAccessor: + """ + Return accessor with functions which work on temporal dtypes. + """ + return ColumnDatetimeAccessor(self) + + +class ColumnDatetimeAccessor: + def __init__(self, column: PolarsColumn | PolarsPermissiveColumn) -> None: + if isinstance(column, PolarsPermissiveColumn): + self.eager = True + self.column = column._to_expression() + else: + self.eager = False + self.column = column + self._api_version = column._api_version + + def _return(self, expr: PolarsColumn): + if not self.eager: + return expr + return PolarsPermissiveColumn._from_expression(expr, self._api_version) + + def year(self) -> Column: + return self._return( + PolarsColumn( + self.column._expr.dt.year(), + api_version=self._api_version, + ) + ) + + def month(self) -> Column: + return self._return( + PolarsColumn( + self.column._expr.dt.month(), + api_version=self._api_version, + ) + ) + + def day(self) -> Column: + return PolarsColumn( + self.column._expr.dt.day(), + api_version=self._api_version, + ) + + def hour(self) -> Column: + return PolarsColumn( + self.column._expr.dt.hour(), + api_version=self._api_version, + ) + + def minute(self) -> Column: + return PolarsColumn( + self.column._expr.dt.minute(), + api_version=self._api_version, + ) + + def second(self) -> Column: + return PolarsColumn( + self.column._expr.dt.second(), + api_version=self._api_version, + ) + + def microsecond(self) -> Column: + return PolarsColumn( + self.column._expr.dt.microsecond(), + api_version=self._api_version, + ) + + def iso_weekday(self) -> Column: + return PolarsColumn( + self.column._expr.dt.weekday(), + api_version=self._api_version, + ) + class PolarsDataFrame(DataFrame): def __init__(self, df: pl.LazyFrame, api_version: str) -> None: diff --git a/tests/column/output_name_test.py b/tests/column/output_name_test.py index fc2d9ed4..a481f2d0 100644 --- a/tests/column/output_name_test.py +++ b/tests/column/output_name_test.py @@ -8,9 +8,9 @@ def test_output_name(library: str) -> None: namespace = df.__dataframe_namespace__() col = namespace.col - assert col("a").output_name == "a" - assert col("b").output_name == "b" - assert col("b").rename("c").output_name == "c" - assert (col("b") + col("a")).output_name == "b" - assert (col("b") + col("a") + col("a")).output_name == "b" - assert namespace.any_rowwise(["a", "b"]).output_name == "any" + assert col("a").output_name() == "a" + assert col("b").output_name() == "b" + assert col("b").rename("c").output_name() == "c" + assert (col("b") + col("a")).output_name() == "b" + assert (col("b") + col("a") + col("a")).output_name() == "b" + assert namespace.any_rowwise(["a", "b"]).output_name() == "any" diff --git a/tests/column/root_names_test.py b/tests/column/root_names_test.py index 03abe125..fc317163 100644 --- a/tests/column/root_names_test.py +++ b/tests/column/root_names_test.py @@ -8,9 +8,9 @@ def test_root_names(library: str) -> None: namespace = df.__dataframe_namespace__() col = namespace.col - assert col("a").root_names == ["a"] - assert col("b").root_names == ["b"] - assert col("b").rename("c").root_names == ["b"] - assert (col("b") + col("a")).root_names == ["a", "b"] - assert (col("b") + col("a") + col("a")).root_names == ["a", "b"] - assert namespace.any_rowwise("a", "b").root_names == ["a", "b"] + assert col("a").root_names() == ["a"] + assert col("b").root_names() == ["b"] + assert col("b").rename("c").root_names() == ["b"] + assert (col("b") + col("a")).root_names() == ["a", "b"] + assert (col("b") + col("a") + col("a")).root_names() == ["a", "b"] + assert namespace.any_rowwise("a", "b").root_names() == ["a", "b"] diff --git a/tests/column/temporal/components_test.py b/tests/column/temporal/components_test.py new file mode 100644 index 00000000..4158e348 --- /dev/null +++ b/tests/column/temporal/components_test.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import pytest + +from tests.utils import temporal_dataframe_1 + + +@pytest.mark.parametrize( + ("attr", "expected"), + [ + ("year", [2020, 2020, 2020]), + ("month", [1, 1, 1]), + ("day", [1, 2, 3]), + ("hour", [1, 3, 5]), + ("minute", [2, 1, 4]), + ("second", [1, 2, 9]), + ("iso_weekday", [3, 4, 5]), + ], +) +def test_expr_components(library: str, attr: str, expected: list[int]) -> None: + df = temporal_dataframe_1(library) + col = df.__dataframe_namespace__().col + for col_name in ("a", "c", "e"): + result = df.select(getattr(col(col_name).dt, attr)()) + result_list = result.collect().get_column_by_name(col_name) + assert result_list.get_value(0) == expected[0] + assert result_list.get_value(1) == expected[1] + assert result_list.get_value(2) == expected[2] + + +@pytest.mark.parametrize( + ("attr", "expected"), + [ + ("year", [2020, 2020, 2020]), + ("month", [1, 1, 1]), + ("day", [1, 2, 3]), + ("hour", [1, 3, 5]), + ("minute", [2, 1, 4]), + ("second", [1, 2, 9]), + ("iso_weekday", [3, 4, 5]), + ], +) +def test_col_components(library: str, attr: str, expected: list[int]) -> None: + df = temporal_dataframe_1(library).collect() + for col_name in ("a", "c", "e"): + result = getattr(df.get_column_by_name(col_name).dt, attr)() + assert result.get_value(0) == expected[0] + assert result.get_value(1) == expected[1] + assert result.get_value(2) == expected[2] + + +@pytest.mark.parametrize( + ("col_name", "expected"), + [ + ("a", [123000, 321000, 987000]), + ("c", [123543, 321654, 987321]), + ("e", [123543, 321654, 987321]), + ], +) +def test_expr_microsecond(library: str, col_name: str, expected: list[int]) -> None: + df = temporal_dataframe_1(library) + col = df.__dataframe_namespace__().col + result = df.select(col(col_name).dt.microsecond()) + result_list = result.collect().get_column_by_name(col_name) + assert result_list.get_value(0) == expected[0] + assert result_list.get_value(1) == expected[1] + assert result_list.get_value(2) == expected[2] + + +@pytest.mark.parametrize( + ("col_name", "expected"), + [ + ("a", [123000, 321000, 987000]), + ("c", [123543, 321654, 987321]), + ("e", [123543, 321654, 987321]), + ], +) +def test_col_microsecond(library: str, col_name: str, expected: list[int]) -> None: + df = temporal_dataframe_1(library).collect() + result = df.get_column_by_name(col_name).dt.microsecond() + assert result.get_value(0) == expected[0] + assert result.get_value(1) == expected[1] + assert result.get_value(2) == expected[2] diff --git a/tests/utils.py b/tests/utils.py index 81435c9c..64f5a3bd 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -422,6 +422,99 @@ def bool_series_1(library) -> Any: raise AssertionError(f"Got unexpected library: {library}") +def temporal_dataframe_1(library: str) -> DataFrame: + if library in ["pandas-numpy", "pandas-nullable"]: + df = pd.DataFrame( + { + "a": [ + datetime(2020, 1, 1, 1, 2, 1, 123543), + datetime(2020, 1, 2, 3, 1, 2, 321654), + datetime(2020, 1, 3, 5, 4, 9, 987321), + ], + "b": [ + timedelta(1, milliseconds=1), + timedelta(2, milliseconds=3), + timedelta(3, milliseconds=5), + ], + "c": [ + datetime(2020, 1, 1, 1, 2, 1, 123543), + datetime(2020, 1, 2, 3, 1, 2, 321654), + datetime(2020, 1, 3, 5, 4, 9, 987321), + ], + "d": [ + timedelta(1, milliseconds=1), + timedelta(2, milliseconds=3), + timedelta(3, milliseconds=5), + ], + "e": [ + datetime(2020, 1, 1, 1, 2, 1, 123543), + datetime(2020, 1, 2, 3, 1, 2, 321654), + datetime(2020, 1, 3, 5, 4, 9, 987321), + ], + "f": [ + timedelta(1, milliseconds=1), + timedelta(2, milliseconds=3), + timedelta(3, milliseconds=5), + ], + } + ).astype( + { + "a": "datetime64[ms]", + "b": "timedelta64[ms]", + "c": "datetime64[us]", + "d": "timedelta64[us]", + "e": "datetime64[ns]", + "f": "timedelta64[ns]", + } + ) + return convert_to_standard_compliant_dataframe(df) + if library == "polars-lazy": + df = pl.DataFrame( + { + "a": [ + datetime(2020, 1, 1, 1, 2, 1, 123543), + datetime(2020, 1, 2, 3, 1, 2, 321654), + datetime(2020, 1, 3, 5, 4, 9, 987321), + ], + "b": [ + timedelta(1, milliseconds=1), + timedelta(2, milliseconds=3), + timedelta(3, milliseconds=5), + ], + "c": [ + datetime(2020, 1, 1, 1, 2, 1, 123543), + datetime(2020, 1, 2, 3, 1, 2, 321654), + datetime(2020, 1, 3, 5, 4, 9, 987321), + ], + "d": [ + timedelta(1, milliseconds=1), + timedelta(2, milliseconds=3), + timedelta(3, milliseconds=5), + ], + "e": [ + datetime(2020, 1, 1, 1, 2, 1, 123543), + datetime(2020, 1, 2, 3, 1, 2, 321654), + datetime(2020, 1, 3, 5, 4, 9, 987321), + ], + "f": [ + timedelta(1, milliseconds=1), + timedelta(2, milliseconds=3), + timedelta(3, milliseconds=5), + ], + }, + schema={ + "a": pl.Datetime("ms"), + "b": pl.Duration("ms"), + "c": pl.Datetime("us"), + "d": pl.Duration("us"), + "e": pl.Datetime("ns"), + "f": pl.Duration("ns"), + }, + ) + return convert_to_standard_compliant_dataframe(df) + raise AssertionError(f"Got unexpected library: {library}") + + def interchange_to_pandas(result: Any, library: str) -> pd.DataFrame: if isinstance(result.dataframe, pl.LazyFrame): df = result.dataframe.collect()