You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by al...@apache.org on 2023/08/05 18:12:36 UTC
[arrow-datafusion-python] branch main updated: feat: add case function (#447) (#448)
This is an automated email from the ASF dual-hosted git repository.
alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-datafusion-python.git
The following commit(s) were added to refs/heads/main by this push:
new 1fde8e4 feat: add case function (#447) (#448)
1fde8e4 is described below
commit 1fde8e47ae6714f02c2d2a088577e92e814dfa34
Author: Daniel Mesejo <me...@gmail.com>
AuthorDate: Sat Aug 5 20:12:30 2023 +0200
feat: add case function (#447) (#448)
---
datafusion/tests/test_functions.py | 22 ++++++++++++++++
src/expr.rs | 1 +
src/expr/conditional_expr.rs | 54 ++++++++++++++++++++++++++++++++++++++
src/functions.rs | 15 ++++++++---
4 files changed, 89 insertions(+), 3 deletions(-)
diff --git a/datafusion/tests/test_functions.py b/datafusion/tests/test_functions.py
index ec33474..c5ddea3 100644
--- a/datafusion/tests/test_functions.py
+++ b/datafusion/tests/test_functions.py
@@ -411,3 +411,25 @@ def test_temporal_functions(df):
assert result.column(9) == pa.array(
[datetime(2023, 9, 7, 5, 6, 14, 523952)] * 3, type=pa.timestamp("us")
)
+
+
+def test_case(df):
+ df = df.select(
+ f.case(column("b"))
+ .when(literal(4), literal(10))
+ .otherwise(literal(8)),
+ f.case(column("a"))
+ .when(literal("Hello"), literal("Hola"))
+ .when(literal("World"), literal("Mundo"))
+ .otherwise(literal("!!")),
+ f.case(column("a"))
+ .when(literal("Hello"), literal("Hola"))
+ .when(literal("World"), literal("Mundo"))
+ .end(),
+ )
+
+ result = df.collect()
+ result = result[0]
+ assert result.column(0) == pa.array([10, 8, 8])
+ assert result.column(1) == pa.array(["Hola", "Mundo", "!!"])
+ assert result.column(2) == pa.array(["Hola", "Mundo", None])
diff --git a/src/expr.rs b/src/expr.rs
index 2fd638a..d1022e9 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -59,6 +59,7 @@ pub mod bool_expr;
pub mod case;
pub mod cast;
pub mod column;
+pub mod conditional_expr;
pub mod create_memory_table;
pub mod create_view;
pub mod cross_join;
diff --git a/src/expr/conditional_expr.rs b/src/expr/conditional_expr.rs
new file mode 100644
index 0000000..96ef58f
--- /dev/null
+++ b/src/expr/conditional_expr.rs
@@ -0,0 +1,54 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use crate::expr::PyExpr;
+use datafusion_expr::conditional_expressions::CaseBuilder;
+use pyo3::prelude::*;
+
+#[pyclass(name = "CaseBuilder", module = "datafusion.expr", subclass)]
+pub struct PyCaseBuilder {
+ pub case_builder: CaseBuilder,
+}
+
+impl From<PyCaseBuilder> for CaseBuilder {
+ fn from(case_builder: PyCaseBuilder) -> Self {
+ case_builder.case_builder
+ }
+}
+
+impl From<CaseBuilder> for PyCaseBuilder {
+ fn from(case_builder: CaseBuilder) -> PyCaseBuilder {
+ PyCaseBuilder { case_builder }
+ }
+}
+
+#[pymethods]
+impl PyCaseBuilder {
+ fn when(&mut self, when: PyExpr, then: PyExpr) -> PyCaseBuilder {
+ PyCaseBuilder {
+ case_builder: self.case_builder.when(when.expr, then.expr),
+ }
+ }
+
+ fn otherwise(&mut self, else_expr: PyExpr) -> PyResult<PyExpr> {
+ Ok(self.case_builder.otherwise(else_expr.expr)?.clone().into())
+ }
+
+ fn end(&mut self) -> PyResult<PyExpr> {
+ Ok(self.case_builder.end()?.clone().into())
+ }
+}
diff --git a/src/functions.rs b/src/functions.rs
index 8b60e64..06d35ef 100644
--- a/src/functions.rs
+++ b/src/functions.rs
@@ -17,6 +17,9 @@
use pyo3::{prelude::*, wrap_pyfunction};
+use crate::errors::DataFusionError;
+use crate::expr::conditional_expr::PyCaseBuilder;
+use crate::expr::PyExpr;
use datafusion_common::Column;
use datafusion_expr::expr::Alias;
use datafusion_expr::{
@@ -27,9 +30,6 @@ use datafusion_expr::{
BuiltinScalarFunction, Expr, WindowFrame,
};
-use crate::errors::DataFusionError;
-use crate::expr::PyExpr;
-
#[pyfunction]
fn in_list(expr: PyExpr, value: Vec<PyExpr>, negated: bool) -> PyExpr {
datafusion_expr::in_list(
@@ -115,6 +115,14 @@ fn count_star() -> PyResult<PyExpr> {
})
}
+/// Create a CASE WHEN statement with literal WHEN expressions for comparison to the base expression.
+#[pyfunction]
+fn case(expr: PyExpr) -> PyResult<PyCaseBuilder> {
+ Ok(PyCaseBuilder {
+ case_builder: datafusion_expr::case(expr.expr),
+ })
+}
+
/// Creates a new Window function expression
#[pyfunction]
fn window(
@@ -355,6 +363,7 @@ pub(crate) fn init_module(m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(chr))?;
m.add_wrapped(wrap_pyfunction!(char_length))?;
m.add_wrapped(wrap_pyfunction!(coalesce))?;
+ m.add_wrapped(wrap_pyfunction!(case))?;
m.add_wrapped(wrap_pyfunction!(col))?;
m.add_wrapped(wrap_pyfunction!(concat_ws))?;
m.add_wrapped(wrap_pyfunction!(concat))?;