-
Notifications
You must be signed in to change notification settings - Fork 162
feat: expose arrow_field, arrow_try_cast, cast_to_type, with_metadata #1568
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 8 commits
152ef81
04979ea
398b388
708cd4d
83dca2e
7d8a435
ce83065
25cb8e2
a8d3a5e
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 |
|---|---|---|
|
|
@@ -1299,30 +1299,93 @@ def test_make_time(df): | |
| assert result.column(0)[0].as_py() == time(12, 30) | ||
|
|
||
|
|
||
| def test_arrow_cast(df): | ||
| df = df.select( | ||
| f.arrow_cast(column("b"), "Float64").alias("b_as_float"), | ||
| f.arrow_cast(column("b"), "Int32").alias("b_as_int"), | ||
| @pytest.mark.parametrize("cast_fn", [f.arrow_cast, f.arrow_try_cast]) | ||
| @pytest.mark.parametrize( | ||
| ("data_type", "expected"), | ||
| [ | ||
| ("Float64", pa.array([4.0, 5.0, 6.0], type=pa.float64())), | ||
| ("Int32", pa.array([4, 5, 6], type=pa.int32())), | ||
| (pa.float64(), pa.array([4.0, 5.0, 6.0], type=pa.float64())), | ||
| (pa.int32(), pa.array([4, 5, 6], type=pa.int32())), | ||
| (pa.string(), pa.array(["4", "5", "6"], type=pa.string())), | ||
| ], | ||
| ) | ||
| def test_arrow_cast_variants(df, cast_fn, data_type, expected): | ||
| """arrow_cast / arrow_try_cast accept str and pyarrow target types.""" | ||
| result = df.select(cast_fn(column("b"), data_type).alias("c")).collect()[0] | ||
| assert result.column(0) == expected | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("data_type", ["Float64", pa.float64()]) | ||
| def test_arrow_try_cast_null_on_failure(data_type): | ||
| ctx = SessionContext() | ||
| batch = pa.RecordBatch.from_arrays([pa.array(["1.5", "oops", "3"])], names=["s"]) | ||
| df = ctx.create_dataframe([[batch]]) | ||
|
|
||
| result = df.select(f.arrow_try_cast(column("s"), data_type).alias("c")).collect()[0] | ||
|
|
||
| assert result.column(0).to_pylist() == [1.5, None, 3.0] | ||
|
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. Since the assert is static, is the parameter necessary?
Member
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. Agreed, the |
||
|
|
||
|
|
||
| def test_arrow_field(): | ||
| ctx = SessionContext() | ||
| field = pa.field("val", pa.int64(), metadata={"k": "v"}) | ||
| schema = pa.schema([field]) | ||
| batch = pa.RecordBatch.from_arrays([pa.array([1])], schema=schema) | ||
| df = ctx.create_dataframe([[batch]]) | ||
|
|
||
| out = ( | ||
| df.select(f.arrow_field(column("val")).alias("f")) | ||
| .collect_column("f")[0] | ||
| .as_py() | ||
| ) | ||
| result = df.collect() | ||
| assert len(result) == 1 | ||
| result = result[0] | ||
| assert out == { | ||
| "name": "val", | ||
| "data_type": "Int64", | ||
| "nullable": True, | ||
| "metadata": [("k", "v")], | ||
| } | ||
|
|
||
| assert result.column(0) == pa.array([4.0, 5.0, 6.0], type=pa.float64()) | ||
| assert result.column(1) == pa.array([4, 5, 6], type=pa.int32()) | ||
|
|
||
| @pytest.mark.parametrize( | ||
| ("values", "try_cast", "expected"), | ||
| [ | ||
| (pa.array([4, 5, 6]), False, [4.0, 5.0, 6.0]), | ||
| (pa.array(["oops", "2", "3"]), True, [None, 2.0, 3.0]), | ||
| ], | ||
| ) | ||
| def test_cast_to_type(values, try_cast, expected): | ||
| """cast_to_type takes target type from ``type_ref``; try_cast nullifies failures.""" | ||
| ctx = SessionContext() | ||
| batch = pa.RecordBatch.from_arrays( | ||
| [values, pa.array([1.0, 2.0, 3.0])], names=["v", "fl"] | ||
| ) | ||
| df = ctx.create_dataframe([[batch]]) | ||
|
|
||
| def test_arrow_cast_with_pyarrow_type(df): | ||
| df = df.select( | ||
| f.arrow_cast(column("b"), pa.float64()).alias("b_as_float"), | ||
| f.arrow_cast(column("b"), pa.int32()).alias("b_as_int"), | ||
| f.arrow_cast(column("b"), pa.string()).alias("b_as_str"), | ||
| result = df.select( | ||
| f.cast_to_type(column("v"), column("fl"), try_cast=try_cast).alias("c") | ||
| ).collect()[0] | ||
|
|
||
| assert result.column(0).to_pylist() == expected | ||
| assert result.column(0).type == pa.float64() | ||
|
|
||
|
|
||
| def test_with_metadata_round_trip(df): | ||
| df = df.select(f.with_metadata(column("b"), {"unit": "ms"}).alias("b")) | ||
| result = df.select(f.arrow_metadata(column("b"), "unit").alias("u")).collect_column( | ||
| "u" | ||
| ) | ||
| result = df.collect()[0] | ||
| assert result[0].as_py() == "ms" | ||
|
|
||
|
|
||
| def test_with_metadata_empty_dict_noop(df): | ||
| out = df.select(f.with_metadata(column("b"), {}).alias("b")).collect()[0] | ||
| assert out.column(0) == pa.array([4, 5, 6]) | ||
|
|
||
|
|
||
| assert result.column(0) == pa.array([4.0, 5.0, 6.0], type=pa.float64()) | ||
| assert result.column(1) == pa.array([4, 5, 6], type=pa.int32()) | ||
| assert result.column(2) == pa.array(["4", "5", "6"], type=pa.string()) | ||
| def test_with_metadata_empty_key_raises(): | ||
| with pytest.raises(ValueError, match="non-empty"): | ||
| f.with_metadata(column("b"), {"": "v"}) | ||
|
|
||
|
|
||
| def test_case(df): | ||
|
|
||
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.
Wouldn't it be better to have separate
cast_to_typeandtry_cast_to_typefunctions like in upstream? This way it would also be consistent with, e.g.,arrow_castandarrow_try_cast.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.
Good call. Split into separate
cast_to_typeandtry_cast_to_typefunctions in a8d3a5e, matching upstream and thearrow_cast/arrow_try_castpair.