Easy way to convert text columns to numeric

Viewed 126

I have this example dataset

x  <- c("hot", "cold", "warm", "hot", "hot")
y <- c("happy", "content", "happy", "sad", "annoyed")
df <- data.frame(x, y)

I want to find a quick way to convert the text to numbers, it doesn't matter which order the numbers are.

So the output would be:

x  y
1  1
2  2
3  1
1  3
1  4

Many Thanks

4 Answers

With Base R:

df[] <- lapply(df, function(x) as.numeric(as.factor(x)))
df
#>   x y
#> 1 2 3
#> 2 1 2
#> 3 3 3
#> 4 2 4
#> 5 2 1

With purrr:

library(purrr)
df %>% map(as.factor) %>% map_dfc(as.numeric)
#> # A tibble: 5 x 2
#>       x     y
#>   <dbl> <dbl>
#> 1     2     3
#> 2     1     2
#> 3     3     3
#> 4     2     4
#> 5     2     1

Keep track of the labels with labelled:

df <- df %>% map(as.factor) %>% map_dfc(labelled::to_labelled)
df
#> # A tibble: 5 x 2
#>           x           y
#>   <dbl+lbl>   <dbl+lbl>
#> 1  2 [hot]  3 [happy]  
#> 2  1 [cold] 2 [content]
#> 3  3 [warm] 3 [happy]  
#> 4  2 [hot]  4 [sad]    
#> 5  2 [hot]  1 [annoyed]

df$x
#> <labelled<double>[5]>
#> [1] 2 1 3 2 2
#> 
#> Labels:
#>  value label
#>      1  cold
#>      2   hot
#>      3  warm

Or keep the numbers next to the original values in a new column:

df[paste0(names(df), "_num")] <- lapply(df, function(x) as.numeric(as.factor(x)))
df
#>      x       y x_num y_num
#> 1  hot   happy     2     3
#> 2 cold content     1     2
#> 3 warm   happy     3     3
#> 4  hot     sad     2     4
#> 5  hot annoyed     2     1

If you want to change only the character columns to numeric:

library(purrr)

df %>% map_if(is.character, as.factor) %>% map_dfc(as.numeric)
df %>% map_if(is.character, as.factor) %>% map_dfc(labelled::to_labelled)

Or choose them by name:

library(purrr)

cols <- c("x", "y") 

df %>% map_at(cols, as.factor) %>% map_dfc(as.numeric)
df %>% map_at(cols, as.factor) %>% map_dfc(labelled::to_labelled)
df[paste0(cols, "_num")] <- lapply(df[cols], function(x) as.numeric(as.factor(x)))

Maybe try this with dplyr:

library(dplyr)
#Code
newdf <- df %>% mutate(across(everything(),~as.numeric(as.factor(.))))

Output:

  x y
1 2 3
2 1 2
3 3 3
4 2 4
5 2 1

In order to see the values, you can try this:

#Code 2
newdf2 <- df %>% mutate(across(everything(),~as.factor(.))) %>%
  mutate(across(everything(),.fns = list(value = ~ as.numeric(.))))

Output:

     x       y x_value y_value
1  hot   happy       2       3
2 cold content       1       2
3 warm   happy       3       3
4  hot     sad       2       4
5  hot annoyed       2       1

If we add a numeric variable, this should work:

#Code 3
newdf <- df %>% mutate(across(x:y,~as.factor(.))) %>%
  mutate(across(x:y,.fns = list(value = ~ as.numeric(.))))

Output:

     x       y number x_value y_value
1  hot   happy     10       2       3
2 cold content     20       1       2
3 warm   happy     30       3       3
4  hot     sad     40       2       4
5  hot annoyed     50       2       1

You could use rapply:

rapply(type.convert(df), function(x)as.integer(factor(x, unique(x))),'factor',how = 'replace')

  x y
1 1 1
2 2 2
3 3 1
4 1 3
5 1 4

We can use match

df[] <- lapply(df, function(x) match(x, unique(x)))
Related