You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by we...@apache.org on 2018/11/10 02:45:32 UTC
[arrow] branch master updated: ARROW-3716: [R] Missing cases for
ChunkedArray conversion
This is an automated email from the ASF dual-hosted git repository.
wesm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/master by this push:
new f63b3e2 ARROW-3716: [R] Missing cases for ChunkedArray conversion
f63b3e2 is described below
commit f63b3e2794feb6ab941648647e50c497aef20a48
Author: Romain Francois <ro...@purrple.cat>
AuthorDate: Fri Nov 9 21:45:24 2018 -0500
ARROW-3716: [R] Missing cases for ChunkedArray conversion
*WIP* and will need some more testing of support of various chunked_array. There's also some room for less code duplication.
But I'll need that before I can follow up on the feather support.
Author: Romain Francois <ro...@purrple.cat>
Closes #2928 from romainfrancois/ARROW-3716/ChunkedArray and squashes the following commits:
f56891b0a <Romain Francois> More ChunkedArray tests ✅
02de7e046 <Romain Francois> 🔀 Array__as_vector and ChunkedArray__as_vector to use the same underlying code
bdb219158 <Romain Francois> ➕ ChunkedArray<DATE32>$as_vector()
cd5a5cedf <Romain Francois> ChunkedArray<DICTIONARY>$as_vector support
b5c19b473 <Romain Francois> refactor DictionaryArray_to_Vector
889d6ef50 <Romain Francois> ➕ ChunkedArray<BOOL>$as_vector() support
85ab699a1 <Romain Francois> ➕ ChunkedArray<String>$as_vector()
1683feb64 <Romain Francois> rebase simple_Array_to_Vector and simple_ChunkedArray_to_Vector on the new simple_Array_to_Vector_Slice
fd3cca208 <Romain Francois> move ChunkedArray__as_vector to array.cpp
---
r/R/ArrayData.R | 2 +-
r/R/RcppExports.R | 8 +-
r/src/RcppExports.cpp | 24 +-
r/src/array.cpp | 671 +++++++++++++++++++----------------
r/src/chunkedarray.cpp | 54 ---
r/src/message.cpp | 8 +-
r/tests/testthat/test-arraydata.R | 2 +-
r/tests/testthat/test-buffer.R | 2 +-
r/tests/testthat/test-bufferreader.R | 2 +-
r/tests/testthat/test-chunkedarray.R | 43 +++
r/tests/testthat/test-read-write.R | 2 +-
r/tests/testthat/test-schema.R | 2 +-
12 files changed, 426 insertions(+), 394 deletions(-)
diff --git a/r/R/ArrayData.R b/r/R/ArrayData.R
index 8075b9e..47b858d 100644
--- a/r/R/ArrayData.R
+++ b/r/R/ArrayData.R
@@ -24,6 +24,6 @@
length = function() ArrayData__get_length(self),
null_count = function() ArrayData__get_null_count(self),
offset = function() ArrayData__get_offset(self),
- buffers = function() map(ArrayData__buffers(self), construct, class = `arrow::Buffer`)
+ buffers = function() map(ArrayData__buffers(self), shared_ptr, class = `arrow::Buffer`)
)
)
diff --git a/r/R/RcppExports.R b/r/R/RcppExports.R
index 6304898..3811c5d 100644
--- a/r/R/RcppExports.R
+++ b/r/R/RcppExports.R
@@ -9,6 +9,10 @@ Array__as_vector <- function(array) {
.Call(`_arrow_Array__as_vector`, array)
}
+ChunkedArray__as_vector <- function(chunked_array) {
+ .Call(`_arrow_ChunkedArray__as_vector`, chunked_array)
+}
+
Array__Slice1 <- function(array, offset) {
.Call(`_arrow_Array__Slice1`, array, offset)
}
@@ -141,10 +145,6 @@ ChunkedArray__type <- function(chunked_array) {
.Call(`_arrow_ChunkedArray__type`, chunked_array)
}
-ChunkedArray__as_vector <- function(chunked_array) {
- .Call(`_arrow_ChunkedArray__as_vector`, chunked_array)
-}
-
ChunkArray__Slice1 <- function(chunked_array, offset) {
.Call(`_arrow_ChunkArray__Slice1`, chunked_array, offset)
}
diff --git a/r/src/RcppExports.cpp b/r/src/RcppExports.cpp
index 2b4338d..79ee497 100644
--- a/r/src/RcppExports.cpp
+++ b/r/src/RcppExports.cpp
@@ -28,6 +28,17 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
+// ChunkedArray__as_vector
+SEXP ChunkedArray__as_vector(const std::shared_ptr<arrow::ChunkedArray>& chunked_array);
+RcppExport SEXP _arrow_ChunkedArray__as_vector(SEXP chunked_arraySEXP) {
+BEGIN_RCPP
+ Rcpp::RObject rcpp_result_gen;
+ Rcpp::RNGScope rcpp_rngScope_gen;
+ Rcpp::traits::input_parameter< const std::shared_ptr<arrow::ChunkedArray>& >::type chunked_array(chunked_arraySEXP);
+ rcpp_result_gen = Rcpp::wrap(ChunkedArray__as_vector(chunked_array));
+ return rcpp_result_gen;
+END_RCPP
+}
// Array__Slice1
std::shared_ptr<arrow::Array> Array__Slice1(const std::shared_ptr<arrow::Array>& array, int offset);
RcppExport SEXP _arrow_Array__Slice1(SEXP arraySEXP, SEXP offsetSEXP) {
@@ -402,17 +413,6 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
-// ChunkedArray__as_vector
-SEXP ChunkedArray__as_vector(const std::shared_ptr<arrow::ChunkedArray>& chunked_array);
-RcppExport SEXP _arrow_ChunkedArray__as_vector(SEXP chunked_arraySEXP) {
-BEGIN_RCPP
- Rcpp::RObject rcpp_result_gen;
- Rcpp::RNGScope rcpp_rngScope_gen;
- Rcpp::traits::input_parameter< const std::shared_ptr<arrow::ChunkedArray>& >::type chunked_array(chunked_arraySEXP);
- rcpp_result_gen = Rcpp::wrap(ChunkedArray__as_vector(chunked_array));
- return rcpp_result_gen;
-END_RCPP
-}
// ChunkArray__Slice1
std::shared_ptr<arrow::ChunkedArray> ChunkArray__Slice1(const std::shared_ptr<arrow::ChunkedArray>& chunked_array, int offset);
RcppExport SEXP _arrow_ChunkArray__Slice1(SEXP chunked_arraySEXP, SEXP offsetSEXP) {
@@ -1821,6 +1821,7 @@ END_RCPP
static const R_CallMethodDef CallEntries[] = {
{"_arrow_Array__from_vector", (DL_FUNC) &_arrow_Array__from_vector, 1},
{"_arrow_Array__as_vector", (DL_FUNC) &_arrow_Array__as_vector, 1},
+ {"_arrow_ChunkedArray__as_vector", (DL_FUNC) &_arrow_ChunkedArray__as_vector, 1},
{"_arrow_Array__Slice1", (DL_FUNC) &_arrow_Array__Slice1, 2},
{"_arrow_Array__Slice2", (DL_FUNC) &_arrow_Array__Slice2, 3},
{"_arrow_Array__IsNull", (DL_FUNC) &_arrow_Array__IsNull, 2},
@@ -1854,7 +1855,6 @@ static const R_CallMethodDef CallEntries[] = {
{"_arrow_ChunkedArray__chunk", (DL_FUNC) &_arrow_ChunkedArray__chunk, 2},
{"_arrow_ChunkedArray__chunks", (DL_FUNC) &_arrow_ChunkedArray__chunks, 1},
{"_arrow_ChunkedArray__type", (DL_FUNC) &_arrow_ChunkedArray__type, 1},
- {"_arrow_ChunkedArray__as_vector", (DL_FUNC) &_arrow_ChunkedArray__as_vector, 1},
{"_arrow_ChunkArray__Slice1", (DL_FUNC) &_arrow_ChunkArray__Slice1, 2},
{"_arrow_ChunkArray__Slice2", (DL_FUNC) &_arrow_ChunkArray__Slice2, 3},
{"_arrow_ChunkedArray__from_list", (DL_FUNC) &_arrow_ChunkedArray__from_list, 1},
diff --git a/r/src/array.cpp b/r/src/array.cpp
index 820adf6..a83929c 100644
--- a/r/src/array.cpp
+++ b/r/src/array.cpp
@@ -506,167 +506,310 @@ std::shared_ptr<arrow::Array> Array__from_vector(SEXP x) {
namespace arrow {
namespace r {
+template <typename Converter, typename... Args>
+SEXP ArrayVector_To_Vector(int64_t n, const ArrayVector& arrays, Args... args) {
+ Converter converter(n, std::forward<Args>(args)...);
+
+ R_xlen_t k = 0;
+ for (const auto& array : arrays) {
+ auto n_chunk = array->length();
+ converter.Ingest(array, k, n_chunk);
+ k += n_chunk;
+ }
+ return converter.data;
+}
+
template <int RTYPE>
-inline SEXP simple_Array_to_Vector(const std::shared_ptr<arrow::Array>& array) {
- using value_type = typename Rcpp::Vector<RTYPE>::stored_type;
- auto n = array->length();
- auto null_count = array->null_count();
+struct Converter_SimpleArray {
+ using Vector = Rcpp::Vector<RTYPE>;
- // special cases
- if (n == 0) return Rcpp::Vector<RTYPE>(0);
- if (n == null_count) {
- return Rcpp::Vector<RTYPE>(n, default_value<RTYPE>());
- }
+ Converter_SimpleArray(R_xlen_t n) : data(no_init(n)) {}
+
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ using value_type = typename Vector::stored_type;
+ auto null_count = array->null_count();
+
+ if (n == null_count) {
+ std::fill_n(data.begin() + start, n, default_value<RTYPE>());
+ } else {
+ auto p_values = GetValuesSafely<value_type>(array->data(), 1, array->offset());
+ STOP_IF_NULL(p_values);
+
+ // first copy all the data
+ std::copy_n(p_values, n, data.begin() + start);
+
+ if (null_count) {
+ // then set the sentinel NA
+ arrow::internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
+ array->offset(), n);
- // first copy all the data
- auto p_values = GetValuesSafely<value_type>(array->data(), 1, array->offset());
- STOP_IF_NULL(p_values);
- Rcpp::Vector<RTYPE> vec(p_values, p_values + n);
-
- // then set the sentinel NA
- if (array->null_count() && RTYPE != RAWSXP) {
- // TODO: not sure what to do with RAWSXP since
- // R raw vector do not have a concept of missing data
-
- arrow::internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
- array->offset(), n);
- for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
- if (bitmap_reader.IsNotSet()) {
- vec[i] = Rcpp::Vector<RTYPE>::get_na();
+ for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
+ if (bitmap_reader.IsNotSet()) {
+ data[i + start] = default_value<RTYPE>();
+ }
+ }
}
}
}
- return vec;
-}
+ Vector data;
+};
-inline SEXP StringArray_to_Vector(const std::shared_ptr<arrow::Array>& array) {
- auto n = array->length();
- auto null_count = array->null_count();
+struct Converter_String {
+ Converter_String(R_xlen_t n) : data(n) {}
- // special cases
- if (n == 0) return Rcpp::CharacterVector_(0);
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ auto null_count = array->null_count();
- // only NA
- if (null_count == n) {
- return StringVector_(n, NA_STRING);
- }
+ if (null_count == n) {
+ std::fill_n(data.begin(), n, NA_STRING);
+ } else {
+ auto p_offset = GetValuesSafely<int32_t>(array->data(), 1, array->offset());
+ STOP_IF_NULL(p_offset);
+ auto p_data = GetValuesSafely<char>(array->data(), 2, *p_offset);
+ if (!p_data) {
+ // There is an offset buffer, but the data buffer is null
+ // There is at least one value in the array and not all the values are null
+ // That means all values are empty strings so there is nothing to do
+ return;
+ }
- Rcpp::CharacterVector res(no_init(n));
- auto p_offset = GetValuesSafely<int32_t>(array->data(), 1, array->offset());
- STOP_IF_NULL(p_offset);
+ if (null_count) {
+ // need to watch for nulls
+ arrow::internal::BitmapReader null_reader(array->null_bitmap_data(),
+ array->offset(), n);
+ for (int i = 0; i < n; i++, null_reader.Next()) {
+ if (null_reader.IsSet()) {
+ auto diff = p_offset[i + 1] - p_offset[i];
+ SET_STRING_ELT(data, start + i, Rf_mkCharLenCE(p_data, diff, CE_UTF8));
+ p_data += diff;
+ } else {
+ SET_STRING_ELT(data, start + i, NA_STRING);
+ }
+ }
- auto p_data = GetValuesSafely<char>(array->data(), 2, *p_offset);
- if (!p_data) {
- // There is an offset buffer, but the data buffer is null
- // There is at least one value in the array and not all the values are null
- // That means all values are empty strings so we can just return `res`
- return res;
- }
- if (null_count) {
- // need to watch for nulls
- arrow::internal::BitmapReader null_reader(array->null_bitmap_data(), array->offset(),
- n);
- for (int i = 0; i < n; i++, null_reader.Next()) {
- if (null_reader.IsSet()) {
- auto diff = p_offset[i + 1] - p_offset[i];
- SET_STRING_ELT(res, i, Rf_mkCharLenCE(p_data, diff, CE_UTF8));
- p_data += diff;
} else {
- SET_STRING_ELT(res, i, NA_STRING);
+ // no need to check for nulls
+ // TODO: altrep mark this as no na
+ for (int i = 0; i < n; i++) {
+ auto diff = p_offset[i + 1] - p_offset[i];
+ SET_STRING_ELT(data, start + i, Rf_mkCharLenCE(p_data, diff, CE_UTF8));
+ p_data += diff;
+ }
}
}
+ }
- } else {
- // no need to check for nulls
- // TODO: altrep mark this as no na
- for (int i = 0; i < n; i++) {
- auto diff = p_offset[i + 1] - p_offset[i];
- SET_STRING_ELT(res, i, Rf_mkCharLenCE(p_data, diff, CE_UTF8));
- p_data += diff;
+ CharacterVector data;
+};
+
+struct Converter_Boolean {
+ Converter_Boolean(R_xlen_t n) : data(n) {}
+
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ auto null_count = array->null_count();
+
+ if (n == null_count) {
+ std::fill_n(data.begin() + start, n, NA_LOGICAL);
+ } else {
+ // process the data
+ auto p_data = GetValuesSafely<uint8_t>(array->data(), 1, 0);
+ STOP_IF_NULL(p_data);
+
+ arrow::internal::BitmapReader data_reader(p_data, array->offset(), n);
+ for (size_t i = 0; i < n; i++, data_reader.Next()) {
+ data[start + i] = data_reader.IsSet();
+ }
+
+ // then the null bitmap if needed
+ if (null_count) {
+ arrow::internal::BitmapReader null_reader(array->null_bitmap()->data(),
+ array->offset(), n);
+ for (size_t i = 0; i < n; i++, null_reader.Next()) {
+ if (null_reader.IsNotSet()) {
+ data[start + i] = NA_LOGICAL;
+ }
+ }
+ }
}
}
- return res;
-}
-
-inline SEXP BooleanArray_to_Vector(const std::shared_ptr<arrow::Array>& array) {
- auto n = array->length();
- auto null_count = array->null_count();
+ LogicalVector data;
+};
- if (n == 0) {
- return LogicalVector(0);
+template <typename Type>
+struct Converter_Dictionary_Int32Indices {
+ Converter_Dictionary_Int32Indices(R_xlen_t n, const std::shared_ptr<arrow::Array>& dict,
+ bool ordered)
+ : data(no_init(n)) {
+ data.attr("levels") = ArrayVector_To_Vector<Converter_String>(dict->length(), {dict});
+ if (ordered) {
+ data.attr("class") = CharacterVector::create("ordered", "factor");
+ } else {
+ data.attr("class") = "factor";
+ }
}
- if (n == null_count) {
- return LogicalVector(n, NA_LOGICAL);
+
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ DictionaryArray* dict_array = static_cast<DictionaryArray*>(array.get());
+ using value_type = typename arrow::TypeTraits<Type>::ArrayType::value_type;
+ auto null_count = array->null_count();
+
+ if (n == null_count) {
+ std::fill_n(data.begin() + start, n, NA_INTEGER);
+ } else {
+ std::shared_ptr<Array> indices = dict_array->indices();
+ auto p_array = GetValuesSafely<value_type>(indices->data(), 1, indices->offset());
+ STOP_IF_NULL(p_array);
+
+ if (array->null_count()) {
+ arrow::internal::BitmapReader bitmap_reader(indices->null_bitmap()->data(),
+ indices->offset(), n);
+ for (size_t i = 0; i < n; i++, bitmap_reader.Next(), ++p_array) {
+ data[start + i] =
+ bitmap_reader.IsNotSet() ? NA_INTEGER : (static_cast<int>(*p_array) + 1);
+ }
+ } else {
+ std::transform(
+ p_array, p_array + n, data.begin() + start,
+ [](const value_type value) { return static_cast<int>(value) + 1; });
+ }
+ }
}
- LogicalVector vec = no_init(n);
+ IntegerVector data;
+};
- // process the data
- auto p_data = GetValuesSafely<uint8_t>(array->data(), 1, 0);
- STOP_IF_NULL(p_data);
- arrow::internal::BitmapReader data_reader(p_data, array->offset(), n);
- for (size_t i = 0; i < n; i++, data_reader.Next()) {
- vec[i] = data_reader.IsSet();
+struct Converter_Date64 {
+ Converter_Date64(R_xlen_t n) : data(n) {
+ data.attr("class") = CharacterVector::create("POSIXct", "POSIXt");
}
- // then the null bitmap if needed
- if (array->null_count()) {
- arrow::internal::BitmapReader null_reader(array->null_bitmap()->data(),
- array->offset(), n);
- for (size_t i = 0; i < n; i++, null_reader.Next()) {
- if (null_reader.IsNotSet()) {
- vec[i] = LogicalVector::get_na();
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ auto null_count = array->null_count();
+ if (null_count == n) {
+ std::fill_n(data.begin() + start, n, NA_REAL);
+ } else {
+ auto p_values = GetValuesSafely<int64_t>(array->data(), 1, array->offset());
+ STOP_IF_NULL(p_values);
+ auto p_vec = data.begin() + start;
+
+ // convert DATE64 milliseconds to R seconds (stored as double)
+ auto seconds = [](int64_t ms) { return static_cast<double>(ms / 1000); };
+
+ if (null_count) {
+ arrow::internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
+ array->offset(), n);
+ for (size_t i = 0; i < n; i++, bitmap_reader.Next(), ++p_vec, ++p_values) {
+ *p_vec = bitmap_reader.IsSet() ? seconds(*p_values) : NA_REAL;
+ }
+ } else {
+ std::transform(p_values, p_values + n, p_vec, seconds);
}
}
}
- return vec;
-}
+ NumericVector data;
+};
-template <typename Type>
-inline SEXP DictionaryArrayInt32Indices_to_Vector(
- const std::shared_ptr<arrow::Array>& array, const std::shared_ptr<arrow::Array>& dict,
- bool ordered) {
- using value_type = typename arrow::TypeTraits<Type>::ArrayType::value_type;
+template <int RTYPE, typename Type>
+struct Converter_Promotion {
+ using r_stored_type = typename Rcpp::Vector<RTYPE>::stored_type;
+ using value_type = typename TypeTraits<Type>::ArrayType::value_type;
- size_t n = array->length();
- IntegerVector vec(no_init(n));
- vec.attr("levels") = StringArray_to_Vector(dict);
- if (ordered) {
- vec.attr("class") = CharacterVector::create("ordered", "factor");
- } else {
- vec.attr("class") = "factor";
+ Converter_Promotion(R_xlen_t n) : data(no_init(n)) {}
+
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ auto null_count = array->null_count();
+ if (null_count == n) {
+ std::fill_n(data.begin() + start, n, default_value<RTYPE>());
+ } else {
+ auto p_values = GetValuesSafely<value_type>(array->data(), 1, array->offset());
+ STOP_IF_NULL(start);
+
+ auto value_convert = [](value_type value) {
+ return static_cast<r_stored_type>(value);
+ };
+ if (null_count) {
+ internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
+ array->offset(), n);
+ for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
+ data[start + i] = bitmap_reader.IsNotSet() ? Rcpp::Vector<RTYPE>::get_na()
+ : value_convert(p_values[i]);
+ }
+ } else {
+ std::transform(p_values, p_values + n, data.begin(), value_convert);
+ }
+ }
}
- if (n == 0) {
- return vec;
+ Rcpp::Vector<RTYPE> data;
+};
+
+template <typename value_type>
+struct Converter_Time {
+ Converter_Time(int64_t n, int32_t multiplier)
+ : data(no_init(n)), multiplier_(multiplier) {
+ data.attr("class") = CharacterVector::create("hms", "difftime");
+ data.attr("units") = "secs";
}
- auto null_count = array->null_count();
- if (n == null_count) {
- std::fill(vec.begin(), vec.end(), NA_INTEGER);
- return vec;
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ auto null_count = array->null_count();
+ if (n == null_count) {
+ std::fill_n(data.begin() + start, n, NA_REAL);
+ } else {
+ auto p_values = GetValuesSafely<value_type>(array->data(), 1, array->offset());
+ STOP_IF_NULL(p_values);
+ auto p_vec = data.begin() + start;
+ auto convert = [this](value_type value) {
+ return static_cast<double>(value) / multiplier_;
+ };
+ if (null_count) {
+ arrow::internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
+ array->offset(), n);
+ for (size_t i = 0; i < n; i++, bitmap_reader.Next(), ++p_vec, ++p_values) {
+ *p_vec = bitmap_reader.IsSet() ? convert(*p_values) : NA_REAL;
+ }
+ } else {
+ std::transform(p_values, p_values + n, p_vec, convert);
+ }
+ }
}
- auto p_array = GetValuesSafely<value_type>(array->data(), 1, array->offset());
- STOP_IF_NULL(p_array);
+ NumericVector data;
+ int32_t multiplier_;
+};
+
+struct Converter_Int64 {
+ Converter_Int64(R_xlen_t n) : data(no_init(n)) { data.attr("class") = "integer64"; }
- if (array->null_count()) {
- arrow::internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
- array->offset(), n);
- for (size_t i = 0; i < n; i++, bitmap_reader.Next(), ++p_array) {
- vec[i] = bitmap_reader.IsNotSet() ? NA_INTEGER : (static_cast<int>(*p_array) + 1);
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ auto null_count = array->null_count();
+ if (null_count == n) {
+ std::fill_n(data.begin() + start, n, NA_INT64);
+ } else {
+ auto p_values = GetValuesSafely<int64_t>(array->data(), 1, array->offset());
+ STOP_IF_NULL(p_values);
+ auto p_vec = reinterpret_cast<int64_t*>(data.begin()) + start;
+
+ if (array->null_count()) {
+ internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
+ array->offset(), n);
+ for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
+ p_vec[i] = bitmap_reader.IsNotSet() ? NA_INT64 : p_values[i];
+ }
+ } else {
+ std::copy_n(p_values, n, p_vec);
+ }
}
- } else {
- std::transform(p_array, p_array + n, vec.begin(),
- [](const value_type value) { return static_cast<int>(value) + 1; });
}
- return vec;
-}
-SEXP DictionaryArray_to_Vector(arrow::DictionaryArray* dict_array) {
+ NumericVector data;
+};
+
+SEXP DictionaryArrays_to_Vector(int64_t n, const ArrayVector& arrays) {
+ DictionaryArray* dict_array = static_cast<DictionaryArray*>(arrays[0].get());
auto dict = dict_array->dictionary();
auto indices = dict_array->indices();
@@ -677,20 +820,25 @@ SEXP DictionaryArray_to_Vector(arrow::DictionaryArray* dict_array) {
bool ordered = dict_array->dict_type()->ordered();
switch (indices->type_id()) {
case Type::UINT8:
- return DictionaryArrayInt32Indices_to_Vector<arrow::UInt8Type>(indices, dict,
- ordered);
+ return ArrayVector_To_Vector<Converter_Dictionary_Int32Indices<arrow::UInt8Type>>(
+ n, arrays, dict, ordered);
+
case Type::INT8:
- return DictionaryArrayInt32Indices_to_Vector<arrow::Int8Type>(indices, dict,
- ordered);
+ return ArrayVector_To_Vector<Converter_Dictionary_Int32Indices<arrow::Int8Type>>(
+ n, arrays, dict, ordered);
+
case Type::UINT16:
- return DictionaryArrayInt32Indices_to_Vector<arrow::UInt16Type>(indices, dict,
- ordered);
+ return ArrayVector_To_Vector<Converter_Dictionary_Int32Indices<arrow::UInt16Type>>(
+ n, arrays, dict, ordered);
+
case Type::INT16:
- return DictionaryArrayInt32Indices_to_Vector<arrow::Int16Type>(indices, dict,
- ordered);
+ return ArrayVector_To_Vector<Converter_Dictionary_Int32Indices<arrow::Int16Type>>(
+ n, arrays, dict, ordered);
+
case Type::INT32:
- return DictionaryArrayInt32Indices_to_Vector<arrow::Int32Type>(indices, dict,
- ordered);
+ return ArrayVector_To_Vector<Converter_Dictionary_Int32Indices<arrow::Int32Type>>(
+ n, arrays, dict, ordered);
+
default:
stop("Cannot convert Dictionary Array of type `%s` to R",
dict_array->type()->ToString());
@@ -698,241 +846,136 @@ SEXP DictionaryArray_to_Vector(arrow::DictionaryArray* dict_array) {
return R_NilValue;
}
-SEXP Date32Array_to_Vector(const std::shared_ptr<arrow::Array>& array) {
- IntegerVector out(simple_Array_to_Vector<INTSXP>(array));
+SEXP Date32ArrayVector_to_Vector(int64_t n, const ArrayVector& arrays) {
+ IntegerVector out(
+ arrow::r::ArrayVector_To_Vector<Converter_SimpleArray<INTSXP>>(n, arrays));
out.attr("class") = "Date";
return out;
}
-SEXP Date64Array_to_Vector(const std::shared_ptr<arrow::Array> array) {
- auto n = array->length();
- NumericVector vec(no_init(n));
- vec.attr("class") = CharacterVector::create("POSIXct", "POSIXt");
- if (n == 0) {
- return vec;
- }
- auto null_count = array->null_count();
- if (null_count == n) {
- std::fill(vec.begin(), vec.end(), NA_REAL);
- return vec;
- }
- auto p_values = GetValuesSafely<int64_t>(array->data(), 1, array->offset());
- STOP_IF_NULL(p_values);
- auto p_vec = vec.begin();
-
- if (null_count) {
- arrow::internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
- array->offset(), n);
- for (size_t i = 0; i < n; i++, bitmap_reader.Next(), ++p_vec, ++p_values) {
- *p_vec = bitmap_reader.IsSet() ? static_cast<double>(*p_values / 1000) : NA_REAL;
- }
- } else {
- std::transform(p_values, p_values + n, vec.begin(),
- [](int64_t value) { return static_cast<double>(value / 1000); });
- }
-
- return vec;
-}
-
-template <int RTYPE, typename Type>
-SEXP promotion_Array_to_Vector(const std::shared_ptr<Array>& array) {
- using r_stored_type = typename Rcpp::Vector<RTYPE>::stored_type;
- using value_type = typename TypeTraits<Type>::ArrayType::value_type;
-
- auto n = array->length();
- Rcpp::Vector<RTYPE> vec(no_init(n));
- if (n == 0) {
- return vec;
- }
- auto null_count = array->null_count();
- if (null_count == n) {
- std::fill(vec.begin(), vec.end(), NA_REAL);
- return vec;
- }
-
- auto start = GetValuesSafely<value_type>(array->data(), 1, array->offset());
- STOP_IF_NULL(start);
-
- if (null_count) {
- internal::BitmapReader bitmap_reader(array->null_bitmap()->data(), array->offset(),
- n);
- for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
- vec[i] = bitmap_reader.IsNotSet() ? Rcpp::Vector<RTYPE>::get_na()
- : static_cast<r_stored_type>(start[i]);
- }
- } else {
- std::transform(start, start + n, vec.begin(),
- [](value_type x) { return static_cast<r_stored_type>(x); });
- }
-
- return vec;
-}
-
-SEXP Int64Array(const std::shared_ptr<Array>& array) {
- auto n = array->length();
- NumericVector vec(no_init(n));
- vec.attr("class") = "integer64";
- if (n == 0) {
- return vec;
- }
- auto null_count = array->null_count();
- if (null_count == n) {
- std::fill(vec.begin(), vec.end(), NA_REAL);
- return vec;
- }
- auto p_values = GetValuesSafely<int64_t>(array->data(), 1, array->offset());
- STOP_IF_NULL(p_values);
- auto p_vec = reinterpret_cast<int64_t*>(vec.begin());
-
- if (array->null_count()) {
- internal::BitmapReader bitmap_reader(array->null_bitmap()->data(), array->offset(),
- n);
- for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
- p_vec[i] = bitmap_reader.IsNotSet() ? NA_INT64 : p_values[i];
- }
- } else {
- std::copy_n(p_values, n, p_vec);
- }
+struct Converter_Decimal {
+ Converter_Decimal(R_xlen_t n) : data(no_init(n)) {}
- return vec;
-}
-
-template <typename value_type>
-SEXP TimeArray_to_Vector(const std::shared_ptr<Array>& array, int32_t multiplier) {
- auto n = array->length();
- NumericVector vec(no_init(n));
- auto null_count = array->null_count();
- vec.attr("class") = CharacterVector::create("hms", "difftime");
- vec.attr("units") = "secs";
- if (n == 0) {
- return vec;
- }
- auto p_values = GetValuesSafely<value_type>(array->data(), 1, array->offset());
- STOP_IF_NULL(p_values);
- auto p_vec = vec.begin();
-
- if (null_count) {
- arrow::internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
- array->offset(), n);
- for (size_t i = 0; i < n; i++, bitmap_reader.Next(), ++p_vec, ++p_values) {
- *p_vec =
- bitmap_reader.IsSet() ? (static_cast<double>(*p_values) / multiplier) : NA_REAL;
- }
- } else {
- std::transform(p_values, p_values + n, vec.begin(), [multiplier](value_type value) {
- return static_cast<double>(value) / multiplier;
- });
- }
- return vec;
-}
-
-SEXP DecimalArray(const std::shared_ptr<Array>& array) {
- auto n = array->length();
- NumericVector vec(no_init(n));
-
- if (n == 0) return vec;
-
- auto null_count = array->null_count();
- if (null_count == n) {
- std::fill(vec.begin(), vec.end(), NA_REAL);
- return vec;
- }
-
- auto p_vec = reinterpret_cast<double*>(vec.begin());
- const auto& decimals_arr =
- internal::checked_cast<const arrow::Decimal128Array&>(*array);
-
- if (array->null_count()) {
- internal::BitmapReader bitmap_reader(array->null_bitmap()->data(), array->offset(),
- n);
-
- for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
- p_vec[i] = bitmap_reader.IsNotSet()
- ? NA_REAL
- : std::stod(decimals_arr.FormatValue(i).c_str());
- }
- } else {
- for (size_t i = 0; i < n; i++) {
- p_vec[i] = std::stod(decimals_arr.FormatValue(i).c_str());
+ void Ingest(const std::shared_ptr<arrow::Array>& array, R_xlen_t start, R_xlen_t n) {
+ auto null_count = array->null_count();
+ if (n == null_count) {
+ std::fill_n(data.begin() + start, n, NA_REAL);
+ } else {
+ auto p_vec = reinterpret_cast<double*>(data.begin()) + start;
+ const auto& decimals_arr =
+ internal::checked_cast<const arrow::Decimal128Array&>(*array);
+
+ if (array->null_count()) {
+ internal::BitmapReader bitmap_reader(array->null_bitmap()->data(),
+ array->offset(), n);
+
+ for (size_t i = 0; i < n; i++, bitmap_reader.Next()) {
+ p_vec[i] = bitmap_reader.IsNotSet()
+ ? NA_REAL
+ : std::stod(decimals_arr.FormatValue(i).c_str());
+ }
+ } else {
+ for (size_t i = 0; i < n; i++) {
+ p_vec[i] = std::stod(decimals_arr.FormatValue(i).c_str());
+ }
+ }
}
}
- return vec;
-}
+ NumericVector data;
+};
} // namespace r
} // namespace arrow
-// [[Rcpp::export]]
-SEXP Array__as_vector(const std::shared_ptr<arrow::Array>& array) {
+SEXP ArrayVector__as_vector(int64_t n, const ArrayVector& arrays) {
using namespace arrow::r;
- switch (array->type_id()) {
+ switch (arrays[0]->type_id()) {
// direct support
case Type::INT8:
- return simple_Array_to_Vector<RAWSXP>(array);
+ return ArrayVector_To_Vector<Converter_SimpleArray<RAWSXP>>(n, arrays);
case Type::INT32:
- return simple_Array_to_Vector<INTSXP>(array);
+ return ArrayVector_To_Vector<Converter_SimpleArray<INTSXP>>(n, arrays);
case Type::DOUBLE:
- return simple_Array_to_Vector<REALSXP>(array);
+ return ArrayVector_To_Vector<Converter_SimpleArray<REALSXP>>(n, arrays);
// need to handle 1-bit case
case Type::BOOL:
- return BooleanArray_to_Vector(array);
+ return ArrayVector_To_Vector<Converter_Boolean>(n, arrays);
- // handle memory dense strings
+ // handle memory dense strings
case Type::STRING:
- return StringArray_to_Vector(array);
+ return ArrayVector_To_Vector<Converter_String>(n, arrays);
case Type::DICTIONARY:
- return DictionaryArray_to_Vector(static_cast<arrow::DictionaryArray*>(array.get()));
+ return DictionaryArrays_to_Vector(n, arrays);
case Type::DATE32:
- return Date32Array_to_Vector(array);
+ return Date32ArrayVector_to_Vector(n, arrays);
case Type::DATE64:
- return Date64Array_to_Vector(array);
+ return ArrayVector_To_Vector<Converter_Date64>(n, arrays);
- // promotions to integer vector
+ // promotions to integer vector
case Type::UINT8:
- return arrow::r::promotion_Array_to_Vector<INTSXP, arrow::UInt8Type>(array);
+ return ArrayVector_To_Vector<Converter_Promotion<INTSXP, arrow::UInt8Type>>(n,
+ arrays);
case Type::INT16:
- return arrow::r::promotion_Array_to_Vector<INTSXP, arrow::Int16Type>(array);
+ return ArrayVector_To_Vector<Converter_Promotion<INTSXP, arrow::Int16Type>>(n,
+ arrays);
case Type::UINT16:
- return arrow::r::promotion_Array_to_Vector<INTSXP, arrow::UInt16Type>(array);
+ return ArrayVector_To_Vector<Converter_Promotion<INTSXP, arrow::UInt16Type>>(
+ n, arrays);
- // promotions to numeric vector
+ // promotions to numeric vector
case Type::UINT32:
- return arrow::r::promotion_Array_to_Vector<REALSXP, arrow::UInt32Type>(array);
+ return ArrayVector_To_Vector<Converter_Promotion<REALSXP, arrow::UInt32Type>>(
+ n, arrays);
case Type::HALF_FLOAT:
- return arrow::r::promotion_Array_to_Vector<REALSXP, arrow::UInt32Type>(array);
+ return ArrayVector_To_Vector<Converter_Promotion<REALSXP, arrow::UInt32Type>>(
+ n, arrays);
case Type::FLOAT:
- return arrow::r::promotion_Array_to_Vector<REALSXP, arrow::UInt32Type>(array);
+ return ArrayVector_To_Vector<Converter_Promotion<REALSXP, arrow::UInt32Type>>(
+ n, arrays);
- // time32 ane time64
+ // time32 ane time64
case Type::TIME32:
- return arrow::r::TimeArray_to_Vector<int32_t>(
- array, static_cast<TimeType*>(array->type().get())->unit() == TimeUnit::SECOND
- ? 1
- : 1000);
+ return ArrayVector_To_Vector<Converter_Time<int32_t>>(
+ n, arrays,
+ static_cast<TimeType*>(arrays[0]->type().get())->unit() == TimeUnit::SECOND
+ ? 1
+ : 1000);
+
case Type::TIME64:
- return arrow::r::TimeArray_to_Vector<int64_t>(
- array, static_cast<TimeType*>(array->type().get())->unit() == TimeUnit::MICRO
- ? 1000000
- : 1000000000);
+ return ArrayVector_To_Vector<Converter_Time<int64_t>>(
+ n, arrays,
+ static_cast<TimeType*>(arrays[0]->type().get())->unit() == TimeUnit::MICRO
+ ? 1000000
+ : 1000000000);
case Type::INT64:
- return arrow::r::Int64Array(array);
+ return ArrayVector_To_Vector<Converter_Int64>(n, arrays);
case Type::DECIMAL:
- return arrow::r::DecimalArray(array);
+ ArrayVector_To_Vector<Converter_Decimal>(n, arrays);
default:
break;
}
- stop(tfm::format("cannot handle Array of type %s", array->type()->name()));
+ stop(tfm::format("cannot handle Array of type %s", arrays[0]->type()->name()));
return R_NilValue;
}
// [[Rcpp::export]]
+SEXP Array__as_vector(const std::shared_ptr<arrow::Array>& array) {
+ return ArrayVector__as_vector(array->length(), {array});
+}
+
+// [[Rcpp::export]]
+SEXP ChunkedArray__as_vector(const std::shared_ptr<arrow::ChunkedArray>& chunked_array) {
+ return ArrayVector__as_vector(chunked_array->length(), chunked_array->chunks());
+}
+
+// [[Rcpp::export]]
std::shared_ptr<arrow::Array> Array__Slice1(const std::shared_ptr<arrow::Array>& array,
int offset) {
return array->Slice(offset);
diff --git a/r/src/chunkedarray.cpp b/r/src/chunkedarray.cpp
index ffb763f..06b32d3 100644
--- a/r/src/chunkedarray.cpp
+++ b/r/src/chunkedarray.cpp
@@ -20,43 +20,6 @@
using namespace Rcpp;
using namespace arrow;
-template <int RTYPE>
-inline SEXP simple_ChunkedArray_to_Vector(
- const std::shared_ptr<arrow::ChunkedArray>& chunked_array) {
- using value_type = typename Rcpp::Vector<RTYPE>::stored_type;
- Rcpp::Vector<RTYPE> out = no_init(chunked_array->length());
- auto p = out.begin();
-
- int k = 0;
- for (int i = 0; i < chunked_array->num_chunks(); i++) {
- auto chunk = chunked_array->chunk(i);
- auto n = chunk->length();
-
- // copy the data
- auto q = p;
- auto p_chunk =
- arrow::r::GetValuesSafely<value_type>(chunk->data(), 1, chunk->offset());
- STOP_IF_NULL(p_chunk);
- p = std::copy_n(p_chunk, n, p);
-
- // set NA using the bitmap
- auto bitmap_data = chunk->null_bitmap();
- if (bitmap_data && RTYPE != RAWSXP) {
- arrow::internal::BitmapReader bitmap_reader(bitmap_data->data(), chunk->offset(),
- n);
-
- for (int j = 0; j < n; j++, bitmap_reader.Next()) {
- if (bitmap_reader.IsNotSet()) {
- q[k + j] = Rcpp::Vector<RTYPE>::get_na();
- }
- }
- }
-
- k += chunk->length();
- }
- return out;
-}
-
// [[Rcpp::export]]
int ChunkedArray__length(const std::shared_ptr<arrow::ChunkedArray>& chunked_array) {
return chunked_array->length();
@@ -90,23 +53,6 @@ std::shared_ptr<arrow::DataType> ChunkedArray__type(
}
// [[Rcpp::export]]
-SEXP ChunkedArray__as_vector(const std::shared_ptr<arrow::ChunkedArray>& chunked_array) {
- switch (chunked_array->type()->id()) {
- case Type::INT8:
- return simple_ChunkedArray_to_Vector<RAWSXP>(chunked_array);
- case Type::INT32:
- return simple_ChunkedArray_to_Vector<INTSXP>(chunked_array);
- case Type::DOUBLE:
- return simple_ChunkedArray_to_Vector<REALSXP>(chunked_array);
- default:
- break;
- }
-
- stop(tfm::format("cannot handle Array of type %d", chunked_array->type()->id()));
- return R_NilValue;
-}
-
-// [[Rcpp::export]]
std::shared_ptr<arrow::ChunkedArray> ChunkArray__Slice1(
const std::shared_ptr<arrow::ChunkedArray>& chunked_array, int offset) {
return chunked_array->Slice(offset);
diff --git a/r/src/message.cpp b/r/src/message.cpp
index 03504b1..9cfa24b 100644
--- a/r/src/message.cpp
+++ b/r/src/message.cpp
@@ -58,7 +58,7 @@ std::shared_ptr<arrow::RecordBatch> ipc___ReadRecordBatch__Message__Schema(
const std::unique_ptr<arrow::ipc::Message>& message,
const std::shared_ptr<arrow::Schema>& schema) {
std::shared_ptr<arrow::RecordBatch> batch;
- R_ERROR_NOT_OK(arrow::ipc::ReadRecordBatch(*message, schema, &batch));
+ STOP_IF_NOT_OK(arrow::ipc::ReadRecordBatch(*message, schema, &batch));
return batch;
}
@@ -66,7 +66,7 @@ std::shared_ptr<arrow::RecordBatch> ipc___ReadRecordBatch__Message__Schema(
std::shared_ptr<arrow::Schema> ipc___ReadSchema_InputStream(
const std::shared_ptr<arrow::io::InputStream>& stream) {
std::shared_ptr<arrow::Schema> schema;
- R_ERROR_NOT_OK(arrow::ipc::ReadSchema(stream.get(), &schema));
+ STOP_IF_NOT_OK(arrow::ipc::ReadSchema(stream.get(), &schema));
return schema;
}
@@ -82,7 +82,7 @@ std::unique_ptr<arrow::ipc::MessageReader> ipc___MessageReader__Open(
std::unique_ptr<arrow::ipc::Message> ipc___MessageReader__ReadNextMessage(
const std::unique_ptr<arrow::ipc::MessageReader>& reader) {
std::unique_ptr<arrow::ipc::Message> message;
- R_ERROR_NOT_OK(reader->ReadNextMessage(&message));
+ STOP_IF_NOT_OK(reader->ReadNextMessage(&message));
return message;
}
@@ -90,6 +90,6 @@ std::unique_ptr<arrow::ipc::Message> ipc___MessageReader__ReadNextMessage(
std::unique_ptr<arrow::ipc::Message> ipc___ReadMessage(
const std::shared_ptr<arrow::io::InputStream>& stream) {
std::unique_ptr<arrow::ipc::Message> message;
- R_ERROR_NOT_OK(arrow::ipc::ReadMessage(stream.get(), &message));
+ STOP_IF_NOT_OK(arrow::ipc::ReadMessage(stream.get(), &message));
return message;
}
diff --git a/r/tests/testthat/test-arraydata.R b/r/tests/testthat/test-arraydata.R
index 8b15419..5d8f8f1 100644
--- a/r/tests/testthat/test-arraydata.R
+++ b/r/tests/testthat/test-arraydata.R
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-context("ArrayData")
+context("arrow::ArrayData")
test_that("string vectors with only empty strings and nulls don't allocate a data buffer (ARROW-3693)", {
a <- array("")
diff --git a/r/tests/testthat/test-buffer.R b/r/tests/testthat/test-buffer.R
index 1c0336e..f003862 100644
--- a/r/tests/testthat/test-buffer.R
+++ b/r/tests/testthat/test-buffer.R
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-context("test-buffer")
+context("arrow::Buffer")
test_that("arrow::Buffer can be created from raw vector", {
vec <- raw(123)
diff --git a/r/tests/testthat/test-bufferreader.R b/r/tests/testthat/test-bufferreader.R
index a78153c..e7680a4 100644
--- a/r/tests/testthat/test-bufferreader.R
+++ b/r/tests/testthat/test-bufferreader.R
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-context("test-bufferreader")
+context("arrow::BufferReader")
test_that("BufferReader can be created from R objects", {
num <- buffer_reader(numeric(13))
diff --git a/r/tests/testthat/test-chunkedarray.R b/r/tests/testthat/test-chunkedarray.R
index b9a5c18..4c41f8a 100644
--- a/r/tests/testthat/test-chunkedarray.R
+++ b/r/tests/testthat/test-chunkedarray.R
@@ -82,17 +82,20 @@ test_that("ChunkedArray supports logical vectors (ARROW-3341)", {
arr_lgl <- chunked_array(!!!data)
expect_equal(arr_lgl$length(), 300L)
expect_equal(arr_lgl$null_count(), sum(unlist(map(data, is.na))))
+ expect_identical(arr_lgl$as_vector(), purrr::flatten_lgl(data))
chunks <- arr_lgl$chunks()
expect_identical(data[[1]], chunks[[1]]$as_vector())
expect_identical(data[[2]], chunks[[2]]$as_vector())
expect_identical(data[[3]], chunks[[3]]$as_vector())
+
# without NA
data <- purrr::rerun(3, sample(c(TRUE, FALSE), 100, replace = TRUE))
arr_lgl <- chunked_array(!!!data)
expect_equal(arr_lgl$length(), 300L)
expect_equal(arr_lgl$null_count(), sum(unlist(map(data, is.na))))
+ expect_identical(arr_lgl$as_vector(), purrr::flatten_lgl(data))
chunks <- arr_lgl$chunks()
expect_identical(data[[1]], chunks[[1]]$as_vector())
@@ -110,8 +113,48 @@ test_that("ChunkedArray supports character vectors (ARROW-3339)", {
arr_chr <- chunked_array(!!!data)
expect_equal(arr_chr$length(), length(unlist(data)))
expect_equal(arr_chr$null_count(), 1L)
+ expect_equal(arr_chr$as_vector(), purrr::flatten_chr(data))
chunks <- arr_chr$chunks()
expect_equal(data, purrr::map(chunks, ~.$as_vector()))
})
+test_that("ChunkedArray supports factors (ARROW-3716)", {
+ f <- factor(c("itsy", "bitsy", "spider", "spider"))
+ arr_fac <- chunked_array(f, f, f)
+ expect_equal(arr_fac$length(), 12L)
+ expect_equal(arr_fac$type()$index_type(), int8())
+ expect_identical(arr_fac$as_vector(), vctrs::vec_c(f, f, f))
+})
+
+test_that("ChunkedArray supports dates (ARROW-3716)", {
+ d <- Sys.Date() + 1:10
+ a <- chunked_array(d, d)
+ expect_equal(a$type(), date32())
+ expect_equal(a$length(), 20L)
+ expect_equal(a$as_vector(), c(d, d))
+})
+
+test_that("ChunkedArray supports POSIXct (ARROW-3716)", {
+ times <- lubridate::ymd_hms("2018-10-07 19:04:05") + 1:10
+ a <- chunked_array(times, times)
+ expect_equal(a$type(), date64())
+ expect_equal(a$length(), 20L)
+ expect_equal(as.numeric(a$as_vector()), as.numeric(c(times, times)))
+})
+
+test_that("ChunkedArray supports integer64 (ARROW-3716)", {
+ x <- bit64::as.integer64(1:10)
+ a <- chunked_array(x, x)
+ expect_equal(a$type(), int64())
+ expect_equal(a$length(), 20L)
+ expect_equal(a$as_vector(), c(x,x))
+})
+
+test_that("ChunkedArray supports difftime", {
+ time <- hms::hms(56, 34, 12)
+ a <- chunked_array(time, time)
+ expect_equal(a$type(), time32(unit = TimeUnit$SECOND))
+ expect_equal(a$length(), 2L)
+ expect_equal(a$as_vector(), c(time, time))
+})
diff --git a/r/tests/testthat/test-read-write.R b/r/tests/testthat/test-read-write.R
index bcf3922..2af718e 100644
--- a/r/tests/testthat/test-read-write.R
+++ b/r/tests/testthat/test-read-write.R
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-context("test-read-write")
+context("read-write")
test_that("arrow::table round trip", {
tbl <- tibble::tibble(
diff --git a/r/tests/testthat/test-schema.R b/r/tests/testthat/test-schema.R
index b5a514e..d40fbfa 100644
--- a/r/tests/testthat/test-schema.R
+++ b/r/tests/testthat/test-schema.R
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
-context("test-schema")
+context("arrow::Schema")
test_that("reading schema from raw vector", {
batch <- record_batch(tibble::tibble(x = 1:10))