Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 4.3k
ARROW-7858: [C++][Python] Support casting from ExtensionArray#6633
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
6891ba88473b24600d7b0ec0c42526750bebfdde93957f191fcde06de03c83fFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -26,9 +26,11 @@ | ||
| #include "arrow/array.h" | ||
| #include "arrow/buffer.h" | ||
| #include "arrow/extension_type.h" | ||
| #include "arrow/memory_pool.h" | ||
| #include "arrow/status.h" | ||
| #include "arrow/table.h" | ||
| #include "arrow/testing/extension_type.h" | ||
| #include "arrow/testing/gtest_common.h" | ||
| #include "arrow/testing/gtest_util.h" | ||
| #include "arrow/testing/random.h" | ||
| @@ -1480,5 +1482,54 @@ TYPED_TEST(TestDictionaryCast, OutTypeError) { | ||
| this->CheckPass(*plain_array, *dict_array, dict_array->type(), options); | ||
| }*/ | ||
| std::shared_ptr<Array> SmallintArrayFromJSON(const std::string& json_data) { | ||
| auto arr = ArrayFromJSON(int16(), json_data); | ||
| auto ext_data = arr->data()->Copy(); | ||
| ext_data->type = smallint(); | ||
| return MakeArray(ext_data); | ||
| } | ||
| TEST_F(TestCast, ExtensionTypeToIntDowncast) { | ||
bkietz marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| auto smallint = std::make_shared<SmallintType>(); | ||
| ASSERT_OK(RegisterExtensionType(smallint)); | ||
| CastOptions options; | ||
| options.allow_int_overflow = false; | ||
| std::shared_ptr<Array> result; | ||
| std::vector<bool> is_valid = {true, false, true, true, true}; | ||
| // Smallint(int16) to int16 | ||
| auto v0 = SmallintArrayFromJSON("[0, 100, 200, 1, 2]"); | ||
| CheckZeroCopy(*v0, int16()); | ||
| // Smallint(int16) to uint8, no overflow/underrun | ||
| auto v1 = SmallintArrayFromJSON("[0, 100, 200, 1, 2]"); | ||
| auto e1 = ArrayFromJSON(uint8(), "[0, 100, 200, 1, 2]"); | ||
| CheckPass(*v1, *e1, uint8(), options); | ||
| // Smallint(int16) to uint8, with overflow | ||
| auto v2 = SmallintArrayFromJSON("[0, null, 256, 1, 3]"); | ||
| auto e2 = ArrayFromJSON(uint8(), "[0, null, 0, 1, 3]"); | ||
| // allow overflow | ||
| options.allow_int_overflow = true; | ||
| CheckPass(*v2, *e2, uint8(), options); | ||
| // disallow overflow | ||
| options.allow_int_overflow = false; | ||
| ASSERT_RAISES(Invalid, Cast(&ctx_, *v2, uint8(), options, &result)); | ||
| // Smallint(int16) to uint8, with underflow | ||
| auto v3 = SmallintArrayFromJSON("[0, null, -1, 1, 0]"); | ||
| auto e3 = ArrayFromJSON(uint8(), "[0, null, 255, 1, 0]"); | ||
| // allow overflow | ||
| options.allow_int_overflow = true; | ||
| CheckPass(*v3, *e3, uint8(), options); | ||
| // disallow overflow | ||
| options.allow_int_overflow = false; | ||
| ASSERT_RAISES(Invalid, Cast(&ctx_, *v3, uint8(), options, &result)); | ||
| ASSERT_OK(UnregisterExtensionType("smallint")); | ||
| } | ||
| } // namespace compute | ||
| } // namespace arrow | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -382,11 +382,7 @@ void SleepFor(double seconds) { | ||
| // Extension types | ||
| bool UUIDType::ExtensionEquals(const ExtensionType& other) const { | ||
| const auto& other_ext = static_cast<const ExtensionType&>(other); | ||
| if (other_ext.extension_name() != this->extension_name()) { | ||
| return false; | ||
| } | ||
| return true; | ||
| return (other.extension_name() == this->extension_name()); | ||
| } | ||
Member 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 wonder if this should be the default implementation of
| ||
| std::shared_ptr<Array> UUIDType::MakeArray(std::shared_ptr<ArrayData> data) const { | ||
| @@ -423,4 +419,38 @@ std::shared_ptr<Array> ExampleUUID() { | ||
| return MakeArray(ext_data); | ||
| } | ||
| bool SmallintType::ExtensionEquals(const ExtensionType& other) const { | ||
| return (other.extension_name() == this->extension_name()); | ||
| } | ||
| std::shared_ptr<Array> SmallintType::MakeArray(std::shared_ptr<ArrayData> data) const { | ||
| DCHECK_EQ(data->type->id(), Type::EXTENSION); | ||
| DCHECK_EQ("smallint", static_cast<const ExtensionType&>(*data->type).extension_name()); | ||
| return std::make_shared<SmallintArray>(data); | ||
| } | ||
| Status SmallintType::Deserialize(std::shared_ptr<DataType> storage_type, | ||
| const std::string& serialized, | ||
| std::shared_ptr<DataType>* out) const { | ||
| if (serialized != "smallint") { | ||
| return Status::Invalid("Type identifier did not match"); | ||
| } | ||
| if (!storage_type->Equals(*int16())) { | ||
| return Status::Invalid("Invalid storage type for SmallintType"); | ||
| } | ||
| *out = std::make_shared<SmallintType>(); | ||
| return Status::OK(); | ||
| } | ||
| std::shared_ptr<DataType> smallint() { return std::make_shared<SmallintType>(); } | ||
| std::shared_ptr<Array> ExampleSmallint() { | ||
| auto storage_type = int16(); | ||
| auto ext_type = smallint(); | ||
| auto arr = ArrayFromJSON(storage_type, "[-32768, null, 1, 2, 3, 4, 32767]"); | ||
| auto ext_data = arr->data()->Copy(); | ||
| ext_data->type = ext_type; | ||
| return MakeArray(ext_data); | ||
| } | ||
| } // namespace arrow | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -24,6 +24,15 @@ | ||
| import pytest | ||
| class IntegerType(pa.PyExtensionType): | ||
| def __init__(self): | ||
| pa.PyExtensionType.__init__(self, pa.int64()) | ||
| def __reduce__(self): | ||
| return IntegerType, () | ||
| class UuidType(pa.PyExtensionType): | ||
| def __init__(self): | ||
| @@ -168,6 +177,42 @@ def test_ext_array_pickling(): | ||
| assert arr.storage.to_pylist() == [b"foo", b"bar"] | ||
| def test_cast_kernel_on_extension_arrays(): | ||
| # test array casting | ||
| storage = pa.array([1, 2, 3, 4], pa.int64()) | ||
| arr = pa.ExtensionArray.from_storage(IntegerType(), storage) | ||
| # test that no allocation happens during identity cast | ||
| allocated_before_cast = pa.total_allocated_bytes() | ||
| casted = arr.cast(pa.int64()) | ||
| assert pa.total_allocated_bytes() == allocated_before_cast | ||
| cases = [ | ||
| (pa.int64(), pa.Int64Array), | ||
| (pa.int32(), pa.Int32Array), | ||
| (pa.int16(), pa.Int16Array), | ||
| (pa.uint64(), pa.UInt64Array), | ||
| (pa.uint32(), pa.UInt32Array), | ||
| (pa.uint16(), pa.UInt16Array) | ||
| ] | ||
| for typ, klass in cases: | ||
| casted = arr.cast(typ) | ||
| assert casted.type == typ | ||
| assert isinstance(casted, klass) | ||
| # test chunked array casting | ||
| arr = pa.chunked_array([arr, arr]) | ||
| casted = arr.cast(pa.int16()) | ||
| assert casted.type == pa.int16() | ||
| assert isinstance(casted, pa.ChunkedArray) | ||
| def test_casting_to_extension_type_raises(): | ||
bkietz marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| arr = pa.array([1, 2, 3, 4], pa.int64()) | ||
| with pytest.raises(pa.ArrowNotImplementedError): | ||
| arr.cast(IntegerType()) | ||
| def example_batch(): | ||
| ty = ParamExtType(3) | ||
| storage = pa.array([b"foo", b"bar"], type=pa.binary(3)) | ||
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.
Does it allocate if the out_type and storage_type are the same?
Uh oh!
There was an error while loading. Please reload this page.
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.
Added tests on both C++ and Python side, seems like no allocation happens.