add proper python -> arrow conversion

This commit is contained in:
Joris Van den Bossche committed 2019-12-10 11:14:55 +01:00
1 parent a502fa7292
commit 88a5cbdd98
2 files changed
+86 -13

No files matched your search

+61 -9
View File
@@ -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:
+25 -4
View File
@@ -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