You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by np...@apache.org on 2021/01/29 21:26:07 UTC

[arrow] branch master updated: ARROW-11423: [R] value_counts and some StructArray methods

This is an automated email from the ASF dual-hosted git repository.

npr 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 f58f29d  ARROW-11423: [R] value_counts and some StructArray methods
f58f29d is described below

commit f58f29dcd43ac03d2e58ec49cecc307a2d918179
Author: Neal Richardson <ne...@gmail.com>
AuthorDate: Fri Jan 29 13:25:10 2021 -0800

    ARROW-11423: [R] value_counts and some StructArray methods
    
    Closes #9357 from nealrichardson/struct-array-methods
    
    Authored-by: Neal Richardson <ne...@gmail.com>
    Signed-off-by: Neal Richardson <ne...@gmail.com>
---
 r/DESCRIPTION                             |  2 --
 r/NAMESPACE                               |  6 ++++
 r/NEWS.md                                 |  9 +++--
 r/R/array.R                               | 33 ++++++++++++++++++
 r/R/arrowExports.R                        |  4 +++
 r/R/compute.R                             | 11 ++++++
 r/R/list.R                                | 57 ------------------------------
 r/R/struct.R                              | 31 -----------------
 r/R/type.R                                | 58 ++++++++++++++++++++++++++++---
 r/_pkgdown.yml                            |  1 +
 r/man/data-type.Rd                        | 12 +++----
 r/man/value_counts.Rd                     | 18 ++++++++++
 r/src/arrowExports.cpp                    |  9 +++++
 r/src/datatype.cpp                        | 11 ++++++
 r/tests/testthat/test-Array.R             | 10 ++++++
 r/tests/testthat/test-compute-aggregate.R | 15 ++++++++
 r/tests/testthat/test-data-type.R         | 52 +++++++++++++--------------
 17 files changed, 210 insertions(+), 129 deletions(-)

diff --git a/r/DESCRIPTION b/r/DESCRIPTION
index 74ed8f4..145a5ae 100644
--- a/r/DESCRIPTION
+++ b/r/DESCRIPTION
@@ -85,7 +85,6 @@ Collate:
     'install-arrow.R'
     'ipc_stream.R'
     'json.R'
-    'list.R'
     'memory-pool.R'
     'message.R'
     'metadata.R'
@@ -96,5 +95,4 @@ Collate:
     'reexports-bit64.R'
     'reexports-tidyselect.R'
     'schema.R'
-    'struct.R'
     'util.R'
diff --git a/r/NAMESPACE b/r/NAMESPACE
index 491bfe6..fbc71e9 100644
--- a/r/NAMESPACE
+++ b/r/NAMESPACE
@@ -3,6 +3,7 @@
 S3method("!=",ArrowObject)
 S3method("$",ArrowTabular)
 S3method("$",Schema)
+S3method("$",StructArray)
 S3method("$",SubTreeFileSystem)
 S3method("$<-",ArrowTabular)
 S3method("==",ArrowObject)
@@ -13,6 +14,7 @@ S3method("[",Schema)
 S3method("[",arrow_dplyr_query)
 S3method("[[",ArrowTabular)
 S3method("[[",Schema)
+S3method("[[",StructArray)
 S3method("[[<-",ArrowTabular)
 S3method("names<-",ArrowTabular)
 S3method(Ops,ArrowDatum)
@@ -22,6 +24,7 @@ S3method(all,equal.ArrowObject)
 S3method(as.character,ArrowDatum)
 S3method(as.character,FileFormat)
 S3method(as.data.frame,ArrowTabular)
+S3method(as.data.frame,StructArray)
 S3method(as.data.frame,arrow_dplyr_query)
 S3method(as.double,ArrowDatum)
 S3method(as.integer,ArrowDatum)
@@ -33,6 +36,7 @@ S3method(as.vector,array_expression)
 S3method(c,Dataset)
 S3method(dim,ArrowTabular)
 S3method(dim,Dataset)
