diff --git a/r/NAMESPACE b/r/NAMESPACE index 0d0d0c2f9d78..316679ac8601 100644 --- a/r/NAMESPACE +++ b/r/NAMESPACE @@ -18,6 +18,8 @@ S3method("[[",RecordBatch) S3method("[[",Schema) S3method("[[",Table) S3method("[[<-",Table) +S3method("names<-",RecordBatch) +S3method("names<-",Table) S3method(Ops,Array) S3method(Ops,ChunkedArray) S3method(Ops,Expression) diff --git a/r/R/arrowExports.R b/r/R/arrowExports.R index a2c34fb96a3c..5e519080cee4 100644 --- a/r/R/arrowExports.R +++ b/r/R/arrowExports.R @@ -1316,6 +1316,10 @@ RecordBatch__schema <- function(x){ .Call(`_arrow_RecordBatch__schema` , x) } +RecordBatch__RenameColumns <- function(batch, names){ + .Call(`_arrow_RecordBatch__RenameColumns` , batch, names) +} + RecordBatch__ReplaceSchemaMetadata <- function(x, metadata){ .Call(`_arrow_RecordBatch__ReplaceSchemaMetadata` , x, metadata) } @@ -1552,6 +1556,10 @@ Table__ColumnNames <- function(table){ .Call(`_arrow_Table__ColumnNames` , table) } +Table__RenameColumns <- function(table, names){ + .Call(`_arrow_Table__RenameColumns` , table, names) +} + Table__Slice1 <- function(table, offset){ .Call(`_arrow_Table__Slice1` , table, offset) } diff --git a/r/R/record-batch.R b/r/R/record-batch.R index c050ef806b82..331a7a772530 100644 --- a/r/R/record-batch.R +++ b/r/R/record-batch.R @@ -75,6 +75,7 @@ RecordBatch <- R6Class("RecordBatch", inherit = ArrowObject, column = function(i) RecordBatch__column(self, i), column_name = function(i) RecordBatch__column_name(self, i), names = function() RecordBatch__names(self), + RenameColumns = function(value) RecordBatch__RenameColumns(self, value), Equals = function(other, check_metadata = FALSE, ...) { inherits(other, "RecordBatch") && RecordBatch__Equals(self, other, isTRUE(check_metadata)) }, @@ -204,6 +205,9 @@ record_batch <- RecordBatch$create #' @export names.RecordBatch <- function(x) x$names() +#' @export +`names<-.RecordBatch` <- function(x, value) x$RenameColumns(value) + #' @importFrom methods as #' @export `[.RecordBatch` <- function(x, i, j, ..., drop = FALSE) { diff --git a/r/R/table.R b/r/R/table.R index 37d01dd3dcd4..1d2190589f7f 100644 --- a/r/R/table.R +++ b/r/R/table.R @@ -96,6 +96,7 @@ Table <- R6Class("Table", inherit = ArrowObject, Table__column(self, i) }, ColumnNames = function() Table__ColumnNames(self), + RenameColumns = function(value) Table__RenameColumns(self, value), GetColumnByName = function(name) { assert_is(name, "character") assert_that(length(name) == 1) @@ -257,6 +258,9 @@ dim.Table <- function(x) c(x$num_rows, x$num_columns) #' @export names.Table <- function(x) x$ColumnNames() +#' @export +`names<-.Table` <- function(x, value) x$RenameColumns(value) + #' @export `[.Table` <- `[.RecordBatch` diff --git a/r/src/arrowExports.cpp b/r/src/arrowExports.cpp index 0beda04ccae9..975ff72f7f15 100644 --- a/r/src/arrowExports.cpp +++ b/r/src/arrowExports.cpp @@ -5163,6 +5163,22 @@ extern "C" SEXP _arrow_RecordBatch__schema(SEXP x_sexp){ } #endif +// recordbatch.cpp +#if defined(ARROW_R_WITH_ARROW) +std::shared_ptr RecordBatch__RenameColumns(const std::shared_ptr& batch, const std::vector& names); +extern "C" SEXP _arrow_RecordBatch__RenameColumns(SEXP batch_sexp, SEXP names_sexp){ +BEGIN_CPP11 + arrow::r::Input&>::type batch(batch_sexp); + arrow::r::Input&>::type names(names_sexp); + return cpp11::as_sexp(RecordBatch__RenameColumns(batch, names)); +END_CPP11 +} +#else +extern "C" SEXP _arrow_RecordBatch__RenameColumns(SEXP batch_sexp, SEXP names_sexp){ + Rf_error("Cannot call RecordBatch__RenameColumns(). Please use arrow::install_arrow() to install required runtime libraries. "); +} +#endif + // recordbatch.cpp #if defined(ARROW_R_WITH_ARROW) std::shared_ptr RecordBatch__ReplaceSchemaMetadata(const std::shared_ptr& x, cpp11::strings metadata); @@ -6085,6 +6101,22 @@ extern "C" SEXP _arrow_Table__ColumnNames(SEXP table_sexp){ } #endif +// table.cpp +#if defined(ARROW_R_WITH_ARROW) +std::shared_ptr Table__RenameColumns(const std::shared_ptr& table, const std::vector& names); +extern "C" SEXP _arrow_Table__RenameColumns(SEXP table_sexp, SEXP names_sexp){ +BEGIN_CPP11 + arrow::r::Input&>::type table(table_sexp); + arrow::r::Input&>::type names(names_sexp); + return cpp11::as_sexp(Table__RenameColumns(table, names)); +END_CPP11 +} +#else +extern "C" SEXP _arrow_Table__RenameColumns(SEXP table_sexp, SEXP names_sexp){ + Rf_error("Cannot call Table__RenameColumns(). Please use arrow::install_arrow() to install required runtime libraries. "); +} +#endif + // table.cpp #if defined(ARROW_R_WITH_ARROW) std::shared_ptr Table__Slice1(const std::shared_ptr& table, R_xlen_t offset); @@ -6703,6 +6735,7 @@ static const R_CallMethodDef CallEntries[] = { { "_arrow_RecordBatch__num_columns", (DL_FUNC) &_arrow_RecordBatch__num_columns, 1}, { "_arrow_RecordBatch__num_rows", (DL_FUNC) &_arrow_RecordBatch__num_rows, 1}, { "_arrow_RecordBatch__schema", (DL_FUNC) &_arrow_RecordBatch__schema, 1}, + { "_arrow_RecordBatch__RenameColumns", (DL_FUNC) &_arrow_RecordBatch__RenameColumns, 2}, { "_arrow_RecordBatch__ReplaceSchemaMetadata", (DL_FUNC) &_arrow_RecordBatch__ReplaceSchemaMetadata, 2}, { "_arrow_RecordBatch__columns", (DL_FUNC) &_arrow_RecordBatch__columns, 1}, { "_arrow_RecordBatch__column", (DL_FUNC) &_arrow_RecordBatch__column, 2}, @@ -6762,6 +6795,7 @@ static const R_CallMethodDef CallEntries[] = { { "_arrow_Table__field", (DL_FUNC) &_arrow_Table__field, 2}, { "_arrow_Table__columns", (DL_FUNC) &_arrow_Table__columns, 1}, { "_arrow_Table__ColumnNames", (DL_FUNC) &_arrow_Table__ColumnNames, 1}, + { "_arrow_Table__RenameColumns", (DL_FUNC) &_arrow_Table__RenameColumns, 2}, { "_arrow_Table__Slice1", (DL_FUNC) &_arrow_Table__Slice1, 2}, { "_arrow_Table__Slice2", (DL_FUNC) &_arrow_Table__Slice2, 3}, { "_arrow_Table__Equals", (DL_FUNC) &_arrow_Table__Equals, 3}, diff --git a/r/src/recordbatch.cpp b/r/src/recordbatch.cpp index bae5b8e713a8..66606a7b05dc 100644 --- a/r/src/recordbatch.cpp +++ b/r/src/recordbatch.cpp @@ -42,6 +42,22 @@ std::shared_ptr RecordBatch__schema( return x->schema(); } +// [[arrow::export]] +std::shared_ptr RecordBatch__RenameColumns( + const std::shared_ptr& batch, + const std::vector& names) { + int n = batch->num_columns(); + if (names.size() != static_cast(n)) { + cpp11::stop("RecordBatch has %d columns but %d names were provided", n, names.size()); + } + std::vector> fields(n); + for (int i = 0; i < n; i++) { + fields[i] = batch->schema()->field(i)->WithName(names[i]); + } + auto schema = std::make_shared(std::move(fields)); + return arrow::RecordBatch::Make(schema, batch->num_rows(), batch->columns()); +} + // [[arrow::export]] std::shared_ptr RecordBatch__ReplaceSchemaMetadata( const std::shared_ptr& x, cpp11::strings metadata) { diff --git a/r/src/table.cpp b/r/src/table.cpp index 56b10088fdfc..14e5f4e92b5d 100644 --- a/r/src/table.cpp +++ b/r/src/table.cpp @@ -74,6 +74,12 @@ std::vector Table__ColumnNames(const std::shared_ptr& return table->ColumnNames(); } +// [[arrow::export]] +std::shared_ptr Table__RenameColumns( + const std::shared_ptr& table, const std::vector& names) { + return ValueOrStop(table->RenameColumns(names)); +} + // [[arrow::export]] std::shared_ptr Table__Slice1(const std::shared_ptr& table, R_xlen_t offset) { diff --git a/r/tests/testthat/test-RecordBatch.R b/r/tests/testthat/test-RecordBatch.R index 1a7f7eecc870..add97823657e 100644 --- a/r/tests/testthat/test-RecordBatch.R +++ b/r/tests/testthat/test-RecordBatch.R @@ -366,3 +366,15 @@ test_that("RecordBatch$Equals(check_metadata)", { expect_false(rb1$Equals(24)) # Not a RecordBatch }) + +test_that("RecordBatch name assignment", { + rb <- record_batch(x = 1:10, y = 1:10) + expect_identical(names(rb), c("x", "y")) + names(rb) <- c("a", "b") + expect_identical(names(rb), c("a", "b")) + expect_error(names(rb) <- "f") + expect_error(names(rb) <- letters) + expect_error(names(rb) <- character(0)) + expect_error(names(rb) <- NULL) + expect_error(names(rb) <- c(TRUE, FALSE)) +}) diff --git a/r/tests/testthat/test-Table.R b/r/tests/testthat/test-Table.R index e33f1ed43a7a..2dbbab6b92cd 100644 --- a/r/tests/testthat/test-Table.R +++ b/r/tests/testthat/test-Table.R @@ -434,3 +434,15 @@ test_that("Table$SelectColumns()", { expect_error(tab$SelectColumns(2:4)) expect_error(tab$SelectColumns("")) }) + +test_that("Table name assignment", { + tab <- Table$create(x = 1:10, y = 1:10) + expect_identical(names(tab), c("x", "y")) + names(tab) <- c("a", "b") + expect_identical(names(tab), c("a", "b")) + expect_error(names(tab) <- "f") + expect_error(names(tab) <- letters) + expect_error(names(tab) <- character(0)) + expect_error(names(tab) <- NULL) + expect_error(names(tab) <- c(TRUE, FALSE)) +})