2

Following How to convert column types in R tidyverse I am trying to convert doubles (numeric) into integers.

For example, using the iris data:

iris1 <- iris %>%
  mutate_at(vars(Petal.Length), integer)

The above throws an error, which I cannot understand despite following the recommended trouble shooting:

Error: Problem with `mutate()` column `Petal.Length`.
ℹ `Petal.Length = (function (length = 0L) ...`
.x invalid 'length' argument

Using the same line of code to convert to factor and the results are fine:

iris1 <- iris %>%
mutate_at(vars(Petal.Length), factor)
class(iris1$Petal.Length)

Can someone explain to me the reason for the error and how I can convert to integer. Ideally I am looking for a solution friendly to the pipe operator %>%.

Rui Barradas
  • 70,273
  • 8
  • 34
  • 66
rainbird
  • 193
  • 1
  • 9

1 Answers1

3

I think the problem you are running into is that integer() creates an integer-typed vector, which is just an empty place-holder for integers to go into. What you want is as.integer(), which takes an existing numeric vector and coerces each element into an integer. It's a minor distinction, so it's easy to miss. Here is your code with the fix implemented:

A technically correct answer

iris1 <- iris %>%
  mutate_at(vars(Petal.Length), as.integer) # note the AS.INTERGER()

A more modern approach

You might be interested to know that mutate_at() has been superseded by the more modern mutate(across(...))) approach. Depending on your goals, you might prefer the more modern way to solve this problem with dplyr:

iris2 <- iris %>% 
  mutate(across(
    .cols = matches('Petal.Length'),
    .fns = ~ as.integer(.x)))

Don't forget about rounding

Just a heads up, coercing something into an integer drops any decimal values, which is basically like rounding down every value to the nearest (lower) integer. You might want to round your numbers first, then coerce them to integer format, depending on your other goals for this code.

iris1 <- iris %>%
  mutate_at(vars(Petal.Length), ~ as.integer(round(.x)))

OR

iris2 <- iris %>% 
  mutate(across(
    .cols = matches('Petal.Length'),
    .fns = ~ round(as.integer(.x))))
Ian Cero
  • 360
  • 1
  • 8
  • Thanks for this excellent and detailed answer. The suggested solution works perfectly. I'm new to the tidyverse workflow so the explanations of the newer mutate options are also very helpful. – rainbird May 31 '21 at 18:04
  • 1
    It may be worthy mentioning across() can be combined with where() to subset the data to enter the function, as in `iris %>% mutate(across(where(is.numeric), ~ as.integer(round(.x))))` – GuedesBF May 31 '21 at 18:17
  • Thanks! I'm glad to have had the chance to help. If you feel like the answer met your needs, feel free to select it as the solution too (no pressure of course). – Ian Cero Jun 01 '21 at 01:57