Path: blob/main/crates/polars-python/src/expr/general.rs
7889 views
use std::ops::Neg;12use polars::lazy::dsl;3use polars::prelude::*;4use polars::series::ops::NullBehavior;5use polars_core::chunked_array::cast::CastOptions;6use polars_core::series::IsSorted;7use polars_plan::plans::predicates::aexpr_to_skip_batch_predicate;8use polars_plan::plans::{ExprToIRContext, RowEncodingVariant, node_to_expr, to_expr_ir};9use polars_utils::arena::Arena;10use pyo3::class::basic::CompareOp;11use pyo3::prelude::*;1213use super::datatype::PyDataTypeExpr;14use super::selector::PySelector;15use crate::PyExpr;16use crate::conversion::{Wrap, parse_fill_null_strategy};17use crate::error::PyPolarsErr;18use crate::utils::EnterPolarsExt;1920#[pymethods]21impl PyExpr {22fn __richcmp__(&self, other: Self, op: CompareOp) -> Self {23match op {24CompareOp::Eq => self.eq(other),25CompareOp::Ne => self.neq(other),26CompareOp::Gt => self.gt(other),27CompareOp::Lt => self.lt(other),28CompareOp::Ge => self.gt_eq(other),29CompareOp::Le => self.lt_eq(other),30}31}3233fn __add__(&self, rhs: Self) -> PyResult<Self> {34Ok(dsl::binary_expr(self.inner.clone(), Operator::Plus, rhs.inner).into())35}36fn __sub__(&self, rhs: Self) -> PyResult<Self> {37Ok(dsl::binary_expr(self.inner.clone(), Operator::Minus, rhs.inner).into())38}39fn __mul__(&self, rhs: Self) -> PyResult<Self> {40Ok(dsl::binary_expr(self.inner.clone(), Operator::Multiply, rhs.inner).into())41}42fn __truediv__(&self, rhs: Self) -> PyResult<Self> {43Ok(dsl::binary_expr(self.inner.clone(), Operator::TrueDivide, rhs.inner).into())44}45fn __mod__(&self, rhs: Self) -> PyResult<Self> {46Ok(dsl::binary_expr(self.inner.clone(), Operator::Modulus, rhs.inner).into())47}48fn __floordiv__(&self, rhs: Self) -> PyResult<Self> {49Ok(dsl::binary_expr(self.inner.clone(), Operator::FloorDivide, rhs.inner).into())50}51fn __neg__(&self) -> PyResult<Self> {52Ok(self.inner.clone().neg().into())53}5455fn to_str(&self) -> String {56format!("{:?}", self.inner)57}58fn eq(&self, other: Self) -> Self {59self.inner.clone().eq(other.inner).into()60}6162fn eq_missing(&self, other: Self) -> Self {63self.inner.clone().eq_missing(other.inner).into()64}65fn neq(&self, other: Self) -> Self {66self.inner.clone().neq(other.inner).into()67}68fn neq_missing(&self, other: Self) -> Self {69self.inner.clone().neq_missing(other.inner).into()70}71fn gt(&self, other: Self) -> Self {72self.inner.clone().gt(other.inner).into()73}74fn gt_eq(&self, other: Self) -> Self {75self.inner.clone().gt_eq(other.inner).into()76}77fn lt_eq(&self, other: Self) -> Self {78self.inner.clone().lt_eq(other.inner).into()79}80fn lt(&self, other: Self) -> Self {81self.inner.clone().lt(other.inner).into()82}8384fn alias(&self, name: &str) -> Self {85self.inner.clone().alias(name).into()86}87fn not_(&self) -> Self {88self.inner.clone().not().into()89}90fn is_null(&self) -> Self {91self.inner.clone().is_null().into()92}93fn is_not_null(&self) -> Self {94self.inner.clone().is_not_null().into()95}9697fn is_infinite(&self) -> Self {98self.inner.clone().is_infinite().into()99}100101fn is_finite(&self) -> Self {102self.inner.clone().is_finite().into()103}104105fn is_nan(&self) -> Self {106self.inner.clone().is_nan().into()107}108109fn is_not_nan(&self) -> Self {110self.inner.clone().is_not_nan().into()111}112113fn min(&self) -> Self {114self.inner.clone().min().into()115}116fn max(&self) -> Self {117self.inner.clone().max().into()118}119#[cfg(feature = "propagate_nans")]120fn nan_max(&self) -> Self {121self.inner.clone().nan_max().into()122}123#[cfg(feature = "propagate_nans")]124fn nan_min(&self) -> Self {125self.inner.clone().nan_min().into()126}127fn mean(&self) -> Self {128self.inner.clone().mean().into()129}130fn median(&self) -> Self {131self.inner.clone().median().into()132}133fn sum(&self) -> Self {134self.inner.clone().sum().into()135}136fn n_unique(&self) -> Self {137self.inner.clone().n_unique().into()138}139fn arg_unique(&self) -> Self {140self.inner.clone().arg_unique().into()141}142fn unique(&self) -> Self {143self.inner.clone().unique().into()144}145fn unique_stable(&self) -> Self {146self.inner.clone().unique_stable().into()147}148fn first(&self, ignore_nulls: bool) -> Self {149if ignore_nulls {150self.inner.clone().first_non_null().into()151} else {152self.inner.clone().first().into()153}154}155fn last(&self, ignore_nulls: bool) -> Self {156if ignore_nulls {157self.inner.clone().last_non_null().into()158} else {159self.inner.clone().last().into()160}161}162fn item(&self, allow_empty: bool) -> Self {163self.inner.clone().item(allow_empty).into()164}165fn implode(&self) -> Self {166self.inner.clone().implode().into()167}168fn quantile(&self, quantile: Self, interpolation: Wrap<QuantileMethod>) -> Self {169self.inner170.clone()171.quantile(quantile.inner, interpolation.0)172.into()173}174175#[pyo3(signature = (breaks, labels, left_closed, include_breaks))]176#[cfg(feature = "cutqcut")]177fn cut(178&self,179breaks: Vec<f64>,180labels: Option<Vec<String>>,181left_closed: bool,182include_breaks: bool,183) -> Self {184self.inner185.clone()186.cut(breaks, labels, left_closed, include_breaks)187.into()188}189#[pyo3(signature = (probs, labels, left_closed, allow_duplicates, include_breaks))]190#[cfg(feature = "cutqcut")]191fn qcut(192&self,193probs: Vec<f64>,194labels: Option<Vec<String>>,195left_closed: bool,196allow_duplicates: bool,197include_breaks: bool,198) -> Self {199self.inner200.clone()201.qcut(probs, labels, left_closed, allow_duplicates, include_breaks)202.into()203}204#[pyo3(signature = (n_bins, labels, left_closed, allow_duplicates, include_breaks))]205#[cfg(feature = "cutqcut")]206fn qcut_uniform(207&self,208n_bins: usize,209labels: Option<Vec<String>>,210left_closed: bool,211allow_duplicates: bool,212include_breaks: bool,213) -> Self {214self.inner215.clone()216.qcut_uniform(217n_bins,218labels,219left_closed,220allow_duplicates,221include_breaks,222)223.into()224}225226#[cfg(feature = "rle")]227fn rle(&self) -> Self {228self.inner.clone().rle().into()229}230#[cfg(feature = "rle")]231fn rle_id(&self) -> Self {232self.inner.clone().rle_id().into()233}234235fn agg_groups(&self) -> Self {236self.inner.clone().agg_groups().into()237}238fn count(&self) -> Self {239self.inner.clone().count().into()240}241fn len(&self) -> Self {242self.inner.clone().len().into()243}244fn value_counts(&self, sort: bool, parallel: bool, name: String, normalize: bool) -> Self {245self.inner246.clone()247.value_counts(sort, parallel, name.as_str(), normalize)248.into()249}250fn unique_counts(&self) -> Self {251self.inner.clone().unique_counts().into()252}253fn null_count(&self) -> Self {254self.inner.clone().null_count().into()255}256fn cast(&self, dtype: PyDataTypeExpr, strict: bool, wrap_numerical: bool) -> Self {257let options = if wrap_numerical {258CastOptions::Overflowing259} else if strict {260CastOptions::Strict261} else {262CastOptions::NonStrict263};264265let expr = self.inner.clone().cast_with_options(dtype.inner, options);266expr.into()267}268fn sort_with(&self, descending: bool, nulls_last: bool) -> Self {269self.inner270.clone()271.sort(SortOptions {272descending,273nulls_last,274multithreaded: true,275maintain_order: false,276limit: None,277})278.into()279}280281fn arg_sort(&self, descending: bool, nulls_last: bool) -> Self {282self.inner.clone().arg_sort(descending, nulls_last).into()283}284285#[cfg(feature = "top_k")]286fn top_k(&self, k: Self) -> Self {287self.inner.clone().top_k(k.inner).into()288}289290#[cfg(feature = "top_k")]291fn top_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {292let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();293self.inner.clone().top_k_by(k.inner, by, reverse).into()294}295296#[cfg(feature = "top_k")]297fn bottom_k(&self, k: Self) -> Self {298self.inner.clone().bottom_k(k.inner).into()299}300301#[cfg(feature = "top_k")]302fn bottom_k_by(&self, by: Vec<Self>, k: Self, reverse: Vec<bool>) -> Self {303let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();304self.inner.clone().bottom_k_by(k.inner, by, reverse).into()305}306307#[cfg(feature = "peaks")]308fn peak_min(&self) -> Self {309self.inner.clone().peak_min().into()310}311312#[cfg(feature = "peaks")]313fn peak_max(&self) -> Self {314self.inner.clone().peak_max().into()315}316317fn arg_max(&self) -> Self {318self.inner.clone().arg_max().into()319}320321fn arg_min(&self) -> Self {322self.inner.clone().arg_min().into()323}324325#[cfg(feature = "index_of")]326fn index_of(&self, element: Self) -> Self {327self.inner.clone().index_of(element.inner).into()328}329330#[cfg(feature = "search_sorted")]331#[pyo3(signature = (element, side, descending))]332fn search_sorted(&self, element: Self, side: Wrap<SearchSortedSide>, descending: bool) -> Self {333self.inner334.clone()335.search_sorted(element.inner, side.0, descending)336.into()337}338339fn gather(&self, idx: Self) -> Self {340self.inner.clone().gather(idx.inner).into()341}342343fn get(&self, idx: Self) -> Self {344self.inner.clone().get(idx.inner).into()345}346347fn sort_by(348&self,349by: Vec<Self>,350descending: Vec<bool>,351nulls_last: Vec<bool>,352multithreaded: bool,353maintain_order: bool,354) -> Self {355let by = by.into_iter().map(|e| e.inner).collect::<Vec<_>>();356self.inner357.clone()358.sort_by(359by,360SortMultipleOptions {361descending,362nulls_last,363multithreaded,364maintain_order,365limit: None,366},367)368.into()369}370371#[pyo3(signature = (n, fill_value))]372fn shift(&self, n: Self, fill_value: Option<Self>) -> Self {373let expr = self.inner.clone();374let out = match fill_value {375Some(v) => expr.shift_and_fill(n.inner, v.inner),376None => expr.shift(n.inner),377};378out.into()379}380381fn fill_null(&self, expr: Self) -> Self {382self.inner.clone().fill_null(expr.inner).into()383}384385fn fill_null_with_strategy(&self, strategy: &str, limit: FillNullLimit) -> PyResult<Self> {386let strategy = parse_fill_null_strategy(strategy, limit)?;387Ok(self.inner.clone().fill_null_with_strategy(strategy).into())388}389390fn fill_nan(&self, expr: Self) -> Self {391self.inner.clone().fill_nan(expr.inner).into()392}393394fn drop_nulls(&self) -> Self {395self.inner.clone().drop_nulls().into()396}397398fn drop_nans(&self) -> Self {399self.inner.clone().drop_nans().into()400}401402fn filter(&self, predicate: Self) -> Self {403self.inner.clone().filter(predicate.inner).into()404}405406fn reverse(&self) -> Self {407self.inner.clone().reverse().into()408}409410fn std(&self, ddof: u8) -> Self {411self.inner.clone().std(ddof).into()412}413414fn var(&self, ddof: u8) -> Self {415self.inner.clone().var(ddof).into()416}417418fn is_unique(&self) -> Self {419self.inner.clone().is_unique().into()420}421422fn is_between(&self, lower: Self, upper: Self, closed: Wrap<ClosedInterval>) -> Self {423self.inner424.clone()425.is_between(lower.inner, upper.inner, closed.0)426.into()427}428429fn is_close(&self, other: Self, abs_tol: f64, rel_tol: f64, nans_equal: bool) -> Self {430self.inner431.clone()432.is_close(other.inner, abs_tol, rel_tol, nans_equal)433.into()434}435436#[cfg(feature = "approx_unique")]437fn approx_n_unique(&self) -> Self {438self.inner.clone().approx_n_unique().into()439}440441fn is_first_distinct(&self) -> Self {442self.inner.clone().is_first_distinct().into()443}444445fn is_last_distinct(&self) -> Self {446self.inner.clone().is_last_distinct().into()447}448449fn explode(&self, empty_as_null: bool, keep_nulls: bool) -> Self {450self.inner451.clone()452.explode(ExplodeOptions {453empty_as_null,454keep_nulls,455})456.into()457}458459fn gather_every(&self, n: usize, offset: usize) -> Self {460self.inner.clone().gather_every(n, offset).into()461}462463fn slice(&self, offset: Self, length: Self) -> Self {464self.inner.clone().slice(offset.inner, length.inner).into()465}466467fn append(&self, other: Self, upcast: bool) -> Self {468self.inner.clone().append(other.inner, upcast).into()469}470471fn rechunk(&self) -> Self {472self.inner.clone().rechunk().into()473}474475fn round(&self, decimals: u32, mode: Wrap<RoundMode>) -> Self {476self.inner.clone().round(decimals, mode.0).into()477}478479fn round_sig_figs(&self, digits: i32) -> Self {480self.clone().inner.round_sig_figs(digits).into()481}482483fn floor(&self) -> Self {484self.inner.clone().floor().into()485}486487fn ceil(&self) -> Self {488self.inner.clone().ceil().into()489}490491#[pyo3(signature = (min, max))]492fn clip(&self, min: Option<Self>, max: Option<Self>) -> Self {493let expr = self.inner.clone();494let out = match (min, max) {495(Some(min), Some(max)) => expr.clip(min.inner, max.inner),496(Some(min), None) => expr.clip_min(min.inner),497(None, Some(max)) => expr.clip_max(max.inner),498(None, None) => expr,499};500out.into()501}502503fn abs(&self) -> Self {504self.inner.clone().abs().into()505}506507#[cfg(feature = "trigonometry")]508fn sin(&self) -> Self {509self.inner.clone().sin().into()510}511512#[cfg(feature = "trigonometry")]513fn cos(&self) -> Self {514self.inner.clone().cos().into()515}516517#[cfg(feature = "trigonometry")]518fn tan(&self) -> Self {519self.inner.clone().tan().into()520}521522#[cfg(feature = "trigonometry")]523fn cot(&self) -> Self {524self.inner.clone().cot().into()525}526527#[cfg(feature = "trigonometry")]528fn arcsin(&self) -> Self {529self.inner.clone().arcsin().into()530}531532#[cfg(feature = "trigonometry")]533fn arccos(&self) -> Self {534self.inner.clone().arccos().into()535}536537#[cfg(feature = "trigonometry")]538fn arctan(&self) -> Self {539self.inner.clone().arctan().into()540}541542#[cfg(feature = "trigonometry")]543fn arctan2(&self, y: Self) -> Self {544self.inner.clone().arctan2(y.inner).into()545}546547#[cfg(feature = "trigonometry")]548fn sinh(&self) -> Self {549self.inner.clone().sinh().into()550}551552#[cfg(feature = "trigonometry")]553fn cosh(&self) -> Self {554self.inner.clone().cosh().into()555}556557#[cfg(feature = "trigonometry")]558fn tanh(&self) -> Self {559self.inner.clone().tanh().into()560}561562#[cfg(feature = "trigonometry")]563fn arcsinh(&self) -> Self {564self.inner.clone().arcsinh().into()565}566567#[cfg(feature = "trigonometry")]568fn arccosh(&self) -> Self {569self.inner.clone().arccosh().into()570}571572#[cfg(feature = "trigonometry")]573fn arctanh(&self) -> Self {574self.inner.clone().arctanh().into()575}576577#[cfg(feature = "trigonometry")]578pub fn degrees(&self) -> Self {579self.inner.clone().degrees().into()580}581582#[cfg(feature = "trigonometry")]583pub fn radians(&self) -> Self {584self.inner.clone().radians().into()585}586587#[cfg(feature = "sign")]588fn sign(&self) -> Self {589self.inner.clone().sign().into()590}591592fn is_duplicated(&self) -> Self {593self.inner.clone().is_duplicated().into()594}595596#[pyo3(signature = (partition_by, order_by, order_by_descending, order_by_nulls_last, mapping_strategy))]597fn over(598&self,599partition_by: Option<Vec<Self>>,600order_by: Option<Vec<Self>>,601order_by_descending: bool,602order_by_nulls_last: bool,603mapping_strategy: Wrap<WindowMapping>,604) -> PyResult<Self> {605let partition_by = partition_by.map(|partition_by| {606partition_by607.into_iter()608.map(|e| e.inner)609.collect::<Vec<Expr>>()610});611612let order_by = order_by.map(|order_by| {613(614order_by.into_iter().map(|e| e.inner).collect::<Vec<Expr>>(),615SortOptions {616descending: order_by_descending,617nulls_last: order_by_nulls_last,618maintain_order: false,619..Default::default()620},621)622});623624Ok(self625.inner626.clone()627.over_with_options(partition_by, order_by, mapping_strategy.0)628.map_err(PyPolarsErr::from)?629.into())630}631632fn rolling(633&self,634index_column: PyExpr,635period: &str,636offset: &str,637closed: Wrap<ClosedWindow>,638) -> PyResult<Self> {639let period = Duration::try_parse(period).map_err(PyPolarsErr::from)?;640let offset = Duration::try_parse(offset).map_err(PyPolarsErr::from)?;641let closed = closed.0;642643Ok(self644.inner645.clone()646.rolling(index_column.inner, period, offset, closed)647.into())648}649650fn and_(&self, expr: Self) -> Self {651self.inner.clone().and(expr.inner).into()652}653654fn or_(&self, expr: Self) -> Self {655self.inner.clone().or(expr.inner).into()656}657658fn xor_(&self, expr: Self) -> Self {659self.inner.clone().xor(expr.inner).into()660}661662#[cfg(feature = "is_in")]663fn is_in(&self, expr: Self, nulls_equal: bool) -> Self {664self.inner.clone().is_in(expr.inner, nulls_equal).into()665}666667#[cfg(feature = "repeat_by")]668fn repeat_by(&self, by: Self) -> Self {669self.inner.clone().repeat_by(by.inner).into()670}671672fn pow(&self, exponent: Self) -> Self {673self.inner.clone().pow(exponent.inner).into()674}675676fn sqrt(&self) -> Self {677self.inner.clone().sqrt().into()678}679680fn cbrt(&self) -> Self {681self.inner.clone().cbrt().into()682}683684fn cum_sum(&self, reverse: bool) -> Self {685self.inner.clone().cum_sum(reverse).into()686}687fn cum_max(&self, reverse: bool) -> Self {688self.inner.clone().cum_max(reverse).into()689}690fn cum_min(&self, reverse: bool) -> Self {691self.inner.clone().cum_min(reverse).into()692}693fn cum_prod(&self, reverse: bool) -> Self {694self.inner.clone().cum_prod(reverse).into()695}696fn cum_count(&self, reverse: bool) -> Self {697self.inner.clone().cum_count(reverse).into()698}699700fn cumulative_eval(&self, expr: Self, min_samples: usize) -> Self {701self.inner702.clone()703.cumulative_eval(expr.inner, min_samples)704.into()705}706707fn product(&self) -> Self {708self.inner.clone().product().into()709}710711fn dot(&self, other: Self) -> Self {712self.inner.clone().dot(other.inner).into()713}714715fn reinterpret(&self, signed: bool) -> Self {716self.inner.clone().reinterpret(signed).into()717}718fn mode(&self, maintain_order: bool) -> Self {719self.inner.clone().mode(maintain_order).into()720}721fn interpolate(&self, method: Wrap<InterpolationMethod>) -> Self {722self.inner.clone().interpolate(method.0).into()723}724fn interpolate_by(&self, by: PyExpr) -> Self {725self.inner.clone().interpolate_by(by.inner).into()726}727728fn lower_bound(&self) -> Self {729self.inner.clone().lower_bound().into()730}731732fn upper_bound(&self) -> Self {733self.inner.clone().upper_bound().into()734}735736#[pyo3(signature = (method, descending, seed))]737fn rank(&self, method: Wrap<RankMethod>, descending: bool, seed: Option<u64>) -> Self {738let options = RankOptions {739method: method.0,740descending,741};742self.inner.clone().rank(options, seed).into()743}744745fn diff(&self, n: PyExpr, null_behavior: Wrap<NullBehavior>) -> Self {746self.inner.clone().diff(n.inner, null_behavior.0).into()747}748749#[cfg(feature = "pct_change")]750fn pct_change(&self, n: Self) -> Self {751self.inner.clone().pct_change(n.inner).into()752}753754fn skew(&self, bias: bool) -> Self {755self.inner.clone().skew(bias).into()756}757fn kurtosis(&self, fisher: bool, bias: bool) -> Self {758self.inner.clone().kurtosis(fisher, bias).into()759}760761#[cfg(feature = "dtype-array")]762fn reshape(&self, dims: Vec<i64>) -> Self {763self.inner.clone().reshape(&dims).into()764}765766fn to_physical(&self) -> Self {767self.inner.clone().to_physical().into()768}769770#[pyo3(signature = (seed))]771fn shuffle(&self, seed: Option<u64>) -> Self {772self.inner.clone().shuffle(seed).into()773}774775#[pyo3(signature = (n, with_replacement, shuffle, seed))]776fn sample_n(&self, n: Self, with_replacement: bool, shuffle: bool, seed: Option<u64>) -> Self {777self.inner778.clone()779.sample_n(n.inner, with_replacement, shuffle, seed)780.into()781}782783#[pyo3(signature = (frac, with_replacement, shuffle, seed))]784fn sample_frac(785&self,786frac: Self,787with_replacement: bool,788shuffle: bool,789seed: Option<u64>,790) -> Self {791self.inner792.clone()793.sample_frac(frac.inner, with_replacement, shuffle, seed)794.into()795}796797fn ewm_mean(&self, alpha: f64, adjust: bool, min_periods: usize, ignore_nulls: bool) -> Self {798let options = EWMOptions {799alpha,800adjust,801bias: false,802min_periods,803ignore_nulls,804};805self.inner.clone().ewm_mean(options).into()806}807fn ewm_mean_by(&self, times: PyExpr, half_life: &str) -> PyResult<Self> {808let half_life = Duration::try_parse(half_life).map_err(PyPolarsErr::from)?;809Ok(self810.inner811.clone()812.ewm_mean_by(times.inner, half_life)813.into())814}815816fn ewm_std(817&self,818alpha: f64,819adjust: bool,820bias: bool,821min_periods: usize,822ignore_nulls: bool,823) -> Self {824let options = EWMOptions {825alpha,826adjust,827bias,828min_periods,829ignore_nulls,830};831self.inner.clone().ewm_std(options).into()832}833fn ewm_var(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_var(options).into()849}850fn extend_constant(&self, value: PyExpr, n: PyExpr) -> Self {851self.inner852.clone()853.extend_constant(value.inner, n.inner)854.into()855}856857fn any(&self, ignore_nulls: bool) -> Self {858self.inner.clone().any(ignore_nulls).into()859}860fn all(&self, ignore_nulls: bool) -> Self {861self.inner.clone().all(ignore_nulls).into()862}863864fn log(&self, base: PyExpr) -> Self {865self.inner.clone().log(base.inner).into()866}867868fn log1p(&self) -> Self {869self.inner.clone().log1p().into()870}871872fn exp(&self) -> Self {873self.inner.clone().exp().into()874}875876fn entropy(&self, base: f64, normalize: bool) -> Self {877self.inner.clone().entropy(base, normalize).into()878}879fn hash(&self, seed: u64, seed_1: u64, seed_2: u64, seed_3: u64) -> Self {880self.inner.clone().hash(seed, seed_1, seed_2, seed_3).into()881}882fn set_sorted_flag(&self, descending: bool) -> Self {883let is_sorted = if descending {884IsSorted::Descending885} else {886IsSorted::Ascending887};888self.inner.clone().set_sorted_flag(is_sorted).into()889}890891fn replace(&self, old: PyExpr, new: PyExpr) -> Self {892self.inner.clone().replace(old.inner, new.inner).into()893}894895#[pyo3(signature = (old, new, default, return_dtype))]896fn replace_strict(897&self,898old: PyExpr,899new: PyExpr,900default: Option<PyExpr>,901return_dtype: Option<PyDataTypeExpr>,902) -> Self {903self.inner904.clone()905.replace_strict(906old.inner,907new.inner,908default.map(|e| e.inner),909return_dtype.map(|dt| dt.inner),910)911.into()912}913914#[cfg(feature = "hist")]915#[pyo3(signature = (bins, bin_count, include_category, include_breakpoint))]916fn hist(917&self,918bins: Option<PyExpr>,919bin_count: Option<usize>,920include_category: bool,921include_breakpoint: bool,922) -> Self {923let bins = bins.map(|e| e.inner);924self.inner925.clone()926.hist(bins, bin_count, include_category, include_breakpoint)927.into()928}929930#[pyo3(signature = (schema))]931fn skip_batch_predicate(&self, py: Python<'_>, schema: Wrap<Schema>) -> PyResult<Option<Self>> {932let mut aexpr_arena = Arena::new();933py.enter_polars(|| {934let mut ctx = ExprToIRContext::new(&mut aexpr_arena, &schema.0);935ctx.allow_unknown = true;936let node = to_expr_ir(self.inner.clone(), &mut ctx)?.node();937let Some(node) = aexpr_to_skip_batch_predicate(node, &mut aexpr_arena, &schema.0)938else {939return Ok(None);940};941let skip_batch_predicate = node_to_expr(node, &aexpr_arena);942PolarsResult::Ok(Some(Self {943inner: skip_batch_predicate,944}))945})946}947948#[staticmethod]949fn row_encode_unordered(exprs: Vec<Self>) -> Self {950Expr::n_ary(951FunctionExpr::RowEncode(RowEncodingVariant::Unordered),952exprs.into_iter().map(|e| e.inner.clone()).collect(),953)954.into()955}956957#[staticmethod]958fn row_encode_ordered(959exprs: Vec<Self>,960descending: Option<Vec<bool>>,961nulls_last: Option<Vec<bool>>,962) -> Self {963Expr::n_ary(964FunctionExpr::RowEncode(RowEncodingVariant::Ordered {965descending,966nulls_last,967}),968exprs.into_iter().map(|e| e.inner.clone()).collect(),969)970.into()971}972973fn row_decode_unordered(&self, names: Vec<String>, datatypes: Vec<PyDataTypeExpr>) -> Self {974let fields = names975.into_iter()976.zip(datatypes)977.map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))978.collect();979self.inner980.clone()981.map_unary(FunctionExpr::RowDecode(982fields,983RowEncodingVariant::Unordered,984))985.into()986}987988fn row_decode_ordered(989&self,990names: Vec<String>,991datatypes: Vec<PyDataTypeExpr>,992descending: Option<Vec<bool>>,993nulls_last: Option<Vec<bool>>,994) -> Self {995let fields = names996.into_iter()997.zip(datatypes)998.map(|(name, dtype)| (PlSmallStr::from_string(name), dtype.inner))999.collect::<Vec<_>>();1000self.inner1001.clone()1002.map_unary(FunctionExpr::RowDecode(1003fields,1004RowEncodingVariant::Ordered {1005descending,1006nulls_last,1007},1008))1009.into()1010}10111012#[allow(clippy::wrong_self_convention)]1013fn into_selector(&self) -> PyResult<PySelector> {1014Ok(self1015.inner1016.clone()1017.into_selector()1018.ok_or_else(1019|| polars_err!(InvalidOperation: "expr `{}` is not a selector", &self.inner),1020)1021.map_err(PyPolarsErr::from)?1022.into())1023}10241025#[staticmethod]1026fn new_selector(selector: PySelector) -> Self {1027Expr::Selector(selector.inner).into()1028}1029}103010311032