mirror of
https://github.com/apache/arrow.git
synced 2026-10-29 09:05:47 +08:00
add proper python -> arrow conversion
This commit is contained in:
1 parent
a502fa7292
commit
88a5cbdd98
2 files changed
+86
-13
No files matched your search
@@ -676,14 +676,13 @@ class StringConverter
|
||||
return value_converter_->AppendMultiple(obj, value_length); \
|
||||
}
|
||||
|
||||
template <typename TypeClass, NullCoding null_coding>
|
||||
class ListConverter
|
||||
: public TypedConverter<TypeClass, ListConverter<TypeClass, null_coding>,
|
||||
null_coding> {
|
||||
// Base class for ListConverter and FixedSizeListConverter (to have both work with CRTP)
|
||||
template <typename TypeClass, class Derived, NullCoding null_coding>
|
||||
class BaseListConverter : public TypedConverter<TypeClass, Derived, null_coding> {
|
||||
public:
|
||||
using BuilderType = typename TypeTraits<TypeClass>::BuilderType;
|
||||
|
||||
explicit ListConverter(bool from_pandas, bool strict_conversions)
|
||||
explicit BaseListConverter(bool from_pandas, bool strict_conversions)
|
||||
: from_pandas_(from_pandas), strict_conversions_(strict_conversions) {}
|
||||
|
||||
Status Init(ArrayBuilder* builder) {
|
||||
@@ -806,6 +805,59 @@ class ListConverter
|
||||
bool strict_conversions_;
|
||||
};
|
||||
|
||||
template <typename TypeClass, NullCoding null_coding>
|
||||
class ListConverter
|
||||
: public BaseListConverter<TypeClass, ListConverter<TypeClass, null_coding>,
|
||||
null_coding> {
|
||||
public:
|
||||
using BASE =
|
||||
BaseListConverter<TypeClass, ListConverter<TypeClass, null_coding>, null_coding>;
|
||||
using BASE::BASE;
|
||||
};
|
||||
|
||||
template <NullCoding null_coding>
|
||||
class FixedSizeListConverter
|
||||
: public BaseListConverter<FixedSizeListType, FixedSizeListConverter<null_coding>,
|
||||
null_coding> {
|
||||
public:
|
||||
using BASE = BaseListConverter<FixedSizeListType, FixedSizeListConverter<null_coding>,
|
||||
null_coding>;
|
||||
using BASE::BASE;
|
||||
|
||||
Status Init(ArrayBuilder* builder) {
|
||||
RETURN_NOT_OK(BASE::Init(builder));
|
||||
list_size_ = checked_pointer_cast<FixedSizeListType>(builder->type())->list_size();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status AppendItem(PyObject* obj) {
|
||||
// the same as BaseListConverter but with additional length checks
|
||||
RETURN_NOT_OK(this->typed_builder_->Append());
|
||||
if (PyArray_Check(obj)) {
|
||||
int64_t list_size = static_cast<int64_t>(PyArray_Size(obj));
|
||||
if (list_size != list_size_) {
|
||||
return Status::Invalid("Length of item not correct: expected ", list_size_,
|
||||
" but got array of size ", list_size);
|
||||
}
|
||||
return this->AppendNdarrayItem(obj);
|
||||
}
|
||||
if (!PySequence_Check(obj)) {
|
||||
return internal::InvalidType(obj,
|
||||
"was not a sequence or recognized null"
|
||||
" for conversion to list type");
|
||||
}
|
||||
int64_t list_size = static_cast<int64_t>(PySequence_Size(obj));
|
||||
if (list_size != list_size_) {
|
||||
return Status::Invalid("Length of item not correct: expected ", list_size_,
|
||||
" but got list of size ", list_size);
|
||||
}
|
||||
return this->value_converter_->AppendMultiple(obj, list_size);
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t list_size_;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Convert maps
|
||||
|
||||
@@ -1152,12 +1204,12 @@ Status GetConverter(const std::shared_ptr<DataType>& type, bool from_pandas,
|
||||
case Type::FIXED_SIZE_LIST:
|
||||
if (from_pandas) {
|
||||
*out = std::unique_ptr<SeqConverter>(
|
||||
new ListConverter<FixedSizeListType, NullCoding::PANDAS_SENTINELS>(
|
||||
from_pandas, strict_conversions));
|
||||
new FixedSizeListConverter<NullCoding::PANDAS_SENTINELS>(from_pandas,
|
||||
strict_conversions));
|
||||
} else {
|
||||
*out = std::unique_ptr<SeqConverter>(
|
||||
new ListConverter<FixedSizeListType, NullCoding::NONE_ONLY>(
|
||||
from_pandas, strict_conversions));
|
||||
new FixedSizeListConverter<NullCoding::NONE_ONLY>(from_pandas,
|
||||
strict_conversions));
|
||||
}
|
||||
return Status::OK();
|
||||
case Type::STRUCT:
|
||||
|
||||
@@ -148,8 +148,14 @@ def _as_dict_values(xs):
|
||||
return six.viewvalues(dct)
|
||||
|
||||
|
||||
def _as_array(xs):
|
||||
arr = np.empty(len(xs), dtype=object)
|
||||
arr[:] = xs
|
||||
return arr
|
||||
|
||||
|
||||
parametrize_with_iterable_types = pytest.mark.parametrize(
|
||||
"seq", [_as_list, _as_tuple, _as_deque, _as_dict_values])
|
||||
"seq", [_as_list, _as_tuple, _as_deque, _as_dict_values, _as_array])
|
||||
|
||||
|
||||
@parametrize_with_iterable_types
|
||||
@@ -236,14 +242,29 @@ def test_nested_arrays(seq):
|
||||
|
||||
@parametrize_with_iterable_types
|
||||
def test_nested_fixed_size_list(seq):
|
||||
# sequence of lists
|
||||
data = [[1, 2], [3, None], None]
|
||||
arr = pa.array(seq(data), type=pa.list_(pa.int32(), 2))
|
||||
arr = pa.array(seq(data), type=pa.list_(pa.int64(), 2))
|
||||
assert len(arr) == 3
|
||||
assert arr.null_count == 1
|
||||
assert arr.type == pa.list_(pa.int32(), 2)
|
||||
assert arr.type == pa.list_(pa.int64(), 2)
|
||||
assert arr.to_pylist() == data
|
||||
|
||||
# TODO check for invalid data (non-equal length nested lists)
|
||||
# sequence of numpy arrays
|
||||
data = [np.array([1, 2], dtype='int64'), np.array([3, 4], dtype='int64'),
|
||||
None]
|
||||
arr = pa.array(seq(data), type=pa.list_(pa.int64(), 2))
|
||||
assert len(arr) == 3
|
||||
assert arr.null_count == 1
|
||||
assert arr.type == pa.list_(pa.int64(), 2)
|
||||
assert arr.to_pylist() == [[1, 2], [3, 4], None]
|
||||
|
||||
# incorrect length of the lists or arrays
|
||||
data = [[1, 2, 4], [3, None], None]
|
||||
for data in [[[1, 2, 3]], [np.array([1, 2, 4], dtype='int64')]]:
|
||||
with pytest.raises(
|
||||
ValueError, match="Length of item not correct: expected 2"):
|
||||
pa.array(seq(data), type=pa.list_(pa.int64(), 2))
|
||||
|
||||
|
||||
@parametrize_with_iterable_types
|
||||
|
||||
Reference in new issue
Block a user