Split word in column in R

Viewed 284

I have a data frame with multiple columns in R. I want to split the "age" column into two column, each with one string in it.

         fas       value age colony
   1:  C12:0 0.002221915  LO   7_13
   2:  C13:0 0.000770179  LO   7_13
   3:  C14:0 0.004525352  LO   7_13
   4:  C15:0 0.000738928  LO   7_13
   5: C16:1a 0.002964627  LO   7_13

Output:

         fas           value size age colony
       1:  C12:0 0.002221915    L   O   7_13
       2:  C13:0 0.000770179    L   O   7_13
       3:  C14:0 0.004525352    L   O   7_13
       4:  C15:0 0.000738928    L   O   7_13
       5: C16:1a 0.002964627    L   O   7_13

I tried:

data_frame<-str_split_fixed(df$age, "", 2)
6 Answers

With base R:

df$size <- substr(df$age,1,1)
df$age  <- substr(df$age,2,2)

And to get the result in the column order you specified:

df[,c("fas","value","age","size","colony")]
     fas       value age size colony
1  C12:0 0.002221915   O    L   7_13
2  C13:0 0.000770179   O    L   7_13
3  C14:0 0.004525352   O    L   7_13
4  C15:0 0.000738928   O    L   7_13
5 C16:1a 0.002964627   O    L   7_13

Since your data appears to be data.table, I'll infer that package is loaded. However, strcapture is base R and will return a data.frame with the two columns (names/classes based on the third argument, proto=).

strcapture("(.)(.)", DT$age, list(size="", age=""))
#   size age
# 1    L   O
# 2    L   O
# 3    L   O
# 4    L   O
# 5    L   O

library(data.table)
DT[, c("size", "age") := strcapture("(.)(.)", age, list(size="", age="")) ]
DT
#       fas       value    age colony   size
#    <char>       <num> <char> <char> <char>
# 1:  C12:0 0.002221915      O   7_13      L
# 2:  C13:0 0.000770179      O   7_13      L
# 3:  C14:0 0.004525352      O   7_13      L
# 4:  C15:0 0.000738928      O   7_13      L
# 5: C16:1a 0.002964627      O   7_13      L

You may choose to be more defensive in the pattern, shifting to "^(.)(.)$", which should not match anything outside of our 2-char expectation.


Data

DT <- data.table::fread(text="
   fas       value age colony
 C12:0 0.002221915  LO   7_13
 C13:0 0.000770179  LO   7_13
 C14:0 0.004525352  LO   7_13
 C15:0 0.000738928  LO   7_13
C16:1a 0.002964627  LO   7_13")

You can use suband backreference:

df$age <- sub("(^\\w)(\\w$)", "\\1", df$age)
df$size <- sub("(^\\w)(\\w$)", "\\2", df$age)

The tidyverse solution uses tidyr::separate():

library("tidyr")

tbl <- read.table(header = TRUE, text = "
   fas       value age colony
 C12:0 0.002221915  LO   7_13
 C13:0 0.000770179  LO   7_13
 C14:0 0.004525352  LO   7_13
 C15:0 0.000738928  LO   7_13
C16:1a 0.002964627  LO   7_13")

separate(tbl, age, c("age", "size"), 1)
#>      fas       value age size colony
#> 1  C12:0 0.002221915   L    O   7_13
#> 2  C13:0 0.000770179   L    O   7_13
#> 3  C14:0 0.004525352   L    O   7_13
#> 4  C15:0 0.000738928   L    O   7_13
#> 5 C16:1a 0.002964627   L    O   7_13

Created on 2021-02-21 by the reprex package (v1.0.0)

To split the column age (contains 2 characters "LO") you can

  1. remove last character gsub('.{1}$', '', df$age) you get "L"

  2. remove first character sub('.', '', df$age) you get "O"

df %>% 
  mutate(size = gsub('.{1}$', '', df$age), # remove last character 
         age = sub('.', '', df$age)) # remove first character 

enter image description here

In addition to tidyr::separate (which is usually the way to go here), we can also use tidyr::extract:

tbl %>% tidyr::extract(age,
                       into = c('age', 'size'),
                       regex = "(.)(.)",
                       remove = TRUE)

     fas       value age size colony
1  C12:0 0.002221915   L    O   7_13
2  C13:0 0.000770179   L    O   7_13
3  C14:0 0.004525352   L    O   7_13
4  C15:0 0.000738928   L    O   7_13
5 C16:1a 0.002964627   L    O   7_13
Related