1#[cfg(feature = "python")]
3mod python;
4
5mod cached_arenas;
6mod err;
7#[cfg(not(target_arch = "wasm32"))]
8mod exitable;
9
10use std::num::NonZeroUsize;
11use std::sync::mpsc::{Receiver, sync_channel};
12use std::sync::{Arc, Mutex};
13
14pub use anonymous_scan::*;
15#[cfg(feature = "csv")]
16pub use csv::*;
17#[cfg(not(target_arch = "wasm32"))]
18pub use exitable::*;
19pub use file_list_reader::*;
20#[cfg(feature = "json")]
21pub use ndjson::*;
22#[cfg(feature = "parquet")]
23pub use parquet::*;
24use polars_compute::rolling::QuantileMethod;
25use polars_core::error::feature_gated;
26#[cfg(feature = "pivot")]
27use polars_core::frame::PivotColumnNaming;
28use polars_core::prelude::*;
29use polars_core::query_result::QueryResult;
30#[cfg(feature = "is_between")]
31use polars_defs::expr::ClosedInterval;
32use polars_defs::join::{
33 JoinArgs, JoinBuildSide, JoinCoalesce, JoinType, JoinValidation, MaintainOrderJoin,
34};
35use polars_defs::time::group_by::{DynamicGroupOptions, RollingGroupOptions};
36use polars_io::RowIndex;
37use polars_mem_engine::scan_predicate::functions::apply_scan_predicate_to_scan_ir;
38use polars_mem_engine::{Executor, create_multiple_physical_plans, create_physical_plan};
39use polars_observer::{PlannedQuery, QueryObserver};
40pub use polars_plan::frame::{AllowedOptimizations, OptFlags};
41use polars_plan::plans::ExecutionHooks;
42use polars_plan::prelude::ir_plan_to_description;
43use polars_utils::pl_str::PlSmallStr;
44
45use crate::frame::cached_arenas::CachedArena;
46use crate::prelude::*;
47
48pub trait IntoLazy {
49 fn lazy(self) -> LazyFrame;
50}
51
52impl IntoLazy for DataFrame {
53 fn lazy(self) -> LazyFrame {
55 let lp = DslBuilder::from_existing_df(self).build();
56 LazyFrame {
57 logical_plan: lp,
58 opt_state: Default::default(),
59 cached_arena: Default::default(),
60 }
61 }
62}
63
64fn hive_join(
66 left: &DataFrame,
67 right: &DataFrame,
68 left_on: &str,
69 right_on: &str,
70 args: JoinArgs,
71) -> PolarsResult<DataFrame> {
72 use polars_ops::frame::DataFrameJoinOps;
73
74 left.join(right, [left_on], [right_on], args, None)
75}
76
77impl IntoLazy for LazyFrame {
78 fn lazy(self) -> LazyFrame {
79 self
80 }
81}
82
83#[derive(Clone, Default)]
88#[must_use]
89pub struct LazyFrame {
90 pub logical_plan: DslPlan,
91 pub(crate) opt_state: OptFlags,
92 pub(crate) cached_arena: Arc<Mutex<Option<CachedArena>>>,
93}
94
95impl From<DslPlan> for LazyFrame {
96 fn from(plan: DslPlan) -> Self {
97 Self {
98 logical_plan: plan,
99 opt_state: OptFlags::default(),
100 cached_arena: Default::default(),
101 }
102 }
103}
104
105impl LazyFrame {
106 pub(crate) fn from_inner(
107 logical_plan: DslPlan,
108 opt_state: OptFlags,
109 cached_arena: Arc<Mutex<Option<CachedArena>>>,
110 ) -> Self {
111 Self {
112 logical_plan,
113 opt_state,
114 cached_arena,
115 }
116 }
117
118 pub(crate) fn get_plan_builder(self) -> DslBuilder {
119 DslBuilder::from(self.logical_plan)
120 }
121
122 fn get_opt_state(&self) -> OptFlags {
123 self.opt_state
124 }
125
126 pub fn from_logical_plan(logical_plan: DslPlan, opt_state: OptFlags) -> Self {
127 LazyFrame {
128 logical_plan,
129 opt_state,
130 cached_arena: Default::default(),
131 }
132 }
133
134 pub fn get_current_optimizations(&self) -> OptFlags {
136 self.opt_state
137 }
138
139 pub fn with_optimizations(mut self, opt_state: OptFlags) -> Self {
141 self.opt_state = opt_state;
142 self
143 }
144
145 pub fn without_optimizations(self) -> Self {
147 self.with_optimizations(OptFlags::from_bits_truncate(0) | OptFlags::TYPE_COERCION)
148 }
149
150 pub fn with_projection_pushdown(mut self, toggle: bool) -> Self {
152 self.opt_state.set(OptFlags::PROJECTION_PUSHDOWN, toggle);
153 self
154 }
155
156 pub fn with_cluster_with_columns(mut self, toggle: bool) -> Self {
158 self.opt_state.set(OptFlags::CLUSTER_WITH_COLUMNS, toggle);
159 self
160 }
161
162 pub fn with_check_order(mut self, toggle: bool) -> Self {
165 self.opt_state.set(OptFlags::CHECK_ORDER_OBSERVE, toggle);
166 self
167 }
168
169 pub fn with_predicate_pushdown(mut self, toggle: bool) -> Self {
171 self.opt_state.set(OptFlags::PREDICATE_PUSHDOWN, toggle);
172 self
173 }
174
175 pub fn with_type_coercion(mut self, toggle: bool) -> Self {
177 self.opt_state.set(OptFlags::TYPE_COERCION, toggle);
178 self
179 }
180
181 pub fn with_type_check(mut self, toggle: bool) -> Self {
183 self.opt_state.set(OptFlags::TYPE_CHECK, toggle);
184 self
185 }
186
187 pub fn with_simplify_expr(mut self, toggle: bool) -> Self {
189 self.opt_state.set(OptFlags::SIMPLIFY_EXPR, toggle);
190 self
191 }
192
193 #[cfg(feature = "cse")]
195 pub fn with_comm_subplan_elim(mut self, toggle: bool) -> Self {
196 self.opt_state.set(OptFlags::COMM_SUBPLAN_ELIM, toggle);
197 self
198 }
199
200 #[cfg(feature = "cse")]
202 pub fn with_comm_subexpr_elim(mut self, toggle: bool) -> Self {
203 self.opt_state.set(OptFlags::COMM_SUBEXPR_ELIM, toggle);
204 self
205 }
206
207 pub fn with_slice_pushdown(mut self, toggle: bool) -> Self {
209 self.opt_state.set(OptFlags::SLICE_PUSHDOWN, toggle);
210 self
211 }
212
213 #[cfg(feature = "streaming")]
214 pub fn with_streaming(mut self, toggle: bool) -> Self {
215 self.opt_state.set(OptFlags::STREAMING, toggle);
216 self
217 }
218
219 pub fn with_gpu(mut self, toggle: bool) -> Self {
220 self.opt_state.set(OptFlags::GPU, toggle);
221 self
222 }
223
224 pub fn with_row_estimate(mut self, toggle: bool) -> Self {
226 self.opt_state.set(OptFlags::ROW_ESTIMATE, toggle);
227 self
228 }
229
230 pub fn _with_eager(mut self, toggle: bool) -> Self {
232 self.opt_state.set(OptFlags::EAGER, toggle);
233 self
234 }
235
236 pub fn describe_plan(&self) -> PolarsResult<String> {
238 Ok(self.clone().to_alp()?.describe())
239 }
240
241 pub fn describe_plan_tree(&self) -> PolarsResult<String> {
243 Ok(self.clone().to_alp()?.describe_tree_format())
244 }
245
246 pub fn describe_optimized_plan(&self) -> PolarsResult<String> {
250 Ok(self.clone().to_alp_optimized()?.describe())
251 }
252
253 pub fn describe_optimized_plan_tree(&self) -> PolarsResult<String> {
257 Ok(self.clone().to_alp_optimized()?.describe_tree_format())
258 }
259
260 pub fn explain(&self, optimized: bool) -> PolarsResult<String> {
265 if optimized {
266 self.describe_optimized_plan()
267 } else {
268 self.describe_plan()
269 }
270 }
271
272 pub fn sort(self, by: impl IntoVec<PlSmallStr>, sort_options: SortMultipleOptions) -> Self {
312 let opt_state = self.get_opt_state();
313 let lp = self
314 .get_plan_builder()
315 .sort(by.into_vec().into_iter().map(col).collect(), sort_options)
316 .build();
317 Self::from_logical_plan(lp, opt_state)
318 }
319
320 pub fn sort_by_exprs<E: AsRef<[Expr]>>(
340 self,
341 by_exprs: E,
342 sort_options: SortMultipleOptions,
343 ) -> Self {
344 let by_exprs = by_exprs.as_ref().to_vec();
345 if by_exprs.is_empty() {
346 self
347 } else {
348 let opt_state = self.get_opt_state();
349 let lp = self.get_plan_builder().sort(by_exprs, sort_options).build();
350 Self::from_logical_plan(lp, opt_state)
351 }
352 }
353
354 pub fn top_k<E: AsRef<[Expr]>>(
355 self,
356 k: IdxSize,
357 by_exprs: E,
358 sort_options: SortMultipleOptions,
359 ) -> Self {
360 self.sort_by_exprs(
362 by_exprs,
363 sort_options.with_order_reversed().with_nulls_last(true),
364 )
365 .slice(0, k)
366 }
367
368 pub fn bottom_k<E: AsRef<[Expr]>>(
369 self,
370 k: IdxSize,
371 by_exprs: E,
372 sort_options: SortMultipleOptions,
373 ) -> Self {
374 self.sort_by_exprs(by_exprs, sort_options.with_nulls_last(true))
376 .slice(0, k)
377 }
378
379 pub fn reverse(self) -> Self {
395 self.select(vec![col(PlSmallStr::from_static("*")).reverse()])
396 }
397
398 pub fn rename<I, J, T, S>(self, existing: I, new: J, strict: bool) -> Self
406 where
407 I: IntoIterator<Item = T>,
408 J: IntoIterator<Item = S>,
409 T: AsRef<str>,
410 S: AsRef<str>,
411 {
412 let iter = existing.into_iter();
413 let cap = iter.size_hint().0;
414 let mut existing_vec: Vec<PlSmallStr> = Vec::with_capacity(cap);
415 let mut new_vec: Vec<PlSmallStr> = Vec::with_capacity(cap);
416
417 for (existing, new) in iter.zip(new) {
420 let existing = existing.as_ref();
421 let new = new.as_ref();
422 if new != existing {
423 existing_vec.push(existing.into());
424 new_vec.push(new.into());
425 }
426 }
427
428 self.map_private(DslFunction::Rename {
429 existing: existing_vec.into(),
430 new: new_vec.into(),
431 strict,
432 })
433 }
434
435 pub fn drop(self, columns: Selector) -> Self {
442 let opt_state = self.get_opt_state();
443 let lp = self.get_plan_builder().drop(columns).build();
444 Self::from_logical_plan(lp, opt_state)
445 }
446
447 pub fn shift<E: Into<Expr>>(self, n: E) -> Self {
452 self.select(vec![col(PlSmallStr::from_static("*")).shift(n.into())])
453 }
454
455 pub fn shift_and_fill<E: Into<Expr>, IE: Into<Expr>>(self, n: E, fill_value: IE) -> Self {
460 self.select(vec![
461 col(PlSmallStr::from_static("*")).shift_and_fill(n.into(), fill_value.into()),
462 ])
463 }
464
465 pub fn fill_null<E: Into<Expr>>(self, fill_value: E) -> LazyFrame {
467 let opt_state = self.get_opt_state();
468 let lp = self.get_plan_builder().fill_null(fill_value.into()).build();
469 Self::from_logical_plan(lp, opt_state)
470 }
471
472 pub fn fill_nan<E: Into<Expr>>(self, fill_value: E) -> LazyFrame {
474 let opt_state = self.get_opt_state();
475 let lp = self.get_plan_builder().fill_nan(fill_value.into()).build();
476 Self::from_logical_plan(lp, opt_state)
477 }
478
479 pub fn cache(self) -> Self {
483 let opt_state = self.get_opt_state();
484 let lp = self.get_plan_builder().cache().build();
485 Self::from_logical_plan(lp, opt_state)
486 }
487
488 pub fn cast(self, dtypes: PlHashMap<&str, DataType>, strict: bool) -> Self {
490 let cast_cols: Vec<Expr> = dtypes
491 .into_iter()
492 .map(|(name, dt)| {
493 let name = PlSmallStr::from_str(name);
494
495 if strict {
496 col(name).strict_cast(dt)
497 } else {
498 col(name).cast(dt)
499 }
500 })
501 .collect();
502
503 if cast_cols.is_empty() {
504 self
505 } else {
506 self.with_columns(cast_cols)
507 }
508 }
509
510 pub fn cast_all(self, dtype: impl Into<DataTypeExpr>, strict: bool) -> Self {
512 self.with_columns(vec![if strict {
513 col(PlSmallStr::from_static("*")).strict_cast(dtype)
514 } else {
515 col(PlSmallStr::from_static("*")).cast(dtype)
516 }])
517 }
518
519 pub fn optimize(
520 self,
521 lp_arena: &mut Arena<IR>,
522 expr_arena: &mut Arena<AExpr>,
523 ) -> PolarsResult<Node> {
524 self.optimize_with_scratch(lp_arena, expr_arena, &mut vec![])
525 }
526
527 pub fn to_alp_optimized(mut self) -> PolarsResult<IRPlan> {
528 let (mut lp_arena, mut expr_arena) = self.get_arenas();
529 let node = self.optimize_with_scratch(&mut lp_arena, &mut expr_arena, &mut vec![])?;
530
531 Ok(IRPlan::new(node, lp_arena, expr_arena))
532 }
533
534 pub fn to_alp(mut self) -> PolarsResult<IRPlan> {
535 let (mut lp_arena, mut expr_arena) = self.get_arenas();
536 let node = to_alp(
537 self.logical_plan,
538 &mut expr_arena,
539 &mut lp_arena,
540 &mut self.opt_state,
541 )?;
542 let plan = IRPlan::new(node, lp_arena, expr_arena);
543 Ok(plan)
544 }
545
546 pub(crate) fn optimize_with_scratch(
547 self,
548 ir_arena: &mut Arena<IR>,
549 expr_arena: &mut Arena<AExpr>,
550 scratch: &mut Vec<Node>,
551 ) -> PolarsResult<Node> {
552 let mut opt_flags = self.opt_state;
553 #[allow(clippy::eq_op)]
556 #[cfg(feature = "cse")]
557 if opt_flags.contains(OptFlags::EAGER) {
558 opt_flags &= !(OptFlags::COMM_SUBEXPR_ELIM | OptFlags::COMM_SUBEXPR_ELIM);
559 }
560 let root = to_alp(self.logical_plan, expr_arena, ir_arena, &mut opt_flags)?;
561
562 let lp_top = optimize(
563 root,
564 opt_flags,
565 ir_arena,
566 expr_arena,
567 scratch,
568 ExecutionHooks {
569 apply_scan_predicate_to_scan_ir,
570 hive_join,
571 },
572 )?;
573
574 Ok(lp_top)
575 }
576
577 fn prepare_collect_post_opt<P>(
578 mut self,
579 check_sink: bool,
580 post_opt: P,
581 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)>
582 where
583 P: FnOnce(Node, &mut Arena<IR>, &mut Arena<AExpr>) -> PolarsResult<()>,
584 {
585 let (mut lp_arena, mut expr_arena) = self.get_arenas();
586
587 let mut scratch = vec![];
588 let lp_top = self.optimize_with_scratch(&mut lp_arena, &mut expr_arena, &mut scratch)?;
589 post_opt(lp_top, &mut lp_arena, &mut expr_arena)?;
590
591 let no_file_sink = if check_sink {
593 !matches!(
594 lp_arena.get(lp_top),
595 IR::Sink {
596 payload: SinkTypeIR::File { .. },
597 ..
598 }
599 )
600 } else {
601 true
602 };
603 let physical_plan = create_physical_plan(
604 lp_top,
605 &mut lp_arena,
606 &mut expr_arena,
607 BUILD_STREAMING_EXECUTOR,
608 )?;
609
610 let state = ExecutionState::new();
611 Ok((state, physical_plan, no_file_sink))
612 }
613
614 pub fn _collect_post_opt<P>(self, post_opt: P) -> PolarsResult<DataFrame>
616 where
617 P: FnOnce(Node, &mut Arena<IR>, &mut Arena<AExpr>) -> PolarsResult<()>,
618 {
619 let (mut state, mut physical_plan, _) = self.prepare_collect_post_opt(false, post_opt)?;
620 physical_plan.execute(&mut state)
621 }
622
623 #[allow(unused_mut)]
624 fn prepare_collect(
625 self,
626 check_sink: bool,
627 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)> {
628 self.prepare_collect_post_opt(check_sink, |_, _, _| Ok(()))
629 }
630
631 pub fn collect_with_engine(mut self, engine: Engine) -> PolarsResult<QueryResult> {
636 let engine = match engine {
637 Engine::Streaming => Engine::Streaming,
638 _ if std::env::var("POLARS_FORCE_STREAMING").as_deref() == Ok("1") => Engine::Streaming,
639 Engine::Auto => {
640 if self.opt_state.eager() {
641 Engine::InMemory
642 } else {
643 Engine::Streaming
644 }
645 },
646 v => v,
647 };
648
649 if engine != Engine::Streaming
650 && std::env::var("POLARS_AUTO_STREAMING").as_deref() == Ok("1")
651 {
652 feature_gated!("streaming", {
653 if let Some(r) = self.clone()._collect_with_streaming_suppress_todo_panic() {
654 return r;
655 }
656 })
657 }
658 match engine {
659 Engine::Streaming => {
660 feature_gated!("streaming", self = self.with_streaming(true))
661 },
662 Engine::Gpu => self = self.with_gpu(true),
663 _ => (),
664 }
665
666 let observer = self
667 .opt_state
668 .query_monitoring()
669 .then(polars_observer::new_query_observer)
670 .flatten();
671
672 if let Some(o) = observer.as_ref() {
673 o.on_query_started()
674 }
675
676 let mut ir_plan = self.to_alp_optimized().inspect_err(|err| {
677 if let Some(o) = observer.as_ref() {
678 o.on_query_failed(err)
679 }
680 })?;
681 ir_plan.ensure_root_node_is_sink();
682
683 match engine {
684 Engine::Streaming => feature_gated!("streaming", {
685 polars_stream::run_query(
686 ir_plan.lp_top,
687 &mut ir_plan.lp_arena,
688 &mut ir_plan.expr_arena,
689 observer,
690 )
691 }),
692 Engine::InMemory | Engine::Gpu => run_in_memory_query(
693 ir_plan.lp_top,
694 &mut ir_plan.lp_arena,
695 &mut ir_plan.expr_arena,
696 engine,
697 observer,
698 ),
699 Engine::Auto => unreachable!(),
700 }
701 }
702
703 pub fn explain_all(plans: Vec<DslPlan>, opt_state: OptFlags) -> PolarsResult<String> {
704 let sink_multiple = LazyFrame {
705 logical_plan: DslPlan::SinkMultiple { inputs: plans },
706 opt_state,
707 cached_arena: Default::default(),
708 };
709 sink_multiple.explain(true)
710 }
711
712 pub fn collect_all_with_engine(
713 plans: Vec<DslPlan>,
714 engine: Engine,
715 opt_state: OptFlags,
716 ) -> PolarsResult<Vec<DataFrame>> {
717 if plans.is_empty() {
718 return Ok(Vec::new());
719 }
720
721 LazyFrame {
722 logical_plan: DslPlan::SinkMultiple { inputs: plans },
723 opt_state,
724 cached_arena: Default::default(),
725 }
726 .collect_with_engine(engine)
727 .map(|r| r.unwrap_multiple())
728 }
729
730 pub fn collect(self) -> PolarsResult<DataFrame> {
748 self.collect_with_engine(Engine::Auto).map(|r| match r {
749 QueryResult::Single(df) => df,
750 QueryResult::Multiple(_) => DataFrame::empty(),
752 })
753 }
754
755 #[cfg(feature = "async")]
760 pub fn collect_batches(
761 self,
762 engine: Engine,
763 maintain_order: bool,
764 chunk_size: Option<NonZeroUsize>,
765 lazy: bool,
766 ) -> PolarsResult<CollectBatches> {
767 let (send, recv) = sync_channel(1);
768 let runner_send = send.clone();
769 let ldf = self.sink_batches(
770 PlanCallback::new(move |df| {
771 let send_result = send.send(Ok(df));
773 Ok(send_result.is_err())
774 }),
775 maintain_order,
776 chunk_size,
777 )?;
778 let runner = move || {
779 polars_core::runtime::ASYNC.spawn_blocking(move || {
781 if let Err(e) = ldf.collect_with_engine(engine) {
782 runner_send.send(Err(e)).ok();
783 }
784 });
785 };
786
787 let mut collect_batches = CollectBatches {
788 recv,
789 runner: Some(Box::new(runner)),
790 };
791 if !lazy {
792 collect_batches.start();
793 }
794 Ok(collect_batches)
795 }
796
797 pub fn sink_batches(
798 mut self,
799 function: PlanCallback<DataFrame, bool>,
800 maintain_order: bool,
801 chunk_size: Option<NonZeroUsize>,
802 ) -> PolarsResult<Self> {
803 use polars_plan::prelude::sink::CallbackSinkType;
804
805 polars_ensure!(
806 !matches!(self.logical_plan, DslPlan::Sink { .. }),
807 InvalidOperation: "cannot create a sink on top of another sink"
808 );
809
810 self.logical_plan = DslPlan::Sink {
811 input: Arc::new(self.logical_plan),
812 payload: SinkType::Callback(CallbackSinkType {
813 function,
814 maintain_order,
815 chunk_size,
816 }),
817 };
818
819 Ok(self)
820 }
821
822 #[cfg(feature = "streaming")]
824 fn _collect_with_streaming_suppress_todo_panic(
825 mut self,
826 ) -> Option<PolarsResult<polars_core::query_result::QueryResult>> {
827 self.opt_state |= OptFlags::STREAMING;
828 let mut ir_plan = match self.to_alp_optimized() {
829 Ok(v) => v,
830 Err(e) => return Some(Err(e)),
831 };
832
833 ir_plan.ensure_root_node_is_sink();
834
835 let f = || {
836 polars_stream::run_query(
837 ir_plan.lp_top,
838 &mut ir_plan.lp_arena,
839 &mut ir_plan.expr_arena,
840 None,
841 )
842 };
843
844 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
845 Ok(v) => Some(v),
846 Err(e) => {
847 if e.downcast_ref::<&str>()
850 .is_some_and(|s| s.starts_with("not yet implemented"))
851 {
852 if polars_core::config::verbose() {
853 eprintln!(
854 "caught unimplemented error in new streaming engine, falling back to normal engine"
855 );
856 }
857 None
858 } else {
859 std::panic::resume_unwind(e)
860 }
861 },
862 }
863 }
864
865 pub fn sink(
866 mut self,
867 sink_type: SinkDestination,
868 file_format: FileWriteFormat,
869 unified_sink_args: UnifiedSinkArgs,
870 ) -> PolarsResult<Self> {
871 polars_ensure!(
872 !matches!(self.logical_plan, DslPlan::Sink { .. }),
873 InvalidOperation: "cannot create a sink on top of another sink"
874 );
875
876 self.logical_plan = DslPlan::Sink {
877 input: Arc::new(self.logical_plan),
878 payload: match sink_type {
879 SinkDestination::File { target } => SinkType::File(FileSinkOptions {
880 target,
881 file_format,
882 unified_sink_args,
883 }),
884 SinkDestination::Partitioned {
885 base_path,
886 file_path_provider,
887 partition_strategy,
888 max_rows_per_file,
889 approximate_bytes_per_file,
890 } => SinkType::Partitioned(PartitionedSinkOptions {
891 base_path,
892 file_path_provider,
893 partition_strategy,
894 file_format,
895 unified_sink_args,
896 max_rows_per_file,
897 approximate_bytes_per_file,
898 }),
899 },
900 };
901 Ok(self)
902 }
903
904 pub fn filter(self, predicate: Expr) -> Self {
922 let opt_state = self.get_opt_state();
923 let lp = self.get_plan_builder().filter(predicate).build();
924 Self::from_logical_plan(lp, opt_state)
925 }
926
927 pub fn remove(self, predicate: Expr) -> Self {
945 self.filter(predicate.neq_missing(lit(true)))
946 }
947
948 pub fn select<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
974 let exprs = exprs.as_ref().to_vec();
975 self.select_impl(
976 exprs,
977 ProjectionOptions {
978 run_parallel: true,
979 duplicate_check: true,
980 should_broadcast: true,
981 maintain_dataframe_height: false,
982 },
983 )
984 }
985
986 pub fn select_seq<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
987 let exprs = exprs.as_ref().to_vec();
988 self.select_impl(
989 exprs,
990 ProjectionOptions {
991 run_parallel: false,
992 duplicate_check: true,
993 should_broadcast: true,
994 maintain_dataframe_height: false,
995 },
996 )
997 }
998
999 fn select_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> Self {
1000 let opt_state = self.get_opt_state();
1001 let lp = self.get_plan_builder().project(exprs, options).build();
1002 Self::from_logical_plan(lp, opt_state)
1003 }
1004
1005 pub fn group_by<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1026 let keys = by
1027 .as_ref()
1028 .iter()
1029 .map(|e| e.clone().into())
1030 .collect::<Vec<_>>();
1031 let opt_state = self.get_opt_state();
1032
1033 #[cfg(feature = "dynamic_group_by")]
1034 {
1035 LazyGroupBy {
1036 logical_plan: self.logical_plan,
1037 opt_state,
1038 keys,
1039 predicates: vec![],
1040 maintain_order: false,
1041 dynamic_options: None,
1042 rolling_options: None,
1043 }
1044 }
1045
1046 #[cfg(not(feature = "dynamic_group_by"))]
1047 {
1048 LazyGroupBy {
1049 logical_plan: self.logical_plan,
1050 opt_state,
1051 keys,
1052 predicates: vec![],
1053 maintain_order: false,
1054 }
1055 }
1056 }
1057
1058 #[cfg(feature = "dynamic_group_by")]
1066 pub fn rolling<E: AsRef<[Expr]>>(
1067 mut self,
1068 index_column: Expr,
1069 group_by: E,
1070 mut options: RollingGroupOptions,
1071 ) -> LazyGroupBy {
1072 if let Expr::Column(name) = index_column {
1073 options.index_column = name;
1074 } else {
1075 let output_field = index_column
1076 .to_field(&self.collect_schema().unwrap())
1077 .unwrap();
1078 return self.with_column(index_column).rolling(
1079 Expr::Column(output_field.name().clone()),
1080 group_by,
1081 options,
1082 );
1083 }
1084 let opt_state = self.get_opt_state();
1085 LazyGroupBy {
1086 logical_plan: self.logical_plan,
1087 opt_state,
1088 predicates: vec![],
1089 keys: group_by.as_ref().to_vec(),
1090 maintain_order: true,
1091 dynamic_options: None,
1092 rolling_options: Some(options),
1093 }
1094 }
1095
1096 #[cfg(feature = "dynamic_group_by")]
1112 pub fn group_by_dynamic<E: AsRef<[Expr]>>(
1113 mut self,
1114 index_column: Expr,
1115 group_by: E,
1116 mut options: DynamicGroupOptions,
1117 ) -> LazyGroupBy {
1118 if let Expr::Column(name) = index_column {
1119 options.index_column = name;
1120 } else {
1121 let output_field = index_column
1122 .to_field(&self.collect_schema().unwrap())
1123 .unwrap();
1124 return self.with_column(index_column).group_by_dynamic(
1125 Expr::Column(output_field.name().clone()),
1126 group_by,
1127 options,
1128 );
1129 }
1130 let opt_state = self.get_opt_state();
1131 LazyGroupBy {
1132 logical_plan: self.logical_plan,
1133 opt_state,
1134 predicates: vec![],
1135 keys: group_by.as_ref().to_vec(),
1136 maintain_order: true,
1137 dynamic_options: Some(options),
1138 rolling_options: None,
1139 }
1140 }
1141
1142 pub fn group_by_stable<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1144 let keys = by
1145 .as_ref()
1146 .iter()
1147 .map(|e| e.clone().into())
1148 .collect::<Vec<_>>();
1149 let opt_state = self.get_opt_state();
1150
1151 #[cfg(feature = "dynamic_group_by")]
1152 {
1153 LazyGroupBy {
1154 logical_plan: self.logical_plan,
1155 opt_state,
1156 keys,
1157 predicates: vec![],
1158 maintain_order: true,
1159 dynamic_options: None,
1160 rolling_options: None,
1161 }
1162 }
1163
1164 #[cfg(not(feature = "dynamic_group_by"))]
1165 {
1166 LazyGroupBy {
1167 logical_plan: self.logical_plan,
1168 opt_state,
1169 keys,
1170 predicates: vec![],
1171 maintain_order: true,
1172 }
1173 }
1174 }
1175
1176 #[cfg(feature = "semi_anti_join")]
1193 pub fn anti_join<E: Into<Expr>>(
1194 self,
1195 other: LazyFrame,
1196 left_on: E,
1197 right_on: E,
1198 ) -> PolarsResult<LazyFrame> {
1199 self.join(
1200 other,
1201 [left_on.into()],
1202 [right_on.into()],
1203 JoinArgs::new(JoinType::Anti),
1204 )
1205 }
1206
1207 #[cfg(feature = "cross_join")]
1209 pub fn cross_join(self, other: LazyFrame, suffix: Option<PlSmallStr>) -> LazyFrame {
1210 self.join(
1211 other,
1212 vec![],
1213 vec![],
1214 JoinArgs::new(JoinType::Cross).with_suffix(suffix),
1215 )
1216 .unwrap()
1217 }
1218
1219 pub fn left_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1236 self.join(
1237 other,
1238 [left_on.into()],
1239 [right_on.into()],
1240 JoinArgs::new(JoinType::Left),
1241 )
1242 .unwrap()
1243 }
1244
1245 pub fn inner_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1262 self.join(
1263 other,
1264 [left_on.into()],
1265 [right_on.into()],
1266 JoinArgs::new(JoinType::Inner),
1267 )
1268 .unwrap()
1269 }
1270
1271 pub fn full_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1288 self.join(
1289 other,
1290 [left_on.into()],
1291 [right_on.into()],
1292 JoinArgs::new(JoinType::Full),
1293 )
1294 .unwrap()
1295 }
1296
1297 #[cfg(feature = "semi_anti_join")]
1314 pub fn semi_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1315 self.join(
1316 other,
1317 [left_on.into()],
1318 [right_on.into()],
1319 JoinArgs::new(JoinType::Semi),
1320 )
1321 .unwrap()
1322 }
1323
1324 pub fn join<E: AsRef<[Expr]>>(
1346 self,
1347 other: LazyFrame,
1348 left_on: E,
1349 right_on: E,
1350 args: JoinArgs,
1351 ) -> PolarsResult<LazyFrame> {
1352 let left_on = left_on.as_ref().to_vec();
1353 let right_on = right_on.as_ref().to_vec();
1354
1355 self._join_impl(other, left_on, right_on, args)
1356 }
1357
1358 fn _join_impl(
1359 self,
1360 other: LazyFrame,
1361 left_on: Vec<Expr>,
1362 right_on: Vec<Expr>,
1363 args: JoinArgs,
1364 ) -> PolarsResult<LazyFrame> {
1365 let JoinArgs {
1366 how,
1367 validation,
1368 suffix,
1369 slice,
1370 nulls_equal,
1371 coalesce,
1372 maintain_order,
1373 build_side,
1374 } = args;
1375
1376 if slice.is_some() {
1377 panic!("impl error: slice is not handled")
1378 }
1379
1380 let mut builder = self
1381 .join_builder()
1382 .with(other)
1383 .left_on(left_on)
1384 .right_on(right_on)
1385 .how(how)
1386 .validate(validation)
1387 .join_nulls(nulls_equal)
1388 .coalesce(coalesce)
1389 .maintain_order(maintain_order)
1390 .build_side(build_side);
1391
1392 if let Some(suffix) = suffix {
1393 builder = builder.suffix(suffix);
1394 }
1395
1396 builder.finish()
1398 }
1399
1400 pub fn join_builder(self) -> JoinBuilder {
1406 JoinBuilder::new(self)
1407 }
1408
1409 pub fn gather(self, idxs: LazyFrame, null_on_oob: bool) -> LazyFrame {
1413 let opt_state = self.get_opt_state();
1414 let lp = self
1415 .get_plan_builder()
1416 .gather(idxs.logical_plan, null_on_oob)
1417 .build();
1418 Self::from_logical_plan(lp, opt_state)
1419 }
1420
1421 pub fn with_column(self, expr: Expr) -> LazyFrame {
1439 let opt_state = self.get_opt_state();
1440 let lp = self
1441 .get_plan_builder()
1442 .with_columns(
1443 vec![expr],
1444 ProjectionOptions {
1445 run_parallel: false,
1446 duplicate_check: true,
1447 should_broadcast: true,
1448 maintain_dataframe_height: false,
1449 },
1450 )
1451 .build();
1452 Self::from_logical_plan(lp, opt_state)
1453 }
1454
1455 pub fn with_columns<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1470 let exprs = exprs.as_ref().to_vec();
1471 self.with_columns_impl(
1472 exprs,
1473 ProjectionOptions {
1474 run_parallel: true,
1475 duplicate_check: true,
1476 should_broadcast: true,
1477 maintain_dataframe_height: false,
1478 },
1479 )
1480 }
1481
1482 pub fn with_columns_seq<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1484 let exprs = exprs.as_ref().to_vec();
1485 self.with_columns_impl(
1486 exprs,
1487 ProjectionOptions {
1488 run_parallel: false,
1489 duplicate_check: true,
1490 should_broadcast: true,
1491 maintain_dataframe_height: false,
1492 },
1493 )
1494 }
1495
1496 pub fn match_to_schema(
1498 self,
1499 schema: SchemaRef,
1500 per_column: Arc<[MatchToSchemaPerColumn]>,
1501 extra_columns: ExtraColumnsPolicy,
1502 ) -> LazyFrame {
1503 let opt_state = self.get_opt_state();
1504 let lp = self
1505 .get_plan_builder()
1506 .match_to_schema(schema, per_column, extra_columns)
1507 .build();
1508 Self::from_logical_plan(lp, opt_state)
1509 }
1510
1511 pub fn pipe_with_schema(
1512 self,
1513 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1514 ) -> Self {
1515 let opt_state = self.get_opt_state();
1516 let lp = self
1517 .get_plan_builder()
1518 .pipe_with_schema(vec![], callback)
1519 .build();
1520 Self::from_logical_plan(lp, opt_state)
1521 }
1522
1523 pub fn pipe_with_schemas(
1524 self,
1525 others: Vec<LazyFrame>,
1526 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1527 ) -> Self {
1528 let opt_state = self.get_opt_state();
1529 let lp = self
1530 .get_plan_builder()
1531 .pipe_with_schema(
1532 others.into_iter().map(|lf| lf.logical_plan).collect(),
1533 callback,
1534 )
1535 .build();
1536 Self::from_logical_plan(lp, opt_state)
1537 }
1538
1539 fn with_columns_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> LazyFrame {
1540 let opt_state = self.get_opt_state();
1541 let lp = self.get_plan_builder().with_columns(exprs, options).build();
1542 Self::from_logical_plan(lp, opt_state)
1543 }
1544
1545 pub fn max(self) -> Self {
1549 self.map_private(DslFunction::Stats(StatsFunction::Max))
1550 }
1551
1552 pub fn min(self) -> Self {
1556 self.map_private(DslFunction::Stats(StatsFunction::Min))
1557 }
1558
1559 pub fn sum(self) -> Self {
1569 self.map_private(DslFunction::Stats(StatsFunction::Sum))
1570 }
1571
1572 pub fn mean(self) -> Self {
1577 self.map_private(DslFunction::Stats(StatsFunction::Mean))
1578 }
1579
1580 pub fn median(self) -> Self {
1586 self.map_private(DslFunction::Stats(StatsFunction::Median))
1587 }
1588
1589 pub fn quantile(self, quantile: Expr, method: QuantileMethod) -> Self {
1591 self.map_private(DslFunction::Stats(StatsFunction::Quantile {
1592 quantile,
1593 method,
1594 }))
1595 }
1596
1597 pub fn std(self, ddof: u8) -> Self {
1610 self.map_private(DslFunction::Stats(StatsFunction::Std { ddof }))
1611 }
1612
1613 pub fn var(self, ddof: u8) -> Self {
1623 self.map_private(DslFunction::Stats(StatsFunction::Var { ddof }))
1624 }
1625
1626 pub fn explode(self, columns: Selector, options: ExplodeOptions) -> LazyFrame {
1628 self.explode_impl(columns, options, false)
1629 }
1630
1631 fn explode_impl(
1633 self,
1634 columns: Selector,
1635 options: ExplodeOptions,
1636 allow_empty: bool,
1637 ) -> LazyFrame {
1638 let opt_state = self.get_opt_state();
1639 let lp = self
1640 .get_plan_builder()
1641 .explode(columns, options, allow_empty)
1642 .build();
1643 Self::from_logical_plan(lp, opt_state)
1644 }
1645
1646 pub fn null_count(self) -> LazyFrame {
1648 self.select(vec![col(PlSmallStr::from_static("*")).null_count()])
1649 }
1650
1651 pub fn unique_stable(
1656 self,
1657 subset: Option<Selector>,
1658 keep_strategy: UniqueKeepStrategy,
1659 ) -> LazyFrame {
1660 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1661 self.unique_stable_generic(subset, keep_strategy)
1662 }
1663
1664 pub fn unique_stable_generic(
1665 self,
1666 subset: Option<Vec<Expr>>,
1667 keep_strategy: UniqueKeepStrategy,
1668 ) -> LazyFrame {
1669 let opt_state = self.get_opt_state();
1670 let options = DistinctOptionsDSL {
1671 subset,
1672 maintain_order: true,
1673 keep_strategy,
1674 };
1675 let lp = self.get_plan_builder().distinct(options).build();
1676 Self::from_logical_plan(lp, opt_state)
1677 }
1678
1679 pub fn unique(self, subset: Option<Selector>, keep_strategy: UniqueKeepStrategy) -> LazyFrame {
1687 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1688 self.unique_generic(subset, keep_strategy)
1689 }
1690
1691 pub fn unique_generic(
1692 self,
1693 subset: Option<Vec<Expr>>,
1694 keep_strategy: UniqueKeepStrategy,
1695 ) -> LazyFrame {
1696 let opt_state = self.get_opt_state();
1697 let options = DistinctOptionsDSL {
1698 subset,
1699 maintain_order: false,
1700 keep_strategy,
1701 };
1702 let lp = self.get_plan_builder().distinct(options).build();
1703 Self::from_logical_plan(lp, opt_state)
1704 }
1705
1706 pub fn drop_nans(self, subset: Option<Selector>) -> LazyFrame {
1711 let opt_state = self.get_opt_state();
1712 let lp = self.get_plan_builder().drop_nans(subset).build();
1713 Self::from_logical_plan(lp, opt_state)
1714 }
1715
1716 pub fn drop_nulls(self, subset: Option<Selector>) -> LazyFrame {
1721 let opt_state = self.get_opt_state();
1722 let lp = self.get_plan_builder().drop_nulls(subset).build();
1723 Self::from_logical_plan(lp, opt_state)
1724 }
1725
1726 pub fn slice(self, offset: i64, len: IdxSize) -> LazyFrame {
1736 let opt_state = self.get_opt_state();
1737 let lp = self.get_plan_builder().slice(offset, len).build();
1738 Self::from_logical_plan(lp, opt_state)
1739 }
1740
1741 pub fn clear(self) -> LazyFrame {
1743 self.slice(0, 0)
1744 }
1745
1746 pub fn first(self) -> LazyFrame {
1750 self.slice(0, 1)
1751 }
1752
1753 pub fn last(self) -> LazyFrame {
1757 self.slice(-1, 1)
1758 }
1759
1760 pub fn tail(self, n: IdxSize) -> LazyFrame {
1764 let neg_tail = -(n as i64);
1765 self.slice(neg_tail, n)
1766 }
1767
1768 #[cfg(feature = "pivot")]
1769 #[expect(clippy::too_many_arguments)]
1770 pub fn pivot(
1771 self,
1772 on: Selector,
1773 on_columns: Arc<DataFrame>,
1774 index: Selector,
1775 values: Selector,
1776 agg: Expr,
1777 maintain_order: bool,
1778 separator: PlSmallStr,
1779 column_naming: PivotColumnNaming,
1780 ) -> LazyFrame {
1781 let opt_state = self.get_opt_state();
1782 let lp = self
1783 .get_plan_builder()
1784 .pivot(
1785 on,
1786 on_columns,
1787 index,
1788 values,
1789 agg,
1790 maintain_order,
1791 separator,
1792 column_naming,
1793 )
1794 .build();
1795 Self::from_logical_plan(lp, opt_state)
1796 }
1797
1798 #[cfg(feature = "pivot")]
1802 pub fn unpivot(self, args: UnpivotArgsDSL) -> LazyFrame {
1803 let opt_state = self.get_opt_state();
1804 let lp = self.get_plan_builder().unpivot(args).build();
1805 Self::from_logical_plan(lp, opt_state)
1806 }
1807
1808 pub fn limit(self, n: IdxSize) -> LazyFrame {
1810 self.slice(0, n)
1811 }
1812
1813 pub fn map<F>(
1827 self,
1828 function: F,
1829 optimizations: AllowedOptimizations,
1830 schema: Option<Arc<dyn UdfSchema>>,
1831 name: Option<&'static str>,
1832 ) -> LazyFrame
1833 where
1834 F: 'static + Fn(DataFrame) -> PolarsResult<DataFrame> + Send + Sync,
1835 {
1836 let opt_state = self.get_opt_state();
1837 let lp = self
1838 .get_plan_builder()
1839 .map(
1840 function,
1841 optimizations,
1842 schema,
1843 PlSmallStr::from_static(name.unwrap_or("ANONYMOUS UDF")),
1844 )
1845 .build();
1846 Self::from_logical_plan(lp, opt_state)
1847 }
1848
1849 #[cfg(feature = "python")]
1850 pub fn map_python(
1851 self,
1852 function: polars_utils::python_function::PythonFunction,
1853 optimizations: AllowedOptimizations,
1854 schema: Option<SchemaRef>,
1855 validate_output: bool,
1856 ) -> LazyFrame {
1857 let opt_state = self.get_opt_state();
1858 let lp = self
1859 .get_plan_builder()
1860 .map_python(function, optimizations, schema, validate_output)
1861 .build();
1862 Self::from_logical_plan(lp, opt_state)
1863 }
1864
1865 pub(crate) fn map_private(self, function: DslFunction) -> LazyFrame {
1866 let opt_state = self.get_opt_state();
1867 let lp = self.get_plan_builder().map_private(function).build();
1868 Self::from_logical_plan(lp, opt_state)
1869 }
1870
1871 pub fn with_row_index<S>(self, name: S, offset: Option<IdxSize>) -> LazyFrame
1880 where
1881 S: Into<PlSmallStr>,
1882 {
1883 let name = name.into();
1884
1885 match &self.logical_plan {
1886 v @ DslPlan::Scan {
1887 scan_type,
1888 unified_scan_args,
1889 ..
1890 } if unified_scan_args.row_index.is_none()
1891 && !matches!(
1892 &**scan_type,
1893 FileScanDsl::Anonymous { .. } | FileScanDsl::ExpandedPaths { .. }
1894 ) =>
1895 {
1896 let DslPlan::Scan {
1897 sources,
1898 mut unified_scan_args,
1899 scan_type,
1900 cached_ir: _,
1901 } = v.clone()
1902 else {
1903 unreachable!()
1904 };
1905
1906 unified_scan_args.row_index = Some(RowIndex {
1907 name,
1908 offset: offset.unwrap_or(0),
1909 });
1910
1911 DslPlan::Scan {
1912 sources,
1913 unified_scan_args,
1914 scan_type,
1915 cached_ir: Default::default(),
1916 }
1917 .into()
1918 },
1919 _ => self.map_private(DslFunction::RowIndex { name, offset }),
1920 }
1921 }
1922
1923 pub fn count(self) -> LazyFrame {
1925 self.select(vec![col(PlSmallStr::from_static("*")).count()])
1926 }
1927
1928 #[cfg(feature = "dtype-struct")]
1931 pub fn unnest(self, cols: Selector, separator: Option<PlSmallStr>) -> Self {
1932 self.map_private(DslFunction::Unnest {
1933 columns: cols,
1934 separator,
1935 })
1936 }
1937
1938 #[cfg(feature = "merge_sorted")]
1939 pub fn merge_sorted<I, S>(
1940 self,
1941 other: LazyFrame,
1942 key: I,
1943 maintain_order: bool,
1944 ) -> PolarsResult<LazyFrame>
1945 where
1946 I: IntoIterator<Item = S>,
1947 S: Into<PlSmallStr>,
1948 {
1949 let key: Arc<[PlSmallStr]> = key.into_iter().map(Into::into).collect();
1950
1951 polars_ensure!(
1952 !key.is_empty(),
1953 ComputeError: "merge_sorted requires at least one key column"
1954 );
1955
1956 let lp = DslPlan::MergeSorted {
1957 input_left: Arc::new(self.logical_plan),
1958 input_right: Arc::new(other.logical_plan),
1959 key,
1960 maintain_order,
1961 };
1962 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1963 }
1964
1965 pub fn hint(self, hint: HintIR) -> PolarsResult<LazyFrame> {
1966 let lp = DslPlan::MapFunction {
1967 input: Arc::new(self.logical_plan),
1968 function: DslFunction::Hint(hint),
1969 };
1970 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1971 }
1972}
1973
1974#[derive(Clone)]
1976pub struct LazyGroupBy {
1977 pub logical_plan: DslPlan,
1978 opt_state: OptFlags,
1979 keys: Vec<Expr>,
1980 predicates: Vec<Expr>,
1981 maintain_order: bool,
1982 #[cfg(feature = "dynamic_group_by")]
1983 dynamic_options: Option<DynamicGroupOptions>,
1984 #[cfg(feature = "dynamic_group_by")]
1985 rolling_options: Option<RollingGroupOptions>,
1986}
1987
1988impl From<LazyGroupBy> for LazyFrame {
1989 fn from(lgb: LazyGroupBy) -> Self {
1990 Self {
1991 logical_plan: lgb.logical_plan,
1992 opt_state: lgb.opt_state,
1993 cached_arena: Default::default(),
1994 }
1995 }
1996}
1997
1998impl LazyGroupBy {
1999 pub fn having(mut self, predicate: Expr) -> Self {
2020 self.predicates.push(predicate);
2021 self
2022 }
2023
2024 pub fn agg<E: AsRef<[Expr]>>(self, aggs: E) -> LazyFrame {
2046 #[cfg(feature = "dynamic_group_by")]
2047 let lp = DslBuilder::from(self.logical_plan)
2048 .group_by(
2049 self.keys,
2050 self.predicates,
2051 aggs,
2052 None,
2053 self.maintain_order,
2054 self.dynamic_options,
2055 self.rolling_options,
2056 )
2057 .build();
2058
2059 #[cfg(not(feature = "dynamic_group_by"))]
2060 let lp = DslBuilder::from(self.logical_plan)
2061 .group_by(self.keys, self.predicates, aggs, None, self.maintain_order)
2062 .build();
2063 LazyFrame::from_logical_plan(lp, self.opt_state)
2064 }
2065
2066 pub fn head(self, n: Option<usize>) -> LazyFrame {
2068 let keys = self
2069 .keys
2070 .iter()
2071 .filter_map(|expr| expr_output_name(expr).ok())
2072 .collect::<Vec<_>>();
2073
2074 self.agg([all().as_expr().head(n)]).explode_impl(
2075 all() - by_name(keys.iter().cloned(), false, false),
2076 ExplodeOptions {
2077 empty_as_null: true,
2078 keep_nulls: true,
2079 },
2080 true,
2081 )
2082 }
2083
2084 pub fn tail(self, n: Option<usize>) -> LazyFrame {
2086 let keys = self
2087 .keys
2088 .iter()
2089 .filter_map(|expr| expr_output_name(expr).ok())
2090 .collect::<Vec<_>>();
2091
2092 self.agg([all().as_expr().tail(n)]).explode_impl(
2093 all() - by_name(keys.iter().cloned(), false, false),
2094 ExplodeOptions {
2095 empty_as_null: true,
2096 keep_nulls: true,
2097 },
2098 true,
2099 )
2100 }
2101
2102 pub fn apply(self, f: PlanCallback<DataFrame, DataFrame>, schema: SchemaRef) -> LazyFrame {
2107 if !self.predicates.is_empty() {
2108 panic!("not yet implemented: `apply` cannot be used with `having` predicates");
2109 }
2110
2111 #[cfg(feature = "dynamic_group_by")]
2112 let options = GroupbyOptions {
2113 dynamic: self.dynamic_options,
2114 rolling: self.rolling_options,
2115 slice: None,
2116 };
2117
2118 #[cfg(not(feature = "dynamic_group_by"))]
2119 let options = GroupbyOptions { slice: None };
2120
2121 let lp = DslPlan::GroupBy {
2122 input: Arc::new(self.logical_plan),
2123 keys: self.keys,
2124 predicates: vec![],
2125 aggs: vec![],
2126 apply: Some((f, schema)),
2127 maintain_order: self.maintain_order,
2128 options: Arc::new(options),
2129 };
2130 LazyFrame::from_logical_plan(lp, self.opt_state)
2131 }
2132}
2133
2134#[must_use]
2135pub struct JoinBuilder {
2136 lf: LazyFrame,
2137 how: JoinType,
2138 other: Option<LazyFrame>,
2139 left_on: Vec<Expr>,
2140 right_on: Vec<Expr>,
2141 allow_parallel: bool,
2142 force_parallel: bool,
2143 suffix: Option<PlSmallStr>,
2144 validation: JoinValidation,
2145 nulls_equal: bool,
2146 coalesce: JoinCoalesce,
2147 maintain_order: MaintainOrderJoin,
2148 build_side: Option<JoinBuildSide>,
2149}
2150impl JoinBuilder {
2151 pub fn new(lf: LazyFrame) -> Self {
2153 Self {
2154 lf,
2155 other: None,
2156 how: JoinType::Inner,
2157 left_on: vec![],
2158 right_on: vec![],
2159 allow_parallel: true,
2160 force_parallel: false,
2161 suffix: None,
2162 validation: Default::default(),
2163 nulls_equal: false,
2164 coalesce: Default::default(),
2165 maintain_order: Default::default(),
2166 build_side: None,
2167 }
2168 }
2169
2170 pub fn with(mut self, other: LazyFrame) -> Self {
2172 self.other = Some(other);
2173 self
2174 }
2175
2176 pub fn how(mut self, how: JoinType) -> Self {
2178 self.how = how;
2179 self
2180 }
2181
2182 pub fn validate(mut self, validation: JoinValidation) -> Self {
2183 self.validation = validation;
2184 self
2185 }
2186
2187 pub fn on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2193 let on = on.as_ref().to_vec();
2194 self.left_on.clone_from(&on);
2195 self.right_on = on;
2196 self
2197 }
2198
2199 pub fn left_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2204 self.left_on = on.as_ref().to_vec();
2205 self
2206 }
2207
2208 pub fn right_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2213 self.right_on = on.as_ref().to_vec();
2214 self
2215 }
2216
2217 pub fn allow_parallel(mut self, allow: bool) -> Self {
2219 self.allow_parallel = allow;
2220 self
2221 }
2222
2223 pub fn force_parallel(mut self, force: bool) -> Self {
2225 self.force_parallel = force;
2226 self
2227 }
2228
2229 pub fn join_nulls(mut self, nulls_equal: bool) -> Self {
2231 self.nulls_equal = nulls_equal;
2232 self
2233 }
2234
2235 pub fn suffix<S>(mut self, suffix: S) -> Self
2238 where
2239 S: Into<PlSmallStr>,
2240 {
2241 self.suffix = Some(suffix.into());
2242 self
2243 }
2244
2245 pub fn coalesce(mut self, coalesce: JoinCoalesce) -> Self {
2247 self.coalesce = coalesce;
2248 self
2249 }
2250
2251 pub fn maintain_order(mut self, maintain_order: MaintainOrderJoin) -> Self {
2253 self.maintain_order = maintain_order;
2254 self
2255 }
2256
2257 pub fn build_side(mut self, build_side: Option<JoinBuildSide>) -> Self {
2259 self.build_side = build_side;
2260 self
2261 }
2262
2263 pub fn finish(self) -> PolarsResult<LazyFrame> {
2265 let opt_state = self.lf.opt_state;
2266 let other = self.other.expect("'with' not set in join builder");
2267
2268 let args = JoinArgs {
2269 how: self.how,
2270 validation: self.validation,
2271 suffix: self.suffix,
2272 slice: None,
2273 nulls_equal: self.nulls_equal,
2274 coalesce: self.coalesce,
2275 maintain_order: self.maintain_order,
2276 build_side: self.build_side,
2277 };
2278
2279 let lp = self
2280 .lf
2281 .get_plan_builder()
2282 .join(
2283 other.logical_plan,
2284 self.left_on,
2285 self.right_on,
2286 JoinOptions {
2287 allow_parallel: self.allow_parallel,
2288 force_parallel: self.force_parallel,
2289 args,
2290 }
2291 .into(),
2292 )?
2293 .build();
2294 Ok(LazyFrame::from_logical_plan(lp, opt_state))
2295 }
2296
2297 pub fn join_where(self, predicates: Vec<Expr>) -> LazyFrame {
2299 let opt_state = self.lf.opt_state;
2300 let other = self.other.expect("with not set");
2301
2302 fn decompose_and(predicate: Expr, expanded_predicates: &mut Vec<Expr>) {
2304 if let Expr::BinaryExpr {
2305 op: Operator::And,
2306 left,
2307 right,
2308 } = predicate
2309 {
2310 decompose_and((*left).clone(), expanded_predicates);
2311 decompose_and((*right).clone(), expanded_predicates);
2312 } else {
2313 expanded_predicates.push(predicate);
2314 }
2315 }
2316 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2317 for predicate in predicates {
2318 decompose_and(predicate, &mut expanded_predicates);
2319 }
2320 let predicates: Vec<Expr> = expanded_predicates;
2321
2322 #[cfg(feature = "is_between")]
2324 let predicates: Vec<Expr> = {
2325 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2326 for predicate in predicates {
2327 if let Expr::Function {
2328 function: FunctionExpr::Boolean(BooleanFunction::IsBetween { closed }),
2329 input,
2330 ..
2331 } = &predicate
2332 {
2333 if let [expr, lower, upper] = input.as_slice() {
2334 match closed {
2335 ClosedInterval::Both => {
2336 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2337 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2338 },
2339 ClosedInterval::Right => {
2340 expanded_predicates.push(expr.clone().gt(lower.clone()));
2341 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2342 },
2343 ClosedInterval::Left => {
2344 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2345 expanded_predicates.push(expr.clone().lt(upper.clone()));
2346 },
2347 ClosedInterval::None => {
2348 expanded_predicates.push(expr.clone().gt(lower.clone()));
2349 expanded_predicates.push(expr.clone().lt(upper.clone()));
2350 },
2351 }
2352 continue;
2353 }
2354 }
2355 expanded_predicates.push(predicate);
2356 }
2357 expanded_predicates
2358 };
2359
2360 let args = JoinArgs {
2361 how: self.how,
2362 validation: self.validation,
2363 suffix: self.suffix,
2364 slice: None,
2365 nulls_equal: self.nulls_equal,
2366 coalesce: self.coalesce,
2367 maintain_order: self.maintain_order,
2368 build_side: self.build_side,
2369 };
2370 let options = JoinOptions {
2371 allow_parallel: self.allow_parallel,
2372 force_parallel: self.force_parallel,
2373 args,
2374 };
2375
2376 let lp = DslPlan::Join {
2377 input_left: Arc::new(self.lf.logical_plan),
2378 input_right: Arc::new(other.logical_plan),
2379 condition: JoinCondition::NonEqui { predicates },
2380 options: Arc::from(options),
2381 };
2382
2383 LazyFrame::from_logical_plan(lp, opt_state)
2384 }
2385}
2386
2387pub const BUILD_STREAMING_EXECUTOR: Option<polars_mem_engine::StreamingExecutorBuilder> = {
2388 #[cfg(not(feature = "streaming"))]
2389 {
2390 None
2391 }
2392 #[cfg(feature = "streaming")]
2393 {
2394 Some(polars_stream::build_streaming_query_executor)
2395 }
2396};
2397
2398fn run_in_memory_query(
2399 node: Node,
2400 ir_arena: &mut Arena<IR>,
2401 expr_arena: &mut Arena<AExpr>,
2402 engine: Engine,
2403 observer: Option<Box<dyn QueryObserver>>,
2404) -> PolarsResult<QueryResult> {
2405 let _guard = observer
2406 .as_ref()
2407 .map(|o| o.on_query_planned(to_planned_query(node, ir_arena, expr_arena)));
2408
2409 let result = if let IR::SinkMultiple { inputs } = ir_arena.get(node) {
2410 polars_ensure!(
2411 engine != Engine::Gpu,
2412 InvalidOperation:
2413 "collect_all is not supported for the gpu engine"
2414 );
2415
2416 let physical_plan = create_multiple_physical_plans(
2417 inputs.clone().as_slice(),
2418 ir_arena,
2419 expr_arena,
2420 BUILD_STREAMING_EXECUTOR,
2421 )?;
2422 physical_plan.execute().map(QueryResult::Multiple)
2423 } else {
2424 let mut physical_plan =
2425 create_physical_plan(node, ir_arena, expr_arena, BUILD_STREAMING_EXECUTOR)?;
2426 let mut state = ExecutionState::new();
2427 physical_plan.execute(&mut state).map(QueryResult::Single)
2428 };
2429
2430 result.inspect_err(|err| {
2431 if let Some(o) = observer.as_ref() {
2432 o.on_query_failed(err);
2433 }
2434 })
2435}
2436
2437fn to_planned_query(node: Node, ir_arena: &Arena<IR>, expr_arena: &Arena<AExpr>) -> PlannedQuery {
2438 let ir = ir_plan_to_description(&[node], ir_arena, expr_arena);
2439 PlannedQuery::new(ir)
2440}
2441
2442pub struct CollectBatches {
2443 recv: Receiver<PolarsResult<DataFrame>>,
2444 runner: Option<Box<dyn FnOnce() + Send + 'static>>,
2445}
2446
2447impl CollectBatches {
2448 pub fn start(&mut self) {
2450 if let Some(runner) = self.runner.take() {
2451 runner()
2452 }
2453 }
2454}
2455
2456impl Iterator for CollectBatches {
2457 type Item = PolarsResult<DataFrame>;
2458
2459 fn next(&mut self) -> Option<Self::Item> {
2460 self.start();
2461 self.recv.recv().ok()
2462 }
2463}