How to concatenate factors, without them being converted to integer level?

Viewed 26732

I was surprised to see that R will coerce factors into a number when concatenating vectors. This happens even when the levels are the same. For example:

> facs <- as.factor(c("i", "want", "to", "be", "a", "factor", "not", "an", "integer"))
> facs
[1] i       want    to      be      a       factor  not     an      integer
Levels: a an be factor i integer not to want
> c(facs[1 : 3], facs[4 : 5])
[1] 5 9 8 3 1

what is the idiomatic way to do this in R (in my case these vectors can be pretty large)? Thank you.

9 Answers

Use fct_c from the forcats package (part of the tidyverse).

> library(forcats)
> facs <- as.factor(c("i", "want", "to", "be", "a", "factor", "not", "an", "integer"))
> fct_c(facs[1:3], facs[4:5])
[1] i    want to   be   a
Levels: a an be factor i integer not to want

fct_c isn't fooled by concatenations of factors with discrepant numerical codings:

> x <- as.factor(c('c', 'z'))
> x
[1] c z
Levels: c z
> y <- as.factor(c('a', 'b', 'z'))
> y
[1] a b z
Levels: a b z
> c(x, y)
[1] 1 2 1 2 3
> fct_c(x, y)
[1] c z a b z
Levels: c z a b
> as.numeric(fct_c(x, y))
[1] 1 2 3 4 2

Based on the other answers which use converting to character I'm using the following function to concatenate factors:

concat.factor <- function(...){
  as.factor(do.call(c, lapply(list(...), as.character)))
}

You can use this function just as you would use c.

Just a quick note to point out that as of of R 4.1.0, this is directly addressed in base R. You can now just intuitively do

c(facs[1 : 3], facs[4 : 5])
Related