Formatting numeric values in tibble with thousand separators gives error

Viewed 433
library(tidyverse)

separator <- function(x){
  format(as.numeric(x), big.mark = ".", decimal.mark = ",")
}

x <- c(1000)
y <- c(1000)
z <- c(1000)
df <- tibble(x, y, z)

df[ , 2:ncol(df)] <- apply(df[ , 2:ncol(df)], 2, separator)

Error:

Error: Assigned data `apply(df[, 2:ncol(df)], 2, separator)` must be compatible with existing data.
x Existing data has 1 row.
x Assigned data has 2 rows. 
i Row updates require a list value. Do you need `list()` or `as.list()`? 
Run `rlang::last_error()` to see where the error occurred.

Why does this happen? Why does the new data suddenly have 2 rows? It should just keep 1 row and add thousands separator, starting from the second variable to end.

If it's a data.frame, R doesn't complain and just does the job but not with tibbles. I know there are a few differences in tibbles and data.frames, but I couldn't find a reason why this happens.

Edit (pointed out by Ric S): The code works with tibbles, that have more than one row, but not with "one-row-tibbles".

2 Answers

Use lapply:

df[ , 2:ncol(df)] <- lapply(df[ , 2:ncol(df)], separator)

If you had posted the full error/warning message, it would be clear:

# Error: Assigned data `apply(df[, 2:ncol(df)], 2, separator)` must be compatible with existing data.
# x Existing data has 1 row.
# x Assigned data has 2 rows.
# i Row updates require a list value. Do you need `list()` or `as.list()`?
# Run `rlang::last_error()` to see where the error occurred.

Then we can check if the assignment is compatible, i.e: list class:

is.list(apply(df[ , 2:ncol(df)], 2, separator))
# [1] FALSE
is.list(lapply(df[ , 2:ncol(df)], separator))
# [1] TRUE
is.list(df[ , 2:ncol(df)])
# [1] TRUE

If you wanted to just apply the separator function to y and z columns, you could do this:

df <- df %>%
  mutate_at(vars(y, z), separator)
Related