+S3method(dim,StructArray)
 S3method(dim,arrow_dplyr_query)
 S3method(dimnames,ArrowTabular)
 S3method(head,ArrowDatum)
@@ -58,6 +62,7 @@ S3method(names,RecordBatch)
 S3method(names,Scanner)
 S3method(names,ScannerBuilder)
 S3method(names,Schema)
+S3method(names,StructArray)
 S3method(names,Table)
 S3method(names,arrow_dplyr_query)
 S3method(print,"arrow-enum")
@@ -243,6 +248,7 @@ export(uint64)
 export(uint8)
 export(unify_schemas)
 export(utf8)
+export(value_counts)
 export(write_arrow)
 export(write_dataset)
 export(write_feather)
diff --git a/r/NEWS.md b/r/NEWS.md
index 5625403..4cdf7eb 100644
--- a/r/NEWS.md
+++ b/r/NEWS.md
@@ -19,6 +19,9 @@
 
 # arrow 3.0.0.9000
 
+* `value_counts()` to tabulate values in an `Array` or `ChunkedArray`, similar to `base::table()`.
+* `StructArray` objects gain data.frame-like methods, including `names()`, `$`, `[[`, and `dim()`.
+
 # arrow 3.0.0
 
 ## Python and Flight
@@ -39,7 +42,7 @@
 * Option `arrow.skip_nul` (default `FALSE`, as in `base::scan()`) allows conversion of Arrow string (`utf8()`) type data containing embedded nul `\0` characters to R. If set to `TRUE`, nuls will be stripped and a warning is emitted if any are found.
 * `arrow_info()` for an overview of various run-time and build-time Arrow configurations, useful for debugging
 * Set environment variable `ARROW_DEFAULT_MEMORY_POOL` before loading the Arrow package to change memory allocators. Windows packages are built with `mimalloc`; most others are built with both `jemalloc` (used by default) and `mimalloc`. These alternative memory allocators are generally much faster than the system memory allocator, so they are used by default when available, but sometimes it is useful to turn them off for debugging purposes. To disable them, set `ARROW_DEFAULT_MEMORY_POO [...]
-* List columns that have attributes on each element are now also included with the metadata that is saved when creating Arrow tables. This allows `sf` tibbles to faithfully preserved and roundtripped (ARROW-10386)[https://issues.apache.org/jira/browse/ARROW-10386].
+* List columns that have attributes on each element are now also included with the metadata that is saved when creating Arrow tables. This allows `sf` tibbles to faithfully preserved and roundtripped ([ARROW-10386](https://issues.apache.org/jira/browse/ARROW-10386)).
 * R metadata that exceeds 100Kb is now compressed before being written to a table; see `schema()` for more details.
 
 ## Bug fixes
@@ -48,8 +51,8 @@
 * C++ functions now trigger garbage collection when needed
 * `write_parquet()` can now write RecordBatches
 * Reading a Table from a RecordBatchStreamReader containing 0 batches no longer crashes
-* `readr`'s `problems` attribute is removed when converting to Arrow RecordBatch and table to prevent large amounts of metadata from accumulating inadvertently [ARROW-10624](https://issues.apache.org/jira/browse/ARROW-10624)
-* Fixed reading of compressed Feather files written with Arrow 0.17 [ARROW-10850](https://issues.apache.org/jira/browse/ARROW-10850)
+* `readr`'s `problems` attribute is removed when converting to Arrow RecordBatch and table to prevent large amounts of metadata from accumulating inadvertently ([ARROW-10624](https://issues.apache.org/jira/browse/ARROW-10624))
+* Fixed reading of compressed Feather files written with Arrow 0.17 ([ARROW-10850](https://issues.apache.org/jira/browse/ARROW-10850))
 * `SubTreeFileSystem` gains a useful print method and no longer errors when printing
 
 ## Packaging and installation
diff --git a/r/R/array.R b/r/R/array.R
index 1c13c32..ec2b545 100644
--- a/r/R/array.R
+++ b/r/R/array.R
@@ -189,6 +189,39 @@ StructArray <- R6Class("StructArray", inherit = Array,
   )
 )
 
+
+#' @export
+`[[.StructArray` <- function(x, i, ...) {
+  if (is.character(i)) {
+    x$GetFieldByName(i)
+  } else if (is.numeric(i)) {
+    x$field(i - 1)
+  } else {
+    stop("'i' must be character or numeric, not ", class(i), call. = FALSE)
+  }
+}
+
+#' @export
+`$.StructArray` <- function(x, name, ...) {
+  assert_that(is.string(name))
+  if (name %in% ls(x)) {
+    get(name, x)
+  } else {
+    x$GetFieldByName(name)
+  }
+}
+
+#' @export
+names.StructArray <- function(x, ...) StructType__field_names(x$type)
+
+#' @export
+dim.StructArray <- function(x, ...) c(length(x), x$type$num_fields)
+
+#' @export
+as.data.frame.StructArray <- function(x, row.names = NULL, optional = FALSE, ...) {
+  as.vector(x)
+}
+
 #' @rdname array
 #' @usage NULL
 #' @format NULL
diff --git a/r/R/arrowExports.R b/r/R/arrowExports.R
index 1631024..e093dac 100644
--- a/r/R/arrowExports.R
+++ b/r/R/arrowExports.R
@@ -696,6 +696,10 @@ StructType__GetFieldIndex <- function(type, name){
     .Call(`_arrow_StructType__GetFieldIndex`, type, name)
 }
 
+StructType__field_names <- function(type){
+    .Call(`_arrow_StructType__field_names`, type)
+}
+
 ListType__value_field <- function(type){
     .Call(`_arrow_ListType__value_field`, type)
 }
diff --git a/r/R/compute.R b/r/R/compute.R
index 6d6837a..cee0ddf 100644
--- a/r/R/compute.R
+++ b/r/R/compute.R
@@ -109,6 +109,17 @@ match_arrow.ArrowDatum <- function(x, table, ...) {
   call_function("index_in_meta_binary", x, table)
 }
 
+#' `table` for Arrow objects
+#'
+#' This function tabulates the values in the array and returns a table of counts.
+#' @param x `Array` or `ChunkedArray`
+#' @return A `StructArray` containing "values" (same type as `x`) and "counts"
+#' `Int64`.
+#' @export
+value_counts <- function(x) {
+  call_function("value_counts", x)
+}
+
 #' Cast options
 #'
 #' @param safe logical: enforce safe conversion? Default `TRUE`
diff --git a/r/R/list.R b/r/R/list.R
deleted file mode 100644
index 7db8d9a..0000000
--- a/r/R/list.R
+++ /dev/null
@@ -1,57 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-#' @include type.R
-
-ListType <- R6Class("ListType",
-  inherit = NestedType,
-  active = list(
-    value_field = function() ListType__value_field(self),
-    value_type = function() ListType__value_type(self)
-  )
-)
-
-#' @rdname data-type
-#' @export
-list_of <- function(type) list__(type)
-
-LargeListType <- R6Class("LargeListType",
-  inherit = NestedType,
-  active = list(
-    value_field = function() LargeListType__value_field(self),
-    value_type = function() LargeListType__value_type(self)
-  )
-)
-
-#' @rdname data-type
-#' @export
-large_list_of <- function(type) large_list__(type)
-
-#' @rdname data-type
-#' @export
-FixedSizeListType <- R6Class("FixedSizeListType",
-  inherit = NestedType,
-  active = list(
-    value_field = function() FixedSizeListType__value_field(self),
-    value_type = function() FixedSizeListType__value_type(self),
-    list_size = function() FixedSizeListType__list_size(self)
-  )
-)
-
-#' @rdname data-type
-#' @export
-fixed_size_list_of <- function(type, list_size) fixed_size_list__(type, list_size)
diff --git a/r/R/struct.R b/r/R/struct.R
deleted file mode 100644
index 7cfa7c9..0000000
--- a/r/R/struct.R
+++ /dev/null
@@ -1,31 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-#' @include type.R
-
-StructType <- R6Class("StructType",
-  inherit = NestedType,
-  public = list(
-    GetFieldByName = function(name) StructType__GetFieldByName(self, name),
-    GetFieldIndex = function(name) StructType__GetFieldIndex(self, name)
-  )
-)
-StructType$create <- function(...) struct__(.fields(list(...)))
-
-#' @rdname data-type
-#' @export
-struct <- StructType$create
diff --git a/r/R/type.R b/r/R/type.R
index 937a2db..4851b48 100644
--- a/r/R/type.R
+++ b/r/R/type.R
@@ -37,9 +37,6 @@ DataType <- R6Class("DataType",
     Equals = function(other, ...) {
       inherits(other, "DataType") && DataType__Equals(self, other)
     },
-    num_fields = function() {
-      DataType__num_fields(self)
-    },
     fields = function() {
       DataType__fields(self)
     }
@@ -47,7 +44,8 @@ DataType <- R6Class("DataType",
 
   active = list(
     id = function() DataType__id(self),
-    name = function() DataType__name(self)
+    name = function() DataType__name(self),
+    num_fields = function() DataType__num_fields(self)
   )
 )
 
@@ -353,6 +351,58 @@ decimal <- function(precision, scale) {
   Decimal128Type__initialize(precision, scale)
 }
 
+StructType <- R6Class("StructType",
+  inherit = NestedType,
+  public = list(
+    GetFieldByName = function(name) StructType__GetFieldByName(self, name),
+    GetFieldIndex = function(name) StructType__GetFieldIndex(self, name)
+  )
+)
+StructType$create <- function(...) struct__(.fields(list(...)))
+
+#' @rdname data-type
+#' @export
+struct <- StructType$create
+
+ListType <- R6Class("ListType",
+  inherit = NestedType,
+  active = list(
+    value_field = function() ListType__value_field(self),
+    value_type = function() ListType__value_type(self)
+  )
+)
+
+#' @rdname data-type
+#' @export
+list_of <- function(type) list__(type)
+
+LargeListType <- R6Class("LargeListType",
+  inherit = NestedType,
+  active = list(
+    value_field = function() LargeListType__value_field(self),
+    value_type = function() LargeListType__value_type(self)
+  )
+)
+
+#' @rdname data-type
+#' @export
+large_list_of <- function(type) large_list__(type)
+
+#' @rdname data-type
+#' @export
+FixedSizeListType <- R6Class("FixedSizeListType",
+  inherit = NestedType,
+  active = list(
+    value_field = function() FixedSizeListType__value_field(self),
+    value_type = function() FixedSizeListType__value_type(self),
+    list_size = function() FixedSizeListType__list_size(self)
+  )
+)
+
+#' @rdname data-type
+#' @export
+fixed_size_list_of <- function(type, list_size) fixed_size_list__(type, list_size)
+
 as_type <- function(type, name = "type") {
   if (identical(type, double())) {
     # Magic so that we don't have to mask this base function
diff --git a/r/_pkgdown.yml b/r/_pkgdown.yml
index 6e9bb9c..ba71aea 100644
--- a/r/_pkgdown.yml
+++ b/r/_pkgdown.yml
@@ -145,6 +145,7 @@ reference:
 - title: Computation
   contents:
   - match_arrow
+  - value_counts
 - title: Configuration
   contents:
   - arrow_info
diff --git a/r/man/data-type.Rd b/r/man/data-type.Rd
index 55021ca..8966509 100644
--- a/r/man/data-type.Rd
+++ b/r/man/data-type.Rd
@@ -1,5 +1,5 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/type.R, R/list.R, R/struct.R
+% Please edit documentation in R/type.R
 \name{data-type}
 \alias{data-type}
 \alias{int8}
@@ -30,11 +30,11 @@
 \alias{null}
 \alias{timestamp}
 \alias{decimal}
+\alias{struct}
 \alias{list_of}
 \alias{large_list_of}
 \alias{FixedSizeListType}
 \alias{fixed_size_list_of}
-\alias{struct}
 \title{Apache Arrow data types}
 \usage{
 int8()
@@ -93,13 +93,13 @@ timestamp(unit = c("s", "ms", "us", "ns"), timezone = "")
 
 decimal(precision, scale)
 
+struct(...)
+
 list_of(type)
 
 large_list_of(type)
 
 fixed_size_list_of(type, list_size)
-
-struct(...)
 }
 \arguments{
 \item{byte_width}{byte width for \code{FixedSizeBinary} type.}
@@ -114,11 +114,11 @@ take any of those four values.}
 
 \item{scale}{For \code{decimal()}, scale}
 
+\item{...}{For \code{struct()}, a named list of types to define the struct columns}
+
 \item{type}{For \code{list_of()}, a data type to make a list-of-type}
 
 \item{list_size}{list size for \code{FixedSizeList} type.}
-
-\item{...}{For \code{struct()}, a named list of types to define the struct columns}
 }
 \value{
 An Arrow type object inheriting from DataType.
diff --git a/r/man/value_counts.Rd b/r/man/value_counts.Rd
new file mode 100644
index 0000000..139af8e
--- /dev/null
+++ b/r/man/value_counts.Rd
@@ -0,0 +1,18 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/compute.R
+\name{value_counts}
+\alias{value_counts}
+\title{\code{table} for Arrow objects}
+\usage{
+value_counts(x)
+}
+\arguments{
+\item{x}{\code{Array} or \code{ChunkedArray}}
+}
+\value{
+A \code{StructArray} containing "values" (same type as \code{x}) and "counts"
+\code{Int64}.
+}
+\description{
+This function tabulates the values in the array and returns a table of counts.
+}
diff --git a/r/src/arrowExports.cpp b/r/src/arrowExports.cpp
index 15791ad..5273208 100644
--- a/r/src/arrowExports.cpp
+++ b/r/src/arrowExports.cpp
@@ -1471,6 +1471,14 @@ BEGIN_CPP11
 END_CPP11
 }
 // datatype.cpp
+std::vector<std::string> StructType__field_names(const std::shared_ptr<arrow::StructType>& type);
+extern "C" SEXP _arrow_StructType__field_names(SEXP type_sexp){
+BEGIN_CPP11
+	arrow::r::Input<const std::shared_ptr<arrow::StructType>&>::type type(type_sexp);
+	return cpp11::as_sexp(StructType__field_names(type));
+END_CPP11
+}
+// datatype.cpp
 std::shared_ptr<arrow::Field> ListType__value_field(const std::shared_ptr<arrow::ListType>& type);
 extern "C" SEXP _arrow_ListType__value_field(SEXP type_sexp){
 BEGIN_CPP11
@@ -3644,6 +3652,7 @@ static const R_CallMethodDef CallEntries[] = {
 		{ "_arrow_DictionaryType__ordered", (DL_FUNC) &_arrow_DictionaryType__ordered, 1}, 
 		{ "_arrow_StructType__GetFieldByName", (DL_FUNC) &_arrow_StructType__GetFieldByName, 2}, 
 		{ "_arrow_StructType__GetFieldIndex", (DL_FUNC) &_arrow_StructType__GetFieldIndex, 2}, 
+		{ "_arrow_StructType__field_names", (DL_FUNC) &_arrow_StructType__field_names, 1}, 
 		{ "_arrow_ListType__value_field", (DL_FUNC) &_arrow_ListType__value_field, 1}, 
 		{ "_arrow_ListType__value_type", (DL_FUNC) &_arrow_ListType__value_type, 1}, 
 		{ "_arrow_LargeListType__value_field", (DL_FUNC) &_arrow_LargeListType__value_field, 1}, 
diff --git a/r/src/datatype.cpp b/r/src/datatype.cpp
index 6e7398b..ffbe1ec 100644
--- a/r/src/datatype.cpp
+++ b/r/src/datatype.cpp
@@ -372,6 +372,17 @@ int StructType__GetFieldIndex(const std::shared_ptr<arrow::StructType>& type,
 }
 
 // [[arrow::export]]
+std::vector<std::string> StructType__field_names(
+    const std::shared_ptr<arrow::StructType>& type) {
+  auto num_fields = type->num_fields();
+  std::vector<std::string> out(num_fields);
+  for (int i = 0; i < num_fields; i++) {
+    out[i] = type->field(i)->name();
+  }
+  return out;
+}
+
+// [[arrow::export]]
 std::shared_ptr<arrow::Field> ListType__value_field(
     const std::shared_ptr<arrow::ListType>& type) {
   return type->value_field();
diff --git a/r/tests/testthat/test-Array.R b/r/tests/testthat/test-Array.R
index 126c9f6..03c4f37 100644
--- a/r/tests/testthat/test-Array.R
+++ b/r/tests/testthat/test-Array.R
@@ -460,6 +460,16 @@ test_that("Array$create() handles data frame -> struct arrays (ARROW-3811)", {
   expect_equivalent(as.vector(a), df)
 })
 
+test_that("StructArray methods", {
+  df <- tibble::tibble(x = 1:10, y = x / 2, z = letters[1:10])
+  a <- Array$create(df)
+  expect_equal(a$x, Array$create(df$x))
+  expect_equal(a[["x"]], Array$create(df$x))
+  expect_equal(a[[1]], Array$create(df$x))
+  expect_identical(names(a), c("x", "y", "z"))
+  expect_identical(dim(a), c(10L, 3L))
+})
+
 test_that("Array$create() can handle data frame with custom struct type (not inferred)", {
   df <- tibble::tibble(x = 1:10, y = 1:10)
   type <- struct(x = float64(), y = int16())
diff --git a/r/tests/testthat/test-compute-aggregate.R b/r/tests/testthat/test-compute-aggregate.R
index d80ff8c..5f4eaba 100644
--- a/r/tests/testthat/test-compute-aggregate.R
+++ b/r/tests/testthat/test-compute-aggregate.R
@@ -214,3 +214,18 @@ test_that("match_arrow", {
   ca <- ChunkedArray$create(c(1, 4, 3, 1, 1, 3, 4))
   expect_equal(match_arrow(ca, tab), ChunkedArray$create(c(3L, 0L, 1L, 3L, 3L, 1L, 0L)))
 })
+
+test_that("value_counts", {
+  a <- Array$create(c(1, 4, 3, 1, 1, 3, 4))
+  result_df <- tibble::tibble(
+    values = c(1, 4, 3),
+    counts = c(3L, 2L, 2L)
+  )
+  result <- Array$create(
+    result_df,
+    type = struct(values = float64(), counts = int64())
+  )
+  expect_equal(value_counts(a), result)
+  expect_identical(as.data.frame(value_counts(a)), result_df)
+  expect_identical(as.vector(value_counts(a)$counts), result_df$counts)
+})
diff --git a/r/tests/testthat/test-data-type.R b/r/tests/testthat/test-data-type.R
index 476b00c..a5ecb41 100644
--- a/r/tests/testthat/test-data-type.R
+++ b/r/tests/testthat/test-data-type.R
@@ -24,7 +24,7 @@ test_that("null type works as expected",{
   expect_equal(x$ToString(), "null")
   expect_true(x == x)
   expect_false(x == int8())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
 })
 
@@ -35,7 +35,7 @@ test_that("boolean type work as expected",{
   expect_equal(x$ToString(), "bool")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 1L)
 })
@@ -47,7 +47,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "uint8")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 8L)
 
@@ -57,7 +57,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "int8")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 8L)
 
@@ -67,7 +67,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "uint16")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 16L)
 
@@ -77,7 +77,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "int16")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 16L)
 
@@ -87,7 +87,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "uint32")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 32L)
 
@@ -97,7 +97,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "int32")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 32L)
 
@@ -107,7 +107,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "uint64")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
 
@@ -117,7 +117,7 @@ test_that("int types works as expected",{
   expect_equal(x$ToString(), "int64")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
 })
@@ -129,7 +129,7 @@ test_that("float types work as expected",{
   expect_equal(x$ToString(), "halffloat")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 16L)
 
@@ -139,7 +139,7 @@ test_that("float types work as expected",{
   expect_equal(x$ToString(), "float")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 32L)
 
@@ -149,7 +149,7 @@ test_that("float types work as expected",{
   expect_equal(x$ToString(), "double")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
 })
@@ -161,7 +161,7 @@ test_that("utf8 type works as expected",{
   expect_equal(x$ToString(), "string")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
 })
 
@@ -172,7 +172,7 @@ test_that("date types work as expected", {
   expect_equal(x$ToString(), "date32[day]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$unit(), unclass(DateUnit$DAY))
 
@@ -182,7 +182,7 @@ test_that("date types work as expected", {
   expect_equal(x$ToString(), "date64[ms]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$unit(), unclass(DateUnit$MILLI))
 })
@@ -194,7 +194,7 @@ test_that("timestamp type works as expected", {
   expect_equal(x$ToString(), "timestamp[s]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
   expect_equal(x$timezone(), "")
@@ -206,7 +206,7 @@ test_that("timestamp type works as expected", {
   expect_equal(x$ToString(), "timestamp[ms]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
   expect_equal(x$timezone(), "")
@@ -218,7 +218,7 @@ test_that("timestamp type works as expected", {
   expect_equal(x$ToString(), "timestamp[us]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
   expect_equal(x$timezone(), "")
@@ -230,7 +230,7 @@ test_that("timestamp type works as expected", {
   expect_equal(x$ToString(), "timestamp[ns]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
   expect_equal(x$timezone(), "")
@@ -248,7 +248,7 @@ test_that("time32 types work as expected", {
   expect_equal(x$ToString(), "time32[s]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 32L)
   expect_equal(x$unit(), unclass(TimeUnit$SECOND))
@@ -259,7 +259,7 @@ test_that("time32 types work as expected", {
   expect_equal(x$ToString(), "time32[ms]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 32L)
   expect_equal(x$unit(), unclass(TimeUnit$MILLI))
@@ -272,7 +272,7 @@ test_that("time64 types work as expected", {
   expect_equal(x$ToString(), "time64[us]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
   expect_equal(x$unit(), unclass(TimeUnit$MICRO))
@@ -283,7 +283,7 @@ test_that("time64 types work as expected", {
   expect_equal(x$ToString(), "time64[ns]")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 0L)
+  expect_equal(x$num_fields, 0L)
   expect_equal(x$fields(), list())
   expect_equal(x$bit_width, 64L)
   expect_equal(x$unit(), unclass(TimeUnit$NANO))
@@ -329,7 +329,7 @@ test_that("list type works as expected", {
   expect_equal(x$ToString(), "list<item: int32>")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 1L)
+  expect_equal(x$num_fields, 1L)
   expect_equal(
     x$fields(),
     list(field("item", int32()))
@@ -345,7 +345,7 @@ test_that("struct type works as expected", {
   expect_equal(x$ToString(), "struct<x: int32, y: bool>")
   expect_true(x == x)
   expect_false(x == null())
-  expect_equal(x$num_fields(), 2L)
+  expect_equal(x$num_fields, 2L)
   expect_equal(
     x$fields(),
     list(field("x", int32()), field("y", boolean()))