You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/09/03 04:34:40 UTC
(datafusion) branch gh-readonly-queue/main/pr-24633-27e81e9a45317afeacd7d7dc2b77f10637ecd5d5 created (now e36c5f842a)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a change to branch gh-readonly-queue/main/pr-24633-27e81e9a45317afeacd7d7dc2b77f10637ecd5d5
in repository https://gitbox.apache.org/repos/asf/datafusion.git
at e36c5f842a feat(dataframe): improve DataFrame::from_columns input types (#24630) (#24633)
This branch includes the following new commits:
new e36c5f842a feat(dataframe): improve DataFrame::from_columns input types (#24630) (#24633)
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org
(datafusion) 01/01: feat(dataframe): improve DataFrame::from_columns input types (#24630) (#24633)
Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-24633-27e81e9a45317afeacd7d7dc2b77f10637ecd5d5
in repository https://gitbox.apache.org/repos/asf/datafusion.git
commit e36c5f842aaedfc1afb8c69572f1f1a323f0cdde
Author: Sergey Zhukov <62...@users.noreply.github.com>
AuthorDate: Thu Sep 3 04:34:09 2026 +0000
feat(dataframe): improve DataFrame::from_columns input types (#24630) (#24633)
## Which issue does this PR close?
<!--
We generally require a GitHub issue to be filed for all bug fixes and
enhancements and this helps us generate change logs for our releases.
You can link an issue to this PR using the GitHub syntax. For example
`Closes #123` indicates that this PR will close issue #123.
-->
- Closes #https://github.com/apache/datafusion/issues/24630.
## Rationale for this change
Improve the `DataFrame::from_columns` API and give users more
flexibility when constructing a `DataFrame` from columns.
<!--
Why are you proposing this change? If this is already explained clearly
in the issue then this section is not needed.
Explaining clearly why changes are proposed helps reviewers understand
your changes and offer better suggestions for fixes.
Please explain the problem you are trying to solve in terms of the
user-visible
behavior, rather than the implementation.
For example, "The code in `foo.rs` doesn't handle nulls" is a symptom of
the
implementation. "COUNT(DISTINCT) returns wrong results when the column
contains
nulls" is the user-visible problem.
-->
## What changes are included in this PR?
- Generalize `DataFrame::from_columns` to accept `IntoIterator` of
columns.
- This allows users to pass both `arrays` and `Vecs` of columns.
- Update tests to cover both input forms.
<!--
There is no need to duplicate the description in the issue here, but it
is sometimes worth providing a summary of the individual changes in this
PR.
-->
## Are these changes tested?
Yes. Tests cover both `array` and `Vec` inputs and verify the resulting
schema, data types, row count, and values.
<!--
We typically require tests for all PRs in order to:
1. Prevent the code from being accidentally broken by subsequent changes
2. Serve as another way to document the expected behavior of the code
If tests are not included in your PR, please explain why (for example,
are they covered by existing tests)?
-->
## Are there any user-facing changes?
Yes. This is a breaking API change. `DataFrame::from_columns` now
accepts an `IntoIterator` of columns instead of specifically accepting a
`Vec`. Existing `Vec` usage continues to work, while users can also pass
`arrays` and other compatible iterators.
Users relying on the exact non-generic function signature may need to
update their code to account for the new generic API.
<!--
If there are user-facing changes then we may require documentation to be
updated before approving the PR.
If there are any breaking changes to public APIs, please add the `api
change` label.
-->
---
datafusion/core/src/dataframe/mod.rs | 23 +++---
datafusion/core/tests/dataframe/mod.rs | 91 ++++++++++++++++++++--
docs/source/library-user-guide/upgrading/56.0.0.md | 23 +++++-
3 files changed, 116 insertions(+), 21 deletions(-)
diff --git a/datafusion/core/src/dataframe/mod.rs b/datafusion/core/src/dataframe/mod.rs
index 1299e5fb7b..ed3dc5ea83 100644
--- a/datafusion/core/src/dataframe/mod.rs
+++ b/datafusion/core/src/dataframe/mod.rs
@@ -2614,7 +2614,7 @@ impl DataFrame {
/// # async fn main() -> Result<()> {
/// let id: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
/// let name: ArrayRef = Arc::new(StringArray::from(vec!["foo", "bar", "baz"]));
- /// let df = DataFrame::from_columns(vec![("id", id), ("name", name)])?;
+ /// let df = DataFrame::from_columns([("id", id), ("name", name)])?;
/// let expected = vec![
/// "+----+------+",
/// "| id | name |",
@@ -2628,17 +2628,16 @@ impl DataFrame {
/// # Ok(())
/// # }
/// ```
- pub fn from_columns(columns: Vec<(&str, ArrayRef)>) -> Result<Self> {
- let fields = columns
- .iter()
- .map(|(name, array)| Field::new(*name, array.data_type().clone(), true))
- .collect::<Vec<_>>();
-
- let arrays = columns
+ pub fn from_columns<'a, I>(columns: I) -> Result<Self>
+ where
+ I: IntoIterator<Item = (&'a str, ArrayRef)>,
+ {
+ let (fields, arrays): (Vec<_>, Vec<_>) = columns
.into_iter()
- .map(|(_, array)| array)
- .collect::<Vec<_>>();
-
+ .map(|(name, array)| {
+ (Field::new(name, array.data_type().clone(), true), array)
+ })
+ .unzip();
let schema = Arc::new(Schema::new(fields));
let batch = RecordBatch::try_new(schema, arrays)?;
let ctx = SessionContext::new();
@@ -2695,7 +2694,7 @@ macro_rules! dataframe {
use datafusion::prelude::DataFrame;
use datafusion::common::test_util::IntoArrayRef;
- let columns = vec![
+ let columns = [
$(
($name, $data.into_array_ref()),
)+
diff --git a/datafusion/core/tests/dataframe/mod.rs b/datafusion/core/tests/dataframe/mod.rs
index 1676a69de8..527c956749 100644
--- a/datafusion/core/tests/dataframe/mod.rs
+++ b/datafusion/core/tests/dataframe/mod.rs
@@ -6968,7 +6968,7 @@ async fn test_dataframe_from_columns() -> Result<()> {
let strings: ArrayRef =
Arc::new(StringArray::from(vec![Some("foo"), Some("bar"), None]));
- let df = DataFrame::from_columns(vec![
+ let columns = [
("bool", bools),
("i8", i8s),
("i16", i16s),
@@ -6982,10 +6982,10 @@ async fn test_dataframe_from_columns() -> Result<()> {
("f32", f32s),
("f64", f64s),
("str", strings),
- ])?;
+ ];
- assert_eq!(df.schema().fields().len(), 13);
- assert_eq!(df.clone().count().await?, 3);
+ let df1 = DataFrame::from_columns(columns.clone())?;
+ let df2 = DataFrame::from_columns(columns.to_vec())?;
let expected_types = [
("bool", DataType::Boolean),
@@ -7003,14 +7003,89 @@ async fn test_dataframe_from_columns() -> Result<()> {
("str", DataType::Utf8),
];
- let schema = df.schema();
+ for df in [df1, df2] {
+ assert_eq!(df.schema().fields().len(), expected_types.len());
+ assert_eq!(df.clone().count().await?, 3);
- for (name, data_type) in expected_types {
- assert_eq!(schema.field_with_name(None, name)?.data_type(), &data_type);
+ let schema = df.schema();
+
+ for (name, data_type) in &expected_types {
+ assert_eq!(schema.field_with_name(None, name)?.data_type(), data_type);
+ }
+
+ let rows = df.sort(vec![col("i32").sort(true, true)])?;
+
+ assert_batches_eq!(
+ &[
+ "+-------+----+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+",
+ "| bool | i8 | i16 | i32 | i64 | u8 | u16 | u32 | u64 | f16 | f32 | f64 | str |",
+ "+-------+----+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+",
+ "| true | -1 | -1 | -1 | -1 | 0 | 0 | 0 | 0 | 1 | 1.0 | 1.0 | foo |",
+ "| false | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 2 | 2.0 | 2.0 | bar |",
+ "| true | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 3 | 3.0 | 3.0 | |",
+ "+-------+----+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+",
+ ],
+ &rows.collect().await?
+ );
}
- let rows = df.sort(vec![col("i32").sort(true, true)])?;
+ Ok(())
+}
+
+#[test]
+fn test_dataframe_from_columns_empty() {
+ let result = DataFrame::from_columns(vec![]);
+ assert!(result.is_err());
+
+ let result = DataFrame::from_columns([]);
+ assert!(result.is_err());
+}
+
+#[tokio::test]
+async fn test_dataframe_from_columns_with_iterator() -> Result<()> {
+ let bools: ArrayRef = Arc::new(BooleanArray::from(vec![true, false, true]));
+ let i8s: ArrayRef = Arc::new(Int8Array::from(vec![-1, 0, 1]));
+ let i16s: ArrayRef = Arc::new(Int16Array::from(vec![-1, 0, 1]));
+ let i32s: ArrayRef = Arc::new(Int32Array::from(vec![-1, 0, 1]));
+ let i64s: ArrayRef = Arc::new(Int64Array::from(vec![-1, 0, 1]));
+
+ let u8s: ArrayRef = Arc::new(UInt8Array::from(vec![0, 1, 2]));
+ let u16s: ArrayRef = Arc::new(UInt16Array::from(vec![0, 1, 2]));
+ let u32s: ArrayRef = Arc::new(UInt32Array::from(vec![0, 1, 2]));
+ let u64s: ArrayRef = Arc::new(UInt64Array::from(vec![0, 1, 2]));
+
+ let f16s: ArrayRef = Arc::new(Float16Array::from(vec![
+ half::f16::from_f64(1.0),
+ half::f16::from_f64(2.0),
+ half::f16::from_f64(3.0),
+ ]));
+ let f32s: ArrayRef = Arc::new(Float32Array::from(vec![1.0, 2.0, 3.0]));
+ let f64s: ArrayRef = Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0]));
+
+ let strings: ArrayRef =
+ Arc::new(StringArray::from(vec![Some("foo"), Some("bar"), None]));
+
+ let columns = [
+ ("bool", bools),
+ ("i8", i8s),
+ ("i16", i16s),
+ ("i32", i32s),
+ ("i64", i64s),
+ ("u8", u8s),
+ ("u16", u16s),
+ ("u32", u32s),
+ ("u64", u64s),
+ ("f16", f16s),
+ ("f32", f32s),
+ ("f64", f64s),
+ ("str", strings),
+ ];
+ let df = DataFrame::from_columns(columns.into_iter())?;
+
+ assert_eq!(df.schema().fields().len(), 13);
+ assert_eq!(df.clone().count().await?, 3);
+ let rows = df.sort(vec![col("i32").sort(true, true)])?;
assert_batches_eq!(
&[
"+-------+----+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+",
diff --git a/docs/source/library-user-guide/upgrading/56.0.0.md b/docs/source/library-user-guide/upgrading/56.0.0.md
index 97ac11a016..881aee0c63 100644
--- a/docs/source/library-user-guide/upgrading/56.0.0.md
+++ b/docs/source/library-user-guide/upgrading/56.0.0.md
@@ -97,4 +97,25 @@ The output type of the `floor` and `ceil` UDFs has been changed from the exact i
Change the expected type or wrap the expression in `CAST`. It's recommended to avoid relying on decimal's exact precision and scale.
-[#24703]: https://github.com/apache/datafusion/pull/24703
+### `DataFrame::from_columns` accepts `IntoIterator`
+
+`DataFrame::from_columns` now accepts any `IntoIterator<Item = (&str, ArrayRef)>`
+instead of specifically accepting a `Vec<(&str, ArrayRef)>`.
+
+```rust,ignore
+// Existing Vec usage continues to work
+let df = DataFrame::from_columns(vec![
+ ("id", id),
+ ("name", name),
+])?;
+
+// Arrays can now be used directly
+let df = DataFrame::from_columns([
+ ("id", id),
+ ("name", name),
+])?;
+```
+
+Most existing call sites using `Vec` require no changes. Code that relies on
+the exact non-generic function signature of `DataFrame::from_columns` may need
+to be updated to account for the new generic API.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org