diff --git a/py-polars/polars/dataframe/frame.py b/py-polars/polars/dataframe/frame.py index 7ef6bb7dceae..d796ca70ff98 100644 --- a/py-polars/polars/dataframe/frame.py +++ b/py-polars/polars/dataframe/frame.py @@ -33,7 +33,6 @@ from polars.dataframe._html import NotebookFormatter from polars.dataframe.group_by import DynamicGroupBy, GroupBy, RollingGroupBy from polars.datatypes import ( - FLOAT_DTYPES, INTEGER_DTYPES, N_INFER_DEFAULT, NUMERIC_DTYPES, @@ -1419,13 +1418,13 @@ def _div(self, other: Any, *, floordiv: bool) -> DataFrame: df = ( df if not floordiv - else df.with_columns([s.floor() for s in df if s.dtype in FLOAT_DTYPES]) + else df.with_columns([s.floor() for s in df if s.dtype.is_float()]) ) if floordiv: int_casts = [ col(column).cast(tp) for i, (column, tp) in enumerate(self.schema.items()) - if tp in INTEGER_DTYPES and orig_dtypes[i] in INTEGER_DTYPES + if tp.is_integer() and orig_dtypes[i].is_integer() ] if int_casts: return df.with_columns(int_casts) @@ -1711,7 +1710,7 @@ def __getitem__( dtype = item.dtype if dtype == Utf8: return self._from_pydf(self._df.select(item)) - elif dtype in INTEGER_DTYPES: + elif dtype.is_integer(): return self._take_with_series(item._pos_idxs(self.shape[0])) # if no data has been returned, the operation is not supported diff --git a/py-polars/polars/datatypes/__init__.py b/py-polars/polars/datatypes/__init__.py index 4576282539c4..78206c12b1d8 100644 --- a/py-polars/polars/datatypes/__init__.py +++ b/py-polars/polars/datatypes/__init__.py @@ -13,7 +13,6 @@ Field, Float32, Float64, - FractionalType, Int8, Int16, Int32, @@ -21,7 +20,6 @@ IntegerType, List, Null, - NumericType, Object, Struct, TemporalType, @@ -89,7 +87,6 @@ "Field", "Float32", "Float64", - "FractionalType", "Int16", "Int32", "Int64", @@ -97,7 +94,6 @@ "IntegerType", "List", "Null", - "NumericType", "Object", "Struct", "TemporalType", diff --git a/py-polars/polars/datatypes/classes.py b/py-polars/polars/datatypes/classes.py index 6804625f1d3a..39d6c989b9af 100644 --- a/py-polars/polars/datatypes/classes.py +++ b/py-polars/polars/datatypes/classes.py @@ -63,6 +63,30 @@ def is_not(cls, other: PolarsDataType) -> bool: # noqa: D102 def is_nested(self) -> bool: # noqa: D102 ... + @classmethod + def is_numeric(cls) -> bool: # noqa: D102 + ... + + @classmethod + def is_integer(cls) -> bool: # noqa: D102 + ... + + @classmethod + def is_signed_integer(cls) -> bool: # noqa: D102 + ... + + @classmethod + def is_unsigned_integer(cls) -> bool: # noqa: D102 + ... + + @classmethod + def is_float(cls) -> bool: # noqa: D102 + ... + + @classmethod + def is_temporal(cls) -> bool: # noqa: D102 + ... + class DataType(metaclass=DataTypeClass): """Base class for all Polars data types.""" @@ -161,6 +185,36 @@ def is_nested(self) -> bool: issue_deprecation_warning(message, version="0.19.10") return False + @classmethod + def is_numeric(cls) -> bool: + """Check whether the data type is a numeric type.""" + return issubclass(cls, NumericType) + + @classmethod + def is_integer(cls) -> bool: + """Check whether the data type is an integer type.""" + return issubclass(cls, IntegerType) + + @classmethod + def is_signed_integer(cls) -> bool: + """Check whether the data type is a signed integer type.""" + return issubclass(cls, SignedIntegerType) + + @classmethod + def is_unsigned_integer(cls) -> bool: + """Check whether the data type is an unsigned integer type.""" + return issubclass(cls, UnsignedIntegerType) + + @classmethod + def is_float(cls) -> bool: + """Check whether the data type is a temporal type.""" + return issubclass(cls, FloatType) + + @classmethod + def is_temporal(cls) -> bool: + """Check whether the data type is a temporal type.""" + return issubclass(cls, TemporalType) + def _custom_reconstruct( cls: type[Any], base: type[Any], state: Any @@ -214,14 +268,18 @@ class NumericType(DataType): class IntegerType(NumericType): - """Base class for integral data types.""" + """Base class for integer data types.""" + + +class SignedIntegerType(IntegerType): + """Base class for signed integer data types.""" -class FractionalType(NumericType): - """Base class for fractional data types.""" +class UnsignedIntegerType(IntegerType): + """Base class for unsigned integer data types.""" -class FloatType(FractionalType): +class FloatType(NumericType): """Base class for float data types.""" @@ -252,35 +310,35 @@ def is_nested(self) -> bool: return True -class Int8(IntegerType): +class Int8(SignedIntegerType): """8-bit signed integer type.""" -class Int16(IntegerType): +class Int16(SignedIntegerType): """16-bit signed integer type.""" -class Int32(IntegerType): +class Int32(SignedIntegerType): """32-bit signed integer type.""" -class Int64(IntegerType): +class Int64(SignedIntegerType): """64-bit signed integer type.""" -class UInt8(IntegerType): +class UInt8(UnsignedIntegerType): """8-bit unsigned integer type.""" -class UInt16(IntegerType): +class UInt16(UnsignedIntegerType): """16-bit unsigned integer type.""" -class UInt32(IntegerType): +class UInt32(UnsignedIntegerType): """32-bit unsigned integer type.""" -class UInt64(IntegerType): +class UInt64(UnsignedIntegerType): """64-bit unsigned integer type.""" @@ -292,7 +350,7 @@ class Float64(FloatType): """64-bit floating point type.""" -class Decimal(FractionalType): +class Decimal(NumericType): """ Decimal 128-bit type with an optional precision and non-negative scale. diff --git a/py-polars/polars/expr/expr.py b/py-polars/polars/expr/expr.py index a899538074c3..6823dcf0d916 100644 --- a/py-polars/polars/expr/expr.py +++ b/py-polars/polars/expr/expr.py @@ -24,8 +24,6 @@ import polars._reexport as pl from polars import functions as F from polars.datatypes import ( - FLOAT_DTYPES, - INTEGER_DTYPES, Categorical, Null, Struct, @@ -9196,8 +9194,8 @@ def _remap_key_or_value_series( # Values Series has same dtype as keys Series. dtype = s.dtype elif ( - (s.dtype in INTEGER_DTYPES and dtype_keys in INTEGER_DTYPES) - or (s.dtype in FLOAT_DTYPES and dtype_keys in FLOAT_DTYPES) + (s.dtype.is_integer() and dtype_keys.is_integer()) + or (s.dtype.is_float() and dtype_keys.is_float()) or (s.dtype == Utf8 and dtype_keys == Categorical) ): # Values Series and keys Series are of similar dtypes, diff --git a/py-polars/polars/io/spreadsheet/_write_utils.py b/py-polars/polars/io/spreadsheet/_write_utils.py index 9738d7352627..406b9a4eabac 100644 --- a/py-polars/polars/io/spreadsheet/_write_utils.py +++ b/py-polars/polars/io/spreadsheet/_write_utils.py @@ -8,7 +8,6 @@ from polars.datatypes import ( FLOAT_DTYPES, INTEGER_DTYPES, - NUMERIC_DTYPES, Date, Datetime, Float64, @@ -371,9 +370,7 @@ def _map_str(s: Series) -> Series: if not row_totals: row_total_funcs = {} else: - numeric_cols = { - col for col, tp in df.schema.items() if tp.base_type() in NUMERIC_DTYPES - } + numeric_cols = {col for col, tp in df.schema.items() if tp.is_numeric()} if not isinstance(row_totals, dict): sum_cols = ( numeric_cols @@ -450,7 +447,7 @@ def _map_str(s: Series) -> Series: if base_type in dtype_formats: fmt = dtype_formats.get(tp, dtype_formats[base_type]) column_formats.setdefault(col, fmt) - if base_type in NUMERIC_DTYPES: + if base_type.is_numeric(): if column_totals is True: column_total_funcs.setdefault(col, "sum") elif isinstance(column_totals, str): diff --git a/py-polars/polars/series/series.py b/py-polars/polars/series/series.py index 18f0af5d2793..a79a09c65804 100644 --- a/py-polars/polars/series/series.py +++ b/py-polars/polars/series/series.py @@ -22,12 +22,6 @@ import polars._reexport as pl from polars import functions as F from polars.datatypes import ( - FLOAT_DTYPES, - INTEGER_DTYPES, - NUMERIC_DTYPES, - SIGNED_INTEGER_DTYPES, - TEMPORAL_DTYPES, - UNSIGNED_INTEGER_DTYPES, Array, Boolean, Categorical, @@ -482,7 +476,7 @@ def _comp(self, other: Any, op: ComparisonOperator) -> Series: return self.clone() elif (other is False and op == "eq") or (other is True and op == "neq"): return ~self - elif isinstance(other, float) and self.dtype in INTEGER_DTYPES: + elif isinstance(other, float) and self.dtype.is_integer(): # require upcast when comparing int series to float value self = self.cast(Float64) f = get_ffi_func(op + "_<>", Float64, self._s) @@ -740,7 +734,7 @@ def _arithmetic(self, other: Any, op_s: str, op_ffi: str) -> Self: return self._from_pyseries(getattr(self._s, op_s)(Series(other)._s)) if ( isinstance(other, (float, date, datetime, timedelta, str)) - and not self.is_float() + and not self.dtype.is_float() ): _s = sequence_to_pyseries(self.name, [other]) if "rhs" in op_ffi: @@ -802,11 +796,11 @@ def __truediv__(self, other: Any) -> Series: def __truediv__(self, other: Any) -> Series | Expr: if isinstance(other, pl.Expr): return F.lit(self) / other - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError("first cast to integer before dividing datelike dtypes") # this branch is exactly the floordiv function without rounding the floats - if self.is_float() or self.dtype == Decimal: + if self.dtype.is_float() or self.dtype == Decimal: return self._arithmetic(other, "div", "div_<>") return self.cast(Float64) / other @@ -822,7 +816,7 @@ def __floordiv__(self, other: Any) -> Series: def __floordiv__(self, other: Any) -> Series | Expr: if isinstance(other, pl.Expr): return F.lit(self) // other - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError("first cast to integer before dividing datelike dtypes") if not isinstance(other, pl.Expr): @@ -847,7 +841,7 @@ def __mul__(self, other: Any) -> Series: def __mul__(self, other: Any) -> Series | DataFrame | Expr: if isinstance(other, pl.Expr): return F.lit(self) * other - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError("first cast to integer before multiplying datelike dtypes") elif isinstance(other, pl.DataFrame): return other * self @@ -865,14 +859,14 @@ def __mod__(self, other: Any) -> Series: def __mod__(self, other: Any) -> Series | Expr: if isinstance(other, pl.Expr): return F.lit(self).__mod__(other) - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError( "first cast to integer before applying modulo on datelike dtypes" ) return self._arithmetic(other, "rem", "rem_<>") def __rmod__(self, other: Any) -> Series: - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError( "first cast to integer before applying modulo on datelike dtypes" ) @@ -887,9 +881,9 @@ def __rsub__(self, other: Any) -> Series: return self._arithmetic(other, "sub", "sub_<>_rhs") def __rtruediv__(self, other: Any) -> Series: - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError("first cast to integer before dividing datelike dtypes") - if self.is_float(): + if self.dtype.is_float(): self.__rfloordiv__(other) if isinstance(other, int): @@ -897,12 +891,12 @@ def __rtruediv__(self, other: Any) -> Series: return self.cast(Float64).__rfloordiv__(other) def __rfloordiv__(self, other: Any) -> Series: - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError("first cast to integer before dividing datelike dtypes") return self._arithmetic(other, "div", "div_<>_rhs") def __rmul__(self, other: Any) -> Series: - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError("first cast to integer before multiplying datelike dtypes") return self._arithmetic(other, "mul", "mul_<>") @@ -910,7 +904,7 @@ def __pow__(self, exponent: int | float | None | Series) -> Series: return self.pow(exponent) def __rpow__(self, other: Any) -> Series: - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError( "first cast to integer before raising datelike dtypes to a power" ) @@ -980,7 +974,7 @@ def _pos_idxs(self, size: int) -> Series: if self.dtype == idx_type: return self - if self.dtype not in INTEGER_DTYPES: + if not self.dtype.is_integer(): raise NotImplementedError("unsupported idxs datatype") if self.len() == 0: @@ -994,7 +988,7 @@ def _pos_idxs(self, size: int) -> Series: if self.min() < -(2**32): # type: ignore[operator] raise ValueError("index positions should be bigger than -2^32 + 1") - if self.dtype in SIGNED_INTEGER_DTYPES: + if self.dtype.is_signed_integer(): if self.min() < 0: # type: ignore[operator] if idx_type == UInt32: idxs = self.cast(Int32) if self.dtype in {Int8, Int16} else self @@ -1035,7 +1029,7 @@ def __getitem__( self, item: (int | Series | range | slice | np.ndarray[Any, Any] | list[int]), ) -> Any: - if isinstance(item, Series) and item.dtype in INTEGER_DTYPES: + if isinstance(item, Series) and item.dtype.is_integer(): return self._take_with_series(item._pos_idxs(self.len())) elif _check_for_numpy(item) and isinstance(item, np.ndarray): @@ -1079,7 +1073,7 @@ def __setitem__( self.set_at_idx(key, value) return None elif isinstance(value, Sequence) and not isinstance(value, str): - if self.is_numeric() or self.is_temporal(): + if self.dtype.is_numeric() or self.dtype.is_temporal(): self.set_at_idx(key, value) # type: ignore[arg-type] return None raise TypeError( @@ -1587,7 +1581,7 @@ def describe( if self.len() == 0: raise ValueError("Series must contain at least one value") - elif self.is_numeric(): + elif self.dtype.is_numeric(): s = self.cast(Float64) stats = { "count": s.len(), @@ -1612,7 +1606,7 @@ def describe( "null_count": self.null_count(), "unique": len(self.unique()), } - elif self.is_temporal(): + elif self.dtype.is_temporal(): # we coerce all to string, because a polars column # only has a single dtype and dates: datetime and count: int don't match stats = { @@ -1685,7 +1679,7 @@ def pow(self, exponent: int | float | None | Series) -> Series: ] """ - if self.is_temporal(): + if self.dtype.is_temporal(): raise TypeError( "first cast to integer before raising datelike dtypes to a power" ) @@ -1757,7 +1751,7 @@ def std(self, ddof: int = 1) -> float | None: 1.0 """ - if not self.is_numeric(): + if not self.dtype.is_numeric(): return None return self.to_frame().select(F.col(self.name).std(ddof)).to_series().item() @@ -1779,7 +1773,7 @@ def var(self, ddof: int = 1) -> float | None: 1.0 """ - if not self.is_numeric(): + if not self.dtype.is_numeric(): return None return self.to_frame().select(F.col(self.name).var(ddof)).to_series().item() @@ -3967,90 +3961,6 @@ def is_between( .to_series() ) - def is_numeric(self) -> bool: - """ - Check if this Series datatype is numeric. - - Examples - -------- - >>> s = pl.Series("a", [1, 2, 3]) - >>> s.is_numeric() - True - - """ - return self.dtype in NUMERIC_DTYPES - - def is_integer(self, signed: bool | None = None) -> bool: - """ - Check if this Series datatype is an integer (signed or unsigned). - - Parameters - ---------- - signed - * if `None`, both signed and unsigned integer dtypes will match. - * if `True`, only signed integer dtypes will be considered a match. - * if `False`, only unsigned integer dtypes will be considered a match. - - Examples - -------- - >>> s = pl.Series("a", [1, 2, 3], dtype=pl.UInt32) - >>> s.is_integer() - True - >>> s.is_integer(signed=False) - True - >>> s.is_integer(signed=True) - False - - """ - if signed is None: - return self.dtype in INTEGER_DTYPES - elif signed is True: - return self.dtype in SIGNED_INTEGER_DTYPES - elif signed is False: - return self.dtype in UNSIGNED_INTEGER_DTYPES - - raise ValueError(f"`signed` must be None, True or False; got {signed!r}") - - def is_temporal(self, excluding: OneOrMoreDataTypes | None = None) -> bool: - """ - Check if this Series datatype is temporal. - - Parameters - ---------- - excluding - Optionally exclude one or more temporal dtypes from matching. - - Examples - -------- - >>> from datetime import date - >>> s = pl.Series([date(2021, 1, 1), date(2021, 1, 2), date(2021, 1, 3)]) - >>> s.is_temporal() - True - >>> s.is_temporal(excluding=[pl.Date]) - False - - """ - if excluding is not None: - if not isinstance(excluding, Iterable): - excluding = [excluding] - if self.dtype in excluding: - return False - - return self.dtype in TEMPORAL_DTYPES - - def is_float(self) -> bool: - """ - Check if this Series has floating point numbers. - - Examples - -------- - >>> s = pl.Series("a", [1.0, 2.0, 3.0]) - >>> s.is_float() - True - - """ - return self.dtype in FLOAT_DTYPES - def is_boolean(self) -> bool: """ Check if this Series is a Boolean. @@ -4185,7 +4095,7 @@ def raise_no_zero_copy() -> None: use_pyarrow and _PYARROW_AVAILABLE and self.dtype != Object - and not self.is_temporal(excluding=Time) + and (self.dtype == Time or not self.dtype.is_temporal()) ): return self.to_arrow().to_numpy( *args, zero_copy_only=zero_copy_only, writable=writable @@ -4197,15 +4107,15 @@ def raise_no_zero_copy() -> None: return np.array(self.to_list(), dtype="object") else: if not self.null_count(): - if self.is_temporal(): + if self.dtype.is_temporal(): np_array = convert_to_date(self.view(ignore_nulls=True)) - elif self.is_numeric(): + elif self.dtype.is_numeric(): np_array = self.view(ignore_nulls=True) else: raise_no_zero_copy() np_array = self._s.to_numpy() - elif self.is_temporal(): + elif self.dtype.is_temporal(): np_array = convert_to_date(self.to_physical()._s.to_numpy()) else: raise_no_zero_copy() @@ -6931,6 +6841,113 @@ def shift_and_fill( """ + @deprecate_function("Use `Series.dtype.is_float()` instead.", version="0.19.13") + def is_float(self) -> bool: + """ + Check if this Series has floating point numbers. + + .. deprecated:: 0.19.13 + Use `Series.dtype.is_float()` instead. + + Examples + -------- + >>> s = pl.Series("a", [1.0, 2.0, 3.0]) + >>> s.is_float() # doctest: +SKIP + True + + """ + return self.dtype.is_float() + + @deprecate_function( + "Use `Series.dtype.is_integer()` instead." + " For signed/unsigned variants, use `Series.dtype.is_signed_integer()`" + " or `Series.dtype.is_unsigned_integer()`.", + version="0.19.13", + ) + def is_integer(self, signed: bool | None = None) -> bool: + """ + Check if this Series datatype is an integer (signed or unsigned). + + .. deprecated:: 0.19.13 + Use `Series.dtype.is_integer()` instead. + For signed/unsigned variants, use `Series.dtype.is_signed_integer()` + or `Series.dtype.is_unsigned_integer()`. + + Parameters + ---------- + signed + * if `None`, both signed and unsigned integer dtypes will match. + * if `True`, only signed integer dtypes will be considered a match. + * if `False`, only unsigned integer dtypes will be considered a match. + + Examples + -------- + >>> s = pl.Series("a", [1, 2, 3], dtype=pl.UInt32) + >>> s.is_integer() # doctest: +SKIP + True + >>> s.is_integer(signed=False) # doctest: +SKIP + True + >>> s.is_integer(signed=True) # doctest: +SKIP + False + + """ + if signed is None: + return self.dtype.is_integer() + elif signed is True: + return self.dtype.is_signed_integer() + elif signed is False: + return self.dtype.is_unsigned_integer() + + raise ValueError(f"`signed` must be None, True or False; got {signed!r}") + + @deprecate_function("Use `Series.dtype.is_numeric()` instead.", version="0.19.13") + def is_numeric(self) -> bool: + """ + Check if this Series datatype is numeric. + + .. deprecated:: 0.19.13 + Use `Series.dtype.is_float()` instead. + + Examples + -------- + >>> s = pl.Series("a", [1, 2, 3]) + >>> s.is_numeric() # doctest: +SKIP + True + + """ + return self.dtype.is_numeric() + + @deprecate_function("Use `Series.dtype.is_temporal()` instead.", version="0.19.13") + def is_temporal(self, excluding: OneOrMoreDataTypes | None = None) -> bool: + """ + Check if this Series datatype is temporal. + + .. deprecated:: 0.19.13 + Use `Series.dtype.is_temporal()` instead. + + Parameters + ---------- + excluding + Optionally exclude one or more temporal dtypes from matching. + + Examples + -------- + >>> from datetime import date + >>> s = pl.Series([date(2021, 1, 1), date(2021, 1, 2), date(2021, 1, 3)]) + >>> s.is_temporal() # doctest: +SKIP + True + >>> s.is_temporal(excluding=[pl.Date]) # doctest: +SKIP + False + + """ + if excluding is not None: + if not isinstance(excluding, Iterable): + excluding = [excluding] + if self.dtype in excluding: + return False + + return self.dtype.is_temporal() + # Keep the `list` and `str` properties below at the end of the definition of Series, # as to not confuse mypy with the type annotation `str` and `list` diff --git a/py-polars/polars/testing/asserts/series.py b/py-polars/polars/testing/asserts/series.py index f9e5999318ad..87f857319551 100644 --- a/py-polars/polars/testing/asserts/series.py +++ b/py-polars/polars/testing/asserts/series.py @@ -3,9 +3,7 @@ from typing import TYPE_CHECKING from polars.datatypes import ( - FLOAT_DTYPES, NUMERIC_DTYPES, - UNSIGNED_INTEGER_DTYPES, Array, Categorical, Decimal, @@ -206,11 +204,7 @@ def _assert_series_values_equal( return # Only do inexact checking for numeric types - if ( - check_exact - or left.dtype not in NUMERIC_DTYPES - or right.dtype not in NUMERIC_DTYPES - ): + if check_exact or not left.dtype.is_numeric() or not right.dtype.is_numeric(): raise_assertion_error( "Series", "exact value mismatch", left=left.to_list(), right=right.to_list() ) @@ -301,7 +295,7 @@ def _assert_series_nan_values_match( def _comparing_floats(left: PolarsDataType, right: PolarsDataType) -> bool: - return left in FLOAT_DTYPES and right in FLOAT_DTYPES + return left.is_float() and right.is_float() def _comparing_lists(left: PolarsDataType, right: PolarsDataType) -> bool: @@ -343,7 +337,7 @@ def _assert_series_values_within_tolerance( def _calc_absolute_diff(left: Series, right: Series) -> Series: - if left.dtype in UNSIGNED_INTEGER_DTYPES and right.dtype in UNSIGNED_INTEGER_DTYPES: + if left.dtype.is_unsigned_integer() and right.dtype.is_unsigned_integer(): try: left = left.cast(Int64) right = right.cast(Int64) diff --git a/py-polars/polars/testing/parametric/primitives.py b/py-polars/polars/testing/parametric/primitives.py index fc2fc2358d47..074c93cedc44 100644 --- a/py-polars/polars/testing/parametric/primitives.py +++ b/py-polars/polars/testing/parametric/primitives.py @@ -14,7 +14,6 @@ from polars.dataframe import DataFrame from polars.datatypes import ( DTYPE_TEMPORAL_UNITS, - FLOAT_DTYPES, Categorical, DataType, DataTypeClass, @@ -387,7 +386,7 @@ def draw_series(draw: DrawFn) -> Series: else: dtype_strategy = strategy - if series_dtype in FLOAT_DTYPES and not allow_infinities: + if series_dtype.is_float() and not allow_infinities: dtype_strategy = dtype_strategy.filter( lambda x: not isinstance(x, float) or isfinite(x) ) diff --git a/py-polars/polars/utils/_construction.py b/py-polars/polars/utils/_construction.py index e48f48f31a6f..b0a48c5a29e4 100644 --- a/py-polars/polars/utils/_construction.py +++ b/py-polars/polars/utils/_construction.py @@ -24,7 +24,6 @@ import polars._reexport as pl from polars import functions as F from polars.datatypes import ( - FLOAT_DTYPES, INTEGER_DTYPES, N_INFER_DEFAULT, TEMPORAL_DTYPES, @@ -478,7 +477,7 @@ def sequence_to_pyseries( if value is None else py_type_to_dtype(type(value), raise_unmatched=False) ) - if values_dtype in FLOAT_DTYPES: + if values_dtype is not None and values_dtype.is_float(): raise TypeError( # we do not accept float values as temporal; if this is # required, the caller should explicitly cast to int first. diff --git a/py-polars/polars/utils/various.py b/py-polars/polars/utils/various.py index ba2894698d90..7ef46e24e371 100644 --- a/py-polars/polars/utils/various.py +++ b/py-polars/polars/utils/various.py @@ -88,7 +88,7 @@ def is_int_sequence( if _check_for_numpy(val) and isinstance(val, np.ndarray): return np.issubdtype(val.dtype, np.integer) elif include_series and isinstance(val, pl.Series): - return val.dtype in pl.INTEGER_DTYPES + return val.dtype.is_integer() return isinstance(val, Sequence) and _is_iterable_of(val, int) diff --git a/py-polars/tests/parametric/test_testing.py b/py-polars/tests/parametric/test_testing.py index fd46295b3f8b..5d036000fa3d 100644 --- a/py-polars/tests/parametric/test_testing.py +++ b/py-polars/tests/parametric/test_testing.py @@ -117,13 +117,13 @@ def test_strategy_dtypes( s3: pl.Series, ) -> None: # dataframe, lazyframe - assert all(tp in TEMPORAL_DTYPES for tp in df.dtypes) - assert all(tp not in TEMPORAL_DTYPES for tp in lf.dtypes) + assert all(tp.is_temporal() for tp in df.dtypes) + assert all(not tp.is_temporal() for tp in lf.dtypes) # series assert s1.dtype == pl.Boolean - assert s2.dtype in TEMPORAL_DTYPES - assert s3.dtype not in TEMPORAL_DTYPES + assert s2.dtype.is_temporal() + assert not s3.dtype.is_temporal() @given( diff --git a/py-polars/tests/unit/dataframe/test_df.py b/py-polars/tests/unit/dataframe/test_df.py index 9e1cab76d38f..f03ba4484e28 100644 --- a/py-polars/tests/unit/dataframe/test_df.py +++ b/py-polars/tests/unit/dataframe/test_df.py @@ -18,7 +18,7 @@ import polars as pl import polars.selectors as cs -from polars.datatypes import DTYPE_TEMPORAL_UNITS, FLOAT_DTYPES, INTEGER_DTYPES +from polars.datatypes import DTYPE_TEMPORAL_UNITS, INTEGER_DTYPES from polars.exceptions import ComputeError, TimeZoneAwareConstructorWarning from polars.testing import ( assert_frame_equal, @@ -1145,7 +1145,7 @@ def test_to_numpy_structured() -> None: list(exported_array[name]), ( df[name].fill_null(float("nan")) - if df.schema[name] in FLOAT_DTYPES + if df.schema[name].is_float() else df[name] ).to_list(), ) diff --git a/py-polars/tests/unit/datatypes/test_decimal.py b/py-polars/tests/unit/datatypes/test_decimal.py index 0c03db0d5107..62511ff0fb75 100644 --- a/py-polars/tests/unit/datatypes/test_decimal.py +++ b/py-polars/tests/unit/datatypes/test_decimal.py @@ -94,12 +94,12 @@ def test_to_from_pydecimal_and_format(trim_zeros: bool, expected: str) -> None: def test_init_decimal_dtype() -> None: s = pl.Series("a", [D("-0.01"), D("1.2345678"), D("500")], dtype=pl.Decimal) - assert s.is_numeric() + assert s.dtype.is_numeric() df = pl.DataFrame( {"a": [D("-0.01"), D("1.2345678"), D("500")]}, schema={"a": pl.Decimal} ) - assert df["a"].is_numeric() + assert df["a"].dtype.is_numeric() def test_decimal_convert_to_float_by_schema() -> None: diff --git a/py-polars/tests/unit/datatypes/test_temporal.py b/py-polars/tests/unit/datatypes/test_temporal.py index 7151ede3d684..1a20cdd83786 100644 --- a/py-polars/tests/unit/datatypes/test_temporal.py +++ b/py-polars/tests/unit/datatypes/test_temporal.py @@ -2673,11 +2673,7 @@ def test_series_is_temporal() -> None: pl.Datetime("ns", "Europe/Amsterdam"), }: s = pl.Series([None], dtype=tp) - assert s.is_temporal() is True - - s = pl.Series([datetime(2023, 2, 14, 11, 12, 13)], dtype=pl.Datetime) - for tp in (pl.Datetime, [pl.Datetime], [pl.Time, pl.Datetime]): # type: ignore[assignment] - assert s.is_temporal(excluding=tp) is False + assert s.dtype.is_temporal() is True @pytest.mark.parametrize( diff --git a/py-polars/tests/unit/series/test_series.py b/py-polars/tests/unit/series/test_series.py index 04accc8aa999..f8bea86a4e27 100644 --- a/py-polars/tests/unit/series/test_series.py +++ b/py-polars/tests/unit/series/test_series.py @@ -529,31 +529,43 @@ def test_various() -> None: def test_series_dtype_is() -> None: s = pl.Series("s", [1, 2, 3]) - assert s.is_numeric() - assert s.is_integer() - assert s.is_integer(signed=True) - assert not s.is_integer(signed=False) - assert (s * 0.99).is_float() + assert s.dtype.is_numeric() + assert s.dtype.is_integer() + assert s.dtype.is_signed_integer() + assert not s.dtype.is_unsigned_integer() + assert (s * 0.99).dtype.is_float() s = pl.Series("s", [1, 2, 3], dtype=pl.UInt8) - assert s.is_numeric() - assert s.is_integer() - assert not s.is_integer(signed=True) - assert s.is_integer(signed=False) + assert s.dtype.is_numeric() + assert s.dtype.is_integer() + assert not s.dtype.is_signed_integer() + assert s.dtype.is_unsigned_integer() s = pl.Series("bool", [True, None, False]) - assert not s.is_numeric() + assert not s.dtype.is_numeric() s = pl.Series("s", ["testing..."]) assert s.is_utf8() s = pl.Series("s", [], dtype=pl.Decimal(precision=20, scale=15)) - assert not s.is_float() - assert s.is_numeric() + assert not s.dtype.is_float() + assert s.dtype.is_numeric() assert s.is_empty() s = pl.Series("s", [], dtype=pl.Datetime("ms", time_zone="UTC")) - assert s.is_temporal() + assert s.dtype.is_temporal() + + +def test_series_is_dtype_deprecated() -> None: + s = pl.Series([1.0, 2.0]) + with pytest.deprecated_call(): + assert s.is_float() is True + with pytest.deprecated_call(): + assert s.is_numeric() is True + with pytest.deprecated_call(): + assert s.is_integer() is False + with pytest.deprecated_call(): + assert s.is_temporal() is False def test_series_head_tail_limit() -> None: