diff --git a/polars/Cargo.toml b/polars/Cargo.toml index 628bd68c7181..83dd0fb7a30d 100644 --- a/polars/Cargo.toml +++ b/polars/Cargo.toml @@ -118,7 +118,7 @@ timezones = ["polars-core/timezones"] string_justify = ["polars-lazy/string_justify", "polars-ops/string_justify"] arg_where = ["polars-lazy/arg_where"] date_offset = ["polars-lazy/date_offset"] -trigo = ["polars-lazy/trigo"] +trigonometry = ["polars-lazy/trigonometry"] test = [ "lazy", diff --git a/polars/polars-lazy/Cargo.toml b/polars/polars-lazy/Cargo.toml index 1d93e6ed396d..5bc8c757a186 100644 --- a/polars/polars-lazy/Cargo.toml +++ b/polars/polars-lazy/Cargo.toml @@ -33,7 +33,7 @@ dtype-categorical = ["polars-core/dtype-categorical"] dtype-struct = ["polars-core/dtype-struct"] object = ["polars-core/object"] date_offset = [] -trigo = [] +trigonometry = [] true_div = [] diff --git a/polars/polars-lazy/src/dsl/function_expr/mod.rs b/polars/polars-lazy/src/dsl/function_expr/mod.rs index c72be03baa42..09e780a093b6 100644 --- a/polars/polars-lazy/src/dsl/function_expr/mod.rs +++ b/polars/polars-lazy/src/dsl/function_expr/mod.rs @@ -7,8 +7,8 @@ mod pow; mod strings; #[cfg(any(feature = "temporal", feature = "date_offset"))] mod temporal; -#[cfg(feature = "trigo")] -mod trigo; +#[cfg(feature = "trigonometry")] +mod trigonometry; use super::*; use polars_core::prelude::*; @@ -37,14 +37,14 @@ pub enum FunctionExpr { StringEndsWith(String), #[cfg(feature = "date_offset")] DateOffset(Duration), - #[cfg(feature = "trigo")] - Trigo(TrigoType), + #[cfg(feature = "trigonometry")] + Trigonometry(TrigonometricFunction), } -#[cfg(feature = "trigo")] +#[cfg(feature = "trigonometry")] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)] -pub enum TrigoType { +pub enum TrigonometricFunction { Sin, Cos, Tan, @@ -54,6 +54,9 @@ pub enum TrigoType { Sinh, Cosh, Tanh, + ArcSinh, + ArcCosh, + ArcTanh, } impl FunctionExpr { @@ -94,8 +97,8 @@ impl FunctionExpr { } #[cfg(feature = "date_offset")] DateOffset(_) => same_type(), - #[cfg(feature = "trigo")] - Trigo(_) => float_dtype(), + #[cfg(feature = "trigonometry")] + Trigonometry(_) => float_dtype(), } } } @@ -174,9 +177,9 @@ impl From for SpecialEq> { DateOffset(offset) => { map_owned_with_args!(temporal::date_offset, offset) } - #[cfg(feature = "trigo")] - Trigo(trigotype) => { - map_with_args!(trigo::trigo, trigotype) + #[cfg(feature = "trigonometry")] + Trigonometry(trig_function) => { + map_with_args!(trigonometry::apply_trigonometric_function, trig_function) } } } diff --git a/polars/polars-lazy/src/dsl/function_expr/trigo.rs b/polars/polars-lazy/src/dsl/function_expr/trigonometry.rs similarity index 56% rename from polars/polars-lazy/src/dsl/function_expr/trigo.rs rename to polars/polars-lazy/src/dsl/function_expr/trigonometry.rs index 73ea54966300..c9f3dca34ec3 100644 --- a/polars/polars-lazy/src/dsl/function_expr/trigo.rs +++ b/polars/polars-lazy/src/dsl/function_expr/trigonometry.rs @@ -2,40 +2,49 @@ use super::*; use num::Float; use polars_core::export::num; -pub(super) fn trigo(s: &Series, trigotype: TrigoType) -> Result { +pub(super) fn apply_trigonometric_function( + s: &Series, + trig_function: TrigonometricFunction, +) -> Result { use DataType::*; match s.dtype() { Float32 => { let ca = s.f32().unwrap(); - trigo_float(ca, trigotype) + apply_trigonometric_function_to_float(ca, trig_function) } Float64 => { let ca = s.f64().unwrap(); - trigo_float(ca, trigotype) + apply_trigonometric_function_to_float(ca, trig_function) } _ => { let s = s.cast(&DataType::Float64)?; - trigo(&s, trigotype) + apply_trigonometric_function(&s, trig_function) } } } -fn trigo_float(ca: &ChunkedArray, trigotype: TrigoType) -> Result +fn apply_trigonometric_function_to_float( + ca: &ChunkedArray, + trig_function: TrigonometricFunction, +) -> Result where T: PolarsFloatType, T::Native: num::Float, ChunkedArray: IntoSeries, { - match trigotype { - TrigoType::Sin => sin(ca), - TrigoType::Cos => cos(ca), - TrigoType::Tan => tan(ca), - TrigoType::ArcSin => arcsin(ca), - TrigoType::ArcCos => arccos(ca), - TrigoType::ArcTan => arctan(ca), - TrigoType::Sinh => sinh(ca), - TrigoType::Cosh => cosh(ca), - TrigoType::Tanh => tanh(ca), + match trig_function { + TrigonometricFunction::Sin => sin(ca), + TrigonometricFunction::Cos => cos(ca), + TrigonometricFunction::Tan => tan(ca), + TrigonometricFunction::ArcSin => arcsin(ca), + TrigonometricFunction::ArcCos => arccos(ca), + TrigonometricFunction::ArcTan => arctan(ca), + TrigonometricFunction::Sinh => sinh(ca), + TrigonometricFunction::Cosh => cosh(ca), + TrigonometricFunction::Tanh => tanh(ca), + TrigonometricFunction::ArcSinh => arcsinh(ca), + TrigonometricFunction::ArcCosh => arccosh(ca), + TrigonometricFunction::ArcTanh => arctanh(ca), } } @@ -119,3 +128,30 @@ where { Ok(ca.apply(|v| v.tanh()).into_series()) } + +fn arcsinh(ca: &ChunkedArray) -> Result +where + T: PolarsFloatType, + T::Native: num::Float, + ChunkedArray: IntoSeries, +{ + Ok(ca.apply(|v| v.asinh()).into_series()) +} + +fn arccosh(ca: &ChunkedArray) -> Result +where + T: PolarsFloatType, + T::Native: num::Float, + ChunkedArray: IntoSeries, +{ + Ok(ca.apply(|v| v.acosh()).into_series()) +} + +fn arctanh(ca: &ChunkedArray) -> Result +where + T: PolarsFloatType, + T::Native: num::Float, + ChunkedArray: IntoSeries, +{ + Ok(ca.apply(|v| v.atanh()).into_series()) +} diff --git a/polars/polars-lazy/src/dsl/mod.rs b/polars/polars-lazy/src/dsl/mod.rs index b4cd7f744de8..6d6f162d2c17 100644 --- a/polars/polars-lazy/src/dsl/mod.rs +++ b/polars/polars-lazy/src/dsl/mod.rs @@ -43,8 +43,8 @@ pub use options::*; use crate::dsl::function_expr::FunctionExpr; -#[cfg(feature = "trigo")] -use crate::dsl::function_expr::TrigoType; +#[cfg(feature = "trigonometry")] +use crate::dsl::function_expr::TrigonometricFunction; use polars_arrow::array::default_arrays::FromData; #[cfg(feature = "diff")] @@ -1203,11 +1203,11 @@ impl Expr { } /// Compute the sine of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn sin(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::Sin), + function: FunctionExpr::Trigonometry(TrigonometricFunction::Sin), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1218,11 +1218,11 @@ impl Expr { } /// Compute the cosine of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn cos(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::Cos), + function: FunctionExpr::Trigonometry(TrigonometricFunction::Cos), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1233,11 +1233,11 @@ impl Expr { } /// Compute the tangent of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn tan(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::Tan), + function: FunctionExpr::Trigonometry(TrigonometricFunction::Tan), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1248,11 +1248,11 @@ impl Expr { } /// Compute the arcsine of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn arcsin(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::ArcSin), + function: FunctionExpr::Trigonometry(TrigonometricFunction::ArcSin), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1263,11 +1263,11 @@ impl Expr { } /// Compute the arccosine of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn arccos(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::ArcCos), + function: FunctionExpr::Trigonometry(TrigonometricFunction::ArcCos), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1278,11 +1278,11 @@ impl Expr { } /// Compute the arctangent of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn arctan(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::ArcTan), + function: FunctionExpr::Trigonometry(TrigonometricFunction::ArcTan), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1293,11 +1293,11 @@ impl Expr { } /// Compute the hyperbolic sine of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn sinh(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::Sinh), + function: FunctionExpr::Trigonometry(TrigonometricFunction::Sinh), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1308,11 +1308,11 @@ impl Expr { } /// Compute the hyperbolic cosine of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn cosh(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::Cosh), + function: FunctionExpr::Trigonometry(TrigonometricFunction::Cosh), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1323,11 +1323,11 @@ impl Expr { } /// Compute the hyperbolic tangent of the given expression - #[cfg(feature = "trigo")] + #[cfg(feature = "trigonometry")] pub fn tanh(self) -> Self { Expr::Function { input: vec![self], - function: FunctionExpr::Trigo(TrigoType::Tanh), + function: FunctionExpr::Trigonometry(TrigonometricFunction::Tanh), options: FunctionOptions { collect_groups: ApplyOptions::ApplyFlat, input_wildcard_expansion: false, @@ -1337,6 +1337,51 @@ impl Expr { } } + /// Compute the hyperbolic sine of the given expression + #[cfg(feature = "trigonometry")] + pub fn arcsinh(self) -> Self { + Expr::Function { + input: vec![self], + function: FunctionExpr::Trigonometry(TrigonometricFunction::ArcSinh), + options: FunctionOptions { + collect_groups: ApplyOptions::ApplyFlat, + input_wildcard_expansion: false, + auto_explode: false, + fmt_str: "arcsinh", + }, + } + } + + /// Compute the hyperbolic cosine of the given expression + #[cfg(feature = "trigonometry")] + pub fn arccosh(self) -> Self { + Expr::Function { + input: vec![self], + function: FunctionExpr::Trigonometry(TrigonometricFunction::ArcCosh), + options: FunctionOptions { + collect_groups: ApplyOptions::ApplyFlat, + input_wildcard_expansion: false, + auto_explode: false, + fmt_str: "arccosh", + }, + } + } + + /// Compute the hyperbolic tangent of the given expression + #[cfg(feature = "trigonometry")] + pub fn arctanh(self) -> Self { + Expr::Function { + input: vec![self], + function: FunctionExpr::Trigonometry(TrigonometricFunction::ArcTanh), + options: FunctionOptions { + collect_groups: ApplyOptions::ApplyFlat, + input_wildcard_expansion: false, + auto_explode: false, + fmt_str: "arctanh", + }, + } + } + /// Filter a single column /// Should be used in aggregation context. If you want to filter on a DataFrame level, use /// [LazyFrame::filter](LazyFrame::filter) diff --git a/polars/src/lib.rs b/polars/src/lib.rs index be84d123f557..34d49115ec51 100644 --- a/polars/src/lib.rs +++ b/polars/src/lib.rs @@ -233,7 +233,7 @@ //! - `cumulative_eval` - Apply expressions over cumulatively increasing windows. //! - `argwhere` Get indices where condition holds. //! - `date_offset` Add an offset to dates that take months and leap years into account. -//! - `trigo` Add trigonometry functionality. +//! - `trigonometry` Trigonometric functions. //! * `DataFrame` pretty printing //! - `fmt` - Activate DataFrame formatting //! diff --git a/py-polars/Cargo.toml b/py-polars/Cargo.toml index bac3c88fbcbb..2748b3908ebc 100644 --- a/py-polars/Cargo.toml +++ b/py-polars/Cargo.toml @@ -109,7 +109,7 @@ features = [ "arg_where", "timezones", "date_offset", - "trigo", + "trigonometry", ] # [patch.crates-io] diff --git a/py-polars/docs/source/reference/expression.rst b/py-polars/docs/source/reference/expression.rst index d90fbcf04b7f..71d249abcbaf 100644 --- a/py-polars/docs/source/reference/expression.rst +++ b/py-polars/docs/source/reference/expression.rst @@ -123,8 +123,11 @@ Computations Expr.abs Expr.arccos + Expr.arccosh Expr.arcsin + Expr.arcsinh Expr.arctan + Expr.arctanh Expr.arg_unique Expr.cos Expr.cosh diff --git a/py-polars/docs/source/reference/series.rst b/py-polars/docs/source/reference/series.rst index c413d87c79f8..854e00fe1838 100644 --- a/py-polars/docs/source/reference/series.rst +++ b/py-polars/docs/source/reference/series.rst @@ -103,8 +103,11 @@ Computations Series.abs Series.arccos + Series.arccosh Series.arcsin + Series.arcsinh Series.arctan + Series.arctanh Series.arg_true Series.arg_unique Series.cos diff --git a/py-polars/polars/internals/expr.py b/py-polars/polars/internals/expr.py index def3920cfa26..a03d539ee2d7 100644 --- a/py-polars/polars/internals/expr.py +++ b/py-polars/polars/internals/expr.py @@ -4424,6 +4424,78 @@ def tanh(self) -> Expr: """ return wrap_expr(self._pyexpr.tanh()) + def arcsinh(self) -> Expr: + """ + Compute the element-wise value for the inverse hyperbolic sine. + + Returns + ------- + Series of dtype Float64 + + Examples + -------- + >>> df = pl.DataFrame({"a": [1.0]}) + >>> df.select(pl.col("a").arcsinh()) + shape: (1, 1) + ┌──────────┐ + │ a │ + │ --- │ + │ f64 │ + ╞══════════╡ + │ 0.881374 │ + └──────────┘ + + """ + return wrap_expr(self._pyexpr.arcsinh()) + + def arccosh(self) -> Expr: + """ + Compute the element-wise value for the inverse hyperbolic cosine. + + Returns + ------- + Series of dtype Float64 + + Examples + -------- + >>> df = pl.DataFrame({"a": [1.0]}) + >>> df.select(pl.col("a").arccosh()) + shape: (1, 1) + ┌─────┐ + │ a │ + │ --- │ + │ f64 │ + ╞═════╡ + │ 0.0 │ + └─────┘ + + """ + return wrap_expr(self._pyexpr.arccosh()) + + def arctanh(self) -> Expr: + """ + Compute the element-wise value for the inverse hyperbolic tangent. + + Returns + ------- + Series of dtype Float64 + + Examples + -------- + >>> df = pl.DataFrame({"a": [1.0]}) + >>> df.select(pl.col("a").arctanh()) + shape: (1, 1) + ┌─────┐ + │ a │ + │ --- │ + │ f64 │ + ╞═════╡ + │ inf │ + └─────┘ + + """ + return wrap_expr(self._pyexpr.arctanh()) + def reshape(self, dims: tuple[int, ...]) -> Expr: """ Reshape this Expr to a flat series, shape: (len,) diff --git a/py-polars/polars/internals/series.py b/py-polars/polars/internals/series.py index 7c16134942cc..c658a9c2ce31 100644 --- a/py-polars/polars/internals/series.py +++ b/py-polars/polars/internals/series.py @@ -2709,6 +2709,68 @@ def arctan(self) -> Series: """ return self.to_frame().select(pli.col(self.name).arctan()).to_series() + def arcsinh(self) -> Series: + """ + Compute the element-wise value for the inverse hyperbolic sine. + + Examples + -------- + >>> s = pl.Series("a", [1.0, 0.0, -1.0]) + >>> s.arcsinh() + shape: (3,) + Series: 'a' [f64] + [ + 0.881374 + 0.0 + -0.881374 + ] + + """ + return self.to_frame().select(pli.col(self.name).arcsinh()).to_series() + + def arccosh(self) -> Series: + """ + Compute the element-wise value for the inverse hyperbolic cosine. + + Examples + -------- + >>> s = pl.Series("a", [5.0, 1.0, 0.0, -1.0]) + >>> s.arccosh() + shape: (4,) + Series: 'a' [f64] + [ + 2.292432 + 0.0 + NaN + NaN + ] + + """ + return self.to_frame().select(pli.col(self.name).arccosh()).to_series() + + def arctanh(self) -> Series: + """ + Compute the element-wise value for the inverse hyperbolic tangent. + + Examples + -------- + >>> s = pl.Series("a", [2.0, 1.0, 0.5, 0.0, -0.5, -1.0, -1.1]) + >>> s.arctanh() + shape: (7,) + Series: 'a' [f64] + [ + NaN + inf + 0.549306 + 0.0 + -0.549306 + -inf + NaN + ] + + """ + return self.to_frame().select(pli.col(self.name).arctanh()).to_series() + def sinh(self) -> Series: """ Compute the element-wise value for the hyperbolic sine. diff --git a/py-polars/src/lazy/dsl.rs b/py-polars/src/lazy/dsl.rs index 36443aa10010..6fbdfd5cbf3c 100644 --- a/py-polars/src/lazy/dsl.rs +++ b/py-polars/src/lazy/dsl.rs @@ -399,6 +399,18 @@ impl PyExpr { self.clone().inner.tanh().into() } + pub fn arcsinh(&self) -> PyExpr { + self.clone().inner.arcsinh().into() + } + + pub fn arccosh(&self) -> PyExpr { + self.clone().inner.arccosh().into() + } + + pub fn arctanh(&self) -> PyExpr { + self.clone().inner.arctanh().into() + } + pub fn is_duplicated(&self) -> PyExpr { self.clone().inner.is_duplicated().into() } diff --git a/py-polars/tests/test_series.py b/py-polars/tests/test_series.py index cf0c1802b9bb..0f3117bd2774 100644 --- a/py-polars/tests/test_series.py +++ b/py-polars/tests/test_series.py @@ -1442,8 +1442,23 @@ def test_is_between_datetime() -> None: @pytest.mark.parametrize( - "f", ["sin", "cos", "tan", "arcsin", "arccos", "arctan", "sinh", "cosh", "tanh"] + "f", + [ + "sin", + "cos", + "tan", + "arcsin", + "arccos", + "arctan", + "sinh", + "cosh", + "tanh", + "arcsinh", + "arccosh", + "arctanh", + ], ) +@pytest.mark.filterwarnings("ignore:invalid value encountered:RuntimeWarning") def test_trigonometric(f: str) -> None: s = pl.Series("a", [0.0]) expected = pl.Series("a", getattr(np, f)(s.to_numpy()))