-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat(dataframe): improve DataFrame::from_columns input types (#24630) #24633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
271bd05
ec0137a
1ce3db7
a01cbbb
51453fd
1a5e7c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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())?; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the final API keeps The current tests cover arrays and
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, it makes sense to extend the tests to cover the |
||
| 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!( | ||
| &[ | ||
| "+-------+----+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating this. One API compatibility concern still remains here.
This changes the released non-generic
from_columns(Vec<(&str, ArrayRef)>)signature tofrom_columns<'a, I>, so the SemVer/API-health issue is still present.cargo-semver-checkswill reportmethod_requires_different_generic_type_params, and downstream code that uses this method as a non-generic function item can break.Using parameter-position
impl IntoIterator<Item = (&str, ArrayRef)>may avoid that specific cargo-semver-checks diagnostic and would preserve normalVeccall syntax, but it is still an implicit generic parameter, so it would not be a strict compatibility fix either.To preserve the existing public API, I think the safest option is to keep
from_columns(Vec<...>)and add the iterator or array-taking behavior under a new method name. The old API could then be deprecated later according to policy if desired.If this signature change is intentional instead, it should be treated and documented as a breaking API change under the API-health policy, including the
api-changelabel and upgrade guidance.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kosiew Thanks for the clarification. I understand the remaining compatibility concern now.
My goal is to allow
from_columnsto accept arrays and otherIntoIteratorinputs while keeping the existingVecusage working. I’d prefer to keep this behavior underfrom_columnsrather than introduce a second method.I agree that this is a breaking change under the API-health policy. I’ll update the PR accordingly, including adding the
api-changelabel, updating the description, and providing upgrade guidance where appropriate.Once the changes are ready, I’ll let you know and ask for another review.