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))