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_ops::frame::{JoinBuildSide, JoinCoalesce, MaintainOrderJoin};
34#[cfg(feature = "is_between")]
35use polars_ops::prelude::ClosedInterval;
36pub use polars_plan::frame::{AllowedOptimizations, OptFlags};
37use polars_utils::pl_str::PlSmallStr;
38
39use crate::frame::cached_arenas::CachedArena;
40use crate::prelude::*;
41
42pub trait IntoLazy {
43 fn lazy(self) -> LazyFrame;
44}
45
46impl IntoLazy for DataFrame {
47 fn lazy(self) -> LazyFrame {
49 let lp = DslBuilder::from_existing_df(self).build();
50 LazyFrame {
51 logical_plan: lp,
52 opt_state: Default::default(),
53 cached_arena: Default::default(),
54 }
55 }
56}
57
58impl IntoLazy for LazyFrame {
59 fn lazy(self) -> LazyFrame {
60 self
61 }
62}
63
64#[derive(Clone, Default)]
69#[must_use]
70pub struct LazyFrame {
71 pub logical_plan: DslPlan,
72 pub(crate) opt_state: OptFlags,
73 pub(crate) cached_arena: Arc<Mutex<Option<CachedArena>>>,
74}
75
76impl From<DslPlan> for LazyFrame {
77 fn from(plan: DslPlan) -> Self {
78 Self {
79 logical_plan: plan,
80 opt_state: OptFlags::default(),
81 cached_arena: Default::default(),
82 }
83 }
84}
85
86impl LazyFrame {
87 pub(crate) fn from_inner(
88 logical_plan: DslPlan,
89 opt_state: OptFlags,
90 cached_arena: Arc<Mutex<Option<CachedArena>>>,
91 ) -> Self {
92 Self {
93 logical_plan,
94 opt_state,
95 cached_arena,
96 }
97 }
98
99 pub(crate) fn get_plan_builder(self) -> DslBuilder {
100 DslBuilder::from(self.logical_plan)
101 }
102
103 fn get_opt_state(&self) -> OptFlags {
104 self.opt_state
105 }
106
107 pub fn from_logical_plan(logical_plan: DslPlan, opt_state: OptFlags) -> Self {
108 LazyFrame {
109 logical_plan,
110 opt_state,
111 cached_arena: Default::default(),
112 }
113 }
114
115 pub fn get_current_optimizations(&self) -> OptFlags {
117 self.opt_state
118 }
119
120 pub fn with_optimizations(mut self, opt_state: OptFlags) -> Self {
122 self.opt_state = opt_state;
123 self
124 }
125
126 pub fn without_optimizations(self) -> Self {
128 self.with_optimizations(OptFlags::from_bits_truncate(0) | OptFlags::TYPE_COERCION)
129 }
130
131 pub fn with_projection_pushdown(mut self, toggle: bool) -> Self {
133 self.opt_state.set(OptFlags::PROJECTION_PUSHDOWN, toggle);
134 self
135 }
136
137 pub fn with_cluster_with_columns(mut self, toggle: bool) -> Self {
139 self.opt_state.set(OptFlags::CLUSTER_WITH_COLUMNS, toggle);
140 self
141 }
142
143 pub fn with_check_order(mut self, toggle: bool) -> Self {
146 self.opt_state.set(OptFlags::CHECK_ORDER_OBSERVE, toggle);
147 self
148 }
149
150 pub fn with_predicate_pushdown(mut self, toggle: bool) -> Self {
152 self.opt_state.set(OptFlags::PREDICATE_PUSHDOWN, toggle);
153 self
154 }
155
156 pub fn with_type_coercion(mut self, toggle: bool) -> Self {
158 self.opt_state.set(OptFlags::TYPE_COERCION, toggle);
159 self
160 }
161
162 pub fn with_type_check(mut self, toggle: bool) -> Self {
164 self.opt_state.set(OptFlags::TYPE_CHECK, toggle);
165 self
166 }
167
168 pub fn with_simplify_expr(mut self, toggle: bool) -> Self {
170 self.opt_state.set(OptFlags::SIMPLIFY_EXPR, toggle);
171 self
172 }
173
174 #[cfg(feature = "cse")]
176 pub fn with_comm_subplan_elim(mut self, toggle: bool) -> Self {
177 self.opt_state.set(OptFlags::COMM_SUBPLAN_ELIM, toggle);
178 self
179 }
180
181 #[cfg(feature = "cse")]
183 pub fn with_comm_subexpr_elim(mut self, toggle: bool) -> Self {
184 self.opt_state.set(OptFlags::COMM_SUBEXPR_ELIM, toggle);
185 self
186 }
187
188 pub fn with_slice_pushdown(mut self, toggle: bool) -> Self {
190 self.opt_state.set(OptFlags::SLICE_PUSHDOWN, toggle);
191 self
192 }
193
194 #[cfg(feature = "streaming")]
195 pub fn with_streaming(mut self, toggle: bool) -> Self {
196 self.opt_state.set(OptFlags::STREAMING, toggle);
197 self
198 }
199
200 pub fn with_gpu(mut self, toggle: bool) -> Self {
201 self.opt_state.set(OptFlags::GPU, toggle);
202 self
203 }
204
205 pub fn with_row_estimate(mut self, toggle: bool) -> Self {
207 self.opt_state.set(OptFlags::ROW_ESTIMATE, toggle);
208 self
209 }
210
211 pub fn _with_eager(mut self, toggle: bool) -> Self {
213 self.opt_state.set(OptFlags::EAGER, toggle);
214 self
215 }
216
217 pub fn describe_plan(&self) -> PolarsResult<String> {
219 Ok(self.clone().to_alp()?.describe())
220 }
221
222 pub fn describe_plan_tree(&self) -> PolarsResult<String> {
224 Ok(self.clone().to_alp()?.describe_tree_format())
225 }
226
227 pub fn describe_optimized_plan(&self) -> PolarsResult<String> {
231 Ok(self.clone().to_alp_optimized()?.describe())
232 }
233
234 pub fn describe_optimized_plan_tree(&self) -> PolarsResult<String> {
238 Ok(self.clone().to_alp_optimized()?.describe_tree_format())
239 }
240
241 pub fn explain(&self, optimized: bool) -> PolarsResult<String> {
246 if optimized {
247 self.describe_optimized_plan()
248 } else {
249 self.describe_plan()
250 }
251 }
252
253 pub fn sort(self, by: impl IntoVec<PlSmallStr>, sort_options: SortMultipleOptions) -> Self {
293 let opt_state = self.get_opt_state();
294 let lp = self
295 .get_plan_builder()
296 .sort(by.into_vec().into_iter().map(col).collect(), sort_options)
297 .build();
298 Self::from_logical_plan(lp, opt_state)
299 }
300
301 pub fn sort_by_exprs<E: AsRef<[Expr]>>(
321 self,
322 by_exprs: E,
323 sort_options: SortMultipleOptions,
324 ) -> Self {
325 let by_exprs = by_exprs.as_ref().to_vec();
326 if by_exprs.is_empty() {
327 self
328 } else {
329 let opt_state = self.get_opt_state();
330 let lp = self.get_plan_builder().sort(by_exprs, sort_options).build();
331 Self::from_logical_plan(lp, opt_state)
332 }
333 }
334
335 pub fn top_k<E: AsRef<[Expr]>>(
336 self,
337 k: IdxSize,
338 by_exprs: E,
339 sort_options: SortMultipleOptions,
340 ) -> Self {
341 self.sort_by_exprs(
343 by_exprs,
344 sort_options.with_order_reversed().with_nulls_last(true),
345 )
346 .slice(0, k)
347 }
348
349 pub fn bottom_k<E: AsRef<[Expr]>>(
350 self,
351 k: IdxSize,
352 by_exprs: E,
353 sort_options: SortMultipleOptions,
354 ) -> Self {
355 self.sort_by_exprs(by_exprs, sort_options.with_nulls_last(true))
357 .slice(0, k)
358 }
359
360 pub fn reverse(self) -> Self {
376 self.select(vec![col(PlSmallStr::from_static("*")).reverse()])
377 }
378
379 pub fn rename<I, J, T, S>(self, existing: I, new: J, strict: bool) -> Self
387 where
388 I: IntoIterator<Item = T>,
389 J: IntoIterator<Item = S>,
390 T: AsRef<str>,
391 S: AsRef<str>,
392 {
393 let iter = existing.into_iter();
394 let cap = iter.size_hint().0;
395 let mut existing_vec: Vec<PlSmallStr> = Vec::with_capacity(cap);
396 let mut new_vec: Vec<PlSmallStr> = Vec::with_capacity(cap);
397
398 for (existing, new) in iter.zip(new) {
401 let existing = existing.as_ref();
402 let new = new.as_ref();
403 if new != existing {
404 existing_vec.push(existing.into());
405 new_vec.push(new.into());
406 }
407 }
408
409 self.map_private(DslFunction::Rename {
410 existing: existing_vec.into(),
411 new: new_vec.into(),
412 strict,
413 })
414 }
415
416 pub fn drop(self, columns: Selector) -> Self {
423 let opt_state = self.get_opt_state();
424 let lp = self.get_plan_builder().drop(columns).build();
425 Self::from_logical_plan(lp, opt_state)
426 }
427
428 pub fn shift<E: Into<Expr>>(self, n: E) -> Self {
433 self.select(vec![col(PlSmallStr::from_static("*")).shift(n.into())])
434 }
435
436 pub fn shift_and_fill<E: Into<Expr>, IE: Into<Expr>>(self, n: E, fill_value: IE) -> Self {
441 self.select(vec![
442 col(PlSmallStr::from_static("*")).shift_and_fill(n.into(), fill_value.into()),
443 ])
444 }
445
446 pub fn fill_null<E: Into<Expr>>(self, fill_value: E) -> LazyFrame {
448 let opt_state = self.get_opt_state();
449 let lp = self.get_plan_builder().fill_null(fill_value.into()).build();
450 Self::from_logical_plan(lp, opt_state)
451 }
452
453 pub fn fill_nan<E: Into<Expr>>(self, fill_value: E) -> LazyFrame {
455 let opt_state = self.get_opt_state();
456 let lp = self.get_plan_builder().fill_nan(fill_value.into()).build();
457 Self::from_logical_plan(lp, opt_state)
458 }
459
460 pub fn cache(self) -> Self {
464 let opt_state = self.get_opt_state();
465 let lp = self.get_plan_builder().cache().build();
466 Self::from_logical_plan(lp, opt_state)
467 }
468
469 pub fn cast(self, dtypes: PlHashMap<&str, DataType>, strict: bool) -> Self {
471 let cast_cols: Vec<Expr> = dtypes
472 .into_iter()
473 .map(|(name, dt)| {
474 let name = PlSmallStr::from_str(name);
475
476 if strict {
477 col(name).strict_cast(dt)
478 } else {
479 col(name).cast(dt)
480 }
481 })
482 .collect();
483
484 if cast_cols.is_empty() {
485 self
486 } else {
487 self.with_columns(cast_cols)
488 }
489 }
490
491 pub fn cast_all(self, dtype: impl Into<DataTypeExpr>, strict: bool) -> Self {
493 self.with_columns(vec![if strict {
494 col(PlSmallStr::from_static("*")).strict_cast(dtype)
495 } else {
496 col(PlSmallStr::from_static("*")).cast(dtype)
497 }])
498 }
499
500 pub fn optimize(
501 self,
502 lp_arena: &mut Arena<IR>,
503 expr_arena: &mut Arena<AExpr>,
504 ) -> PolarsResult<Node> {
505 self.optimize_with_scratch(lp_arena, expr_arena, &mut vec![])
506 }
507
508 pub fn to_alp_optimized(mut self) -> PolarsResult<IRPlan> {
509 let (mut lp_arena, mut expr_arena) = self.get_arenas();
510 let node = self.optimize_with_scratch(&mut lp_arena, &mut expr_arena, &mut vec![])?;
511
512 Ok(IRPlan::new(node, lp_arena, expr_arena))
513 }
514
515 pub fn to_alp(mut self) -> PolarsResult<IRPlan> {
516 let (mut lp_arena, mut expr_arena) = self.get_arenas();
517 let node = to_alp(
518 self.logical_plan,
519 &mut expr_arena,
520 &mut lp_arena,
521 &mut self.opt_state,
522 )?;
523 let plan = IRPlan::new(node, lp_arena, expr_arena);
524 Ok(plan)
525 }
526
527 pub(crate) fn optimize_with_scratch(
528 self,
529 ir_arena: &mut Arena<IR>,
530 expr_arena: &mut Arena<AExpr>,
531 scratch: &mut Vec<Node>,
532 ) -> PolarsResult<Node> {
533 let mut opt_flags = self.opt_state;
534 #[allow(clippy::eq_op)]
537 #[cfg(feature = "cse")]
538 if opt_flags.contains(OptFlags::EAGER) {
539 opt_flags &= !(OptFlags::COMM_SUBEXPR_ELIM | OptFlags::COMM_SUBEXPR_ELIM);
540 }
541 let root = to_alp(self.logical_plan, expr_arena, ir_arena, &mut opt_flags)?;
542
543 let lp_top = optimize(
544 root,
545 opt_flags,
546 ir_arena,
547 expr_arena,
548 scratch,
549 apply_scan_predicate_to_scan_ir,
550 )?;
551
552 Ok(lp_top)
553 }
554
555 fn prepare_collect_post_opt<P>(
556 mut self,
557 check_sink: bool,
558 query_start: Option<std::time::Instant>,
559 post_opt: P,
560 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)>
561 where
562 P: FnOnce(
563 Node,
564 &mut Arena<IR>,
565 &mut Arena<AExpr>,
566 Option<std::time::Duration>,
567 ) -> PolarsResult<()>,
568 {
569 let (mut lp_arena, mut expr_arena) = self.get_arenas();
570
571 let mut scratch = vec![];
572 let lp_top = self.optimize_with_scratch(&mut lp_arena, &mut expr_arena, &mut scratch)?;
573
574 post_opt(
575 lp_top,
576 &mut lp_arena,
577 &mut expr_arena,
578 query_start.map(|s| s.elapsed()),
581 )?;
582
583 let no_file_sink = if check_sink {
585 !matches!(
586 lp_arena.get(lp_top),
587 IR::Sink {
588 payload: SinkTypeIR::File { .. },
589 ..
590 }
591 )
592 } else {
593 true
594 };
595 let physical_plan = create_physical_plan(
596 lp_top,
597 &mut lp_arena,
598 &mut expr_arena,
599 BUILD_STREAMING_EXECUTOR,
600 )?;
601
602 let state = ExecutionState::new();
603 Ok((state, physical_plan, no_file_sink))
604 }
605
606 pub fn _collect_post_opt<P>(self, post_opt: P) -> PolarsResult<DataFrame>
608 where
609 P: FnOnce(
610 Node,
611 &mut Arena<IR>,
612 &mut Arena<AExpr>,
613 Option<std::time::Duration>,
614 ) -> PolarsResult<()>,
615 {
616 let (mut state, mut physical_plan, _) =
617 self.prepare_collect_post_opt(false, None, post_opt)?;
618 physical_plan.execute(&mut state)
619 }
620
621 #[allow(unused_mut)]
622 fn prepare_collect(
623 self,
624 check_sink: bool,
625 query_start: Option<std::time::Instant>,
626 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)> {
627 self.prepare_collect_post_opt(check_sink, query_start, |_, _, _, _| Ok(()))
628 }
629
630 pub fn collect_with_engine(mut self, engine: Engine) -> PolarsResult<QueryResult> {
635 let engine = match engine {
636 Engine::Streaming => Engine::Streaming,
637 _ if std::env::var("POLARS_FORCE_STREAMING").as_deref() == Ok("1") => Engine::Streaming,
638 Engine::Auto => Engine::InMemory,
639 v => v,
640 };
641
642 if engine != Engine::Streaming
643 && std::env::var("POLARS_AUTO_STREAMING").as_deref() == Ok("1")
644 {
645 feature_gated!("streaming", {
646 if let Some(r) = self.clone()._collect_with_streaming_suppress_todo_panic() {
647 return r;
648 }
649 })
650 }
651 match engine {
652 Engine::Streaming => {
653 feature_gated!("streaming", self = self.with_streaming(true))
654 },
655 Engine::Gpu => self = self.with_gpu(true),
656 _ => (),
657 }
658
659 let mut ir_plan = self.to_alp_optimized()?;
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 )
670 }),
671 Engine::InMemory | Engine::Gpu => {
672 if let IR::SinkMultiple { inputs } = ir_plan.root() {
673 polars_ensure!(
674 engine != Engine::Gpu,
675 InvalidOperation:
676 "collect_all is not supported for the gpu engine"
677 );
678
679 return create_multiple_physical_plans(
680 inputs.clone().as_slice(),
681 &mut ir_plan.lp_arena,
682 &mut ir_plan.expr_arena,
683 BUILD_STREAMING_EXECUTOR,
684 )?
685 .execute()
686 .map(QueryResult::Multiple);
687 }
688
689 let mut physical_plan = create_physical_plan(
690 ir_plan.lp_top,
691 &mut ir_plan.lp_arena,
692 &mut ir_plan.expr_arena,
693 BUILD_STREAMING_EXECUTOR,
694 )?;
695 let mut state = ExecutionState::new();
696 physical_plan.execute(&mut state).map(QueryResult::Single)
697 },
698 Engine::Auto => unreachable!(),
699 }
700 }
701
702 pub fn explain_all(plans: Vec<DslPlan>, opt_state: OptFlags) -> PolarsResult<String> {
703 let sink_multiple = LazyFrame {
704 logical_plan: DslPlan::SinkMultiple { inputs: plans },
705 opt_state,
706 cached_arena: Default::default(),
707 };
708 sink_multiple.explain(true)
709 }
710
711 pub fn collect_all_with_engine(
712 plans: Vec<DslPlan>,
713 engine: Engine,
714 opt_state: OptFlags,
715 ) -> PolarsResult<Vec<DataFrame>> {
716 if plans.is_empty() {
717 return Ok(Vec::new());
718 }
719
720 LazyFrame {
721 logical_plan: DslPlan::SinkMultiple { inputs: plans },
722 opt_state,
723 cached_arena: Default::default(),
724 }
725 .collect_with_engine(engine)
726 .map(|r| r.unwrap_multiple())
727 }
728
729 pub fn collect(self) -> PolarsResult<DataFrame> {
747 self.collect_with_engine(Engine::Auto).map(|r| match r {
748 QueryResult::Single(df) => df,
749 QueryResult::Multiple(_) => DataFrame::empty(),
751 })
752 }
753
754 #[cfg(feature = "async")]
759 pub fn collect_batches(
760 self,
761 engine: Engine,
762 maintain_order: bool,
763 chunk_size: Option<NonZeroUsize>,
764 lazy: bool,
765 ) -> PolarsResult<CollectBatches> {
766 let (send, recv) = sync_channel(1);
767 let runner_send = send.clone();
768 let ldf = self.sink_batches(
769 PlanCallback::new(move |df| {
770 let send_result = send.send(Ok(df));
772 Ok(send_result.is_err())
773 }),
774 maintain_order,
775 chunk_size,
776 )?;
777 let runner = move || {
778 polars_core::runtime::ASYNC.spawn_blocking(move || {
780 if let Err(e) = ldf.collect_with_engine(engine) {
781 runner_send.send(Err(e)).ok();
782 }
783 });
784 };
785
786 let mut collect_batches = CollectBatches {
787 recv,
788 runner: Some(Box::new(runner)),
789 };
790 if !lazy {
791 collect_batches.start();
792 }
793 Ok(collect_batches)
794 }
795
796 pub fn _profile_post_opt<P>(self, post_opt: P) -> PolarsResult<(DataFrame, DataFrame)>
799 where
800 P: FnOnce(
801 Node,
802 &mut Arena<IR>,
803 &mut Arena<AExpr>,
804 Option<std::time::Duration>,
805 ) -> PolarsResult<()>,
806 {
807 let query_start = std::time::Instant::now();
808 let (mut state, mut physical_plan, _) =
809 self.prepare_collect_post_opt(false, Some(query_start), post_opt)?;
810 state.time_nodes(query_start, query_start.elapsed());
811 let out = physical_plan.execute(&mut state)?;
812 let timer_df = state.finish_timer()?;
813 Ok((out, timer_df))
814 }
815
816 pub fn profile(self) -> PolarsResult<(DataFrame, DataFrame)> {
824 self._profile_post_opt(|_, _, _, _| Ok(()))
825 }
826
827 pub fn sink_batches(
828 mut self,
829 function: PlanCallback<DataFrame, bool>,
830 maintain_order: bool,
831 chunk_size: Option<NonZeroUsize>,
832 ) -> PolarsResult<Self> {
833 use polars_plan::prelude::sink::CallbackSinkType;
834
835 polars_ensure!(
836 !matches!(self.logical_plan, DslPlan::Sink { .. }),
837 InvalidOperation: "cannot create a sink on top of another sink"
838 );
839
840 self.logical_plan = DslPlan::Sink {
841 input: Arc::new(self.logical_plan),
842 payload: SinkType::Callback(CallbackSinkType {
843 function,
844 maintain_order,
845 chunk_size,
846 }),
847 };
848
849 Ok(self)
850 }
851
852 #[cfg(feature = "streaming")]
854 fn _collect_with_streaming_suppress_todo_panic(
855 mut self,
856 ) -> Option<PolarsResult<polars_core::query_result::QueryResult>> {
857 self.opt_state |= OptFlags::STREAMING;
858 let mut ir_plan = match self.to_alp_optimized() {
859 Ok(v) => v,
860 Err(e) => return Some(Err(e)),
861 };
862
863 ir_plan.ensure_root_node_is_sink();
864
865 let f = || {
866 polars_stream::run_query(
867 ir_plan.lp_top,
868 &mut ir_plan.lp_arena,
869 &mut ir_plan.expr_arena,
870 )
871 };
872
873 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
874 Ok(v) => Some(v),
875 Err(e) => {
876 if e.downcast_ref::<&str>()
879 .is_some_and(|s| s.starts_with("not yet implemented"))
880 {
881 if polars_core::config::verbose() {
882 eprintln!(
883 "caught unimplemented error in new streaming engine, falling back to normal engine"
884 );
885 }
886 None
887 } else {
888 std::panic::resume_unwind(e)
889 }
890 },
891 }
892 }
893
894 pub fn sink(
895 mut self,
896 sink_type: SinkDestination,
897 file_format: FileWriteFormat,
898 unified_sink_args: UnifiedSinkArgs,
899 ) -> PolarsResult<Self> {
900 polars_ensure!(
901 !matches!(self.logical_plan, DslPlan::Sink { .. }),
902 InvalidOperation: "cannot create a sink on top of another sink"
903 );
904
905 self.logical_plan = DslPlan::Sink {
906 input: Arc::new(self.logical_plan),
907 payload: match sink_type {
908 SinkDestination::File { target } => SinkType::File(FileSinkOptions {
909 target,
910 file_format,
911 unified_sink_args,
912 }),
913 SinkDestination::Partitioned {
914 base_path,
915 file_path_provider,
916 partition_strategy,
917 max_rows_per_file,
918 approximate_bytes_per_file,
919 } => SinkType::Partitioned(PartitionedSinkOptions {
920 base_path,
921 file_path_provider,
922 partition_strategy,
923 file_format,
924 unified_sink_args,
925 max_rows_per_file,
926 approximate_bytes_per_file,
927 }),
928 },
929 };
930 Ok(self)
931 }
932
933 pub fn filter(self, predicate: Expr) -> Self {
951 let opt_state = self.get_opt_state();
952 let lp = self.get_plan_builder().filter(predicate).build();
953 Self::from_logical_plan(lp, opt_state)
954 }
955
956 pub fn remove(self, predicate: Expr) -> Self {
974 self.filter(predicate.neq_missing(lit(true)))
975 }
976
977 pub fn select<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
1003 let exprs = exprs.as_ref().to_vec();
1004 self.select_impl(
1005 exprs,
1006 ProjectionOptions {
1007 run_parallel: true,
1008 duplicate_check: true,
1009 should_broadcast: true,
1010 },
1011 )
1012 }
1013
1014 pub fn select_seq<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
1015 let exprs = exprs.as_ref().to_vec();
1016 self.select_impl(
1017 exprs,
1018 ProjectionOptions {
1019 run_parallel: false,
1020 duplicate_check: true,
1021 should_broadcast: true,
1022 },
1023 )
1024 }
1025
1026 fn select_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> Self {
1027 let opt_state = self.get_opt_state();
1028 let lp = self.get_plan_builder().project(exprs, options).build();
1029 Self::from_logical_plan(lp, opt_state)
1030 }
1031
1032 pub fn group_by<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1053 let keys = by
1054 .as_ref()
1055 .iter()
1056 .map(|e| e.clone().into())
1057 .collect::<Vec<_>>();
1058 let opt_state = self.get_opt_state();
1059
1060 #[cfg(feature = "dynamic_group_by")]
1061 {
1062 LazyGroupBy {
1063 logical_plan: self.logical_plan,
1064 opt_state,
1065 keys,
1066 predicates: vec![],
1067 maintain_order: false,
1068 dynamic_options: None,
1069 rolling_options: None,
1070 }
1071 }
1072
1073 #[cfg(not(feature = "dynamic_group_by"))]
1074 {
1075 LazyGroupBy {
1076 logical_plan: self.logical_plan,
1077 opt_state,
1078 keys,
1079 predicates: vec![],
1080 maintain_order: false,
1081 }
1082 }
1083 }
1084
1085 #[cfg(feature = "dynamic_group_by")]
1093 pub fn rolling<E: AsRef<[Expr]>>(
1094 mut self,
1095 index_column: Expr,
1096 group_by: E,
1097 mut options: RollingGroupOptions,
1098 ) -> LazyGroupBy {
1099 if let Expr::Column(name) = index_column {
1100 options.index_column = name;
1101 } else {
1102 let output_field = index_column
1103 .to_field(&self.collect_schema().unwrap())
1104 .unwrap();
1105 return self.with_column(index_column).rolling(
1106 Expr::Column(output_field.name().clone()),
1107 group_by,
1108 options,
1109 );
1110 }
1111 let opt_state = self.get_opt_state();
1112 LazyGroupBy {
1113 logical_plan: self.logical_plan,
1114 opt_state,
1115 predicates: vec![],
1116 keys: group_by.as_ref().to_vec(),
1117 maintain_order: true,
1118 dynamic_options: None,
1119 rolling_options: Some(options),
1120 }
1121 }
1122
1123 #[cfg(feature = "dynamic_group_by")]
1139 pub fn group_by_dynamic<E: AsRef<[Expr]>>(
1140 mut self,
1141 index_column: Expr,
1142 group_by: E,
1143 mut options: DynamicGroupOptions,
1144 ) -> LazyGroupBy {
1145 if let Expr::Column(name) = index_column {
1146 options.index_column = name;
1147 } else {
1148 let output_field = index_column
1149 .to_field(&self.collect_schema().unwrap())
1150 .unwrap();
1151 return self.with_column(index_column).group_by_dynamic(
1152 Expr::Column(output_field.name().clone()),
1153 group_by,
1154 options,
1155 );
1156 }
1157 let opt_state = self.get_opt_state();
1158 LazyGroupBy {
1159 logical_plan: self.logical_plan,
1160 opt_state,
1161 predicates: vec![],
1162 keys: group_by.as_ref().to_vec(),
1163 maintain_order: true,
1164 dynamic_options: Some(options),
1165 rolling_options: None,
1166 }
1167 }
1168
1169 pub fn group_by_stable<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1171 let keys = by
1172 .as_ref()
1173 .iter()
1174 .map(|e| e.clone().into())
1175 .collect::<Vec<_>>();
1176 let opt_state = self.get_opt_state();
1177
1178 #[cfg(feature = "dynamic_group_by")]
1179 {
1180 LazyGroupBy {
1181 logical_plan: self.logical_plan,
1182 opt_state,
1183 keys,
1184 predicates: vec![],
1185 maintain_order: true,
1186 dynamic_options: None,
1187 rolling_options: None,
1188 }
1189 }
1190
1191 #[cfg(not(feature = "dynamic_group_by"))]
1192 {
1193 LazyGroupBy {
1194 logical_plan: self.logical_plan,
1195 opt_state,
1196 keys,
1197 predicates: vec![],
1198 maintain_order: true,
1199 }
1200 }
1201 }
1202
1203 #[cfg(feature = "semi_anti_join")]
1220 pub fn anti_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1221 self.join(
1222 other,
1223 [left_on.into()],
1224 [right_on.into()],
1225 JoinArgs::new(JoinType::Anti),
1226 )
1227 }
1228
1229 #[cfg(feature = "cross_join")]
1231 pub fn cross_join(self, other: LazyFrame, suffix: Option<PlSmallStr>) -> LazyFrame {
1232 self.join(
1233 other,
1234 vec![],
1235 vec![],
1236 JoinArgs::new(JoinType::Cross).with_suffix(suffix),
1237 )
1238 }
1239
1240 pub fn left_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1257 self.join(
1258 other,
1259 [left_on.into()],
1260 [right_on.into()],
1261 JoinArgs::new(JoinType::Left),
1262 )
1263 }
1264
1265 pub fn inner_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1282 self.join(
1283 other,
1284 [left_on.into()],
1285 [right_on.into()],
1286 JoinArgs::new(JoinType::Inner),
1287 )
1288 }
1289
1290 pub fn full_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1307 self.join(
1308 other,
1309 [left_on.into()],
1310 [right_on.into()],
1311 JoinArgs::new(JoinType::Full),
1312 )
1313 }
1314
1315 #[cfg(feature = "semi_anti_join")]
1332 pub fn semi_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1333 self.join(
1334 other,
1335 [left_on.into()],
1336 [right_on.into()],
1337 JoinArgs::new(JoinType::Semi),
1338 )
1339 }
1340
1341 pub fn join<E: AsRef<[Expr]>>(
1363 self,
1364 other: LazyFrame,
1365 left_on: E,
1366 right_on: E,
1367 args: JoinArgs,
1368 ) -> LazyFrame {
1369 let left_on = left_on.as_ref().to_vec();
1370 let right_on = right_on.as_ref().to_vec();
1371
1372 self._join_impl(other, left_on, right_on, args)
1373 }
1374
1375 fn _join_impl(
1376 self,
1377 other: LazyFrame,
1378 left_on: Vec<Expr>,
1379 right_on: Vec<Expr>,
1380 args: JoinArgs,
1381 ) -> LazyFrame {
1382 let JoinArgs {
1383 how,
1384 validation,
1385 suffix,
1386 slice,
1387 nulls_equal,
1388 coalesce,
1389 maintain_order,
1390 build_side,
1391 } = args;
1392
1393 if slice.is_some() {
1394 panic!("impl error: slice is not handled")
1395 }
1396
1397 let mut builder = self
1398 .join_builder()
1399 .with(other)
1400 .left_on(left_on)
1401 .right_on(right_on)
1402 .how(how)
1403 .validate(validation)
1404 .join_nulls(nulls_equal)
1405 .coalesce(coalesce)
1406 .maintain_order(maintain_order)
1407 .build_side(build_side);
1408
1409 if let Some(suffix) = suffix {
1410 builder = builder.suffix(suffix);
1411 }
1412
1413 builder.finish()
1415 }
1416
1417 pub fn join_builder(self) -> JoinBuilder {
1423 JoinBuilder::new(self)
1424 }
1425
1426 pub fn gather(self, idxs: LazyFrame, null_on_oob: bool) -> LazyFrame {
1430 let opt_state = self.get_opt_state();
1431 let lp = self
1432 .get_plan_builder()
1433 .gather(idxs.logical_plan, null_on_oob)
1434 .build();
1435 Self::from_logical_plan(lp, opt_state)
1436 }
1437
1438 pub fn with_column(self, expr: Expr) -> LazyFrame {
1456 let opt_state = self.get_opt_state();
1457 let lp = self
1458 .get_plan_builder()
1459 .with_columns(
1460 vec![expr],
1461 ProjectionOptions {
1462 run_parallel: false,
1463 duplicate_check: true,
1464 should_broadcast: true,
1465 },
1466 )
1467 .build();
1468 Self::from_logical_plan(lp, opt_state)
1469 }
1470
1471 pub fn with_columns<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1486 let exprs = exprs.as_ref().to_vec();
1487 self.with_columns_impl(
1488 exprs,
1489 ProjectionOptions {
1490 run_parallel: true,
1491 duplicate_check: true,
1492 should_broadcast: true,
1493 },
1494 )
1495 }
1496
1497 pub fn with_columns_seq<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1499 let exprs = exprs.as_ref().to_vec();
1500 self.with_columns_impl(
1501 exprs,
1502 ProjectionOptions {
1503 run_parallel: false,
1504 duplicate_check: true,
1505 should_broadcast: true,
1506 },
1507 )
1508 }
1509
1510 pub fn match_to_schema(
1512 self,
1513 schema: SchemaRef,
1514 per_column: Arc<[MatchToSchemaPerColumn]>,
1515 extra_columns: ExtraColumnsPolicy,
1516 ) -> LazyFrame {
1517 let opt_state = self.get_opt_state();
1518 let lp = self
1519 .get_plan_builder()
1520 .match_to_schema(schema, per_column, extra_columns)
1521 .build();
1522 Self::from_logical_plan(lp, opt_state)
1523 }
1524
1525 pub fn pipe_with_schema(
1526 self,
1527 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1528 ) -> Self {
1529 let opt_state = self.get_opt_state();
1530 let lp = self
1531 .get_plan_builder()
1532 .pipe_with_schema(vec![], callback)
1533 .build();
1534 Self::from_logical_plan(lp, opt_state)
1535 }
1536
1537 pub fn pipe_with_schemas(
1538 self,
1539 others: Vec<LazyFrame>,
1540 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1541 ) -> Self {
1542 let opt_state = self.get_opt_state();
1543 let lp = self
1544 .get_plan_builder()
1545 .pipe_with_schema(
1546 others.into_iter().map(|lf| lf.logical_plan).collect(),
1547 callback,
1548 )
1549 .build();
1550 Self::from_logical_plan(lp, opt_state)
1551 }
1552
1553 fn with_columns_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> LazyFrame {
1554 let opt_state = self.get_opt_state();
1555 let lp = self.get_plan_builder().with_columns(exprs, options).build();
1556 Self::from_logical_plan(lp, opt_state)
1557 }
1558
1559 pub fn with_context<C: AsRef<[LazyFrame]>>(self, contexts: C) -> LazyFrame {
1560 let contexts = contexts
1561 .as_ref()
1562 .iter()
1563 .map(|lf| lf.logical_plan.clone())
1564 .collect();
1565 let opt_state = self.get_opt_state();
1566 let lp = self.get_plan_builder().with_context(contexts).build();
1567 Self::from_logical_plan(lp, opt_state)
1568 }
1569
1570 pub fn max(self) -> Self {
1574 self.map_private(DslFunction::Stats(StatsFunction::Max))
1575 }
1576
1577 pub fn min(self) -> Self {
1581 self.map_private(DslFunction::Stats(StatsFunction::Min))
1582 }
1583
1584 pub fn sum(self) -> Self {
1594 self.map_private(DslFunction::Stats(StatsFunction::Sum))
1595 }
1596
1597 pub fn mean(self) -> Self {
1602 self.map_private(DslFunction::Stats(StatsFunction::Mean))
1603 }
1604
1605 pub fn median(self) -> Self {
1611 self.map_private(DslFunction::Stats(StatsFunction::Median))
1612 }
1613
1614 pub fn quantile(self, quantile: Expr, method: QuantileMethod) -> Self {
1616 self.map_private(DslFunction::Stats(StatsFunction::Quantile {
1617 quantile,
1618 method,
1619 }))
1620 }
1621
1622 pub fn std(self, ddof: u8) -> Self {
1635 self.map_private(DslFunction::Stats(StatsFunction::Std { ddof }))
1636 }
1637
1638 pub fn var(self, ddof: u8) -> Self {
1648 self.map_private(DslFunction::Stats(StatsFunction::Var { ddof }))
1649 }
1650
1651 pub fn explode(self, columns: Selector, options: ExplodeOptions) -> LazyFrame {
1653 self.explode_impl(columns, options, false)
1654 }
1655
1656 fn explode_impl(
1658 self,
1659 columns: Selector,
1660 options: ExplodeOptions,
1661 allow_empty: bool,
1662 ) -> LazyFrame {
1663 let opt_state = self.get_opt_state();
1664 let lp = self
1665 .get_plan_builder()
1666 .explode(columns, options, allow_empty)
1667 .build();
1668 Self::from_logical_plan(lp, opt_state)
1669 }
1670
1671 pub fn null_count(self) -> LazyFrame {
1673 self.select(vec![col(PlSmallStr::from_static("*")).null_count()])
1674 }
1675
1676 pub fn unique_stable(
1681 self,
1682 subset: Option<Selector>,
1683 keep_strategy: UniqueKeepStrategy,
1684 ) -> LazyFrame {
1685 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1686 self.unique_stable_generic(subset, keep_strategy)
1687 }
1688
1689 pub fn unique_stable_generic(
1690 self,
1691 subset: Option<Vec<Expr>>,
1692 keep_strategy: UniqueKeepStrategy,
1693 ) -> LazyFrame {
1694 let opt_state = self.get_opt_state();
1695 let options = DistinctOptionsDSL {
1696 subset,
1697 maintain_order: true,
1698 keep_strategy,
1699 };
1700 let lp = self.get_plan_builder().distinct(options).build();
1701 Self::from_logical_plan(lp, opt_state)
1702 }
1703
1704 pub fn unique(self, subset: Option<Selector>, keep_strategy: UniqueKeepStrategy) -> LazyFrame {
1712 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1713 self.unique_generic(subset, keep_strategy)
1714 }
1715
1716 pub fn unique_generic(
1717 self,
1718 subset: Option<Vec<Expr>>,
1719 keep_strategy: UniqueKeepStrategy,
1720 ) -> LazyFrame {
1721 let opt_state = self.get_opt_state();
1722 let options = DistinctOptionsDSL {
1723 subset,
1724 maintain_order: false,
1725 keep_strategy,
1726 };
1727 let lp = self.get_plan_builder().distinct(options).build();
1728 Self::from_logical_plan(lp, opt_state)
1729 }
1730
1731 pub fn drop_nans(self, subset: Option<Selector>) -> LazyFrame {
1736 let opt_state = self.get_opt_state();
1737 let lp = self.get_plan_builder().drop_nans(subset).build();
1738 Self::from_logical_plan(lp, opt_state)
1739 }
1740
1741 pub fn drop_nulls(self, subset: Option<Selector>) -> LazyFrame {
1746 let opt_state = self.get_opt_state();
1747 let lp = self.get_plan_builder().drop_nulls(subset).build();
1748 Self::from_logical_plan(lp, opt_state)
1749 }
1750
1751 pub fn slice(self, offset: i64, len: IdxSize) -> LazyFrame {
1761 let opt_state = self.get_opt_state();
1762 let lp = self.get_plan_builder().slice(offset, len).build();
1763 Self::from_logical_plan(lp, opt_state)
1764 }
1765
1766 pub fn clear(self) -> LazyFrame {
1768 self.slice(0, 0)
1769 }
1770
1771 pub fn first(self) -> LazyFrame {
1775 self.slice(0, 1)
1776 }
1777
1778 pub fn last(self) -> LazyFrame {
1782 self.slice(-1, 1)
1783 }
1784
1785 pub fn tail(self, n: IdxSize) -> LazyFrame {
1789 let neg_tail = -(n as i64);
1790 self.slice(neg_tail, n)
1791 }
1792
1793 #[cfg(feature = "pivot")]
1794 #[expect(clippy::too_many_arguments)]
1795 pub fn pivot(
1796 self,
1797 on: Selector,
1798 on_columns: Arc<DataFrame>,
1799 index: Selector,
1800 values: Selector,
1801 agg: Expr,
1802 maintain_order: bool,
1803 separator: PlSmallStr,
1804 column_naming: PivotColumnNaming,
1805 ) -> LazyFrame {
1806 let opt_state = self.get_opt_state();
1807 let lp = self
1808 .get_plan_builder()
1809 .pivot(
1810 on,
1811 on_columns,
1812 index,
1813 values,
1814 agg,
1815 maintain_order,
1816 separator,
1817 column_naming,
1818 )
1819 .build();
1820 Self::from_logical_plan(lp, opt_state)
1821 }
1822
1823 #[cfg(feature = "pivot")]
1827 pub fn unpivot(self, args: UnpivotArgsDSL) -> LazyFrame {
1828 let opt_state = self.get_opt_state();
1829 let lp = self.get_plan_builder().unpivot(args).build();
1830 Self::from_logical_plan(lp, opt_state)
1831 }
1832
1833 pub fn limit(self, n: IdxSize) -> LazyFrame {
1835 self.slice(0, n)
1836 }
1837
1838 pub fn map<F>(
1852 self,
1853 function: F,
1854 optimizations: AllowedOptimizations,
1855 schema: Option<Arc<dyn UdfSchema>>,
1856 name: Option<&'static str>,
1857 ) -> LazyFrame
1858 where
1859 F: 'static + Fn(DataFrame) -> PolarsResult<DataFrame> + Send + Sync,
1860 {
1861 let opt_state = self.get_opt_state();
1862 let lp = self
1863 .get_plan_builder()
1864 .map(
1865 function,
1866 optimizations,
1867 schema,
1868 PlSmallStr::from_static(name.unwrap_or("ANONYMOUS UDF")),
1869 )
1870 .build();
1871 Self::from_logical_plan(lp, opt_state)
1872 }
1873
1874 #[cfg(feature = "python")]
1875 pub fn map_python(
1876 self,
1877 function: polars_utils::python_function::PythonFunction,
1878 optimizations: AllowedOptimizations,
1879 schema: Option<SchemaRef>,
1880 validate_output: bool,
1881 ) -> LazyFrame {
1882 let opt_state = self.get_opt_state();
1883 let lp = self
1884 .get_plan_builder()
1885 .map_python(function, optimizations, schema, validate_output)
1886 .build();
1887 Self::from_logical_plan(lp, opt_state)
1888 }
1889
1890 pub(crate) fn map_private(self, function: DslFunction) -> LazyFrame {
1891 let opt_state = self.get_opt_state();
1892 let lp = self.get_plan_builder().map_private(function).build();
1893 Self::from_logical_plan(lp, opt_state)
1894 }
1895
1896 pub fn with_row_index<S>(self, name: S, offset: Option<IdxSize>) -> LazyFrame
1905 where
1906 S: Into<PlSmallStr>,
1907 {
1908 let name = name.into();
1909
1910 match &self.logical_plan {
1911 v @ DslPlan::Scan {
1912 scan_type,
1913 unified_scan_args,
1914 ..
1915 } if unified_scan_args.row_index.is_none()
1916 && !matches!(
1917 &**scan_type,
1918 FileScanDsl::Anonymous { .. } | FileScanDsl::ExpandedPaths { .. }
1919 ) =>
1920 {
1921 let DslPlan::Scan {
1922 sources,
1923 mut unified_scan_args,
1924 scan_type,
1925 cached_ir: _,
1926 } = v.clone()
1927 else {
1928 unreachable!()
1929 };
1930
1931 unified_scan_args.row_index = Some(RowIndex {
1932 name,
1933 offset: offset.unwrap_or(0),
1934 });
1935
1936 DslPlan::Scan {
1937 sources,
1938 unified_scan_args,
1939 scan_type,
1940 cached_ir: Default::default(),
1941 }
1942 .into()
1943 },
1944 _ => self.map_private(DslFunction::RowIndex { name, offset }),
1945 }
1946 }
1947
1948 pub fn count(self) -> LazyFrame {
1950 self.select(vec![col(PlSmallStr::from_static("*")).count()])
1951 }
1952
1953 #[cfg(feature = "dtype-struct")]
1956 pub fn unnest(self, cols: Selector, separator: Option<PlSmallStr>) -> Self {
1957 self.map_private(DslFunction::Unnest {
1958 columns: cols,
1959 separator,
1960 })
1961 }
1962
1963 #[cfg(feature = "merge_sorted")]
1964 pub fn merge_sorted<I, S>(
1965 self,
1966 other: LazyFrame,
1967 key: I,
1968 maintain_order: bool,
1969 ) -> PolarsResult<LazyFrame>
1970 where
1971 I: IntoIterator<Item = S>,
1972 S: Into<PlSmallStr>,
1973 {
1974 let key: Arc<[PlSmallStr]> = key.into_iter().map(Into::into).collect();
1975
1976 polars_ensure!(
1977 !key.is_empty(),
1978 ComputeError: "merge_sorted requires at least one key column"
1979 );
1980
1981 let lp = DslPlan::MergeSorted {
1982 input_left: Arc::new(self.logical_plan),
1983 input_right: Arc::new(other.logical_plan),
1984 key,
1985 maintain_order,
1986 };
1987 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1988 }
1989
1990 pub fn hint(self, hint: HintIR) -> PolarsResult<LazyFrame> {
1991 let lp = DslPlan::MapFunction {
1992 input: Arc::new(self.logical_plan),
1993 function: DslFunction::Hint(hint),
1994 };
1995 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1996 }
1997}
1998
1999#[derive(Clone)]
2001pub struct LazyGroupBy {
2002 pub logical_plan: DslPlan,
2003 opt_state: OptFlags,
2004 keys: Vec<Expr>,
2005 predicates: Vec<Expr>,
2006 maintain_order: bool,
2007 #[cfg(feature = "dynamic_group_by")]
2008 dynamic_options: Option<DynamicGroupOptions>,
2009 #[cfg(feature = "dynamic_group_by")]
2010 rolling_options: Option<RollingGroupOptions>,
2011}
2012
2013impl From<LazyGroupBy> for LazyFrame {
2014 fn from(lgb: LazyGroupBy) -> Self {
2015 Self {
2016 logical_plan: lgb.logical_plan,
2017 opt_state: lgb.opt_state,
2018 cached_arena: Default::default(),
2019 }
2020 }
2021}
2022
2023impl LazyGroupBy {
2024 pub fn having(mut self, predicate: Expr) -> Self {
2045 self.predicates.push(predicate);
2046 self
2047 }
2048
2049 pub fn agg<E: AsRef<[Expr]>>(self, aggs: E) -> LazyFrame {
2071 #[cfg(feature = "dynamic_group_by")]
2072 let lp = DslBuilder::from(self.logical_plan)
2073 .group_by(
2074 self.keys,
2075 self.predicates,
2076 aggs,
2077 None,
2078 self.maintain_order,
2079 self.dynamic_options,
2080 self.rolling_options,
2081 )
2082 .build();
2083
2084 #[cfg(not(feature = "dynamic_group_by"))]
2085 let lp = DslBuilder::from(self.logical_plan)
2086 .group_by(self.keys, self.predicates, aggs, None, self.maintain_order)
2087 .build();
2088 LazyFrame::from_logical_plan(lp, self.opt_state)
2089 }
2090
2091 pub fn head(self, n: Option<usize>) -> LazyFrame {
2093 let keys = self
2094 .keys
2095 .iter()
2096 .filter_map(|expr| expr_output_name(expr).ok())
2097 .collect::<Vec<_>>();
2098
2099 self.agg([all().as_expr().head(n)]).explode_impl(
2100 all() - by_name(keys.iter().cloned(), false, false),
2101 ExplodeOptions {
2102 empty_as_null: true,
2103 keep_nulls: true,
2104 },
2105 true,
2106 )
2107 }
2108
2109 pub fn tail(self, n: Option<usize>) -> LazyFrame {
2111 let keys = self
2112 .keys
2113 .iter()
2114 .filter_map(|expr| expr_output_name(expr).ok())
2115 .collect::<Vec<_>>();
2116
2117 self.agg([all().as_expr().tail(n)]).explode_impl(
2118 all() - by_name(keys.iter().cloned(), false, false),
2119 ExplodeOptions {
2120 empty_as_null: true,
2121 keep_nulls: true,
2122 },
2123 true,
2124 )
2125 }
2126
2127 pub fn apply(self, f: PlanCallback<DataFrame, DataFrame>, schema: SchemaRef) -> LazyFrame {
2132 if !self.predicates.is_empty() {
2133 panic!("not yet implemented: `apply` cannot be used with `having` predicates");
2134 }
2135
2136 #[cfg(feature = "dynamic_group_by")]
2137 let options = GroupbyOptions {
2138 dynamic: self.dynamic_options,
2139 rolling: self.rolling_options,
2140 slice: None,
2141 };
2142
2143 #[cfg(not(feature = "dynamic_group_by"))]
2144 let options = GroupbyOptions { slice: None };
2145
2146 let lp = DslPlan::GroupBy {
2147 input: Arc::new(self.logical_plan),
2148 keys: self.keys,
2149 predicates: vec![],
2150 aggs: vec![],
2151 apply: Some((f, schema)),
2152 maintain_order: self.maintain_order,
2153 options: Arc::new(options),
2154 };
2155 LazyFrame::from_logical_plan(lp, self.opt_state)
2156 }
2157}
2158
2159#[must_use]
2160pub struct JoinBuilder {
2161 lf: LazyFrame,
2162 how: JoinType,
2163 other: Option<LazyFrame>,
2164 left_on: Vec<Expr>,
2165 right_on: Vec<Expr>,
2166 allow_parallel: bool,
2167 force_parallel: bool,
2168 suffix: Option<PlSmallStr>,
2169 validation: JoinValidation,
2170 nulls_equal: bool,
2171 coalesce: JoinCoalesce,
2172 maintain_order: MaintainOrderJoin,
2173 build_side: Option<JoinBuildSide>,
2174}
2175impl JoinBuilder {
2176 pub fn new(lf: LazyFrame) -> Self {
2178 Self {
2179 lf,
2180 other: None,
2181 how: JoinType::Inner,
2182 left_on: vec![],
2183 right_on: vec![],
2184 allow_parallel: true,
2185 force_parallel: false,
2186 suffix: None,
2187 validation: Default::default(),
2188 nulls_equal: false,
2189 coalesce: Default::default(),
2190 maintain_order: Default::default(),
2191 build_side: None,
2192 }
2193 }
2194
2195 pub fn with(mut self, other: LazyFrame) -> Self {
2197 self.other = Some(other);
2198 self
2199 }
2200
2201 pub fn how(mut self, how: JoinType) -> Self {
2203 self.how = how;
2204 self
2205 }
2206
2207 pub fn validate(mut self, validation: JoinValidation) -> Self {
2208 self.validation = validation;
2209 self
2210 }
2211
2212 pub fn on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2216 let on = on.as_ref().to_vec();
2217 self.left_on.clone_from(&on);
2218 self.right_on = on;
2219 self
2220 }
2221
2222 pub fn left_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2226 self.left_on = on.as_ref().to_vec();
2227 self
2228 }
2229
2230 pub fn right_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2234 self.right_on = on.as_ref().to_vec();
2235 self
2236 }
2237
2238 pub fn allow_parallel(mut self, allow: bool) -> Self {
2240 self.allow_parallel = allow;
2241 self
2242 }
2243
2244 pub fn force_parallel(mut self, force: bool) -> Self {
2246 self.force_parallel = force;
2247 self
2248 }
2249
2250 pub fn join_nulls(mut self, nulls_equal: bool) -> Self {
2252 self.nulls_equal = nulls_equal;
2253 self
2254 }
2255
2256 pub fn suffix<S>(mut self, suffix: S) -> Self
2259 where
2260 S: Into<PlSmallStr>,
2261 {
2262 self.suffix = Some(suffix.into());
2263 self
2264 }
2265
2266 pub fn coalesce(mut self, coalesce: JoinCoalesce) -> Self {
2268 self.coalesce = coalesce;
2269 self
2270 }
2271
2272 pub fn maintain_order(mut self, maintain_order: MaintainOrderJoin) -> Self {
2274 self.maintain_order = maintain_order;
2275 self
2276 }
2277
2278 pub fn build_side(mut self, build_side: Option<JoinBuildSide>) -> Self {
2280 self.build_side = build_side;
2281 self
2282 }
2283
2284 pub fn finish(self) -> LazyFrame {
2286 let opt_state = self.lf.opt_state;
2287 let other = self.other.expect("'with' not set in join builder");
2288
2289 let args = JoinArgs {
2290 how: self.how,
2291 validation: self.validation,
2292 suffix: self.suffix,
2293 slice: None,
2294 nulls_equal: self.nulls_equal,
2295 coalesce: self.coalesce,
2296 maintain_order: self.maintain_order,
2297 build_side: self.build_side,
2298 };
2299
2300 let lp = self
2301 .lf
2302 .get_plan_builder()
2303 .join(
2304 other.logical_plan,
2305 self.left_on,
2306 self.right_on,
2307 JoinOptions {
2308 allow_parallel: self.allow_parallel,
2309 force_parallel: self.force_parallel,
2310 args,
2311 }
2312 .into(),
2313 )
2314 .build();
2315 LazyFrame::from_logical_plan(lp, opt_state)
2316 }
2317
2318 pub fn join_where(self, predicates: Vec<Expr>) -> LazyFrame {
2320 let opt_state = self.lf.opt_state;
2321 let other = self.other.expect("with not set");
2322
2323 fn decompose_and(predicate: Expr, expanded_predicates: &mut Vec<Expr>) {
2325 if let Expr::BinaryExpr {
2326 op: Operator::And,
2327 left,
2328 right,
2329 } = predicate
2330 {
2331 decompose_and((*left).clone(), expanded_predicates);
2332 decompose_and((*right).clone(), expanded_predicates);
2333 } else {
2334 expanded_predicates.push(predicate);
2335 }
2336 }
2337 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2338 for predicate in predicates {
2339 decompose_and(predicate, &mut expanded_predicates);
2340 }
2341 let predicates: Vec<Expr> = expanded_predicates;
2342
2343 #[cfg(feature = "is_between")]
2345 let predicates: Vec<Expr> = {
2346 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2347 for predicate in predicates {
2348 if let Expr::Function {
2349 function: FunctionExpr::Boolean(BooleanFunction::IsBetween { closed }),
2350 input,
2351 ..
2352 } = &predicate
2353 {
2354 if let [expr, lower, upper] = input.as_slice() {
2355 match closed {
2356 ClosedInterval::Both => {
2357 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2358 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2359 },
2360 ClosedInterval::Right => {
2361 expanded_predicates.push(expr.clone().gt(lower.clone()));
2362 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2363 },
2364 ClosedInterval::Left => {
2365 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2366 expanded_predicates.push(expr.clone().lt(upper.clone()));
2367 },
2368 ClosedInterval::None => {
2369 expanded_predicates.push(expr.clone().gt(lower.clone()));
2370 expanded_predicates.push(expr.clone().lt(upper.clone()));
2371 },
2372 }
2373 continue;
2374 }
2375 }
2376 expanded_predicates.push(predicate);
2377 }
2378 expanded_predicates
2379 };
2380
2381 let args = JoinArgs {
2382 how: self.how,
2383 validation: self.validation,
2384 suffix: self.suffix,
2385 slice: None,
2386 nulls_equal: self.nulls_equal,
2387 coalesce: self.coalesce,
2388 maintain_order: self.maintain_order,
2389 build_side: self.build_side,
2390 };
2391 let options = JoinOptions {
2392 allow_parallel: self.allow_parallel,
2393 force_parallel: self.force_parallel,
2394 args,
2395 };
2396
2397 let lp = DslPlan::Join {
2398 input_left: Arc::new(self.lf.logical_plan),
2399 input_right: Arc::new(other.logical_plan),
2400 left_on: Default::default(),
2401 right_on: Default::default(),
2402 predicates,
2403 options: Arc::from(options),
2404 };
2405
2406 LazyFrame::from_logical_plan(lp, opt_state)
2407 }
2408}
2409
2410pub const BUILD_STREAMING_EXECUTOR: Option<polars_mem_engine::StreamingExecutorBuilder> = {
2411 #[cfg(not(feature = "streaming"))]
2412 {
2413 None
2414 }
2415 #[cfg(feature = "streaming")]
2416 {
2417 Some(polars_stream::build_streaming_query_executor)
2418 }
2419};
2420
2421pub struct CollectBatches {
2422 recv: Receiver<PolarsResult<DataFrame>>,
2423 runner: Option<Box<dyn FnOnce() + Send + 'static>>,
2424}
2425
2426impl CollectBatches {
2427 pub fn start(&mut self) {
2429 if let Some(runner) = self.runner.take() {
2430 runner()
2431 }
2432 }
2433}
2434
2435impl Iterator for CollectBatches {
2436 type Item = PolarsResult<DataFrame>;
2437
2438 fn next(&mut self) -> Option<Self::Item> {
2439 self.start();
2440 self.recv.recv().ok()
2441 }
2442}