Разделить строку без потери символа — R

У меня есть два столбца в гораздо большем фрейме данных, который мне сложно разбить. Я использовал strsplit в прошлом, когда пытался разделить с помощью «пробела», «,» или какого-либо другого разделителя. Сложность здесь в том, что я не хочу потерять какую-либо информацию, И когда я разделю некоторые части, я получу недостающую информацию. В конце я хотел бы получить четыре столбца. Вот пример пары строк того, что у меня есть сейчас.

age-gen  surv-camp
45M      1LC
9F       0
12M      1AC
67M      1LC

Вот что я хотел бы получить в итоге.

age   gen   surv   camp
45    M     1      LC
9     F     0      
12    M     1      AC
67    M     1      LC

Я довольно много искал здесь и нашел ряд ответов на Java, C++, html и т. д., но я не нашел ничего, что объясняло бы, как это сделать в R и когда у вас отсутствуют данные.

Я видел это о добавлении пробела между значениями, а затем просто разбиение по пространству, но я не понимаю, как это будет работать 1) с отсутствующими данными, 2) когда у меня нет согласованных числовых или символьных значений в каждой строке.


person Sam Marshal    schedule 10.09.2016    source источник
comment
Вы по-прежнему можете использовать strsplit() и сохранять разделенные значения с помощью perl. Какой у вас был код?   -  person Rich Scriven    schedule 10.09.2016


Ответы (1)


Мы перебираем столбцы 'df1' (lapply(df1, ..), создаем разделитель после числовой подстроки, используя sub, читаем vector как data.frame с read.table, rbind list из data.frames и меняем имена столбцов вывода.

res <- do.call(cbind, lapply(df1, function(x)
      read.table(text=sub("(\\d+)", "\\1,", x), 
          header=FALSE, sep=",", stringsAsFactors=FALSE)))
colnames(res) <- scan(text=names(df1), sep=".", what="", quiet = TRUE)
res
#  age gen surv camp
#1  45   M    1   LC
#2   9   F    0     
#3  12   M    1   AC
#4  67   M    1   LC

Или используя separate из tidyr

library(tidyr)
library(dplyr)
separate(df1, age.gen, into = c("age", "gen"), "(?<=\\d)(?=[A-Za-z])", convert= TRUE) %>% 
       separate(surv.camp, into = c("surv", "camp"), "(?<=\\d)(?=[A-Za-z])", convert = TRUE)
#  age gen surv camp
#1  45   M    1   LC
#2   9   F    0 <NA>
#3  12   M    1   AC
#4  67   M    1   LC

Или, как упомянул @Frank, мы можем использовать tstrsplit из data.table

library(data.table)
setDT(df1)[, unlist(lapply(.SD, function(x) 
    tstrsplit(x, "(?<=[0-9])(?=[a-zA-Z])", perl=TRUE, 
                        type.convert=TRUE)), recursive = FALSE)]

РЕДАКТИРОВАТЬ: добавлено convert = TRUE в separate для изменения type столбцов после разделения.

данные

df1 <- structure(list(age.gen = c("45M", "9F", "12M", "67M"), surv.camp = c("1LC", 
 "0", "1AC", "1LC")), .Names = c("age.gen", "surv.camp"), 
class = "data.frame", row.names = c(NA, -4L))
person akrun    schedule 10.09.2016
comment
Использование separate сработало отлично. Спасибо. Мне не удалось адаптировать первое предложение и заставить его работать успешно, но я воспользуюсь логикой, которую вы написали в начале, и посмотрю, повезет ли мне. - person Sam Marshal; 10.09.2016
comment
@SamMarshal Возможно, в вашем исходном наборе данных есть некоторые шаблоны, которые не совпадают с тем, который вы показали. - person akrun; 10.09.2016
comment
Возможно, стоит также показать способ data.table, у которого есть хорошая функция type.convert (не уверен, что separate есть): data.table::tstrsplit(x, "(?<=[0-9])(?=[a-zA-Z])", perl=TRUE, type.convert=TRUE) - person Frank; 10.09.2016
comment
@akrun это тоже была моя мысль, поэтому я буду работать над логикой на каждом этапе и посмотрю, смогу ли я найти, где что-то застревает. - person Sam Marshal; 10.09.2016
comment
@Frank Да, у separate тоже есть эта опция, но по умолчанию это FALSE - person akrun; 10.09.2016