dplyr change many data types

RDataframeDplyr

R Problem Overview


I have a data.frame:

dat <- data.frame(fac1 = c(1, 2),
                  fac2 = c(4, 5),
                  fac3 = c(7, 8),
                  dbl1 = c('1', '2'),
                  dbl2 = c('4', '5'),
                  dbl3 = c('6', '7')
                  )

To change data types I can use something like

l1 <- c("fac1", "fac2", "fac3")
l2 <- c("dbl1", "dbl2", "dbl3")
dat[, l1] <- lapply(dat[, l1], factor)
dat[, l2] <- lapply(dat[, l2], as.numeric)

with dplyr

dat <- dat %>% mutate(
    fac1 = factor(fac1), fac2 = factor(fac2), fac3 = factor(fac3),
    dbl1 = as.numeric(dbl1), dbl2 = as.numeric(dbl2), dbl3 = as.numeric(dbl3)
)

is there a more elegant (shorter) way in dplyr?

thx Christof

R Solutions


Solution 1 - R

Edit (as of 2021-03)

As also pointed out in Eric's answer, mutate_[at|if|all] has been superseded by a combination of mutate() and across(). For reference, I will add the respective pendants to the examples in the original answer (see below):

# convert all factor to character
dat %>% mutate(across(where(is.factor), as.character))

# apply function (change encoding) to all character columns 
dat %>% mutate(across(where(is.character), 
               function(x){iconv(x, to = "ASCII//TRANSLIT")}))

# subsitute all NA in numeric columns
dat %>% mutate(across(where(is.numeric), function(x) tidyr::replace_na(x, 0)))

Original answer

Since Nick's answer is deprecated by now and Rafael's comment is really useful, I want to add this as an Answer. If you want to change all factor columns to character use mutate_if:

dat %>% mutate_if(is.factor, as.character)

Also other functions are allowed. I for instance used iconv to change the encoding of all character columns:

dat %>% mutate_if(is.character, function(x){iconv(x, to = "ASCII//TRANSLIT")})

or to substitute all NA by 0 in numeric columns:

dat %>% mutate_if(is.numeric, function(x){ifelse(is.na(x), 0, x)})

Solution 2 - R

You can use the standard evaluation version of mutate_each (which is mutate_each_) to change the column classes:

dat %>% mutate_each_(funs(factor), l1) %>% mutate_each_(funs(as.numeric), l2)

Solution 3 - R

EDIT - The syntax of this answer has been deprecated, loki's updated answer is more appropriate.

ORIGINAL-

From the bottom of the ?mutate_each (at least in dplyr 0.5) it looks like that function, as in @docendo discimus's answer, will be deprecated and replaced with more flexible alternatives mutate_if, mutate_all, and mutate_at. The one most similar to what @hadley mentions in his comment is probably using mutate_at. Note the order of the arguments is reversed, compared to mutate_each, and vars() uses select() like semantics, which I interpret to mean the ?select_helpers functions.

dat %>% mutate_at(vars(starts_with("fac")),funs(factor)) %>%   
  mutate_at(vars(starts_with("dbl")),funs(as.numeric))

But mutate_at can take column numbers instead of a vars() argument, and after reading through this page, and looking at the alternatives, I ended up using mutate_at but with grep to capture many different kinds of column names at once (unless you always have such obvious column names!)

dat %>% mutate_at(grep("^(fac|fctr|fckr)",colnames(.)),funs(factor)) %>%
  mutate_at(grep("^(dbl|num|qty)",colnames(.)),funs(as.numeric))

I was pretty excited about figuring out mutate_at + grep, because now one line can work on lots of columns.

EDIT - now I see matches() in among the select_helpers, which handles regex, so now I like this.

dat %>% mutate_at(vars(matches("fac|fctr|fckr")),funs(factor)) %>%
  mutate_at(vars(matches("dbl|num|qty")),funs(as.numeric))

Another generally-related comment - if you have all your date columns with matchable names, and consistent formats, this is powerful. In my case, this turns all my YYYYMMDD columns, which were read as numbers, into dates.

  mutate_at(vars(matches("_DT$")),funs(as.Date(as.character(.),format="%Y%m%d")))

Solution 4 - R

Dplyr across function has superseded _if, _at, and _all. See vignette("colwise").

dat %>% 
mutate(across(all_of(l1), as.factor),
       across(all_of(l2), as.numeric))

Solution 5 - R

It's a one-liner with mutate_at:

dat %>% mutate_at("l1", factor) %>% mutate_at("l2", as.numeric)

Solution 6 - R

A more general way of achieving column type transformation is as follows:

If you want to transform all your factor columns to character columns, e.g., this can be done using one pipe:

df %>%  mutate_each_( funs(as.character(.)), names( .[,sapply(., is.factor)] ))

Solution 7 - R

Or mayby even more simple with convert from hablar:

library(hablar)

dat %>% 
  convert(fct(fac1, fac2, fac3),
          num(dbl1, dbl2, dbl3))

or combines with tidyselect:

dat %>% 
  convert(fct(contains("fac")),
          num(contains("dbl")))

Solution 8 - R

Try this

df[,1:11] <- sapply(df[,1:11],as.character)

Solution 9 - R

For future readers, if you are ok with dplyr guessing the column types, you can convert the col types of an entire df as if you were originally reading it in with readr and col_guess() with

library(tidyverse)
df %>% type_convert()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncklussView Question on Stackoverflow
Solution 1 - RlokiView Answer on Stackoverflow
Solution 2 - RtalatView Answer on Stackoverflow
Solution 3 - RRafael ZayasView Answer on Stackoverflow
Solution 4 - REric KrantzView Answer on Stackoverflow
Solution 5 - RnexonvantecView Answer on Stackoverflow
Solution 6 - RNickView Answer on Stackoverflow
Solution 7 - RdavsjobView Answer on Stackoverflow
Solution 8 - RRupesh KumarView Answer on Stackoverflow
Solution 9 - RLeoView Answer on Stackoverflow