Path: blob/main/crates/polars-python/src/expr/general.rs
8354 views
use std::hash::{BuildHasher, Hash, Hasher};1use std::ops::Neg;23use polars::lazy::dsl;4use polars::prelude::*;5use polars::series::ops::NullBehavior;6use polars_core::chunked_array::cast::CastOptions;7use polars_core::series::IsSorted;8use polars_plan::plans::predicates::aexpr_to_skip_batch_predicate;9use polars_plan::plans::{ExprToIRContext, RowEncodingVariant, node_to_expr, to_expr_ir};10use polars_utils::arena::Arena;11use pyo3::class::basic::CompareOp;12use pyo3::prelude::*;1314use super::datatype::PyDataTypeExpr;15use super::selector::PySelector;16use crate::PyExpr;17use crate::conversion::{Wrap, parse_fill_null_strategy};18use crate::error::PyPolarsErr;19use crate::utils::EnterPolarsExt;2021#[pymethods]22impl PyExpr {23fn __richcmp__(&self, other: Self, op: CompareOp) -> Self {24match op {25CompareOp::Eq => self.eq(other),26CompareOp::Ne => self.neq(other),27CompareOp::Gt => self.gt(other),28CompareOp::Lt => self.lt(other),29CompareOp::Ge => self.gt_eq(other),30CompareOp::Le => self.lt_eq(other),31}32}3334fn __hash__(&self) -> isize {35let mut state = PlFixedStateQuality::with_seed(0).build_hasher();36Hash::hash(&self.inner, &mut state);37state.finish() as _38}3940fn __add__(&self, rhs: Self) -> PyResult<Self> {41Ok(dsl::binary_expr(self.inner.clone(), Operator::Plus, rhs.inner).into())42}43fn __sub__(&self, rhs: Self) -> PyResult<Self> {44Ok(dsl::binary_expr(self.inner.clone(), Operator::Minus, rhs.inner).into())45}46fn __mul__(&self, rhs: Self) -> PyResult<Self> {47Ok(dsl::binary_expr(self.inner.clone(), Operator::Multiply, rhs.inner).into())48}49fn __truediv__(&self, rhs: Self) -> PyResult<Self> {50Ok(dsl::binary_expr(self.inner.clone(), Operator::TrueDivide, rhs.inner).into())51}52fn __mod__(&self, rhs: Self) -> PyResult<Self> {53Ok(dsl::binary_expr(self.inner.clone(), Operator::Modulus, rhs.inner).into())54}55fn __floordiv__(&self, rhs: Self) -> PyResult<Self> {56Ok(dsl::binary_expr(self.inner.clone(), Operator::FloorDivide, rhs.inner).into())57}58fn __neg__(&self) -> PyResult<Self> {59Ok(self.inner.clone().neg().into())60}6162fn to_str(&self) -> String {63format!("{:?}", self.inner)64}65fn eq(&self, other: Self) -> Self {66self.inner.clone().eq(other.inner).into()67}6869fn eq_missing(&self, other: Self) -> Self {70self.inner.clone().eq_missing(other.inner).into()71}72fn neq(&self, other: Self) -> Self {73self.inner.clone().neq(other.inner).into()74}75fn neq_missing(&self, other: Self) -> Self {76self.inner.clone().neq_missing(other.inner).into()77}78fn gt(&self, other: Self) -> Self {79self.inner.clone().gt(other.inner).into()80}81fn gt_eq(&self, other: Self) -> Self {82self.inner.clone().gt_eq(other.inner).into()83}84fn lt_eq(&self, other: Self) -> Self {85self.inner.clone().lt_eq(other.inner).into()86}87fn lt(&self, other: Self) -> Self {88self.inner.clone().lt(other.inner).into()89}9091fn alias(&self, name: &str) -> Self {92self.inner.clone().alias(name).into()93}94fn not_(&self) -> Self {95self.inner.clone().not().into()96}97fn is_null(&self) -> Self {98self.inner.clone().is_null().into()99}100fn is_not_null(&self) -> Self {101self.inner.clone().is_not_null().into()102}103104fn is_infinite(&self) -> Self {105self.inner.clone().is_infinite().into()106}107108fn is_finite(&self) -> Self {109self.inner.clone().is_finite().into()110}111112fn is_nan(&self) -> Self {113self.inner.clone().is_nan().into()114}115116fn is_not_nan(&self) -> Self {117self.inner.clone().is_not_nan().into()118}119120fn min(&self) -> Self {121self.inner.clone().min().into()122}123124fn max(&self) -> Self {125self.inner.clone().max().into()126}127128fn min_by(&self, by: Self) -> Self {129self.inner.clone().min_by(by.inner).into()130}131132fn max_by(&self, by: Self) -> Self {133self.inner.clone().max_by(by.inner).into()134}135136#[cfg(feature = "propagate_nans")]137fn nan_max(&self) -> Self {138self.inner.clone().nan_max().into()139}140#[cfg(feature = "propagate_nans")]141fn nan_min(&self) -> Self {142self.inner.clone().nan_min().into()143}144fn mean(&self) -> Self {145self.inner.clone().mean().into()146}147fn median(&self) -> Self {148self.inner.clone().median().into()149}150fn sum(&self) -> Self {151self.inner.clone().sum().into()152}153fn n_unique(&self) -> Self {154self.inner.clone().n_unique().into()155}156fn arg_unique(&self) -> Self {157self.inner.clone().arg_unique().into()158}159fn unique(&self) -> Self {160self.inner.clone().unique().into()161}162fn unique_stable(&self) -> Self {163self.inner.clone().unique_stable().into()164}165fn first(&self, ignore_nulls: bool) -> Self {166if ignore_nulls {167self.inner.clone().first_non_null().into()168} else {169self.inner.clone().first().into()170}171}172fn last(&self, ignore_nulls: bool) -> Self {173if ignore_nulls {174self.inner.clone().last_non_null().into()175} else {176self.inner.clone().last().into()177}178}179fn item(&self, allow_empty: bool) -> Self {180self.inner.clone().item(allow_empty).into()181}182fn implode(&self) -> Self {183self.inner.clone().implode().into()184}185fn quantile(&self, quantile: Self, interpolation: Wrap<QuantileMethod>) -> Self {186self.inner187.clone()188.quantile(quantile.inner, interpolation.0)189.into()190}191192#[pyo3(signature = (breaks, labels, left_closed, include_breaks))]193#[cfg(feature = "cutqcut")]194fn cut(195&self,196breaks: Vec<f64>,197labels: Option<Vec<String>>,198left_closed: bool,199include_breaks: bool,200) -> Self {201self.inner202.clone()203.cut(breaks, labels, left_closed, include_breaks)204.into()205}206#[pyo3(signature = (probs, labels, left_closed, allow_duplicates, include_breaks))]207#[cfg(feature = "cutqcut")]208fn qcut(209&self,210probs: Vec<f64>,211labels: Option<Vec<String>>,212left_closed: bool,213allow_duplicates: bool,214include_breaks: bool,215) -> Self {216self.inner217.clone()218.qcut(probs, labels, left_closed, allow_duplicates, include_breaks)219.into()220}221#[pyo3(signature = (n_bins, labels, left_closed, allow_duplicates, include_breaks))]222#[cfg(feature = "cutqcut")]223fn qcut_uniform(224&self,225n_bins: usize,226labels: Option<Vec<String>>,227left_closed: bool,228allow_duplicates: bool,229include_breaks: bool,230) -> Self {231self.inner232.clone()233.qcut_uniform(234n_bins,235labels,236left_closed,237allow_duplicates,238include_breaks,239)240.into()241}242243#[cfg(feature = "rle")]244fn rle(&self) -> Self {245self.inner.clone().rle().into()246}247#[cfg(feature = "rle")]248fn rle_id(&self) -> Self {249self.inner.clone().rle_id().into()250}251252fn agg_groups(&self) -> Self {253self.inner.clone().agg_groups().into()254}255fn count(&self) -> Self {256self.inner.clone().count().into()257}258fn len(&self) -> Self {259self.inner.clone().len().into()260}261fn value_counts(&self, sort: bool, parallel: bool, name: String, normalize: bool) -> Self {262self.inner263.clone()264.value_counts(sort, parallel, name.as_str(), normalize)265.into()266}267fn unique_counts(&self) -> Self {268self.inner.clone().unique_counts().into()269}270fn null_count(&self) -> Self {271self.inner.clone().null_count().into()272}273fn cast(&self, dtype: PyDataTypeExpr, strict: bool, wrap_numerical: bool) -> Self {274let options = if wrap_numerical {275CastOptions::Overflowing276} else if strict {277CastOptions::Strict278} else {279CastOptions::NonStrict280};281282let expr = self.inner.clone().cast_with_options(dtype.inner, options);283expr.into()284}285fn sort_with(&self, descending: bool, nulls_last: bool) -> Self {286self.inner287.clone()288.sort(SortOptions {289descending,290nulls_last,291multithreaded: true,292maintain_order: false,293limit: None,294})295.into()296}297298fn arg_sort(&self, descending: bool, nulls_last: bool) -> Self {299self.inner.clone().arg_sort(descending, nulls_last).into()300}301302#[cfg(feature = "top_k")]303fn top_k(&self, k: Self) -> Self {304self.inner.clone().top_k(k.inner).into()305}306307#[cfg(feature = "top_k")]308fn top_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {309let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();310self.inner.clone().top_k_by(k.inner, by, reverse).into()311}312313#[cfg(feature = "top_k")]314fn bottom_k(&self, k: Self) -> Self {315self.inner.clone().bottom_k(k.inner).into()316}317318#[cfg(feature = "top_k")]319fn bottom_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {320let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();321self.inner.clone().bottom_k_by(k.inner, by, reverse).into()322}323324#[cfg(feature = "peaks")]325fn peak_min(&self) -> Self {326self.inner.clone().peak_min().into()327}328329#[cfg(feature = "peaks")]330fn peak_max(&self) -> Self {331self.inner.clone().peak_max().into()332}333334fn arg_max(&self) -> Self {335self.inner.clone().arg_max().into()336}337338fn arg_min(&self) -> Self {339self.inner.clone().arg_min().into()340}341342#[cfg(feature = "index_of")]343fn index_of(&self, element: Self) -> Self {344self.inner.clone().index_of(element.inner).into()345}346347#[cfg(feature = "search_sorted")]348#[pyo3(signature = (element, side, descending))]349fn search_sorted(&self, element: Self, side: Wrap<SearchSortedSide>, descending: bool) -> Self {350self.inner351.clone()352.search_sorted(element.inner, side.0, descending)353.into()354}355356fn gather(&self, idx: Self) -> Self {357self.inner.clone().gather(idx.inner).into()358}359360#[pyo3(signature = (idx, null_on_oob=false))]361fn get(&self, idx: Self, null_on_oob: bool) -> Self {362self.inner.clone().get(idx.inner, null_on_oob).into()363}364fn sort_by(365&self,366by: Vec<Self>,367descending: Vec<bool>,368nulls_last: Vec<bool>,369multithreaded: bool,370maintain_order: bool,371) -> Self {372let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();373self.inner374.clone()375.sort_by(376by,377SortMultipleOptions {378descending,379nulls_last,380multithreaded,381maintain_order,382limit: None,383},384)385.into()386}387388#[pyo3(signature = (n, fill_value))]389fn shift(&self, n: Self, fill_value: Option<Self>) -> Self {390let expr = self.inner.clone();391let out = match fill_value {392Some(v) => expr.shift_and_fill(n.inner, v.inner),393None => expr.shift(n.inner),394};395out.into()396}397398fn fill_null(&self, expr: Self) -> Self {399self.inner.clone().fill_null(expr.inner).into()400}401402fn fill_null_with_strategy(&self, strategy: &str, limit: FillNullLimit) -> PyResult<Self> {403let strategy = parse_fill_null_strategy(strategy, limit)?;404Ok(self.inner.clone().fill_null_with_strategy(strategy).into())405}406407fn fill_nan(&self, expr: Self) -> Self {408self.inner.clone().fill_nan(expr.inner).into()409}410411fn drop_nulls(&self) -> Self {412self.inner.clone().drop_nulls().into()413}414415fn drop_nans(&self) -> Self {416self.inner.clone().drop_nans().into()417}418419fn filter(&self, predicate: Self) -> Self {420self.inner.clone().filter(predicate.inner).into()421}422423fn reverse(&self) -> Self {424self.inner.clone().reverse().into()425}426427fn std(&self, ddof: u8) -> Self {428self.inner.clone().std(ddof).into()429}430431fn var(&self, ddof: u8) -> Self {432self.inner.clone().var(ddof).into()433}434435fn is_unique(&self) -> Self {436self.inner.clone().is_unique().into()437}438439fn is_between(&self, lower: Self, upper: Self, closed: Wrap<ClosedInterval>) -> Self {440self.inner441.clone()442.is_between(lower.inner, upper.inner, closed.0)443.into()444}445446fn is_close(&self, other: Self, abs_tol: f64, rel_tol: f64, nans_equal: bool) -> Self {447self.inner448.clone()449.is_close(other.inner, abs_tol, rel_tol, nans_equal)450.into()451}452453#[cfg(feature = "approx_unique")]454fn approx_n_unique(&self) -> Self {455self.inner.clone().approx_n_unique().into()456}457458fn is_first_distinct(&self) -> Self {459self.inner.clone().is_first_distinct().into()460}461462fn is_last_distinct(&self) -> Self {463self.inner.clone().is_last_distinct().into()464}465466fn explode(&self, empty_as_null: bool, keep_nulls: bool) -> Self {467self.inner468.clone()469.explode(ExplodeOptions {470empty_as_null,471keep_nulls,472})473.into()474}475476fn gather_every(&self, n: usize, offset: usize) -> Self {477self.inner.clone().gather_every(n, offset).into()478}479480fn slice(&self, offset: Self, length: Self) -> Self {481self.inner.clone().slice(offset.inner, length.inner).into()482}483484fn append(&self, other: Self, upcast: bool) -> Self {485self.inner.clone().append(other.inner, upcast).into()486}487488fn rechunk(&self) -> Self {489self.inner.clone().rechunk().into()490}491492fn round(&self, decimals: u32, mode: Wrap<RoundMode>) -> Self {493self.inner.clone().round(decimals, mode.0).into()494}495496fn round_sig_figs(&self, digits: i32) -> Self {497self.clone().inner.round_sig_figs(digits).into()498}499500fn floor(&self) -> Self {501self.inner.clone().floor().into()502}503504fn ceil(&self) -> Self {505self.inner.clone().ceil().into()506}507508#[pyo3(signature = (min, max))]509fn clip(&self, min: Option<Self>, max: Option<Self>) -> Self {510let expr = self.inner.clone();511let out = match (min, max) {512(Some(min), Some(max)) => expr.clip(min.inner, max.inner),513(Some(min), None) => expr.clip_min(min.inner),514(None, Some(max)) => expr.clip_max(max.inner),515(None, None) => expr,516};517out.into()518}519520fn abs(&self) -> Self {521self.inner.clone().abs().into()522}523524#[cfg(feature = "trigonometry")]525fn sin(&self) -> Self {526self.inner.clone().sin().into()527}528529#[cfg(feature = "trigonometry")]530fn cos(&self) -> Self {531self.inner.clone().cos().into()532}533534#[cfg(feature = "trigonometry")]535fn tan(&self) -> Self {536self.inner.clone().tan().into()537}538539#[cfg(feature = "trigonometry")]540fn cot(&self) -> Self {541self.inner.clone().cot().into()542}543544#[cfg(feature = "trigonometry")]545fn arcsin(&self) -> Self {546self.inner.clone().arcsin().into()547}548549#[cfg(feature = "trigonometry")]550fn arccos(&self) -> Self {551self.inner.clone().arccos().into()552}553554#[cfg(feature = "trigonometry")]555fn arctan(&self) -> Self {556self.inner.clone().arctan().into()557}558559#[cfg(feature = "trigonometry")]560fn arctan2(&self, y: Self) -> Self {561self.inner.clone().arctan2(y.inner).into()562}563564#[cfg(feature = "trigonometry")]565fn sinh(&self) -> Self {566self.inner.clone().sinh().into()567}568569#[cfg(feature = "trigonometry")]570fn cosh(&self) -> Self {571self.inner.clone().cosh().into()572}573574#[cfg(feature = "trigonometry")]575fn tanh(&self) -> Self {576self.inner.clone().tanh().into()577}578579#[cfg(feature = "trigonometry")]580fn arcsinh(&self) -> Self {581self.inner.clone().arcsinh().into()582}583584#[cfg(feature = "trigonometry")]585fn arccosh(&self) -> Self {586self.inner.clone().arccosh().into()587}588589#[cfg(feature = "trigonometry")]590fn arctanh(&self) -> Self {591self.inner.clone().arctanh().into()592}593594#[cfg(feature = "trigonometry")]595pub fn degrees(&self) -> Self {596self.inner.clone().degrees().into()597}598599#[cfg(feature = "trigonometry")]600pub fn radians(&self) -> Self {601self.inner.clone().radians().into()602}603604#[cfg(feature = "sign")]605fn sign(&self) -> Self {606self.inner.clone().sign().into()607}608609fn is_duplicated(&self) -> Self {610self.inner.clone().is_duplicated().into()611}612613#[pyo3(signature = (partition_by, order_by, order_by_descending, order_by_nulls_last, mapping_strategy))]614fn over(615&self,616partition_by: Option<Vec<Self>>,617order_by: Option<Vec<Self>>,618order_by_descending: bool,619order_by_nulls_last: bool,620mapping_strategy: Wrap<WindowMapping>,621) -> PyResult<Self> {622let partition_by = partition_by.map(|partition_by| {623partition_by624.into_iter()625.map(|e| e.inner)626.collect::<Vec<Expr>>()627});628629let order_by = order_by.map(|order_by| {630(631order_by.into_iter().map(|e| e.inner).collect::<Vec<Expr>>(),632SortOptions {633descending: order_by_descending,634nulls_last: order_by_nulls_last,635maintain_order: false,636..Default::default()637},638)639});640641Ok(self642.inner643.clone()644.over_with_options(partition_by, order_by, mapping_strategy.0)645.map_err(PyPolarsErr::from)?646.into())647}648649fn rolling(650&self,651index_column: PyExpr,652period: &str,653offset: &str,654closed: Wrap<ClosedWindow>,655) -> PyResult<Self> {656let period = Duration::try_parse(period).map_err(PyPolarsErr::from)?;657let offset = Duration::try_parse(offset).map_err(PyPolarsErr::from)?;658let closed = closed.0;659660Ok(self661.inner662.clone()663.rolling(index_column.inner, period, offset, closed)664.into())665}666667fn and_(&self, expr: Self) -> Self {668self.inner.clone().and(expr.inner).into()669}670671fn or_(&self, expr: Self) -> Self {672self.inner.clone().or(expr.inner).into()673}674675fn xor_(&self, expr: Self) -> Self {676self.inner.clone().xor(expr.inner).into()677}678679#[cfg(feature = "is_in")]680fn is_in(&self, expr: Self, nulls_equal: bool) -> Self {681self.inner.clone().is_in(expr.inner, nulls_equal).into()682}683684#[cfg(feature = "repeat_by")]685fn repeat_by(&self, by: Self) -> Self {686self.inner.clone().repeat_by(by.inner).into()687}688689fn pow(&self, exponent: Self) -> Self {690self.inner.clone().pow(exponent.inner).into()691}692693fn sqrt(&self) -> Self {694self.inner.clone().sqrt().into()695}696697fn cbrt(&self) -> Self {698self.inner.clone().cbrt().into()699}700701fn cum_sum(&self, reverse: bool) -> Self {702self.inner.clone().cum_sum(reverse).into()703}704fn cum_max(&self, reverse: bool) -> Self {705self.inner.clone().cum_max(reverse).into()706}707fn cum_min(&self, reverse: bool) -> Self {708self.inner.clone().cum_min(reverse).into()709}710fn cum_prod(&self, reverse: bool) -> Self {711self.inner.clone().cum_prod(reverse).into()712}713fn cum_count(&self, reverse: bool) -> Self {714self.inner.clone().cum_count(reverse).into()715}716717fn cumulative_eval(&self, expr: Self, min_samples: usize) -> Self {718self.inner719.clone()720.cumulative_eval(expr.inner, min_samples)721.into()722}723724fn product(&self) -> Self {725self.inner.clone().product().into()726}727728fn dot(&self, other: Self) -> Self {729self.inner.clone().dot(other.inner).into()730}731732fn reinterpret(&self, signed: bool) -> Self {733self.inner.clone().reinterpret(signed).into()734}735fn mode(&self, maintain_order: bool) -> Self {736self.inner.clone().mode(maintain_order).into()737}738fn interpolate(&self, method: Wrap<InterpolationMethod>) -> Self {739self.inner.clone().interpolate(method.0).into()740}741fn interpolate_by(&self, by: PyExpr) -> Self {742self.inner.clone().interpolate_by(by.inner).into()743}744745fn lower_bound(&self) -> Self {746self.inner.clone().lower_bound().into()747}748749fn upper_bound(&self) -> Self {750self.inner.clone().upper_bound().into()751}752753#[pyo3(signature = (method, descending, seed))]754fn rank(&self, method: Wrap<RankMethod>, descending: bool, seed: Option<u64>) -> Self {755let options = RankOptions {756method: method.0,757descending,758};759self.inner.clone().rank(options, seed).into()760}761762fn diff(&self, n: PyExpr, null_behavior: Wrap<NullBehavior>) -> Self {763self.inner.clone().diff(n.inner, null_behavior.0).into()764}765766#[cfg(feature = "pct_change")]767fn pct_change(&self, n: Self) -> Self {768self.inner.clone().pct_change(n.inner).into()769}770771fn skew(&self, bias: bool) -> Self {772self.inner.clone().skew(bias).into()773}774fn kurtosis(&self, fisher: bool, bias: bool) -> Self {775self.inner.clone().kurtosis(fisher, bias).into()776}777778#[cfg(feature = "dtype-array")]779fn reshape(&self, dims: Vec<i64>) -> Self {780self.inner.clone().reshape(&dims).into()781}782783fn to_physical(&self) -> Self {784self.inner.clone().to_physical().into()785}786787#[pyo3(signature = (seed))]788fn shuffle(&self, seed: Option<u64>) -> Self {789self.inner.clone().shuffle(seed).into()790}791792#[pyo3(signature = (n, with_replacement, shuffle, seed))]793fn sample_n(&self, n: Self, with_replacement: bool, shuffle: bool, seed: Option<u64>) -> Self {794self.inner795.clone()796.sample_n(n.inner, with_replacement, shuffle, seed)797.into()798}799800#[pyo3(signature = (frac, with_replacement, shuffle, seed))]801fn sample_frac(802&self,803frac: Self,804with_replacement: bool,805shuffle: bool,806seed: Option<u64>,807) -> Self {808self.inner809.clone()810.sample_frac(frac.inner, with_replacement, shuffle, seed)811.into()812}813814fn ewm_mean(&self, alpha: f64, adjust: bool, min_periods: usize, ignore_nulls: bool) -> Self {815let options = EWMOptions {816alpha,817adjust,818bias: false,819min_periods,820ignore_nulls,821};822self.inner.clone().ewm_mean(options).into()823}824fn ewm_mean_by(&self, times: PyExpr, half_life: &str) -> PyResult<Self> {825let half_life = Duration::try_parse(half_life).map_err(PyPolarsErr::from)?;826Ok(self827.inner828.clone()829.ewm_mean_by(times.inner, half_life)830.into())831}832833fn ewm_std(834&self,835alpha: f64,836adjust: bool,837bias: bool,838min_periods: usize,839ignore_nulls: bool,840) -> Self {841let options = EWMOptions {842alpha,843adjust,844bias,845min_periods,846ignore_nulls,847};848self.inner.clone().ewm_std(options).into()849}850fn ewm_var(851&self,852alpha: f64,853adjust: bool,854bias: bool,855min_periods: usize,856ignore_nulls: bool,857) -> Self {858let options = EWMOptions {859alpha,860adjust,861bias,862min_periods,863ignore_nulls,864};865self.inner.clone().ewm_var(options).into()866}867fn extend_constant(&self, value: PyExpr, n: PyExpr) -> Self {868self.inner869.clone()870.extend_constant(value.inner, n.inner)871.into()872}873874fn any(&self, ignore_nulls: bool) -> Self {875self.inner.clone().any(ignore_nulls).into()876}877fn all(&self, ignore_nulls: bool) -> Self {878self.inner.clone().all(ignore_nulls).into()879}880881fn log(&self, base: PyExpr) -> Self {882self.inner.clone().log(base.inner).into()883}884885fn log1p(&self) -> Self {886self.inner.clone().log1p().into()887}888889fn exp(&self) -> Self {890self.inner.clone().exp().into()891}892893fn entropy(&self, base: f64, normalize: bool) -> Self {894self.inner.clone().entropy(base, normalize).into()895}896fn hash(&self, seed: u64, seed_1: u64, seed_2: u64, seed_3: u64) -> Self {897self.inner.clone().hash(seed, seed_1, seed_2, seed_3).into()898}899fn set_sorted_flag(&self, descending: bool) -> Self {900let is_sorted = if descending {901IsSorted::Descending902} else {903IsSorted::Ascending904};905self.inner.clone().set_sorted_flag(is_sorted).into()906}907908fn replace(&self, old: PyExpr, new: PyExpr) -> Self {909self.inner.clone().replace(old.inner, new.inner).into()910}911912#[pyo3(signature = (old, new, default, return_dtype))]913fn replace_strict(914&self,915old: PyExpr,916new: PyExpr,917default: Option<PyExpr>,918return_dtype: Option<PyDataTypeExpr>,919) -> Self {920self.inner921.clone()922.replace_strict(923old.inner,924new.inner,925default.map(|e| e.inner),926return_dtype.map(|dt| dt.inner),927)928.into()929}930931#[cfg(feature = "hist")]932#[pyo3(signature = (bins, bin_count, include_category, include_breakpoint))]933fn hist(934&self,935bins: Option<PyExpr>,936bin_count: Option<usize>,937include_category: bool,938include_breakpoint: bool,939) -> Self {940let bins = bins.map(|e| e.inner);941self.inner942.clone()943.hist(bins, bin_count, include_category, include_breakpoint)944.into()945}946947#[pyo3(signature = (schema))]948fn skip_batch_predicate(&self, py: Python<'_>, schema: Wrap<Schema>) -> PyResult<Option<Self>> {949let mut aexpr_arena = Arena::new();950py.enter_polars(|| {951let mut ctx = ExprToIRContext::new(&mut aexpr_arena, &schema.0);952ctx.allow_unknown = true;953let node = to_expr_ir(self.inner.clone(), &mut ctx)?.node();954let Some(node) = aexpr_to_skip_batch_predicate(node, &mut aexpr_arena, &schema.0)955else {956return Ok(None);957};958let skip_batch_predicate = node_to_expr(node, &aexpr_arena);959PolarsResult::Ok(Some(Self {960inner: skip_batch_predicate,961}))962})963}964965#[staticmethod]966fn row_encode_unordered(exprs: Vec<Self>) -> Self {967Expr::n_ary(968FunctionExpr::RowEncode(RowEncodingVariant::Unordered),969exprs.into_iter().map(|e| e.inner.clone()).collect(),970)971.into()972}973974#[staticmethod]975fn row_encode_ordered(976exprs: Vec<Self>,977descending: Option<Vec<bool>>,978nulls_last: Option<Vec<bool>>,979) -> Self {980Expr::n_ary(981FunctionExpr::RowEncode(RowEncodingVariant::Ordered {982descending,983nulls_last,984broadcast_nulls: None,985}),986exprs.into_iter().map(|e| e.inner.clone()).collect(),987)988.into()989}990991fn row_decode_unordered(&self, names: Vec<String>, datatypes: Vec<PyDataTypeExpr>) -> Self {992let fields = names993.into_iter()994.zip(datatypes)995.map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))996.collect();997self.inner998.clone()999.map_unary(FunctionExpr::RowDecode(1000fields,1001RowEncodingVariant::Unordered,1002))1003.into()1004}10051006fn row_decode_ordered(1007&self,1008names: Vec<String>,1009datatypes: Vec<PyDataTypeExpr>,1010descending: Option<Vec<bool>>,1011nulls_last: Option<Vec<bool>>,1012) -> Self {1013let fields = names1014.into_iter()1015.zip(datatypes)1016.map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))1017.collect::<Vec<_>>();1018self.inner1019.clone()1020.map_unary(FunctionExpr::RowDecode(1021fields,1022RowEncodingVariant::Ordered {1023descending,1024nulls_last,1025broadcast_nulls: None,1026},1027))1028.into()1029}10301031#[allow(clippy::wrong_self_convention)]1032fn into_selector(&self) -> PyResult<PySelector> {1033Ok(self1034.inner1035.clone()1036.into_selector()1037.ok_or_else(1038|| polars_err!(InvalidOperation: "expr `{}` is not a selector", &self.inner),1039)1040.map_err(PyPolarsErr::from)?1041.into())1042}10431044#[staticmethod]1045fn new_selector(selector: PySelector) -> Self {1046Expr::Selector(selector.inner).into()1047}1048}104910501051