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 query_start: Option<std::time::Instant>,
561 post_opt: P,
562 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)>
563 where
564 P: FnOnce(
565 Node,
566 &mut Arena<IR>,
567 &mut Arena<AExpr>,
568 Option<std::time::Duration>,
569 ) -> PolarsResult<()>,
570 {
571 let (mut lp_arena, mut expr_arena) = self.get_arenas();
572
573 let mut scratch = vec![];
574 let lp_top = self.optimize_with_scratch(&mut lp_arena, &mut expr_arena, &mut scratch)?;
575
576 post_opt(
577 lp_top,
578 &mut lp_arena,
579 &mut expr_arena,
580 query_start.map(|s| s.elapsed()),
583 )?;
584
585 let no_file_sink = if check_sink {
587 !matches!(
588 lp_arena.get(lp_top),
589 IR::Sink {
590 payload: SinkTypeIR::File { .. },
591 ..
592 }
593 )
594 } else {
595 true
596 };
597 let physical_plan = create_physical_plan(
598 lp_top,
599 &mut lp_arena,
600 &mut expr_arena,
601 BUILD_STREAMING_EXECUTOR,
602 )?;
603
604 let state = ExecutionState::new();
605 Ok((state, physical_plan, no_file_sink))
606 }
607
608 pub fn _collect_post_opt<P>(self, post_opt: P) -> PolarsResult<DataFrame>
610 where
611 P: FnOnce(
612 Node,
613 &mut Arena<IR>,
614 &mut Arena<AExpr>,
615 Option<std::time::Duration>,
616 ) -> PolarsResult<()>,
617 {
618 let (mut state, mut physical_plan, _) =
619 self.prepare_collect_post_opt(false, None, post_opt)?;
620 physical_plan.execute(&mut state)
621 }
622
623 #[allow(unused_mut)]
624 fn prepare_collect(
625 self,
626 check_sink: bool,
627 query_start: Option<std::time::Instant>,
628 ) -> PolarsResult<(ExecutionState, Box<dyn Executor>, bool)> {
629 self.prepare_collect_post_opt(check_sink, query_start, |_, _, _, _| Ok(()))
630 }
631
632 pub fn collect_with_engine(mut self, engine: Engine) -> PolarsResult<QueryResult> {
637 let engine = match engine {
638 Engine::Streaming => Engine::Streaming,
639 _ if std::env::var("POLARS_FORCE_STREAMING").as_deref() == Ok("1") => Engine::Streaming,
640 Engine::Auto => Engine::InMemory,
641 v => v,
642 };
643
644 if engine != Engine::Streaming
645 && std::env::var("POLARS_AUTO_STREAMING").as_deref() == Ok("1")
646 {
647 feature_gated!("streaming", {
648 if let Some(r) = self.clone()._collect_with_streaming_suppress_todo_panic() {
649 return r;
650 }
651 })
652 }
653 match engine {
654 Engine::Streaming => {
655 feature_gated!("streaming", self = self.with_streaming(true))
656 },
657 Engine::Gpu => self = self.with_gpu(true),
658 _ => (),
659 }
660
661 let observer = self
662 .opt_state
663 .query_monitoring()
664 .then(polars_observer::new_query_observer)
665 .flatten();
666
667 if let Some(o) = observer.as_ref() {
668 o.on_query_started()
669 }
670
671 let mut ir_plan = self.to_alp_optimized().inspect_err(|err| {
672 if let Some(o) = observer.as_ref() {
673 o.on_query_failed(err)
674 }
675 })?;
676 ir_plan.ensure_root_node_is_sink();
677
678 match engine {
679 Engine::Streaming => feature_gated!("streaming", {
680 polars_stream::run_query(
681 ir_plan.lp_top,
682 &mut ir_plan.lp_arena,
683 &mut ir_plan.expr_arena,
684 observer,
685 )
686 }),
687 Engine::InMemory | Engine::Gpu => run_in_memory_query(
688 ir_plan.lp_top,
689 &mut ir_plan.lp_arena,
690 &mut ir_plan.expr_arena,
691 engine,
692 observer,
693 ),
694 Engine::Auto => unreachable!(),
695 }
696 }
697
698 pub fn explain_all(plans: Vec<DslPlan>, opt_state: OptFlags) -> PolarsResult<String> {
699 let sink_multiple = LazyFrame {
700 logical_plan: DslPlan::SinkMultiple { inputs: plans },
701 opt_state,
702 cached_arena: Default::default(),
703 };
704 sink_multiple.explain(true)
705 }
706
707 pub fn collect_all_with_engine(
708 plans: Vec<DslPlan>,
709 engine: Engine,
710 opt_state: OptFlags,
711 ) -> PolarsResult<Vec<DataFrame>> {
712 if plans.is_empty() {
713 return Ok(Vec::new());
714 }
715
716 LazyFrame {
717 logical_plan: DslPlan::SinkMultiple { inputs: plans },
718 opt_state,
719 cached_arena: Default::default(),
720 }
721 .collect_with_engine(engine)
722 .map(|r| r.unwrap_multiple())
723 }
724
725 pub fn collect(self) -> PolarsResult<DataFrame> {
743 self.collect_with_engine(Engine::Auto).map(|r| match r {
744 QueryResult::Single(df) => df,
745 QueryResult::Multiple(_) => DataFrame::empty(),
747 })
748 }
749
750 #[cfg(feature = "async")]
755 pub fn collect_batches(
756 self,
757 engine: Engine,
758 maintain_order: bool,
759 chunk_size: Option<NonZeroUsize>,
760 lazy: bool,
761 ) -> PolarsResult<CollectBatches> {
762 let (send, recv) = sync_channel(1);
763 let runner_send = send.clone();
764 let ldf = self.sink_batches(
765 PlanCallback::new(move |df| {
766 let send_result = send.send(Ok(df));
768 Ok(send_result.is_err())
769 }),
770 maintain_order,
771 chunk_size,
772 )?;
773 let runner = move || {
774 polars_core::runtime::ASYNC.spawn_blocking(move || {
776 if let Err(e) = ldf.collect_with_engine(engine) {
777 runner_send.send(Err(e)).ok();
778 }
779 });
780 };
781
782 let mut collect_batches = CollectBatches {
783 recv,
784 runner: Some(Box::new(runner)),
785 };
786 if !lazy {
787 collect_batches.start();
788 }
789 Ok(collect_batches)
790 }
791
792 pub fn _profile_post_opt<P>(self, post_opt: P) -> PolarsResult<(DataFrame, DataFrame)>
795 where
796 P: FnOnce(
797 Node,
798 &mut Arena<IR>,
799 &mut Arena<AExpr>,
800 Option<std::time::Duration>,
801 ) -> PolarsResult<()>,
802 {
803 let query_start = std::time::Instant::now();
804 let (mut state, mut physical_plan, _) =
805 self.prepare_collect_post_opt(false, Some(query_start), post_opt)?;
806 state.time_nodes(query_start, query_start.elapsed());
807 let out = physical_plan.execute(&mut state)?;
808 let timer_df = state.finish_timer()?;
809 Ok((out, timer_df))
810 }
811
812 pub fn profile(self) -> PolarsResult<(DataFrame, DataFrame)> {
820 self._profile_post_opt(|_, _, _, _| Ok(()))
821 }
822
823 pub fn sink_batches(
824 mut self,
825 function: PlanCallback<DataFrame, bool>,
826 maintain_order: bool,
827 chunk_size: Option<NonZeroUsize>,
828 ) -> PolarsResult<Self> {
829 use polars_plan::prelude::sink::CallbackSinkType;
830
831 polars_ensure!(
832 !matches!(self.logical_plan, DslPlan::Sink { .. }),
833 InvalidOperation: "cannot create a sink on top of another sink"
834 );
835
836 self.logical_plan = DslPlan::Sink {
837 input: Arc::new(self.logical_plan),
838 payload: SinkType::Callback(CallbackSinkType {
839 function,
840 maintain_order,
841 chunk_size,
842 }),
843 };
844
845 Ok(self)
846 }
847
848 #[cfg(feature = "streaming")]
850 fn _collect_with_streaming_suppress_todo_panic(
851 mut self,
852 ) -> Option<PolarsResult<polars_core::query_result::QueryResult>> {
853 self.opt_state |= OptFlags::STREAMING;
854 let mut ir_plan = match self.to_alp_optimized() {
855 Ok(v) => v,
856 Err(e) => return Some(Err(e)),
857 };
858
859 ir_plan.ensure_root_node_is_sink();
860
861 let f = || {
862 polars_stream::run_query(
863 ir_plan.lp_top,
864 &mut ir_plan.lp_arena,
865 &mut ir_plan.expr_arena,
866 None,
867 )
868 };
869
870 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
871 Ok(v) => Some(v),
872 Err(e) => {
873 if e.downcast_ref::<&str>()
876 .is_some_and(|s| s.starts_with("not yet implemented"))
877 {
878 if polars_core::config::verbose() {
879 eprintln!(
880 "caught unimplemented error in new streaming engine, falling back to normal engine"
881 );
882 }
883 None
884 } else {
885 std::panic::resume_unwind(e)
886 }
887 },
888 }
889 }
890
891 pub fn sink(
892 mut self,
893 sink_type: SinkDestination,
894 file_format: FileWriteFormat,
895 unified_sink_args: UnifiedSinkArgs,
896 ) -> PolarsResult<Self> {
897 polars_ensure!(
898 !matches!(self.logical_plan, DslPlan::Sink { .. }),
899 InvalidOperation: "cannot create a sink on top of another sink"
900 );
901
902 self.logical_plan = DslPlan::Sink {
903 input: Arc::new(self.logical_plan),
904 payload: match sink_type {
905 SinkDestination::File { target } => SinkType::File(FileSinkOptions {
906 target,
907 file_format,
908 unified_sink_args,
909 }),
910 SinkDestination::Partitioned {
911 base_path,
912 file_path_provider,
913 partition_strategy,
914 max_rows_per_file,
915 approximate_bytes_per_file,
916 } => SinkType::Partitioned(PartitionedSinkOptions {
917 base_path,
918 file_path_provider,
919 partition_strategy,
920 file_format,
921 unified_sink_args,
922 max_rows_per_file,
923 approximate_bytes_per_file,
924 }),
925 },
926 };
927 Ok(self)
928 }
929
930 pub fn filter(self, predicate: Expr) -> Self {
948 let opt_state = self.get_opt_state();
949 let lp = self.get_plan_builder().filter(predicate).build();
950 Self::from_logical_plan(lp, opt_state)
951 }
952
953 pub fn remove(self, predicate: Expr) -> Self {
971 self.filter(predicate.neq_missing(lit(true)))
972 }
973
974 pub fn select<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
1000 let exprs = exprs.as_ref().to_vec();
1001 self.select_impl(
1002 exprs,
1003 ProjectionOptions {
1004 run_parallel: true,
1005 duplicate_check: true,
1006 should_broadcast: true,
1007 },
1008 )
1009 }
1010
1011 pub fn select_seq<E: AsRef<[Expr]>>(self, exprs: E) -> Self {
1012 let exprs = exprs.as_ref().to_vec();
1013 self.select_impl(
1014 exprs,
1015 ProjectionOptions {
1016 run_parallel: false,
1017 duplicate_check: true,
1018 should_broadcast: true,
1019 },
1020 )
1021 }
1022
1023 fn select_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> Self {
1024 let opt_state = self.get_opt_state();
1025 let lp = self.get_plan_builder().project(exprs, options).build();
1026 Self::from_logical_plan(lp, opt_state)
1027 }
1028
1029 pub fn group_by<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1050 let keys = by
1051 .as_ref()
1052 .iter()
1053 .map(|e| e.clone().into())
1054 .collect::<Vec<_>>();
1055 let opt_state = self.get_opt_state();
1056
1057 #[cfg(feature = "dynamic_group_by")]
1058 {
1059 LazyGroupBy {
1060 logical_plan: self.logical_plan,
1061 opt_state,
1062 keys,
1063 predicates: vec![],
1064 maintain_order: false,
1065 dynamic_options: None,
1066 rolling_options: None,
1067 }
1068 }
1069
1070 #[cfg(not(feature = "dynamic_group_by"))]
1071 {
1072 LazyGroupBy {
1073 logical_plan: self.logical_plan,
1074 opt_state,
1075 keys,
1076 predicates: vec![],
1077 maintain_order: false,
1078 }
1079 }
1080 }
1081
1082 #[cfg(feature = "dynamic_group_by")]
1090 pub fn rolling<E: AsRef<[Expr]>>(
1091 mut self,
1092 index_column: Expr,
1093 group_by: E,
1094 mut options: RollingGroupOptions,
1095 ) -> LazyGroupBy {
1096 if let Expr::Column(name) = index_column {
1097 options.index_column = name;
1098 } else {
1099 let output_field = index_column
1100 .to_field(&self.collect_schema().unwrap())
1101 .unwrap();
1102 return self.with_column(index_column).rolling(
1103 Expr::Column(output_field.name().clone()),
1104 group_by,
1105 options,
1106 );
1107 }
1108 let opt_state = self.get_opt_state();
1109 LazyGroupBy {
1110 logical_plan: self.logical_plan,
1111 opt_state,
1112 predicates: vec![],
1113 keys: group_by.as_ref().to_vec(),
1114 maintain_order: true,
1115 dynamic_options: None,
1116 rolling_options: Some(options),
1117 }
1118 }
1119
1120 #[cfg(feature = "dynamic_group_by")]
1136 pub fn group_by_dynamic<E: AsRef<[Expr]>>(
1137 mut self,
1138 index_column: Expr,
1139 group_by: E,
1140 mut options: DynamicGroupOptions,
1141 ) -> LazyGroupBy {
1142 if let Expr::Column(name) = index_column {
1143 options.index_column = name;
1144 } else {
1145 let output_field = index_column
1146 .to_field(&self.collect_schema().unwrap())
1147 .unwrap();
1148 return self.with_column(index_column).group_by_dynamic(
1149 Expr::Column(output_field.name().clone()),
1150 group_by,
1151 options,
1152 );
1153 }
1154 let opt_state = self.get_opt_state();
1155 LazyGroupBy {
1156 logical_plan: self.logical_plan,
1157 opt_state,
1158 predicates: vec![],
1159 keys: group_by.as_ref().to_vec(),
1160 maintain_order: true,
1161 dynamic_options: Some(options),
1162 rolling_options: None,
1163 }
1164 }
1165
1166 pub fn group_by_stable<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, by: E) -> LazyGroupBy {
1168 let keys = by
1169 .as_ref()
1170 .iter()
1171 .map(|e| e.clone().into())
1172 .collect::<Vec<_>>();
1173 let opt_state = self.get_opt_state();
1174
1175 #[cfg(feature = "dynamic_group_by")]
1176 {
1177 LazyGroupBy {
1178 logical_plan: self.logical_plan,
1179 opt_state,
1180 keys,
1181 predicates: vec![],
1182 maintain_order: true,
1183 dynamic_options: None,
1184 rolling_options: None,
1185 }
1186 }
1187
1188 #[cfg(not(feature = "dynamic_group_by"))]
1189 {
1190 LazyGroupBy {
1191 logical_plan: self.logical_plan,
1192 opt_state,
1193 keys,
1194 predicates: vec![],
1195 maintain_order: true,
1196 }
1197 }
1198 }
1199
1200 #[cfg(feature = "semi_anti_join")]
1217 pub fn anti_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1218 self.join(
1219 other,
1220 [left_on.into()],
1221 [right_on.into()],
1222 JoinArgs::new(JoinType::Anti),
1223 )
1224 }
1225
1226 #[cfg(feature = "cross_join")]
1228 pub fn cross_join(self, other: LazyFrame, suffix: Option<PlSmallStr>) -> LazyFrame {
1229 self.join(
1230 other,
1231 vec![],
1232 vec![],
1233 JoinArgs::new(JoinType::Cross).with_suffix(suffix),
1234 )
1235 }
1236
1237 pub fn left_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1254 self.join(
1255 other,
1256 [left_on.into()],
1257 [right_on.into()],
1258 JoinArgs::new(JoinType::Left),
1259 )
1260 }
1261
1262 pub fn inner_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1279 self.join(
1280 other,
1281 [left_on.into()],
1282 [right_on.into()],
1283 JoinArgs::new(JoinType::Inner),
1284 )
1285 }
1286
1287 pub fn full_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1304 self.join(
1305 other,
1306 [left_on.into()],
1307 [right_on.into()],
1308 JoinArgs::new(JoinType::Full),
1309 )
1310 }
1311
1312 #[cfg(feature = "semi_anti_join")]
1329 pub fn semi_join<E: Into<Expr>>(self, other: LazyFrame, left_on: E, right_on: E) -> LazyFrame {
1330 self.join(
1331 other,
1332 [left_on.into()],
1333 [right_on.into()],
1334 JoinArgs::new(JoinType::Semi),
1335 )
1336 }
1337
1338 pub fn join<E: AsRef<[Expr]>>(
1360 self,
1361 other: LazyFrame,
1362 left_on: E,
1363 right_on: E,
1364 args: JoinArgs,
1365 ) -> LazyFrame {
1366 let left_on = left_on.as_ref().to_vec();
1367 let right_on = right_on.as_ref().to_vec();
1368
1369 self._join_impl(other, left_on, right_on, args)
1370 }
1371
1372 fn _join_impl(
1373 self,
1374 other: LazyFrame,
1375 left_on: Vec<Expr>,
1376 right_on: Vec<Expr>,
1377 args: JoinArgs,
1378 ) -> LazyFrame {
1379 let JoinArgs {
1380 how,
1381 validation,
1382 suffix,
1383 slice,
1384 nulls_equal,
1385 coalesce,
1386 maintain_order,
1387 build_side,
1388 } = args;
1389
1390 if slice.is_some() {
1391 panic!("impl error: slice is not handled")
1392 }
1393
1394 let mut builder = self
1395 .join_builder()
1396 .with(other)
1397 .left_on(left_on)
1398 .right_on(right_on)
1399 .how(how)
1400 .validate(validation)
1401 .join_nulls(nulls_equal)
1402 .coalesce(coalesce)
1403 .maintain_order(maintain_order)
1404 .build_side(build_side);
1405
1406 if let Some(suffix) = suffix {
1407 builder = builder.suffix(suffix);
1408 }
1409
1410 builder.finish()
1412 }
1413
1414 pub fn join_builder(self) -> JoinBuilder {
1420 JoinBuilder::new(self)
1421 }
1422
1423 pub fn gather(self, idxs: LazyFrame, null_on_oob: bool) -> LazyFrame {
1427 let opt_state = self.get_opt_state();
1428 let lp = self
1429 .get_plan_builder()
1430 .gather(idxs.logical_plan, null_on_oob)
1431 .build();
1432 Self::from_logical_plan(lp, opt_state)
1433 }
1434
1435 pub fn with_column(self, expr: Expr) -> LazyFrame {
1453 let opt_state = self.get_opt_state();
1454 let lp = self
1455 .get_plan_builder()
1456 .with_columns(
1457 vec![expr],
1458 ProjectionOptions {
1459 run_parallel: false,
1460 duplicate_check: true,
1461 should_broadcast: true,
1462 },
1463 )
1464 .build();
1465 Self::from_logical_plan(lp, opt_state)
1466 }
1467
1468 pub fn with_columns<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1483 let exprs = exprs.as_ref().to_vec();
1484 self.with_columns_impl(
1485 exprs,
1486 ProjectionOptions {
1487 run_parallel: true,
1488 duplicate_check: true,
1489 should_broadcast: true,
1490 },
1491 )
1492 }
1493
1494 pub fn with_columns_seq<E: AsRef<[Expr]>>(self, exprs: E) -> LazyFrame {
1496 let exprs = exprs.as_ref().to_vec();
1497 self.with_columns_impl(
1498 exprs,
1499 ProjectionOptions {
1500 run_parallel: false,
1501 duplicate_check: true,
1502 should_broadcast: true,
1503 },
1504 )
1505 }
1506
1507 pub fn match_to_schema(
1509 self,
1510 schema: SchemaRef,
1511 per_column: Arc<[MatchToSchemaPerColumn]>,
1512 extra_columns: ExtraColumnsPolicy,
1513 ) -> LazyFrame {
1514 let opt_state = self.get_opt_state();
1515 let lp = self
1516 .get_plan_builder()
1517 .match_to_schema(schema, per_column, extra_columns)
1518 .build();
1519 Self::from_logical_plan(lp, opt_state)
1520 }
1521
1522 pub fn pipe_with_schema(
1523 self,
1524 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1525 ) -> Self {
1526 let opt_state = self.get_opt_state();
1527 let lp = self
1528 .get_plan_builder()
1529 .pipe_with_schema(vec![], callback)
1530 .build();
1531 Self::from_logical_plan(lp, opt_state)
1532 }
1533
1534 pub fn pipe_with_schemas(
1535 self,
1536 others: Vec<LazyFrame>,
1537 callback: PlanCallback<(Vec<DslPlan>, Vec<SchemaRef>), DslPlan>,
1538 ) -> Self {
1539 let opt_state = self.get_opt_state();
1540 let lp = self
1541 .get_plan_builder()
1542 .pipe_with_schema(
1543 others.into_iter().map(|lf| lf.logical_plan).collect(),
1544 callback,
1545 )
1546 .build();
1547 Self::from_logical_plan(lp, opt_state)
1548 }
1549
1550 fn with_columns_impl(self, exprs: Vec<Expr>, options: ProjectionOptions) -> LazyFrame {
1551 let opt_state = self.get_opt_state();
1552 let lp = self.get_plan_builder().with_columns(exprs, options).build();
1553 Self::from_logical_plan(lp, opt_state)
1554 }
1555
1556 pub fn with_context<C: AsRef<[LazyFrame]>>(self, contexts: C) -> LazyFrame {
1557 let contexts = contexts
1558 .as_ref()
1559 .iter()
1560 .map(|lf| lf.logical_plan.clone())
1561 .collect();
1562 let opt_state = self.get_opt_state();
1563 let lp = self.get_plan_builder().with_context(contexts).build();
1564 Self::from_logical_plan(lp, opt_state)
1565 }
1566
1567 pub fn max(self) -> Self {
1571 self.map_private(DslFunction::Stats(StatsFunction::Max))
1572 }
1573
1574 pub fn min(self) -> Self {
1578 self.map_private(DslFunction::Stats(StatsFunction::Min))
1579 }
1580
1581 pub fn sum(self) -> Self {
1591 self.map_private(DslFunction::Stats(StatsFunction::Sum))
1592 }
1593
1594 pub fn mean(self) -> Self {
1599 self.map_private(DslFunction::Stats(StatsFunction::Mean))
1600 }
1601
1602 pub fn median(self) -> Self {
1608 self.map_private(DslFunction::Stats(StatsFunction::Median))
1609 }
1610
1611 pub fn quantile(self, quantile: Expr, method: QuantileMethod) -> Self {
1613 self.map_private(DslFunction::Stats(StatsFunction::Quantile {
1614 quantile,
1615 method,
1616 }))
1617 }
1618
1619 pub fn std(self, ddof: u8) -> Self {
1632 self.map_private(DslFunction::Stats(StatsFunction::Std { ddof }))
1633 }
1634
1635 pub fn var(self, ddof: u8) -> Self {
1645 self.map_private(DslFunction::Stats(StatsFunction::Var { ddof }))
1646 }
1647
1648 pub fn explode(self, columns: Selector, options: ExplodeOptions) -> LazyFrame {
1650 self.explode_impl(columns, options, false)
1651 }
1652
1653 fn explode_impl(
1655 self,
1656 columns: Selector,
1657 options: ExplodeOptions,
1658 allow_empty: bool,
1659 ) -> LazyFrame {
1660 let opt_state = self.get_opt_state();
1661 let lp = self
1662 .get_plan_builder()
1663 .explode(columns, options, allow_empty)
1664 .build();
1665 Self::from_logical_plan(lp, opt_state)
1666 }
1667
1668 pub fn null_count(self) -> LazyFrame {
1670 self.select(vec![col(PlSmallStr::from_static("*")).null_count()])
1671 }
1672
1673 pub fn unique_stable(
1678 self,
1679 subset: Option<Selector>,
1680 keep_strategy: UniqueKeepStrategy,
1681 ) -> LazyFrame {
1682 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1683 self.unique_stable_generic(subset, keep_strategy)
1684 }
1685
1686 pub fn unique_stable_generic(
1687 self,
1688 subset: Option<Vec<Expr>>,
1689 keep_strategy: UniqueKeepStrategy,
1690 ) -> LazyFrame {
1691 let opt_state = self.get_opt_state();
1692 let options = DistinctOptionsDSL {
1693 subset,
1694 maintain_order: true,
1695 keep_strategy,
1696 };
1697 let lp = self.get_plan_builder().distinct(options).build();
1698 Self::from_logical_plan(lp, opt_state)
1699 }
1700
1701 pub fn unique(self, subset: Option<Selector>, keep_strategy: UniqueKeepStrategy) -> LazyFrame {
1709 let subset = subset.map(|s| vec![Expr::Selector(s)]);
1710 self.unique_generic(subset, keep_strategy)
1711 }
1712
1713 pub fn unique_generic(
1714 self,
1715 subset: Option<Vec<Expr>>,
1716 keep_strategy: UniqueKeepStrategy,
1717 ) -> LazyFrame {
1718 let opt_state = self.get_opt_state();
1719 let options = DistinctOptionsDSL {
1720 subset,
1721 maintain_order: false,
1722 keep_strategy,
1723 };
1724 let lp = self.get_plan_builder().distinct(options).build();
1725 Self::from_logical_plan(lp, opt_state)
1726 }
1727
1728 pub fn drop_nans(self, subset: Option<Selector>) -> LazyFrame {
1733 let opt_state = self.get_opt_state();
1734 let lp = self.get_plan_builder().drop_nans(subset).build();
1735 Self::from_logical_plan(lp, opt_state)
1736 }
1737
1738 pub fn drop_nulls(self, subset: Option<Selector>) -> LazyFrame {
1743 let opt_state = self.get_opt_state();
1744 let lp = self.get_plan_builder().drop_nulls(subset).build();
1745 Self::from_logical_plan(lp, opt_state)
1746 }
1747
1748 pub fn slice(self, offset: i64, len: IdxSize) -> LazyFrame {
1758 let opt_state = self.get_opt_state();
1759 let lp = self.get_plan_builder().slice(offset, len).build();
1760 Self::from_logical_plan(lp, opt_state)
1761 }
1762
1763 pub fn clear(self) -> LazyFrame {
1765 self.slice(0, 0)
1766 }
1767
1768 pub fn first(self) -> LazyFrame {
1772 self.slice(0, 1)
1773 }
1774
1775 pub fn last(self) -> LazyFrame {
1779 self.slice(-1, 1)
1780 }
1781
1782 pub fn tail(self, n: IdxSize) -> LazyFrame {
1786 let neg_tail = -(n as i64);
1787 self.slice(neg_tail, n)
1788 }
1789
1790 #[cfg(feature = "pivot")]
1791 #[expect(clippy::too_many_arguments)]
1792 pub fn pivot(
1793 self,
1794 on: Selector,
1795 on_columns: Arc<DataFrame>,
1796 index: Selector,
1797 values: Selector,
1798 agg: Expr,
1799 maintain_order: bool,
1800 separator: PlSmallStr,
1801 column_naming: PivotColumnNaming,
1802 ) -> LazyFrame {
1803 let opt_state = self.get_opt_state();
1804 let lp = self
1805 .get_plan_builder()
1806 .pivot(
1807 on,
1808 on_columns,
1809 index,
1810 values,
1811 agg,
1812 maintain_order,
1813 separator,
1814 column_naming,
1815 )
1816 .build();
1817 Self::from_logical_plan(lp, opt_state)
1818 }
1819
1820 #[cfg(feature = "pivot")]
1824 pub fn unpivot(self, args: UnpivotArgsDSL) -> LazyFrame {
1825 let opt_state = self.get_opt_state();
1826 let lp = self.get_plan_builder().unpivot(args).build();
1827 Self::from_logical_plan(lp, opt_state)
1828 }
1829
1830 pub fn limit(self, n: IdxSize) -> LazyFrame {
1832 self.slice(0, n)
1833 }
1834
1835 pub fn map<F>(
1849 self,
1850 function: F,
1851 optimizations: AllowedOptimizations,
1852 schema: Option<Arc<dyn UdfSchema>>,
1853 name: Option<&'static str>,
1854 ) -> LazyFrame
1855 where
1856 F: 'static + Fn(DataFrame) -> PolarsResult<DataFrame> + Send + Sync,
1857 {
1858 let opt_state = self.get_opt_state();
1859 let lp = self
1860 .get_plan_builder()
1861 .map(
1862 function,
1863 optimizations,
1864 schema,
1865 PlSmallStr::from_static(name.unwrap_or("ANONYMOUS UDF")),
1866 )
1867 .build();
1868 Self::from_logical_plan(lp, opt_state)
1869 }
1870
1871 #[cfg(feature = "python")]
1872 pub fn map_python(
1873 self,
1874 function: polars_utils::python_function::PythonFunction,
1875 optimizations: AllowedOptimizations,
1876 schema: Option<SchemaRef>,
1877 validate_output: bool,
1878 ) -> LazyFrame {
1879 let opt_state = self.get_opt_state();
1880 let lp = self
1881 .get_plan_builder()
1882 .map_python(function, optimizations, schema, validate_output)
1883 .build();
1884 Self::from_logical_plan(lp, opt_state)
1885 }
1886
1887 pub(crate) fn map_private(self, function: DslFunction) -> LazyFrame {
1888 let opt_state = self.get_opt_state();
1889 let lp = self.get_plan_builder().map_private(function).build();
1890 Self::from_logical_plan(lp, opt_state)
1891 }
1892
1893 pub fn with_row_index<S>(self, name: S, offset: Option<IdxSize>) -> LazyFrame
1902 where
1903 S: Into<PlSmallStr>,
1904 {
1905 let name = name.into();
1906
1907 match &self.logical_plan {
1908 v @ DslPlan::Scan {
1909 scan_type,
1910 unified_scan_args,
1911 ..
1912 } if unified_scan_args.row_index.is_none()
1913 && !matches!(
1914 &**scan_type,
1915 FileScanDsl::Anonymous { .. } | FileScanDsl::ExpandedPaths { .. }
1916 ) =>
1917 {
1918 let DslPlan::Scan {
1919 sources,
1920 mut unified_scan_args,
1921 scan_type,
1922 cached_ir: _,
1923 } = v.clone()
1924 else {
1925 unreachable!()
1926 };
1927
1928 unified_scan_args.row_index = Some(RowIndex {
1929 name,
1930 offset: offset.unwrap_or(0),
1931 });
1932
1933 DslPlan::Scan {
1934 sources,
1935 unified_scan_args,
1936 scan_type,
1937 cached_ir: Default::default(),
1938 }
1939 .into()
1940 },
1941 _ => self.map_private(DslFunction::RowIndex { name, offset }),
1942 }
1943 }
1944
1945 pub fn count(self) -> LazyFrame {
1947 self.select(vec![col(PlSmallStr::from_static("*")).count()])
1948 }
1949
1950 #[cfg(feature = "dtype-struct")]
1953 pub fn unnest(self, cols: Selector, separator: Option<PlSmallStr>) -> Self {
1954 self.map_private(DslFunction::Unnest {
1955 columns: cols,
1956 separator,
1957 })
1958 }
1959
1960 #[cfg(feature = "merge_sorted")]
1961 pub fn merge_sorted<I, S>(
1962 self,
1963 other: LazyFrame,
1964 key: I,
1965 maintain_order: bool,
1966 ) -> PolarsResult<LazyFrame>
1967 where
1968 I: IntoIterator<Item = S>,
1969 S: Into<PlSmallStr>,
1970 {
1971 let key: Arc<[PlSmallStr]> = key.into_iter().map(Into::into).collect();
1972
1973 polars_ensure!(
1974 !key.is_empty(),
1975 ComputeError: "merge_sorted requires at least one key column"
1976 );
1977
1978 let lp = DslPlan::MergeSorted {
1979 input_left: Arc::new(self.logical_plan),
1980 input_right: Arc::new(other.logical_plan),
1981 key,
1982 maintain_order,
1983 };
1984 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1985 }
1986
1987 pub fn hint(self, hint: HintIR) -> PolarsResult<LazyFrame> {
1988 let lp = DslPlan::MapFunction {
1989 input: Arc::new(self.logical_plan),
1990 function: DslFunction::Hint(hint),
1991 };
1992 Ok(LazyFrame::from_logical_plan(lp, self.opt_state))
1993 }
1994}
1995
1996#[derive(Clone)]
1998pub struct LazyGroupBy {
1999 pub logical_plan: DslPlan,
2000 opt_state: OptFlags,
2001 keys: Vec<Expr>,
2002 predicates: Vec<Expr>,
2003 maintain_order: bool,
2004 #[cfg(feature = "dynamic_group_by")]
2005 dynamic_options: Option<DynamicGroupOptions>,
2006 #[cfg(feature = "dynamic_group_by")]
2007 rolling_options: Option<RollingGroupOptions>,
2008}
2009
2010impl From<LazyGroupBy> for LazyFrame {
2011 fn from(lgb: LazyGroupBy) -> Self {
2012 Self {
2013 logical_plan: lgb.logical_plan,
2014 opt_state: lgb.opt_state,
2015 cached_arena: Default::default(),
2016 }
2017 }
2018}
2019
2020impl LazyGroupBy {
2021 pub fn having(mut self, predicate: Expr) -> Self {
2042 self.predicates.push(predicate);
2043 self
2044 }
2045
2046 pub fn agg<E: AsRef<[Expr]>>(self, aggs: E) -> LazyFrame {
2068 #[cfg(feature = "dynamic_group_by")]
2069 let lp = DslBuilder::from(self.logical_plan)
2070 .group_by(
2071 self.keys,
2072 self.predicates,
2073 aggs,
2074 None,
2075 self.maintain_order,
2076 self.dynamic_options,
2077 self.rolling_options,
2078 )
2079 .build();
2080
2081 #[cfg(not(feature = "dynamic_group_by"))]
2082 let lp = DslBuilder::from(self.logical_plan)
2083 .group_by(self.keys, self.predicates, aggs, None, self.maintain_order)
2084 .build();
2085 LazyFrame::from_logical_plan(lp, self.opt_state)
2086 }
2087
2088 pub fn head(self, n: Option<usize>) -> LazyFrame {
2090 let keys = self
2091 .keys
2092 .iter()
2093 .filter_map(|expr| expr_output_name(expr).ok())
2094 .collect::<Vec<_>>();
2095
2096 self.agg([all().as_expr().head(n)]).explode_impl(
2097 all() - by_name(keys.iter().cloned(), false, false),
2098 ExplodeOptions {
2099 empty_as_null: true,
2100 keep_nulls: true,
2101 },
2102 true,
2103 )
2104 }
2105
2106 pub fn tail(self, n: Option<usize>) -> LazyFrame {
2108 let keys = self
2109 .keys
2110 .iter()
2111 .filter_map(|expr| expr_output_name(expr).ok())
2112 .collect::<Vec<_>>();
2113
2114 self.agg([all().as_expr().tail(n)]).explode_impl(
2115 all() - by_name(keys.iter().cloned(), false, false),
2116 ExplodeOptions {
2117 empty_as_null: true,
2118 keep_nulls: true,
2119 },
2120 true,
2121 )
2122 }
2123
2124 pub fn apply(self, f: PlanCallback<DataFrame, DataFrame>, schema: SchemaRef) -> LazyFrame {
2129 if !self.predicates.is_empty() {
2130 panic!("not yet implemented: `apply` cannot be used with `having` predicates");
2131 }
2132
2133 #[cfg(feature = "dynamic_group_by")]
2134 let options = GroupbyOptions {
2135 dynamic: self.dynamic_options,
2136 rolling: self.rolling_options,
2137 slice: None,
2138 };
2139
2140 #[cfg(not(feature = "dynamic_group_by"))]
2141 let options = GroupbyOptions { slice: None };
2142
2143 let lp = DslPlan::GroupBy {
2144 input: Arc::new(self.logical_plan),
2145 keys: self.keys,
2146 predicates: vec![],
2147 aggs: vec![],
2148 apply: Some((f, schema)),
2149 maintain_order: self.maintain_order,
2150 options: Arc::new(options),
2151 };
2152 LazyFrame::from_logical_plan(lp, self.opt_state)
2153 }
2154}
2155
2156#[must_use]
2157pub struct JoinBuilder {
2158 lf: LazyFrame,
2159 how: JoinType,
2160 other: Option<LazyFrame>,
2161 left_on: Vec<Expr>,
2162 right_on: Vec<Expr>,
2163 allow_parallel: bool,
2164 force_parallel: bool,
2165 suffix: Option<PlSmallStr>,
2166 validation: JoinValidation,
2167 nulls_equal: bool,
2168 coalesce: JoinCoalesce,
2169 maintain_order: MaintainOrderJoin,
2170 build_side: Option<JoinBuildSide>,
2171}
2172impl JoinBuilder {
2173 pub fn new(lf: LazyFrame) -> Self {
2175 Self {
2176 lf,
2177 other: None,
2178 how: JoinType::Inner,
2179 left_on: vec![],
2180 right_on: vec![],
2181 allow_parallel: true,
2182 force_parallel: false,
2183 suffix: None,
2184 validation: Default::default(),
2185 nulls_equal: false,
2186 coalesce: Default::default(),
2187 maintain_order: Default::default(),
2188 build_side: None,
2189 }
2190 }
2191
2192 pub fn with(mut self, other: LazyFrame) -> Self {
2194 self.other = Some(other);
2195 self
2196 }
2197
2198 pub fn how(mut self, how: JoinType) -> Self {
2200 self.how = how;
2201 self
2202 }
2203
2204 pub fn validate(mut self, validation: JoinValidation) -> Self {
2205 self.validation = validation;
2206 self
2207 }
2208
2209 pub fn on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2213 let on = on.as_ref().to_vec();
2214 self.left_on.clone_from(&on);
2215 self.right_on = on;
2216 self
2217 }
2218
2219 pub fn left_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2223 self.left_on = on.as_ref().to_vec();
2224 self
2225 }
2226
2227 pub fn right_on<E: AsRef<[Expr]>>(mut self, on: E) -> Self {
2231 self.right_on = on.as_ref().to_vec();
2232 self
2233 }
2234
2235 pub fn allow_parallel(mut self, allow: bool) -> Self {
2237 self.allow_parallel = allow;
2238 self
2239 }
2240
2241 pub fn force_parallel(mut self, force: bool) -> Self {
2243 self.force_parallel = force;
2244 self
2245 }
2246
2247 pub fn join_nulls(mut self, nulls_equal: bool) -> Self {
2249 self.nulls_equal = nulls_equal;
2250 self
2251 }
2252
2253 pub fn suffix<S>(mut self, suffix: S) -> Self
2256 where
2257 S: Into<PlSmallStr>,
2258 {
2259 self.suffix = Some(suffix.into());
2260 self
2261 }
2262
2263 pub fn coalesce(mut self, coalesce: JoinCoalesce) -> Self {
2265 self.coalesce = coalesce;
2266 self
2267 }
2268
2269 pub fn maintain_order(mut self, maintain_order: MaintainOrderJoin) -> Self {
2271 self.maintain_order = maintain_order;
2272 self
2273 }
2274
2275 pub fn build_side(mut self, build_side: Option<JoinBuildSide>) -> Self {
2277 self.build_side = build_side;
2278 self
2279 }
2280
2281 pub fn finish(self) -> LazyFrame {
2283 let opt_state = self.lf.opt_state;
2284 let other = self.other.expect("'with' not set in join builder");
2285
2286 let args = JoinArgs {
2287 how: self.how,
2288 validation: self.validation,
2289 suffix: self.suffix,
2290 slice: None,
2291 nulls_equal: self.nulls_equal,
2292 coalesce: self.coalesce,
2293 maintain_order: self.maintain_order,
2294 build_side: self.build_side,
2295 };
2296
2297 let lp = self
2298 .lf
2299 .get_plan_builder()
2300 .join(
2301 other.logical_plan,
2302 self.left_on,
2303 self.right_on,
2304 JoinOptions {
2305 allow_parallel: self.allow_parallel,
2306 force_parallel: self.force_parallel,
2307 args,
2308 }
2309 .into(),
2310 )
2311 .build();
2312 LazyFrame::from_logical_plan(lp, opt_state)
2313 }
2314
2315 pub fn join_where(self, predicates: Vec<Expr>) -> LazyFrame {
2317 let opt_state = self.lf.opt_state;
2318 let other = self.other.expect("with not set");
2319
2320 fn decompose_and(predicate: Expr, expanded_predicates: &mut Vec<Expr>) {
2322 if let Expr::BinaryExpr {
2323 op: Operator::And,
2324 left,
2325 right,
2326 } = predicate
2327 {
2328 decompose_and((*left).clone(), expanded_predicates);
2329 decompose_and((*right).clone(), expanded_predicates);
2330 } else {
2331 expanded_predicates.push(predicate);
2332 }
2333 }
2334 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2335 for predicate in predicates {
2336 decompose_and(predicate, &mut expanded_predicates);
2337 }
2338 let predicates: Vec<Expr> = expanded_predicates;
2339
2340 #[cfg(feature = "is_between")]
2342 let predicates: Vec<Expr> = {
2343 let mut expanded_predicates = Vec::with_capacity(predicates.len() * 2);
2344 for predicate in predicates {
2345 if let Expr::Function {
2346 function: FunctionExpr::Boolean(BooleanFunction::IsBetween { closed }),
2347 input,
2348 ..
2349 } = &predicate
2350 {
2351 if let [expr, lower, upper] = input.as_slice() {
2352 match closed {
2353 ClosedInterval::Both => {
2354 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2355 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2356 },
2357 ClosedInterval::Right => {
2358 expanded_predicates.push(expr.clone().gt(lower.clone()));
2359 expanded_predicates.push(expr.clone().lt_eq(upper.clone()));
2360 },
2361 ClosedInterval::Left => {
2362 expanded_predicates.push(expr.clone().gt_eq(lower.clone()));
2363 expanded_predicates.push(expr.clone().lt(upper.clone()));
2364 },
2365 ClosedInterval::None => {
2366 expanded_predicates.push(expr.clone().gt(lower.clone()));
2367 expanded_predicates.push(expr.clone().lt(upper.clone()));
2368 },
2369 }
2370 continue;
2371 }
2372 }
2373 expanded_predicates.push(predicate);
2374 }
2375 expanded_predicates
2376 };
2377
2378 let args = JoinArgs {
2379 how: self.how,
2380 validation: self.validation,
2381 suffix: self.suffix,
2382 slice: None,
2383 nulls_equal: self.nulls_equal,
2384 coalesce: self.coalesce,
2385 maintain_order: self.maintain_order,
2386 build_side: self.build_side,
2387 };
2388 let options = JoinOptions {
2389 allow_parallel: self.allow_parallel,
2390 force_parallel: self.force_parallel,
2391 args,
2392 };
2393
2394 let lp = DslPlan::Join {
2395 input_left: Arc::new(self.lf.logical_plan),
2396 input_right: Arc::new(other.logical_plan),
2397 left_on: Default::default(),
2398 right_on: Default::default(),
2399 predicates,
2400 options: Arc::from(options),
2401 };
2402
2403 LazyFrame::from_logical_plan(lp, opt_state)
2404 }
2405}
2406
2407pub const BUILD_STREAMING_EXECUTOR: Option<polars_mem_engine::StreamingExecutorBuilder> = {
2408 #[cfg(not(feature = "streaming"))]
2409 {
2410 None
2411 }
2412 #[cfg(feature = "streaming")]
2413 {
2414 Some(polars_stream::build_streaming_query_executor)
2415 }
2416};
2417
2418fn run_in_memory_query(
2419 node: Node,
2420 ir_arena: &mut Arena<IR>,
2421 expr_arena: &mut Arena<AExpr>,
2422 engine: Engine,
2423 observer: Option<Box<dyn QueryObserver>>,
2424) -> PolarsResult<QueryResult> {
2425 let _guard = observer
2426 .as_ref()
2427 .map(|o| o.on_query_planned(to_planned_query(node, ir_arena, expr_arena)));
2428
2429 let result = if let IR::SinkMultiple { inputs } = ir_arena.get(node) {
2430 polars_ensure!(
2431 engine != Engine::Gpu,
2432 InvalidOperation:
2433 "collect_all is not supported for the gpu engine"
2434 );
2435
2436 let physical_plan = create_multiple_physical_plans(
2437 inputs.clone().as_slice(),
2438 ir_arena,
2439 expr_arena,
2440 BUILD_STREAMING_EXECUTOR,
2441 )?;
2442 physical_plan.execute().map(QueryResult::Multiple)
2443 } else {
2444 let mut physical_plan =
2445 create_physical_plan(node, ir_arena, expr_arena, BUILD_STREAMING_EXECUTOR)?;
2446 let mut state = ExecutionState::new();
2447 physical_plan.execute(&mut state).map(QueryResult::Single)
2448 };
2449
2450 result.inspect_err(|err| {
2451 if let Some(o) = observer.as_ref() {
2452 o.on_query_failed(err);
2453 }
2454 })
2455}
2456
2457fn to_planned_query(node: Node, ir_arena: &Arena<IR>, expr_arena: &Arena<AExpr>) -> PlannedQuery {
2458 let ir = ir_plan_to_description(&[node], ir_arena, expr_arena);
2459 PlannedQuery::new(ir)
2460}
2461
2462pub struct CollectBatches {
2463 recv: Receiver<PolarsResult<DataFrame>>,
2464 runner: Option<Box<dyn FnOnce() + Send + 'static>>,
2465}
2466
2467impl CollectBatches {
2468 pub fn start(&mut self) {
2470 if let Some(runner) = self.runner.take() {
2471 runner()
2472 }
2473 }
2474}
2475
2476impl Iterator for CollectBatches {
2477 type Item = PolarsResult<DataFrame>;
2478
2479 fn next(&mut self) -> Option<Self::Item> {
2480 self.start();
2481 self.recv.recv().ok()
2482 }
2483}