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;
30use polars_io::RowIndex;
31use polars_mem_engine::scan_predicate::functions::apply_scan_predicate_to_scan_ir;
32use polars_mem_engine::{Executor, create_multiple_physical_plans, create_physical_plan};
33use polars_observer::{PlannedQuery, QueryObserver};
34use polars_ops::frame::{JoinBuildSide, JoinCoalesce, MaintainOrderJoin};
35#[cfg(feature = "is_between")]
36use polars_ops::prelude::ClosedInterval;
37pub use polars_plan::frame::{AllowedOptimizations, OptFlags};
38use polars_plan::prelude::ir_plan_to_description;
39use polars_utils::pl_str::PlSmallStr;
40
41use crate::frame::cached_arenas::CachedArena;
42use crate::prelude::*;
43
44pub trait IntoLazy {
45 fn lazy(self) -> LazyFrame;
46}
47
48impl IntoLazy for DataFrame {
49 fn lazy(self) -> LazyFrame {
51 let lp = DslBuilder::from_existing_df(self).build();
52 LazyFrame {
53 logical_plan: lp,
54 opt_state: Default::default(),
55 cached_arena: Default::default(),
56 }
57 }
58}
59
60impl IntoLazy for LazyFrame {
61 fn lazy(self) -> LazyFrame {
62 self
63 }
64}
65
66#[derive(Clone, Default)]
71#[must_use]
72pub struct LazyFrame {
73 pub logical_plan: DslPlan,
74 pub(crate) opt_state: OptFlags,
75 pub(crate) cached_arena: Arc<Mutex<Option<CachedArena>>>,
76}
77
78impl From<DslPlan> for LazyFrame {
79 fn from(plan: DslPlan) -> Self {
80 Self {
81 logical_plan: plan,
82 opt_state: OptFlags::default(),
83 cached_arena: Default::default(),
84 }
85 }
86}
87
88impl LazyFrame {
89 pub(crate) fn from_inner(
90 logical_plan: DslPlan,
91 opt_state: OptFlags,
92 cached_arena: Arc<Mutex<Option<CachedArena>>>,
93 ) -> Self {
94 Self {
95 logical_plan,
96 opt_state,
97 cached_arena,
98 }
99 }
100
101 pub(crate) fn get_plan_builder(self) -> DslBuilder {
102 DslBuilder::from(self.logical_plan)
103 }
104
105 fn get_opt_state(&self) -> OptFlags {
106 self.opt_state
107 }
108
109 pub fn from_logical_plan(logical_plan: DslPlan, opt_state: OptFlags) -> Self {
110 LazyFrame {
111 logical_plan,
112 opt_state,
113 cached_arena: Default::default(),
114 }
115 }
116
117 pub fn get_current_optimizations(&self) -> OptFlags {
119 self.opt_state
120 }
121
122 pub fn with_optimizations(mut self, opt_state: OptFlags) -> Self {
124 self.opt_state = opt_state;
125 self
126 }
127
128 pub fn without_optimizations(self) -> Self {
130 self.with_optimizations(OptFlags::from_bits_truncate(0) | OptFlags::TYPE_COERCION)
131 }
132
133 pub fn with_projection_pushdown(mut self, toggle: bool) -> Self {
135 self.opt_state.set(OptFlags::PROJECTION_PUSHDOWN, toggle);
136 self
137 }
138
139 pub fn with_cluster_with_columns(mut self, toggle: bool) -> Self {
141 self.opt_state.set(OptFlags::CLUSTER_WITH_COLUMNS, toggle);
142 self
143 }
144
145 pub fn with_check_order(mut self, toggle: bool) -> Self {
148 self.opt_state.set(OptFlags::CHECK_ORDER_OBSERVE, toggle);
149 self
150 }
151
152 pub fn with_predicate_pushdown(mut self, toggle: bool) -> Self {
154 self.opt_state.set(OptFlags::PREDICATE_PUSHDOWN, toggle);
155 self
156 }
157
158 pub fn with_type_coercion(mut self, toggle: bool) -> Self {
160 self.opt_state.set(OptFlags::TYPE_COERCION, toggle);
161 self
162 }
163
164 pub fn with_type_check(mut self, toggle: bool) -> Self {
166 self.opt_state.set(OptFlags::TYPE_CHECK, toggle);
167 self
168 }
169
170 pub fn with_simplify_expr(mut self, toggle: bool) -> Self {
172 self.opt_state.set(OptFlags::SIMPLIFY_EXPR, toggle);
173 self
174 }
175
176 #[cfg(feature = "cse")]
178 pub fn with_comm_subplan_elim(mut self, toggle: bool) -> Self {
179 self.opt_state.set(OptFlags::COMM_SUBPLAN_ELIM, toggle);
180 self
181 }
182
183 #[cfg(feature = "cse")]
185 pub fn with_comm_subexpr_elim(mut self, toggle: bool) -> Self {
186 self.opt_state.set(OptFlags::COMM_SUBEXPR_ELIM, toggle);
187 self
188 }
189
190 pub fn with_slice_pushdown(mut self, toggle: bool) -> Self {
192 self.opt_state.set(OptFlags::SLICE_PUSHDOWN, toggle);
193 self
194 }
195
196 #[cfg(feature = "streaming")]
197 pub fn with_streaming(mut self, toggle: bool) -> Self {
198 self.opt_state.set(OptFlags::STREAMING, toggle);
199 self
200 }
201
202 pub fn with_gpu(mut self, toggle: bool) -> Self {
203 self.opt_state.set(OptFlags::GPU, toggle);
204 self
205 }
206
207 pub fn with_row_estimate(mut self, toggle: bool) -> Self {
209 self.opt_state.set(OptFlags::ROW_ESTIMATE, toggle);
210 self
211 }
212
213 pub fn _with_eager(mut self, toggle: bool) -> Self {
215 self.opt_state.set(OptFlags::EAGER, toggle);
216 self
217 }
218
219 pub fn describe_plan(&self) -> PolarsResult<String> {
221 Ok(self.clone().to_alp()?.describe())
222 }
223
224 pub fn describe_plan_tree(&self) -> PolarsResult<String> {
226 Ok(self.clone().to_alp()?.describe_tree_format())
227 }
228
229 pub fn describe_optimized_plan(&self) -> PolarsResult<String> {
233 Ok(self.clone().to_alp_optimized()?.describe())
234 }
235
236 pub fn describe_optimized_plan_tree(&self) -> PolarsResult<String> {
240 Ok(self.clone().to_alp_optimized()?.describe_tree_format())
241 }
242
243 pub fn explain(&self, optimized: bool) -> PolarsResult<String> {
248 if optimized {
249 self.describe_optimized_plan()
250 } else {
251 self.describe_plan()
252 }
253 }
254
255 pub fn sort(self, by: impl IntoVec<PlSmallStr>, sort_options: SortMultipleOptions) -> Self {
295 let opt_state = self.get_opt_state();
296 let lp = self
297 .get_plan_builder()
298 .sort(by.into_vec().into_iter().map(col).collect(), sort_options)
299 .build();
300 Self::from_logical_plan(lp, opt_state)
301 }
302
303 pub fn sort_by_exprs<E: AsRef<[Expr]>>(
323 self,
324 by_exprs: E,
325 sort_options: SortMultipleOptions,
326 ) -> Self {
327 let by_exprs = by_exprs.as_ref().to_vec();
328 if by_exprs.is_empty() {
329 self
330 } else {
331 let opt_state = self.get_opt_state();
332 let lp = self.get_plan_builder().sort(by_exprs, sort_options).build();
333 Self::from_logical_plan(lp, opt_state)
334 }
335 }
336
337 pub fn top_k<E: AsRef<[Expr]>>(
338 self,
339 k: IdxSize,
340 by_exprs: E,
341 sort_options: SortMultipleOptions,
342 ) -> Self {
343 self.sort_by_exprs(
345 by_exprs,
346 sort_options.with_order_reversed().with_nulls_last(true),
347 )
348 .slice(0, k)
349 }
350
351 pub fn bottom_k<E: AsRef<[Expr]>>(
352 self,
353 k: IdxSize,
354 by_exprs: E,
355 sort_options: SortMultipleOptions,
356 ) -> Self {
357 self.sort_by_exprs(by_exprs, sort_options.with_nulls_last(true))
359 .slice(0, k)
360 }
361
362 pub fn reverse(self) -> Self {
378 self.select(vec![col(PlSmallStr::from_static("*")).reverse()])
379 }
380
381 pub fn rename<I, J, T, S>(self, existing: I, new: J, strict: bool) -> Self
389 where
390 I: IntoIterator<Item = T>,
391 J: IntoIterator<Item = S>,
392 T: AsRef<str>,
393 S: AsRef<str>,
394 {
395 let iter = existing.into_iter();
396 let cap = iter.size_hint().0;
397 let mut existing_vec: Vec<PlSmallStr> = Vec::with_capacity(cap);
398 let mut new_vec: Vec<PlSmallStr> = Vec::with_capacity(cap);
399
400 for (existing, new) in iter.zip(new) {
403 let existing = existing.as_ref();
404 let new = new.as_ref();
405 if new != existing {
406 existing_vec.push(existing.into());
407 new_vec.push(new.into());
408 }
409 }
410
411 self.map_private(DslFunction::Rename {
412 existing: existing_vec.into(),
413 new: new_vec.into(),
414 strict,
415 })
416 }
417
418 pub fn drop(self, columns: Selector) -> Self {
425 let opt_state = self.get_opt_state();
426 let lp = self.get_plan_builder().drop(columns).build();
427 Self::from_logical_plan(lp, opt_state)
428 }
429
430 pub fn shift<E: Into<Expr>>(self, n: E) -> Self {
435 self.select(vec![col(PlSmallStr::from_static("*")).shift(n.into())])
436 }
437
438 pub fn shift_and_fill<E: Into<Expr>, IE: Into<Expr>>(self, n: E, fill_value: IE) -> Self {
443 self.select(vec![
444 col(PlSmallStr::from_static("*")).shift_and_fill(n.into(), fill_value.into()),
445 ])
446 }
447
448 pub fn fill_null<E: Into<Expr>>(self, fill_value: E) -> LazyFrame {
450 let opt_state = self.get_opt_state();
451 let lp = self.get_plan_builder().fill_null(fill_value.into()).build();
452 Self::from_logical_plan(lp, opt_state)
453 }
454
455 pub fn fill_nan<E: Into<Expr>>(self, fill_value: E) -> LazyFrame {
457 let opt_state = self.get_opt_state();
458 let lp = self.get_plan_builder().fill_nan(fill_value.into()).build();
459 Self::from_logical_plan(lp, opt_state)
460 }
461
462 pub fn cache(self) -> Self {
466 let opt_state = self.get_opt_state();
467 let lp = self.get_plan_builder().cache().build();
468 Self::from_logical_plan(lp, opt_state)
469 }
470
471 pub fn cast(self, dtypes: PlHashMap<&str, DataType>, strict: bool) -> Self {
473 let cast_cols: Vec<Expr> = dtypes
474 .into_iter()
475 .map(|(name, dt)| {
476 let name = PlSmallStr::from_str(name);
477
478 if strict {
479 col(name).strict_cast(dt)
480 } else {
481 col(name).cast(dt)
482 }
483 })
484 .collect();
485
486 if cast_cols.is_empty() {
487 self
488 } else {
489 self.with_columns(cast_cols)
490 }
491 }
492
493 pub fn cast_all(self, dtype: impl Into<DataTypeExpr>, strict: bool) -> Self {
495 self.with_columns(vec![if strict {
496 col(PlSmallStr::from_static("*")).strict_cast(dtype)
497 } else {
498 col(PlSmallStr::from_static("*")).cast(dtype)
499 }])
500 }
501
502 pub fn optimize(
503 self,
504 lp_arena: &mut Arena<IR>,
505 expr_arena: &mut Arena<AExpr>,
506 ) -> PolarsResult<Node> {
507 self.optimize_with_scratch(lp_arena, expr_arena, &mut vec![])
508 }
509
510 pub fn to_alp_optimized(mut self) -> PolarsResult<IRPlan> {
511 let (mut lp_arena, mut expr_arena) = self.get_arenas();
512 let node = self.optimize_with_scratch(&mut lp_arena, &mut expr_arena, &mut vec![])?;
513
514 Ok(IRPlan::new(node, lp_arena, expr_arena))
515 }
516
517 pub fn to_alp(mut self) -> PolarsResult<IRPlan> {
518 let (mut lp_arena, mut expr_arena) = self.get_arenas();
519 let node = to_alp(
520 self.logical_plan,
521 &mut expr_arena,
522 &mut lp_arena,
523 &mut self.opt_state,
524 )?;
525 let plan = IRPlan::new(node, lp_arena, expr_arena);
526 Ok(plan)
527 }
528
529 pub(crate) fn optimize_with_scratch(
530 self,
531 ir_arena: &mut Arena<IR>,
532 expr_arena: &mut Arena<AExpr>,
533 scratch: &mut Vec<Node>,
534 ) -> PolarsResult<Node> {
535 let mut opt_flags = self.opt_state;
536 #[allow(clippy::eq_op)]
539 #[cfg(feature = "cse")]
540 if opt_flags.contains(OptFlags::EAGER) {
541 opt_flags &= !(OptFlags::COMM_SUBEXPR_ELIM | OptFlags::COMM_SUBEXPR_ELIM);
542 }
543 let root = to_alp(self.logical_plan, expr_arena, ir_arena, &mut opt_flags)?;
544
545 let lp_top = optimize(
546 root,
547 opt_flags,
548 ir_arena,
549 expr_arena,
550 scratch,
551 apply_scan_predicate_to_scan_ir,
552 )?;
553
554 Ok(lp_top)
555 }
556
557 fn prepare_collect_post_opt<P>(
558 mut self,
559 check_sink: bool,
560 post_opt: P,
561 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)>
562 where
563 P: FnOnce(Node, &mut Arena<IR>, &mut Arena<AExpr>) -> PolarsResult<()>,
564 {
565 let (mut lp_arena, mut expr_arena) = self.get_arenas();
566
567 let mut scratch = vec![];
568 let lp_top = self.optimize_with_scratch(&mut lp_arena, &mut expr_arena, &mut scratch)?;
569 post_opt(lp_top, &mut lp_arena, &mut expr_arena)?;
570
571 let no_file_sink = if check_sink {
573 !matches!(
574 lp_arena.get(lp_top),
575 IR::Sink {
576 payload: SinkTypeIR::File { .. },
577 ..
578 }
579 )
580 } else {
581 true
582 };
583 let physical_plan = create_physical_plan(
584 lp_top,
585 &mut lp_arena,
586 &mut expr_arena,
587 BUILD_STREAMING_EXECUTOR,
588 )?;
589
590 let state = ExecutionState::new();
591 Ok((state, physical_plan, no_file_sink))
592 }
593
594 pub fn _collect_post_opt<P>(self, post_opt: P) -> PolarsResult<DataFrame>
596 where
597 P: FnOnce(Node, &mut Arena<IR>, &mut Arena<AExpr>) -> PolarsResult<()>,
598 {
599 let (mut state, mut physical_plan, _) = self.prepare_collect_post_opt(false, post_opt)?;
600 physical_plan.execute(&mut state)
601 }
602
603 #[allow(unused_mut)]
604 fn prepare_collect(
605 self,
606 check_sink: bool,
607 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)> {
608 self.prepare_collect_post_opt(check_sink, |_, _, _| Ok(()))
609 }
610
611 pub fn collect_with_engine(mut self, engine: Engine) -> PolarsResult<QueryResult> {
616 let engine = match engine {
617 Engine::Streaming => Engine::Streaming,
618 _ if std::env::var("POLARS_FORCE_STREAMING").as_deref() == Ok("1") => Engine::Streaming,
619 Engine::Auto => {
620 if self.opt_state.eager() {
621 Engine::InMemory
622 } else {
623 Engine::Streaming
624 }
625 },
626 v => v,
627 };
628
629 if engine != Engine::Streaming
630 && std::env::var("POLARS_AUTO_STREAMING").as_deref() == Ok("1")
631 {
632 feature_gated!("streaming", {
633 if let Some(r) = self.clone()._collect_with_streaming_suppress_todo_panic() {
634 return r;
635 }
636 })
637 }
638 match engine {
639 Engine::Streaming => {
640 feature_gated!("streaming", self = self.with_streaming(true))
641 },
642 Engine::Gpu => self = self.with_gpu(true),
643 _ => (),
644 }
645
646 let observer = self
647 .opt_state
648 .query_monitoring()
649 .then(polars_observer::new_query_observer)
650 .flatten();
651
652 if let Some(o) = observer.as_ref() {
653 o.on_query_started()
654 }
655
656 let mut ir_plan = self.to_alp_optimized().inspect_err(|err| {
657 if let Some(o) = observer.as_ref() {
658 o.on_query_failed(err)
659 }
660 })?;
661 ir_plan.ensure_root_node_is_sink();
662
663 match engine {
664 Engine::Streaming => feature_gated!("streaming", {
665 polars_stream::run_query(
666 ir_plan.lp_top,
667 &mut ir_plan.lp_arena,
668 &mut ir_plan.expr_arena,
669 observer,
670 )
671 }),
672 Engine::InMemory | Engine::Gpu => run_in_memory_query(
673 ir_plan.lp_top,
674 &mut ir_plan.lp_arena,
675 &mut ir_plan.expr_arena,
676 engine,
677 observer,
678 ),
679 Engine::Auto => unreachable!(),
680 }
681 }
682
683 pub fn explain_all(plans: Vec<DslPlan>, opt_state: OptFlags) -> PolarsResult<String> {
684 let sink_multiple = LazyFrame {
685 logical_plan: DslPlan::SinkMultiple { inputs: plans },
686 opt_state,
687 cached_arena: Default::default(),
688 };
689 sink_multiple.explain(true)
690 }
691
692 pub fn collect_all_with_engine(
693 plans: Vec<DslPlan>,
694 engine: Engine,
695 opt_state: OptFlags,
696 ) -> PolarsResult<Vec<DataFrame>> {
697 if plans.is_empty() {
698 return Ok(Vec::new());
699 }
700
701 LazyFrame {
702 logical_plan: DslPlan::SinkMultiple { inputs: plans },
703 opt_state,
704 cached_arena: Default::default(),
705 }
706 .collect_with_engine(engine)
707 .map(|r| r.unwrap_multiple())
708 }
709
710 pub fn collect(self) -> PolarsResult<DataFrame> {
728 self.collect_with_engine(Engine::Auto).map(|r| match r {
729 QueryResult::Single(df) => df,
730 QueryResult::Multiple(_) => DataFrame::empty(),
732 })
733 }
734
735 #[cfg(feature = "async")]
740 pub fn collect_batches(
741 self,
742 engine: Engine,
743 maintain_order: bool,
744 chunk_size: Option<NonZeroUsize>,
745 lazy: bool,
746 ) -> PolarsResult<CollectBatches> {
747 let (send, recv) = sync_channel(1);
748 let runner_send = send.clone();
749 let ldf = self.sink_batches(
750 PlanCallback::new(move |df| {
751 let send_result = send.send(Ok(df));
753 Ok(send_result.is_err())
754 }),
755 maintain_order,
756 chunk_size,
757 )?;
758 let runner = move || {
759 polars_core::runtime::ASYNC.spawn_blocking(move || {
761 if let Err(e) = ldf.collect_with_engine(engine) {
762 runner_send.send(Err(e)).ok();
763 }
764 });
765 };
766
767 let mut collect_batches = CollectBatches {
768 recv,
769 runner: Some(Box::new(runner)),
770 };
771 if !lazy {
772 collect_batches.start();
773 }
774 Ok(collect_batches)
775 }
776
777 pub fn sink_batches(
778 mut self,
779 function: PlanCallback<DataFrame, bool>,
780 maintain_order: bool,
781 chunk_size: Option<NonZeroUsize>,
782 ) -> PolarsResult<Self> {
783 use polars_plan::prelude::sink::CallbackSinkType;
784
785 polars_ensure!(
786 !matches!(self.logical_plan, DslPlan::Sink { .. }),
787 InvalidOperation: "cannot create a sink on top of another sink"
788 );
789
790 self.logical_plan = DslPlan::Sink {
791 input: Arc::new(self.logical_plan),
792 payload: SinkType::Callback(CallbackSinkType {
793 function,
794 maintain_order,
795 chunk_size,
796 }),
797 };
798
799 Ok(self)
800 }
801
802 #[cfg(feature = "streaming")]
804 fn _collect_with_streaming_suppress_todo_panic(
805 mut self,
806 ) -> Option<PolarsResult<polars_core::query_result::QueryResult>> {
807 self.opt_state |= OptFlags::STREAMING;
808 let mut ir_plan = match self.to_alp_optimized() {
809 Ok(v) => v,
810 Err(e) => return Some(Err(e)),
811 };
812
813 ir_plan.ensure_root_node_is_sink();
814
815 let f = || {
816 polars_stream::run_query(
817 ir_plan.lp_top,
818 &mut ir_plan.lp_arena,
819 &mut ir_plan.expr_arena,
820 None,
821 )
822 };
823
824 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
825 Ok(v) => Some(v),
826 Err(e) => {
827 if e.downcast_ref::<&str>()
830 .is_some_and(|s| s.starts_with("not yet implemented"))
831 {
832 if polars_core::config::verbose() {
833 eprintln!(
834 "caught unimplemented error in new streaming engine, falling back to normal engine"
835 );
836 }
837 None
838 } else {
839 std::panic::resume_unwind(e)
840 }
841 },
842 }
843 }
844
845 pub fn sink(
846 mut self,
847 sink_type: SinkDestination,
848 file_format: FileWriteFormat,
849 unified_sink_args: UnifiedSinkArgs,
850 ) -> PolarsResult<Self> {
851 polars_ensure!(
852 !matches!(self.logical_plan, DslPlan::Sink { .. }),
853 InvalidOperation: "cannot create a sink on top of another sink"
854 );
855
856 self.logical_plan = DslPlan::Sink {
857 input: Arc::new(self.logical_plan),
858 payload: match sink_type {
859 SinkDestination::File { target } => SinkType::File(FileSinkOptions {
860 target,
861 file_format,
862 unified_sink_args,
863 }),
864 SinkDestination::Partitioned {
865 base_path,
866 file_path_provider,
867 partition_strategy,
868 max_rows_per_file,
869 approximate_bytes_per_file,
870 } => SinkType::Partitioned(PartitionedSinkOptions {
871 base_path,
872 file_path_provider,
873 partition_strategy,
874 file_format,
875 unified_sink_args,
876 max_rows_per_file,
877 approximate_bytes_per_file,
878 }),
879 },
880 };
881 Ok(self)
882 }
883
884 pub fn filter(self, predicate: Expr) -> Self {
902 let opt_state = self.get_opt_state();
903 let lp = self.get_plan_builder().filter(predicate).build();
904 Self::from_logical_plan(lp, opt_state)
905 }
906
907 pub fn remove(self, predicate: Expr) -> Self {
925 self.filter(predicate.neq_missing(lit(true)))
926 }
927
928 pub fn select<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
954 let exprs = exprs.as_ref().to_vec();
955 self.select_impl(
956 exprs,
957 ProjectionOptions {
958 run_parallel: true,
959 duplicate_check: true,
960 should_broadcast: true,
961 maintain_dataframe_height: false,
962 },
963 )
964 }
965
966 pub fn select_seq<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
967 let exprs = exprs.as_ref().to_vec();
968 self.select_impl(
969 exprs,
970 ProjectionOptions {
971 run_parallel: false,
972 duplicate_check: true,
973 should_broadcast: true,
974 maintain_dataframe_height: false,
975 },
976 )
977 }
978
979 fn select_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> Self {
980 let opt_state = self.get_opt_state();
981 let lp = self.get_plan_builder().project(exprs, options).build();
982 Self::from_logical_plan(lp, opt_state)
983 }
984
985 pub fn group_by<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1006 let keys = by
1007 .as_ref()
1008 .iter()
1009 .map(|e| e.clone().into())
1010 .collect::<Vec<_>>();
1011 let opt_state = self.get_opt_state();
1012
1013 #[cfg(feature = "dynamic_group_by")]
1014 {
1015 LazyGroupBy {
1016 logical_plan: self.logical_plan,
1017 opt_state,
1018 keys,
1019 predicates: vec![],
1020 maintain_order: false,
1021 dynamic_options: None,
1022 rolling_options: None,
1023 }
1024 }
1025
1026 #[cfg(not(feature = "dynamic_group_by"))]
1027 {
1028 LazyGroupBy {
1029 logical_plan: self.logical_plan,
1030 opt_state,
1031 keys,
1032 predicates: vec![],
1033 maintain_order: false,
1034 }
1035 }
1036 }
1037
1038 #[cfg(feature = "dynamic_group_by")]
1046 pub fn rolling<E: AsRef<[Expr]>>(
1047 mut self,
1048 index_column: Expr,
1049 group_by: E,
1050 mut options: RollingGroupOptions,
1051 ) -> LazyGroupBy {
1052 if let Expr::Column(name) = index_column {
1053 options.index_column = name;
1054 } else {
1055 let output_field = index_column
1056 .to_field(&self.collect_schema().unwrap())
1057 .unwrap();
1058 return self.with_column(index_column).rolling(
1059 Expr::Column(output_field.name().clone()),
1060 group_by,
1061 options,
1062 );
1063 }
1064 let opt_state = self.get_opt_state();
1065 LazyGroupBy {
1066 logical_plan: self.logical_plan,
1067 opt_state,
1068 predicates: vec![],
1069 keys: group_by.as_ref().to_vec(),
1070 maintain_order: true,
1071 dynamic_options: None,
1072 rolling_options: Some(options),
1073 }
1074 }
1075
1076 #[cfg(feature = "dynamic_group_by")]
1092 pub fn group_by_dynamic<E: AsRef<[Expr]>>(
1093 mut self,
1094 index_column: Expr,
1095 group_by: E,
1096 mut options: DynamicGroupOptions,
1097 ) -> LazyGroupBy {
1098 if let Expr::Column(name) = index_column {
1099 options.index_column = name;
1100 } else {
1101 let output_field = index_column
1102 .to_field(&self.collect_schema().unwrap())
1103 .unwrap();
1104 return self.with_column(index_column).group_by_dynamic(
1105 Expr::Column(output_field.name().clone()),
1106 group_by,
1107 options,
1108 );
1109 }
1110 let opt_state = self.get_opt_state();
1111 LazyGroupBy {
1112 logical_plan: self.logical_plan,
1113 opt_state,
1114 predicates: vec![],
1115 keys: group_by.as_ref().to_vec(),
1116 maintain_order: true,
1117 dynamic_options: Some(options),
1118 rolling_options: None,
1119 }
1120 }
1121
1122 pub fn group_by_stable<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1124 let keys = by
1125 .as_ref()
1126 .iter()
1127 .map(|e| e.clone().into())
1128 .collect::<Vec<_>>();
1129 let opt_state = self.get_opt_state();
1130
1131 #[cfg(feature = "dynamic_group_by")]
1132 {
1133 LazyGroupBy {
1134 logical_plan: self.logical_plan,
1135 opt_state,
1136 keys,
1137 predicates: vec![],
1138 maintain_order: true,
1139 dynamic_options: None,
1140 rolling_options: None,
1141 }
1142 }
1143
1144 #[cfg(not(feature = "dynamic_group_by"))]
1145 {
1146 LazyGroupBy {
1147 logical_plan: self.logical_plan,
1148 opt_state,
1149 keys,
1150 predicates: vec![],
1151 maintain_order: true,
1152 }
1153 }
1154 }
1155
1156 #[cfg(feature = "semi_anti_join")]
1173 pub fn anti_join<E: Into<Expr>>(
1174 self,
1175 other: LazyFrame,
1176 left_on: E,
1177 right_on: E,
1178 ) -> PolarsResult<LazyFrame> {
1179 self.join(
1180 other,
1181 [left_on.into()],
1182 [right_on.into()],
1183 JoinArgs::new(JoinType::Anti),
1184 )
1185 }
1186
1187 #[cfg(feature = "cross_join")]
1189 pub fn cross_join(self, other: LazyFrame, suffix: Option<PlSmallStr>) -> LazyFrame {
1190 self.join(
1191 other,
1192 vec![],
1193 vec![],
1194 JoinArgs::new(JoinType::Cross).with_suffix(suffix),
1195 )
1196 .unwrap()
1197 }
1198
1199 pub fn left_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1216 self.join(
1217 other,
1218 [left_on.into()],
1219 [right_on.into()],
1220 JoinArgs::new(JoinType::Left),
1221 )
1222 .unwrap()
1223 }
1224
1225 pub fn inner_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1242 self.join(
1243 other,
1244 [left_on.into()],
1245 [right_on.into()],
1246 JoinArgs::new(JoinType::Inner),
1247 )
1248 .unwrap()
1249 }
1250
1251 pub fn full_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1268 self.join(
1269 other,
1270 [left_on.into()],
1271 [right_on.into()],
1272 JoinArgs::new(JoinType::Full),
1273 )
1274 .unwrap()
1275 }
1276
1277 #[cfg(feature = "semi_anti_join")]
1294 pub fn semi_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1295 self.join(
1296 other,
1297 [left_on.into()],
1298 [right_on.into()],
1299 JoinArgs::new(JoinType::Semi),
1300 )
1301 .unwrap()
1302 }
1303
1304 pub fn join<E: AsRef<[Expr]>>(
1326 self,
1327 other: LazyFrame,
1328 left_on: E,
1329 right_on: E,
1330 args: JoinArgs,
1331 ) -> PolarsResult<LazyFrame> {
1332 let left_on = left_on.as_ref().to_vec();
1333 let right_on = right_on.as_ref().to_vec();
1334
1335 self._join_impl(other, left_on, right_on, args)
1336 }
1337
1338 fn _join_impl(
1339 self,
1340 other: LazyFrame,
1341 left_on: Vec<Expr>,
1342 right_on: Vec<Expr>,
1343 args: JoinArgs,
1344 ) -> PolarsResult<LazyFrame> {
1345 let JoinArgs {
1346 how,
1347 validation,
1348 suffix,
1349 slice,
1350 nulls_equal,
1351 coalesce,
1352 maintain_order,
1353 build_side,
1354 } = args;
1355
1356 if slice.is_some() {
1357 panic!("impl error: slice is not handled")
1358 }
1359
1360 let mut builder = self
1361 .join_builder()
1362 .with(other)
1363 .left_on(left_on)
1364 .right_on(right_on)
1365 .how(how)
1366 .validate(validation)
1367 .join_nulls(nulls_equal)
1368 .coalesce(coalesce)
1369 .maintain_order(maintain_order)
1370 .build_side(build_side);
1371
1372 if let Some(suffix) = suffix {
1373 builder = builder.suffix(suffix);
1374 }
1375
1376 builder.finish()
1378 }
1379
1380 pub fn join_builder(self) -> JoinBuilder {
1386 JoinBuilder::new(self)
1387 }
1388
1389 pub fn gather(self, idxs: LazyFrame, null_on_oob: bool) -> LazyFrame {
1393 let opt_state = self.get_opt_state();
1394 let lp = self
1395 .get_plan_builder()
1396 .gather(idxs.logical_plan, null_on_oob)
1397 .build();
1398 Self::from_logical_plan(lp, opt_state)
1399 }
1400
1401 pub fn with_column(self, expr: Expr) -> LazyFrame {
1419 let opt_state = self.get_opt_state();
1420 let lp = self
1421 .get_plan_builder()
1422 .with_columns(
1423 vec![expr],
1424 ProjectionOptions {
1425 run_parallel: false,
1426 duplicate_check: true,
1427 should_broadcast: true,
1428 maintain_dataframe_height: false,
1429 },
1430 )
1431 .build();
1432 Self::from_logical_plan(lp, opt_state)
1433 }
1434
1435 pub fn with_columns<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1450 let exprs = exprs.as_ref().to_vec();
1451 self.with_columns_impl(
1452 exprs,
1453 ProjectionOptions {
1454 run_parallel: true,
1455 duplicate_check: true,
1456 should_broadcast: true,
1457 maintain_dataframe_height: false,
1458 },
1459 )
1460 }
1461
1462 pub fn with_columns_seq<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1464 let exprs = exprs.as_ref().to_vec();
1465 self.with_columns_impl(
1466 exprs,
1467 ProjectionOptions {
1468 run_parallel: false,
1469 duplicate_check: true,
1470 should_broadcast: true,
1471 maintain_dataframe_height: false,
1472 },
1473 )
1474 }
1475
1476 pub fn match_to_schema(
1478 self,
1479 schema: SchemaRef,
1480 per_column: Arc<[MatchToSchemaPerColumn]>,
1481 extra_columns: ExtraColumnsPolicy,
1482 ) -> LazyFrame {
1483 let opt_state = self.get_opt_state();
1484 let lp = self
1485 .get_plan_builder()
1486 .match_to_schema(schema, per_column, extra_columns)
1487 .build();
1488 Self::from_logical_plan(lp, opt_state)
1489 }
1490
1491 pub fn pipe_with_schema(
1492 self,
1493 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1494 ) -> Self {
1495 let opt_state = self.get_opt_state();
1496 let lp = self
1497 .get_plan_builder()
1498 .pipe_with_schema(vec![], callback)
1499 .build();
1500 Self::from_logical_plan(lp, opt_state)
1501 }
1502
1503 pub fn pipe_with_schemas(
1504 self,
1505 others: Vec<LazyFrame>,
1506 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1507 ) -> Self {
1508 let opt_state = self.get_opt_state();
1509 let lp = self
1510 .get_plan_builder()
1511 .pipe_with_schema(
1512 others.into_iter().map(|lf| lf.logical_plan).collect(),
1513 callback,
1514 )
1515 .build();
1516 Self::from_logical_plan(lp, opt_state)
1517 }
1518
1519 fn with_columns_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> LazyFrame {
1520 let opt_state = self.get_opt_state();
1521 let lp = self.get_plan_builder().with_columns(exprs, options).build();
1522 Self::from_logical_plan(lp, opt_state)
1523 }
1524
1525 pub fn max(self) -> Self {
1529 self.map_private(DslFunction::Stats(StatsFunction::Max))
1530 }
1531
1532 pub fn min(self) -> Self {
1536 self.map_private(DslFunction::Stats(StatsFunction::Min))
1537 }
1538
1539 pub fn sum(self) -> Self {
1549 self.map_private(DslFunction::Stats(StatsFunction::Sum))
1550 }
1551
1552 pub fn mean(self) -> Self {
1557 self.map_private(DslFunction::Stats(StatsFunction::Mean))
1558 }
1559
1560 pub fn median(self) -> Self {
1566 self.map_private(DslFunction::Stats(StatsFunction::Median))
1567 }
1568
1569 pub fn quantile(self, quantile: Expr, method: QuantileMethod) -> Self {
1571 self.map_private(DslFunction::Stats(StatsFunction::Quantile {
1572 quantile,
1573 method,
1574 }))
1575 }
1576
1577 pub fn std(self, ddof: u8) -> Self {
1590 self.map_private(DslFunction::Stats(StatsFunction::Std { ddof }))
1591 }
1592
1593 pub fn var(self, ddof: u8) -> Self {
1603 self.map_private(DslFunction::Stats(StatsFunction::Var { ddof }))
1604 }
1605
1606 pub fn explode(self, columns: Selector, options: ExplodeOptions) -> LazyFrame {
1608 self.explode_impl(columns, options, false)
1609 }
1610
1611 fn explode_impl(
1613 self,
1614 columns: Selector,
1615 options: ExplodeOptions,
1616 allow_empty: bool,
1617 ) -> LazyFrame {
1618 let opt_state = self.get_opt_state();
1619 let lp = self
1620 .get_plan_builder()
1621 .explode(columns, options, allow_empty)
1622 .build();
1623 Self::from_logical_plan(lp, opt_state)
1624 }
1625
1626 pub fn null_count(self) -> LazyFrame {
1628 self.select(vec![col(PlSmallStr::from_static("*")).null_count()])
1629 }
1630
1631 pub fn unique_stable(
1636 self,
1637 subset: Option<Selector>,
1638 keep_strategy: UniqueKeepStrategy,
1639 ) -> LazyFrame {
1640 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1641 self.unique_stable_generic(subset, keep_strategy)
1642 }
1643
1644 pub fn unique_stable_generic(
1645 self,
1646 subset: Option<Vec<Expr>>,
1647 keep_strategy: UniqueKeepStrategy,
1648 ) -> LazyFrame {
1649 let opt_state = self.get_opt_state();
1650 let options = DistinctOptionsDSL {
1651 subset,
1652 maintain_order: true,
1653 keep_strategy,
1654 };
1655 let lp = self.get_plan_builder().distinct(options).build();
1656 Self::from_logical_plan(lp, opt_state)
1657 }
1658
1659 pub fn unique(self, subset: Option<Selector>, keep_strategy: UniqueKeepStrategy) -> LazyFrame {
1667 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1668 self.unique_generic(subset, keep_strategy)
1669 }
1670
1671 pub fn unique_generic(
1672 self,
1673 subset: Option<Vec<Expr>>,
1674 keep_strategy: UniqueKeepStrategy,
1675 ) -> LazyFrame {
1676 let opt_state = self.get_opt_state();
1677 let options = DistinctOptionsDSL {
1678 subset,
1679 maintain_order: false,
1680 keep_strategy,
1681 };
1682 let lp = self.get_plan_builder().distinct(options).build();
1683 Self::from_logical_plan(lp, opt_state)
1684 }
1685
1686 pub fn drop_nans(self, subset: Option<Selector>) -> LazyFrame {
1691 let opt_state = self.get_opt_state();
1692 let lp = self.get_plan_builder().drop_nans(subset).build();
1693 Self::from_logical_plan(lp, opt_state)
1694 }
1695
1696 pub fn drop_nulls(self, subset: Option<Selector>) -> LazyFrame {
1701 let opt_state = self.get_opt_state();
1702 let lp = self.get_plan_builder().drop_nulls(subset).build();
1703 Self::from_logical_plan(lp, opt_state)
1704 }
1705
1706 pub fn slice(self, offset: i64, len: IdxSize) -> LazyFrame {
1716 let opt_state = self.get_opt_state();
1717 let lp = self.get_plan_builder().slice(offset, len).build();
1718 Self::from_logical_plan(lp, opt_state)
1719 }
1720
1721 pub fn clear(self) -> LazyFrame {
1723 self.slice(0, 0)
1724 }
1725
1726 pub fn first(self) -> LazyFrame {
1730 self.slice(0, 1)
1731 }
1732
1733 pub fn last(self) -> LazyFrame {
1737 self.slice(-1, 1)
1738 }
1739
1740 pub fn tail(self, n: IdxSize) -> LazyFrame {
1744 let neg_tail = -(n as i64);
1745 self.slice(neg_tail, n)
1746 }
1747
1748 #[cfg(feature = "pivot")]
1749 #[expect(clippy::too_many_arguments)]
1750 pub fn pivot(
1751 self,
1752 on: Selector,
1753 on_columns: Arc<DataFrame>,
1754 index: Selector,
1755 values: Selector,
1756 agg: Expr,
1757 maintain_order: bool,
1758 separator: PlSmallStr,
1759 column_naming: PivotColumnNaming,
1760 ) -> LazyFrame {
1761 let opt_state = self.get_opt_state();
1762 let lp = self
1763 .get_plan_builder()
1764 .pivot(
1765 on,
1766 on_columns,
1767 index,
1768 values,
1769 agg,
1770 maintain_order,
1771 separator,
1772 column_naming,
1773 )
1774 .build();
1775 Self::from_logical_plan(lp, opt_state)
1776 }
1777
1778 #[cfg(feature = "pivot")]
1782 pub fn unpivot(self, args: UnpivotArgsDSL) -> LazyFrame {
1783 let opt_state = self.get_opt_state();
1784 let lp = self.get_plan_builder().unpivot(args).build();
1785 Self::from_logical_plan(lp, opt_state)
1786 }
1787
1788 pub fn limit(self, n: IdxSize) -> LazyFrame {
1790 self.slice(0, n)
1791 }
1792
1793 pub fn map<F>(
1807 self,
1808 function: F,
1809 optimizations: AllowedOptimizations,
1810 schema: Option<Arc<dyn UdfSchema>>,
1811 name: Option<&'static str>,
1812 ) -> LazyFrame
1813 where
1814 F: 'static + Fn(DataFrame) -> PolarsResult<DataFrame> + Send + Sync,
1815 {
1816 let opt_state = self.get_opt_state();
1817 let lp = self
1818 .get_plan_builder()
1819 .map(
1820 function,
1821 optimizations,
1822 schema,
1823 PlSmallStr::from_static(name.unwrap_or("ANONYMOUS UDF")),
1824 )
1825 .build();
1826 Self::from_logical_plan(lp, opt_state)
1827 }
1828
1829 #[cfg(feature = "python")]
1830 pub fn map_python(
1831 self,
1832 function: polars_utils::python_function::PythonFunction,
1833 optimizations: AllowedOptimizations,
1834 schema: Option<SchemaRef>,
1835 validate_output: bool,
1836 ) -> LazyFrame {
1837 let opt_state = self.get_opt_state();
1838 let lp = self
1839 .get_plan_builder()
1840 .map_python(function, optimizations, schema, validate_output)
1841 .build();
1842 Self::from_logical_plan(lp, opt_state)
1843 }
1844
1845 pub(crate) fn map_private(self, function: DslFunction) -> LazyFrame {
1846 let opt_state = self.get_opt_state();
1847 let lp = self.get_plan_builder().map_private(function).build();
1848 Self::from_logical_plan(lp, opt_state)
1849 }
1850
1851 pub fn with_row_index<S>(self, name: S, offset: Option<IdxSize>) -> LazyFrame
1860 where
1861 S: Into<PlSmallStr>,
1862 {
1863 let name = name.into();
1864
1865 match &self.logical_plan {
1866 v @ DslPlan::Scan {
1867 scan_type,
1868 unified_scan_args,
1869 ..
1870 } if unified_scan_args.row_index.is_none()
1871 && !matches!(
1872 &**scan_type,
1873 FileScanDsl::Anonymous { .. } | FileScanDsl::ExpandedPaths { .. }
1874 ) =>
1875 {
1876 let DslPlan::Scan {
1877 sources,
1878 mut unified_scan_args,
1879 scan_type,
1880 cached_ir: _,
1881 } = v.clone()
1882 else {
1883 unreachable!()
1884 };
1885
1886 unified_scan_args.row_index = Some(RowIndex {
1887 name,
1888 offset: offset.unwrap_or(0),
1889 });
1890
1891 DslPlan::Scan {
1892 sources,
1893 unified_scan_args,
1894 scan_type,
1895 cached_ir: Default::default(),
1896 }
1897 .into()
1898 },
1899 _ => self.map_private(DslFunction::RowIndex { name, offset }),
1900 }
1901 }
1902
1903 pub fn count(self) -> LazyFrame {
1905 self.select(vec![col(PlSmallStr::from_static("*")).count()])
1906 }
1907
1908 #[cfg(feature = "dtype-struct")]
1911 pub fn unnest(self, cols: Selector, separator: Option<PlSmallStr>) -> Self {
1912 self.map_private(DslFunction::Unnest {
1913 columns: cols,
1914 separator,
1915 })
1916 }
1917
1918 #[cfg(feature = "merge_sorted")]
1919 pub fn merge_sorted<I, S>(
1920 self,
1921 other: LazyFrame,
1922 key: I,
1923 maintain_order: bool,
1924 ) -> PolarsResult<LazyFrame>
1925 where
1926 I: IntoIterator<Item = S>,
1927 S: Into<PlSmallStr>,
1928 {
1929 let key: Arc<[PlSmallStr]> = key.into_iter().map(Into::into).collect();
1930
1931 polars_ensure!(
1932 !key.is_empty(),
1933 ComputeError: "merge_sorted requires at least one key column"
1934 );
1935
1936 let lp = DslPlan::MergeSorted {
1937 input_left: Arc::new(self.logical_plan),
1938 input_right: Arc::new(other.logical_plan),
1939 key,
1940 maintain_order,
1941 };
1942 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1943 }
1944
1945 pub fn hint(self, hint: HintIR) -> PolarsResult<LazyFrame> {
1946 let lp = DslPlan::MapFunction {
1947 input: Arc::new(self.logical_plan),
1948 function: DslFunction::Hint(hint),
1949 };
1950 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1951 }
1952}
1953
1954#[derive(Clone)]
1956pub struct LazyGroupBy {
1957 pub logical_plan: DslPlan,
1958 opt_state: OptFlags,
1959 keys: Vec<Expr>,
1960 predicates: Vec<Expr>,
1961 maintain_order: bool,
1962 #[cfg(feature = "dynamic_group_by")]
1963 dynamic_options: Option<DynamicGroupOptions>,
1964 #[cfg(feature = "dynamic_group_by")]
1965 rolling_options: Option<RollingGroupOptions>,
1966}
1967
1968impl From<LazyGroupBy> for LazyFrame {
1969 fn from(lgb: LazyGroupBy) -> Self {
1970 Self {
1971 logical_plan: lgb.logical_plan,
1972 opt_state: lgb.opt_state,
1973 cached_arena: Default::default(),
1974 }
1975 }
1976}
1977
1978impl LazyGroupBy {
1979 pub fn having(mut self, predicate: Expr) -> Self {
2000 self.predicates.push(predicate);
2001 self
2002 }
2003
2004 pub fn agg<E: AsRef<[Expr]>>(self, aggs: E) -> LazyFrame {
2026 #[cfg(feature = "dynamic_group_by")]
2027 let lp = DslBuilder::from(self.logical_plan)
2028 .group_by(
2029 self.keys,
2030 self.predicates,
2031 aggs,
2032 None,
2033 self.maintain_order,
2034 self.dynamic_options,
2035 self.rolling_options,
2036 )
2037 .build();
2038
2039 #[cfg(not(feature = "dynamic_group_by"))]
2040 let lp = DslBuilder::from(self.logical_plan)
2041 .group_by(self.keys, self.predicates, aggs, None, self.maintain_order)
2042 .build();
2043 LazyFrame::from_logical_plan(lp, self.opt_state)
2044 }
2045
2046 pub fn head(self, n: Option<usize>) -> LazyFrame {
2048 let keys = self
2049 .keys
2050 .iter()
2051 .filter_map(|expr| expr_output_name(expr).ok())
2052 .collect::<Vec<_>>();
2053
2054 self.agg([all().as_expr().head(n)]).explode_impl(
2055 all() - by_name(keys.iter().cloned(), false, false),
2056 ExplodeOptions {
2057 empty_as_null: true,
2058 keep_nulls: true,
2059 },
2060 true,
2061 )
2062 }
2063
2064 pub fn tail(self, n: Option<usize>) -> LazyFrame {
2066 let keys = self
2067 .keys
2068 .iter()
2069 .filter_map(|expr| expr_output_name(expr).ok())
2070 .collect::<Vec<_>>();
2071
2072 self.agg([all().as_expr().tail(n)]).explode_impl(
2073 all() - by_name(keys.iter().cloned(), false, false),
2074 ExplodeOptions {
2075 empty_as_null: true,
2076 keep_nulls: true,
2077 },
2078 true,
2079 )
2080 }
2081
2082 pub fn apply(self, f: PlanCallback<DataFrame, DataFrame>, schema: SchemaRef) -> LazyFrame {
2087 if !self.predicates.is_empty() {
2088 panic!("not yet implemented: `apply` cannot be used with `having` predicates");
2089 }
2090
2091 #[cfg(feature = "dynamic_group_by")]
2092 let options = GroupbyOptions {
2093 dynamic: self.dynamic_options,
2094 rolling: self.rolling_options,
2095 slice: None,
2096 };
2097
2098 #[cfg(not(feature = "dynamic_group_by"))]
2099 let options = GroupbyOptions { slice: None };
2100
2101 let lp = DslPlan::GroupBy {
2102 input: Arc::new(self.logical_plan),
2103 keys: self.keys,
2104 predicates: vec![],
2105 aggs: vec![],
2106 apply: Some((f, schema)),
2107 maintain_order: self.maintain_order,
2108 options: Arc::new(options),
2109 };
2110 LazyFrame::from_logical_plan(lp, self.opt_state)
2111 }
2112}
2113
2114#[must_use]
2115pub struct JoinBuilder {
2116 lf: LazyFrame,
2117 how: JoinType,
2118 other: Option<LazyFrame>,
2119 left_on: Vec<Expr>,
2120 right_on: Vec<Expr>,
2121 allow_parallel: bool,
2122 force_parallel: bool,
2123 suffix: Option<PlSmallStr>,
2124 validation: JoinValidation,
2125 nulls_equal: bool,
2126 coalesce: JoinCoalesce,
2127 maintain_order: MaintainOrderJoin,
2128 build_side: Option<JoinBuildSide>,
2129}
2130impl JoinBuilder {
2131 pub fn new(lf: LazyFrame) -> Self {
2133 Self {
2134 lf,
2135 other: None,
2136 how: JoinType::Inner,
2137 left_on: vec![],
2138 right_on: vec![],
2139 allow_parallel: true,
2140 force_parallel: false,
2141 suffix: None,
2142 validation: Default::default(),
2143 nulls_equal: false,
2144 coalesce: Default::default(),
2145 maintain_order: Default::default(),
2146 build_side: None,
2147 }
2148 }
2149
2150 pub fn with(mut self, other: LazyFrame) -> Self {
2152 self.other = Some(other);
2153 self
2154 }
2155
2156 pub fn how(mut self, how: JoinType) -> Self {
2158 self.how = how;
2159 self
2160 }
2161
2162 pub fn validate(mut self, validation: JoinValidation) -> Self {
2163 self.validation = validation;
2164 self
2165 }
2166
2167 pub fn on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2171 let on = on.as_ref().to_vec();
2172 self.left_on.clone_from(&on);
2173 self.right_on = on;
2174 self
2175 }
2176
2177 pub fn left_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2181 self.left_on = on.as_ref().to_vec();
2182 self
2183 }
2184
2185 pub fn right_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2189 self.right_on = on.as_ref().to_vec();
2190 self
2191 }
2192
2193 pub fn allow_parallel(mut self, allow: bool) -> Self {
2195 self.allow_parallel = allow;
2196 self
2197 }
2198
2199 pub fn force_parallel(mut self, force: bool) -> Self {
2201 self.force_parallel = force;
2202 self
2203 }
2204
2205 pub fn join_nulls(mut self, nulls_equal: bool) -> Self {
2207 self.nulls_equal = nulls_equal;
2208 self
2209 }
2210
2211 pub fn suffix<S>(mut self, suffix: S) -> Self
2214 where
2215 S: Into<PlSmallStr>,
2216 {
2217 self.suffix = Some(suffix.into());
2218 self
2219 }
2220
2221 pub fn coalesce(mut self, coalesce: JoinCoalesce) -> Self {
2223 self.coalesce = coalesce;
2224 self
2225 }
2226
2227 pub fn maintain_order(mut self, maintain_order: MaintainOrderJoin) -> Self {
2229 self.maintain_order = maintain_order;
2230 self
2231 }
2232
2233 pub fn build_side(mut self, build_side: Option<JoinBuildSide>) -> Self {
2235 self.build_side = build_side;
2236 self
2237 }
2238
2239 pub fn finish(self) -> PolarsResult<LazyFrame> {
2241 let opt_state = self.lf.opt_state;
2242 let other = self.other.expect("'with' not set in join builder");
2243
2244 let args = JoinArgs {
2245 how: self.how,
2246 validation: self.validation,
2247 suffix: self.suffix,
2248 slice: None,
2249 nulls_equal: self.nulls_equal,
2250 coalesce: self.coalesce,
2251 maintain_order: self.maintain_order,
2252 build_side: self.build_side,
2253 };
2254
2255 let lp = self
2256 .lf
2257 .get_plan_builder()
2258 .join(
2259 other.logical_plan,
2260 self.left_on,
2261 self.right_on,
2262 JoinOptions {
2263 allow_parallel: self.allow_parallel,
2264 force_parallel: self.force_parallel,
2265 args,
2266 }
2267 .into(),
2268 )?
2269 .build();
2270 Ok(LazyFrame::from_logical_plan(lp, opt_state))
2271 }
2272
2273 pub fn join_where(self, predicates: Vec<Expr>) -> LazyFrame {
2275 let opt_state = self.lf.opt_state;
2276 let other = self.other.expect("with not set");
2277
2278 fn decompose_and(predicate: Expr, expanded_predicates: &mut Vec<Expr>) {
2280 if let Expr::BinaryExpr {
2281 op: Operator::And,
2282 left,
2283 right,
2284 } = predicate
2285 {
2286 decompose_and((*left).clone(), expanded_predicates);
2287 decompose_and((*right).clone(), expanded_predicates);
2288 } else {
2289 expanded_predicates.push(predicate);
2290 }
2291 }
2292 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2293 for predicate in predicates {
2294 decompose_and(predicate, &mut expanded_predicates);
2295 }
2296 let predicates: Vec<Expr> = expanded_predicates;
2297
2298 #[cfg(feature = "is_between")]
2300 let predicates: Vec<Expr> = {
2301 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2302 for predicate in predicates {
2303 if let Expr::Function {
2304 function: FunctionExpr::Boolean(BooleanFunction::IsBetween { closed }),
2305 input,
2306 ..
2307 } = &predicate
2308 {
2309 if let [expr, lower, upper] = input.as_slice() {
2310 match closed {
2311 ClosedInterval::Both => {
2312 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2313 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2314 },
2315 ClosedInterval::Right => {
2316 expanded_predicates.push(expr.clone().gt(lower.clone()));
2317 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2318 },
2319 ClosedInterval::Left => {
2320 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2321 expanded_predicates.push(expr.clone().lt(upper.clone()));
2322 },
2323 ClosedInterval::None => {
2324 expanded_predicates.push(expr.clone().gt(lower.clone()));
2325 expanded_predicates.push(expr.clone().lt(upper.clone()));
2326 },
2327 }
2328 continue;
2329 }
2330 }
2331 expanded_predicates.push(predicate);
2332 }
2333 expanded_predicates
2334 };
2335
2336 let args = JoinArgs {
2337 how: self.how,
2338 validation: self.validation,
2339 suffix: self.suffix,
2340 slice: None,
2341 nulls_equal: self.nulls_equal,
2342 coalesce: self.coalesce,
2343 maintain_order: self.maintain_order,
2344 build_side: self.build_side,
2345 };
2346 let options = JoinOptions {
2347 allow_parallel: self.allow_parallel,
2348 force_parallel: self.force_parallel,
2349 args,
2350 };
2351
2352 let lp = DslPlan::Join {
2353 input_left: Arc::new(self.lf.logical_plan),
2354 input_right: Arc::new(other.logical_plan),
2355 condition: JoinCondition::NonEqui { predicates },
2356 options: Arc::from(options),
2357 };
2358
2359 LazyFrame::from_logical_plan(lp, opt_state)
2360 }
2361}
2362
2363pub const BUILD_STREAMING_EXECUTOR: Option<polars_mem_engine::StreamingExecutorBuilder> = {
2364 #[cfg(not(feature = "streaming"))]
2365 {
2366 None
2367 }
2368 #[cfg(feature = "streaming")]
2369 {
2370 Some(polars_stream::build_streaming_query_executor)
2371 }
2372};
2373
2374fn run_in_memory_query(
2375 node: Node,
2376 ir_arena: &mut Arena<IR>,
2377 expr_arena: &mut Arena<AExpr>,
2378 engine: Engine,
2379 observer: Option<Box<dyn QueryObserver>>,
2380) -> PolarsResult<QueryResult> {
2381 let _guard = observer
2382 .as_ref()
2383 .map(|o| o.on_query_planned(to_planned_query(node, ir_arena, expr_arena)));
2384
2385 let result = if let IR::SinkMultiple { inputs } = ir_arena.get(node) {
2386 polars_ensure!(
2387 engine != Engine::Gpu,
2388 InvalidOperation:
2389 "collect_all is not supported for the gpu engine"
2390 );
2391
2392 let physical_plan = create_multiple_physical_plans(
2393 inputs.clone().as_slice(),
2394 ir_arena,
2395 expr_arena,
2396 BUILD_STREAMING_EXECUTOR,
2397 )?;
2398 physical_plan.execute().map(QueryResult::Multiple)
2399 } else {
2400 let mut physical_plan =
2401 create_physical_plan(node, ir_arena, expr_arena, BUILD_STREAMING_EXECUTOR)?;
2402 let mut state = ExecutionState::new();
2403 physical_plan.execute(&mut state).map(QueryResult::Single)
2404 };
2405
2406 result.inspect_err(|err| {
2407 if let Some(o) = observer.as_ref() {
2408 o.on_query_failed(err);
2409 }
2410 })
2411}
2412
2413fn to_planned_query(node: Node, ir_arena: &Arena<IR>, expr_arena: &Arena<AExpr>) -> PlannedQuery {
2414 let ir = ir_plan_to_description(&[node], ir_arena, expr_arena);
2415 PlannedQuery::new(ir)
2416}
2417
2418pub struct CollectBatches {
2419 recv: Receiver<PolarsResult<DataFrame>>,
2420 runner: Option<Box<dyn FnOnce() + Send + 'static>>,
2421}
2422
2423impl CollectBatches {
2424 pub fn start(&mut self) {
2426 if let Some(runner) = self.runner.take() {
2427 runner()
2428 }
2429 }
2430}
2431
2432impl Iterator for CollectBatches {
2433 type Item = PolarsResult<DataFrame>;
2434
2435 fn next(&mut self) -> Option<Self::Item> {
2436 self.start();
2437 self.recv.recv().ok()
2438 }
2439}