From 50002d5f6c1304cdc2e6b654cd05ad48eb2ee1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 17 Jul 2021 19:38:03 +0200 Subject: [PATCH 01/12] Coerce column if it consists of NAs --- R/subsetting.R | 28 +++++++++++++++++++++++++--- tests/testthat/_snaps/invariants.md | 13 ++++++++----- tests/testthat/_snaps/subsetting.md | 13 ++++++++----- tests/testthat/test-subsetting.R | 1 + 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/R/subsetting.R b/R/subsetting.R index 430838b23..f9ef614da 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -644,9 +644,7 @@ tbl_subassign_row <- function(x, i, value, value_arg) { withCallingHandlers( for (j in seq_along(x)) { - xj <- x[[j]] - vec_slice(xj, i) <- value[[j]] - x[[j]] <- xj + x[[j]] <- vectbl_assign(x[[j]], i, value[[j]]) }, vctrs_error = function(cnd) { @@ -661,6 +659,30 @@ fast_nrow <- function(x) { .row_names_info(x, 2L) } +vectbl_assign <- function(x, i, value) { + if (!is.logical(value) && vec_size(i) == vec_size(value) && tibble_need_coerce(x)) { + # x is a vector full of logical NAs here! + x <- value[NA_integer_][x] + } + + vec_slice(x, i) <- value + x +} + +tibble_need_coerce <- function(x) { + if (!is.logical(x)) { + return(FALSE) + } + + for (i in seq_along(x)) { + if (!is.na(x[[i]])) { + return(FALSE) + } + } + + TRUE +} + vectbl_strip_names <- function(x) { maybe_row_names <- is.data.frame(x) || is.array(x) diff --git a/tests/testthat/_snaps/invariants.md b/tests/testthat/_snaps/invariants.md index dcf1b96a7..9a848c7d0 100644 --- a/tests/testthat/_snaps/invariants.md +++ b/tests/testthat/_snaps/invariants.md @@ -1191,11 +1191,14 @@ df$x <- NA df[2:3, "x"] <- 3:2 }) - Error - Assigned data `3:2` must be compatible with existing data. - i Error occurred for column `x`. - x Can't convert from to due to loss of precision. - * Locations: 1, 2. + Output + # A tibble: 4 x 4 + n c li x + + 1 1 e NA + 2 NA f 3 + 3 3 g 2 + 4 NA h NA Code with_df({ df$x <- NA_integer_ diff --git a/tests/testthat/_snaps/subsetting.md b/tests/testthat/_snaps/subsetting.md index a985af3c2..35525c7d7 100644 --- a/tests/testthat/_snaps/subsetting.md +++ b/tests/testthat/_snaps/subsetting.md @@ -559,11 +559,14 @@ # # [<-.tbl_df and overwriting NA df <- tibble(x = rep(NA, 3)) df[1, "x"] <- 5 - Error - Assigned data `5` must be compatible with existing data. - i Error occurred for column `x`. - x Can't convert from to due to loss of precision. - * Locations: 1. + df + Output + # A tibble: 3 x 1 + x + + 1 5 + 2 NA + 3 NA Code # # [<-.tbl_df and matrix subsetting foo <- tibble(a = 1:3, b = letters[1:3]) diff --git a/tests/testthat/test-subsetting.R b/tests/testthat/test-subsetting.R index fa753aad0..e3ab7fc03 100644 --- a/tests/testthat/test-subsetting.R +++ b/tests/testthat/test-subsetting.R @@ -920,6 +920,7 @@ test_that("output test", { "# [<-.tbl_df and overwriting NA" df <- tibble(x = rep(NA, 3)) df[1, "x"] <- 5 + df "# [<-.tbl_df and matrix subsetting" foo <- tibble(a = 1:3, b = letters[1:3]) From 6773c0c8a29e7db349c7054521903c05d106dfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 17 Jul 2021 19:47:40 +0200 Subject: [PATCH 02/12] Reimplement in C for speed --- R/subsetting.R | 16 +--------------- src/coerce.c | 16 ++++++++++++++++ src/init.c | 1 + src/tibble.h | 1 + 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/R/subsetting.R b/R/subsetting.R index f9ef614da..5dbd5dab4 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -660,7 +660,7 @@ fast_nrow <- function(x) { } vectbl_assign <- function(x, i, value) { - if (!is.logical(value) && vec_size(i) == vec_size(value) && tibble_need_coerce(x)) { + if (!is.logical(value) && vec_size(i) == vec_size(value) && .Call("tibble_need_coerce", x)) { # x is a vector full of logical NAs here! x <- value[NA_integer_][x] } @@ -669,20 +669,6 @@ vectbl_assign <- function(x, i, value) { x } -tibble_need_coerce <- function(x) { - if (!is.logical(x)) { - return(FALSE) - } - - for (i in seq_along(x)) { - if (!is.na(x[[i]])) { - return(FALSE) - } - } - - TRUE -} - vectbl_strip_names <- function(x) { maybe_row_names <- is.data.frame(x) || is.array(x) diff --git a/src/coerce.c b/src/coerce.c index 5b32a8a82..bcf6cc7f4 100644 --- a/src/coerce.c +++ b/src/coerce.c @@ -61,3 +61,19 @@ SEXP tibble_string_to_indices(SEXP x) { UNPROTECT(1); return out; } + +SEXP tibble_need_coerce(SEXP x) { + if (TYPEOF(x) != LGLSXP) { + return(Rf_ScalarLogical(0)); + } + + const R_xlen_t len = Rf_xlength(x); + const int* px = LOGICAL(x); + for (R_xlen_t i = 0; i < len; ++i) { + if (px[i] != NA_LOGICAL) { + return(Rf_ScalarLogical(0)); + } + } + + return(Rf_ScalarLogical(1)); +} diff --git a/src/init.c b/src/init.c index 9354eca7d..e37781391 100644 --- a/src/init.c +++ b/src/init.c @@ -9,6 +9,7 @@ static const R_CallMethodDef CallEntries[] = { {"tibble_string_to_indices", (DL_FUNC) &tibble_string_to_indices, 1}, {"tibble_update_attrs", (DL_FUNC) &tibble_update_attrs, 2}, {"tibble_restore_impl", (DL_FUNC) &tibble_restore_impl, 2}, + {"tibble_need_coerce", (DL_FUNC) &tibble_need_coerce, 1}, {NULL, NULL, 0} }; diff --git a/src/tibble.h b/src/tibble.h index 5fab472fb..b1c762fb2 100644 --- a/src/tibble.h +++ b/src/tibble.h @@ -6,6 +6,7 @@ SEXP tibble_matrixToDataFrame(SEXP xSEXP); SEXP tibble_string_to_indices(SEXP x); +SEXP tibble_need_coerce(SEXP x); SEXP tibble_update_attrs(SEXP x, SEXP dots); SEXP tibble_restore_impl(SEXP xo, SEXP x); From f49e36d3e0cc57bbd862b606517c82bea45bc0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 17 Jul 2021 19:53:18 +0200 Subject: [PATCH 03/12] Simplify --- R/subsetting.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/subsetting.R b/R/subsetting.R index 5dbd5dab4..f10d39021 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -662,7 +662,7 @@ fast_nrow <- function(x) { vectbl_assign <- function(x, i, value) { if (!is.logical(value) && vec_size(i) == vec_size(value) && .Call("tibble_need_coerce", x)) { # x is a vector full of logical NAs here! - x <- value[NA_integer_][x] + x <- value[x] } vec_slice(x, i) <- value From 99352e6e5a5a35a22c67ae2990a069229c221a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sat, 17 Jul 2021 21:12:46 +0200 Subject: [PATCH 04/12] Prefer vec_slice() for compound vectors --- R/subsetting.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/subsetting.R b/R/subsetting.R index f10d39021..39db6d602 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -661,8 +661,7 @@ fast_nrow <- function(x) { vectbl_assign <- function(x, i, value) { if (!is.logical(value) && vec_size(i) == vec_size(value) && .Call("tibble_need_coerce", x)) { - # x is a vector full of logical NAs here! - x <- value[x] + x <- vec_slice(value, rep(NA_integer_, length(x))) } vec_slice(x, i) <- value From 99d9fcbd0f846ead33c5c2014b3bdc099eb92c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 18 Jul 2021 14:58:11 +0200 Subject: [PATCH 05/12] Simplify --- R/subsetting.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/subsetting.R b/R/subsetting.R index 39db6d602..0138f33ad 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -660,7 +660,7 @@ fast_nrow <- function(x) { } vectbl_assign <- function(x, i, value) { - if (!is.logical(value) && vec_size(i) == vec_size(value) && .Call("tibble_need_coerce", x)) { + if (!is.logical(value) && .Call("tibble_need_coerce", x)) { x <- vec_slice(value, rep(NA_integer_, length(x))) } From 6297435474a16d0eac34fbf63c14e87b4b1a0fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 18 Jul 2021 15:04:58 +0200 Subject: [PATCH 06/12] Also fix NA on RHS --- R/subsetting.R | 11 +++++-- tests/testthat/_snaps/subsetting.md | 48 +++++++++++++++++++++++++++++ tests/testthat/test-subsetting.R | 39 +++++++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/R/subsetting.R b/R/subsetting.R index 0138f33ad..064e7d2d4 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -660,8 +660,15 @@ fast_nrow <- function(x) { } vectbl_assign <- function(x, i, value) { - if (!is.logical(value) && .Call("tibble_need_coerce", x)) { - x <- vec_slice(value, rep(NA_integer_, length(x))) + if (is.logical(value)) { + if (.Call("tibble_need_coerce", value)) { + vec_slice(x, i) <- vec_slice(x, NA_integer_) + return(x) + } + } else { + if (.Call("tibble_need_coerce", x)) { + x <- vec_slice(value, rep(NA_integer_, length(x))) + } } vec_slice(x, i) <- value diff --git a/tests/testthat/_snaps/subsetting.md b/tests/testthat/_snaps/subsetting.md index 35525c7d7..de5abdc99 100644 --- a/tests/testthat/_snaps/subsetting.md +++ b/tests/testthat/_snaps/subsetting.md @@ -567,6 +567,54 @@ 1 5 2 NA 3 NA + Code + # # [<-.tbl_df and overwriting with NA + df <- tibble(a = TRUE, b = 1L, c = sqrt(2), d = 0+3i + 1, e = "e", f = raw(1), + g = tibble(x = 1, y = 1), h = matrix(1:3, nrow = 1)) + df[FALSE, "a"] <- NA + df[FALSE, "b"] <- NA + df[FALSE, "c"] <- NA + df[FALSE, "d"] <- NA + df[FALSE, "e"] <- NA + df[FALSE, "f"] <- NA + df[FALSE, "g"] <- NA + df[FALSE, "h"] <- NA + df + Output + # A tibble: 1 x 8 + a b c d e f g$x $y h[,1] [,2] [,3] + + 1 TRUE 1 1.41 1+3i e 00 1 1 1 2 3 + Code + df[integer(), "a"] <- NA + df[integer(), "b"] <- NA + df[integer(), "c"] <- NA + df[integer(), "d"] <- NA + df[integer(), "e"] <- NA + df[integer(), "f"] <- NA + df[integer(), "g"] <- NA + df[integer(), "h"] <- NA + df + Output + # A tibble: 1 x 8 + a b c d e f g$x $y h[,1] [,2] [,3] + + 1 TRUE 1 1.41 1+3i e 00 1 1 1 2 3 + Code + df[1, "a"] <- NA + df[1, "b"] <- NA + df[1, "c"] <- NA + df[1, "d"] <- NA + df[1, "e"] <- NA + df[1, "f"] <- NA + df[1, "g"] <- NA + df[1, "h"] <- NA + df + Output + # A tibble: 1 x 8 + a b c d e f g$x $y h[,1] [,2] [,3] + + 1 NA NA NA NA 00 NA NA NA NA NA Code # # [<-.tbl_df and matrix subsetting foo <- tibble(a = 1:3, b = letters[1:3]) diff --git a/tests/testthat/test-subsetting.R b/tests/testthat/test-subsetting.R index e3ab7fc03..d6bf51060 100644 --- a/tests/testthat/test-subsetting.R +++ b/tests/testthat/test-subsetting.R @@ -922,6 +922,45 @@ test_that("output test", { df[1, "x"] <- 5 df + "# [<-.tbl_df and overwriting with NA" + df <- tibble( + a = TRUE, + b = 1L, + c = sqrt(2), + d = 3i + 1, + e = "e", + f = raw(1), + g = tibble(x = 1, y = 1), + h = matrix(1:3, nrow = 1) + ) + df[FALSE, "a"] <- NA + df[FALSE, "b"] <- NA + df[FALSE, "c"] <- NA + df[FALSE, "d"] <- NA + df[FALSE, "e"] <- NA + df[FALSE, "f"] <- NA + df[FALSE, "g"] <- NA + df[FALSE, "h"] <- NA + df + df[integer(), "a"] <- NA + df[integer(), "b"] <- NA + df[integer(), "c"] <- NA + df[integer(), "d"] <- NA + df[integer(), "e"] <- NA + df[integer(), "f"] <- NA + df[integer(), "g"] <- NA + df[integer(), "h"] <- NA + df + df[1, "a"] <- NA + df[1, "b"] <- NA + df[1, "c"] <- NA + df[1, "d"] <- NA + df[1, "e"] <- NA + df[1, "f"] <- NA + df[1, "g"] <- NA + df[1, "h"] <- NA + df + "# [<-.tbl_df and matrix subsetting" foo <- tibble(a = 1:3, b = letters[1:3]) foo[!is.na(foo)] <- "bogus" From 56999727dbcefdde65f8d0b8bb2f89532d629e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 20 Jul 2021 05:56:25 +0200 Subject: [PATCH 07/12] Avoid early return --- R/subsetting.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/subsetting.R b/R/subsetting.R index 064e7d2d4..228e43233 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -662,8 +662,7 @@ fast_nrow <- function(x) { vectbl_assign <- function(x, i, value) { if (is.logical(value)) { if (.Call("tibble_need_coerce", value)) { - vec_slice(x, i) <- vec_slice(x, NA_integer_) - return(x) + value <- vec_slice(x, NA_integer_) } } else { if (.Call("tibble_need_coerce", x)) { From 000fb0a5ad8ab6e5667e539fb534a55eabfa598e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 20 Jul 2021 17:24:56 +0200 Subject: [PATCH 08/12] Prefer vec_assign() Co-authored-by: Davis Vaughan --- R/subsetting.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/subsetting.R b/R/subsetting.R index 228e43233..c195b05a9 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -670,7 +670,7 @@ vectbl_assign <- function(x, i, value) { } } - vec_slice(x, i) <- value + x <- vec_assign(x, i, value) x } From 4e91cbe90ad46622ed827657e0f9ec3aa52bba25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 20 Jul 2021 18:01:11 +0200 Subject: [PATCH 09/12] Import vec_assign() --- NAMESPACE | 1 + R/tibble-package.R | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index c86b53647..83e28b7e6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -93,6 +93,7 @@ importFrom(vctrs,vec_as_location2) importFrom(vctrs,vec_as_names) importFrom(vctrs,vec_as_names_legacy) importFrom(vctrs,vec_as_subscript2) +importFrom(vctrs,vec_assign) importFrom(vctrs,vec_c) importFrom(vctrs,vec_is) importFrom(vctrs,vec_ptype_abbr) diff --git a/R/tibble-package.R b/R/tibble-package.R index 0c36a6bad..572341092 100644 --- a/R/tibble-package.R +++ b/R/tibble-package.R @@ -5,7 +5,7 @@ #' @import lifecycle #' @import ellipsis #' @importFrom vctrs vec_as_location vec_as_location2 vec_as_names vec_as_names_legacy vec_c -#' @importFrom vctrs vec_is vec_rbind vec_recycle vec_size vec_slice vec_slice<- +#' @importFrom vctrs vec_is vec_rbind vec_recycle vec_size vec_slice vec_assign #' @importFrom vctrs unspecified vec_as_subscript2 num_as_location vec_ptype_abbr #' @aliases NULL tibble-package #' @details From 0b75c8d187e7c080aeb57bc8a3a3993b2f08801b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 20 Jul 2021 18:07:47 +0200 Subject: [PATCH 10/12] Remove vec_slice<-() --- NAMESPACE | 1 - R/subsetting-matrix.R | 13 ++++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 83e28b7e6..52b94aa1d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -85,7 +85,6 @@ importFrom(pillar,type_sum) importFrom(pkgconfig,set_config) importFrom(utils,head) importFrom(utils,tail) -importFrom(vctrs,"vec_slice<-") importFrom(vctrs,num_as_location) importFrom(vctrs,unspecified) importFrom(vctrs,vec_as_location) diff --git a/R/subsetting-matrix.R b/R/subsetting-matrix.R index 619bcc2d2..525c2eff4 100644 --- a/R/subsetting-matrix.R +++ b/R/subsetting-matrix.R @@ -28,9 +28,7 @@ tbl_subassign_matrix <- function(x, j, value, j_arg, value_arg) { withCallingHandlers( for (j in col_idx) { - xj <- x[[j]] - vec_slice(xj, cells[[j]]) <- value - x[[j]] <- xj + x[[j]] <- vectbl_assign_matrix(x[[j]], cells[[j]], value) }, vctrs_error_incompatible_type = function(cnd) { @@ -63,6 +61,15 @@ cells_to_col_idx <- function(cells) { col_idx } +vectbl_assign_matrix <- function(x, i, value) { + d <- dim(x) + dn <- dimnames(x) + x <- vectbl_assign(x, i, value) + dim(x) <- d + dimnames(x) <- dn + x +} + # Errors ------------------------------------------------------------------ error_subset_matrix_must_be_logical <- function(j_arg) { From 22544f1a778c9290e371e23d3582a8f5c95ade24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 20 Jul 2021 18:07:52 +0200 Subject: [PATCH 11/12] Direct return --- R/subsetting.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/subsetting.R b/R/subsetting.R index c195b05a9..97a582bd3 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -670,8 +670,7 @@ vectbl_assign <- function(x, i, value) { } } - x <- vec_assign(x, i, value) - x + vec_assign(x, i, value) } vectbl_strip_names <- function(x) { From f30b5d95150b4652bdfcb249dc61618a1bfbe55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Tue, 20 Jul 2021 19:27:08 +0200 Subject: [PATCH 12/12] Need to preserve dim and dimnames --- R/subsetting-matrix.R | 11 +---------- R/subsetting.R | 4 ++++ tests/testthat/_snaps/subsetting.md | 16 +++++++++------- tests/testthat/test-subsetting.R | 3 ++- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/R/subsetting-matrix.R b/R/subsetting-matrix.R index 525c2eff4..f643039ef 100644 --- a/R/subsetting-matrix.R +++ b/R/subsetting-matrix.R @@ -28,7 +28,7 @@ tbl_subassign_matrix <- function(x, j, value, j_arg, value_arg) { withCallingHandlers( for (j in col_idx) { - x[[j]] <- vectbl_assign_matrix(x[[j]], cells[[j]], value) + x[[j]] <- vectbl_assign(x[[j]], cells[[j]], value) }, vctrs_error_incompatible_type = function(cnd) { @@ -61,15 +61,6 @@ cells_to_col_idx <- function(cells) { col_idx } -vectbl_assign_matrix <- function(x, i, value) { - d <- dim(x) - dn <- dimnames(x) - x <- vectbl_assign(x, i, value) - dim(x) <- d - dimnames(x) <- dn - x -} - # Errors ------------------------------------------------------------------ error_subset_matrix_must_be_logical <- function(j_arg) { diff --git a/R/subsetting.R b/R/subsetting.R index d5ecdee61..b818bfe22 100644 --- a/R/subsetting.R +++ b/R/subsetting.R @@ -668,7 +668,11 @@ vectbl_assign <- function(x, i, value) { } } else { if (.Call("tibble_need_coerce", x)) { + d <- dim(x) + dn <- dimnames(x) x <- vec_slice(value, rep(NA_integer_, length(x))) + dim(x) <- d + dimnames(x) <- dn } } diff --git a/tests/testthat/_snaps/subsetting.md b/tests/testthat/_snaps/subsetting.md index de5abdc99..db611d566 100644 --- a/tests/testthat/_snaps/subsetting.md +++ b/tests/testthat/_snaps/subsetting.md @@ -557,16 +557,18 @@ `NULL` must be a vector, a bare list, a data frame or a matrix. Code # # [<-.tbl_df and overwriting NA - df <- tibble(x = rep(NA, 3)) + df <- tibble(x = rep(NA, 3), z = matrix(NA, ncol = 2, dimnames = list(NULL, c( + "a", "b")))) df[1, "x"] <- 5 + df[1, "z"] <- 5 df Output - # A tibble: 3 x 1 - x - - 1 5 - 2 NA - 3 NA + # A tibble: 3 x 2 + x z[,"a"] [,"b"] + + 1 5 5 5 + 2 NA NA NA + 3 NA NA NA Code # # [<-.tbl_df and overwriting with NA df <- tibble(a = TRUE, b = 1L, c = sqrt(2), d = 0+3i + 1, e = "e", f = raw(1), diff --git a/tests/testthat/test-subsetting.R b/tests/testthat/test-subsetting.R index ff7e261d8..24352c57d 100644 --- a/tests/testthat/test-subsetting.R +++ b/tests/testthat/test-subsetting.R @@ -924,8 +924,9 @@ test_that("output test", { df[1:3, 1:3] <- NULL "# [<-.tbl_df and overwriting NA" - df <- tibble(x = rep(NA, 3)) + df <- tibble(x = rep(NA, 3), z = matrix(NA, ncol = 2, dimnames = list(NULL, c("a", "b")))) df[1, "x"] <- 5 + df[1, "z"] <- 5 df "# [<-.tbl_df and overwriting with NA"