Skip to content

Commit

Permalink
Merge branch 'f-#99-add-column'
Browse files Browse the repository at this point in the history
- New `add_column()`, analogously to `add_row()` (#99).
  • Loading branch information
krlmlr committed Aug 15, 2016
2 parents 88ac02d + 6347970 commit 6d16c3d
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 0 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ S3method(type_sum,difftime)
S3method(type_sum,factor)
S3method(type_sum,ordered)
S3method(type_sum,tbl_df)
export(add_column)
export(add_row)
export(as_data_frame)
export(as_tibble)
Expand Down
47 changes: 47 additions & 0 deletions R/dataframe.R
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ is_tibble <- is.tibble
#' @param .data Data frame to append to.
#' @param ... Name-value pairs. If you don't supply the name of a variable,
#' it'll be given the value \code{NA}.
#' @family addition
#' @examples
#' # add_row ---------------------------------
#' df <- tibble(x = 1:3, y = 3:1)
Expand Down Expand Up @@ -312,6 +313,52 @@ add_row <- function(.data, ...) {
rbind(.data, df)
}

#' Add columns to a data frame
#'
#' This is a convenient way to add one or more columns to an existing data
#' frame.
#'
#' @param .data Data frame to append to.
#' @param ... Name-value pairs, all values must have one element for each row
#' in the data frame, or be of length 1
#' @family addition
#' @examples
#' # add_row ---------------------------------
#' df <- tibble(x = 1:3, y = 3:1)
#'
#' add_column(df, z = -1:1, w = 0)
#'
#' # You can't overwrite existing columns
#' \dontrun{
#' add_column(df, x = 4:6)
#' }

#' # You can't create new observations
#' \dontrun{
#' add_column(df, z = 1:5)
#' }
#' @export
add_column <- function(.data, ...) {
df <- tibble(...)

if (ncol(df) == 0L) {
return(.data)
}

if (nrow(df) != nrow(.data)) {
stopc("Expected ", nrow(.data), " rows, got ", nrow(df))
}

extra_vars <- intersect(names(df), names(.data))
if (length(extra_vars) > 0) {
stopc(
"Columns already in data frame: ", format_n(extra_vars)
)
}

structure(cbind(.data, df), class = class(.data))
}

# Validity checks --------------------------------------------------------------

check_tibble <- function(x) {
Expand Down
37 changes: 37 additions & 0 deletions man/add_column.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions man/add_row.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 53 additions & 0 deletions tests/testthat/test-data_frame.R
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,22 @@ test_that("names must be unique (#820)", {

test_that("can add new row", {
df_all_new <- add_row(df_all, a = 4, b = 3L)
expect_identical(colnames(df_all_new), colnames(df_all))
expect_identical(nrow(df_all_new), nrow(df_all) + 1L)
expect_identical(df_all_new$a, c(df_all$a, 4))
expect_identical(df_all_new$b, c(df_all$b, 3L))
expect_identical(df_all_new$c, c(df_all$c, NA))
})

test_that("add_row() keeps class of object", {
iris_new <- add_row(iris, Species = "unknown")
expect_equal(class(iris), class(iris_new))

iris_new <- add_row(as_tibble(iris), Species = "unknown")
expect_equal(class(as_tibble(iris)), class(iris_new))
})

test_that("adds empty row if no arguments", {
new_iris_row <- add_row(iris)[nrow(iris) + 1, , drop = TRUE]
expect_true(all(is.na(new_iris_row)))
})
Expand All @@ -247,3 +252,51 @@ test_that("can recycle when adding rows", {
expect_identical(as.character(iris_new$Species),
c(as.character(iris$Species), "unknown", "unknown"))
})

# add_column ------------------------------------------------------------

test_that("can add new column", {
df_all_new <- add_column(df_all, j = 1:3, k = 3:1)
expect_identical(nrow(df_all_new), nrow(df_all))
expect_identical(df_all_new[seq_along(df_all)], df_all)
expect_identical(df_all_new$j, 1:3)
expect_identical(df_all_new$k, 3:1)
})

test_that("add_column() keeps class of object", {
iris_new <- add_column(iris, x = 1:150)
expect_equal(class(iris), class(iris_new))

iris_new <- add_column(as_tibble(iris), x = 1:150)
expect_equal(class(as_tibble(iris)), class(iris_new))
})

test_that("add_column() keeps unchanged if no arguments", {
expect_identical(iris, add_column(iris))
})

test_that("error if adding existing columns", {
expect_error(add_column(tibble(a = 3), a = 5),
"Columns already in data frame")
})

test_that("error if adding wrong number of rows with add_column()", {
expect_error(add_column(tibble(a = 3), b = 4:5),
"Expected 1 rows, got 2")
})

test_that("can add multiple columns", {
df <- tibble(a = 1:3)
df_new <- add_column(df, b = 4:6, c = 3:1)
expect_identical(ncol(df_new), ncol(df) + 2L)
expect_identical(df_new$b, 4:6)
expect_identical(df_new$c, 3:1)
})

test_that("can recycle when adding columns", {
df <- tibble(a = 1:3)
df_new <- add_column(df, b = 4, c = 3:1)
expect_identical(ncol(df_new), ncol(df) + 2L)
expect_identical(df_new$b, rep(4, 3))
expect_identical(df_new$c, 3:1)
})

0 comments on commit 6d16c3d

Please sign in to comment.