Разделить столбцы в dataframe с NA

У меня есть df:

df <- data.frame(FOO = c('A|B|C', 'A|B', 'B|C', 'A', 'C'))

> df
    FOO
1 A|B|C
2   A|B
3   B|C
4     A
5     C

И я хотел бы иметь такой вывод:

> df
  X1 X2 X3
1 A  B  C
2 A  B
3    B  C
4 A
5       C

До сих пор я пытался использовать этот пример: Разделить столбец в разделителе в кадре данных, но он не разделяет столбцы без повторяющихся значений, что я получаю:

df <- data.frame(do.call('rbind', strsplit(as.character(df$FOO),'|',fixed=TRUE)))

> df
  X1 X2 X3
1  A  B  C
2  A  B  A
3  B  C  B
4  A  A  A
5  C  C  C

И я также получаю это предупреждение:

Предупреждающее сообщение: В rbind (c ("A", "B", "C"), c ("A", "B"), c ("B", "C"), "A", "C", ): количество столбцов результата не кратно длины вектора (arg 2)

Что я могу сделать в этих случаях? Предпочтительно с baseR

Ответ 1

Просто выполните:

splt <- strsplit(as.character(df$FOO),"\\|")
all_val <- sort(unique(unlist(splt)))
t(sapply(splt,function(x){all_val[!(all_val %in% x)]<-NA;all_val}))


#     [,1] [,2] [,3]
#[1,] "A"  "B"  "C" 
#[2,] "A"  "B"  NA  
#[3,] NA   "B"  "C" 
#[4,] "A"  NA   NA  
#[5,] NA   NA   "C" 

данные:

df <- data.frame(FOO = c('A|B|C', 'A|B', 'B|C', 'A', 'C'))

Пожалуйста, обратите внимание:

Моя версия является base:: (нет необходимости в библиотеках) и вообще:

Он также будет работать с:

df <- data.frame(FOO = c('A|B|C', 'A|B', 'B|C', 'A', 'C', 'B|D|F'))

Ответ 2

Оказалось, что ОП обратился к base R решению. Пожалуйста, попробуйте @AndreElrico's, @r.user.05apr или @milan.


Это можно сделать с помощью cSplit_e из пакета splitstackshape:

library(splitstackshape)
cSplit_e(
  data = df,
  split.col = "FOO",
  sep = "|",
  mode = "value",
  type = "character",
  fill = " ",
  drop = TRUE
)
#  FOO_A FOO_B FOO_C
#1     A     B     C
#2     A     B      
#3           B     C
#4     A            
#5                 C

Также работает в случае следующего df (см. Комментарий выше).

(df1 <- data.frame(FOO = c('A|B|C', 'A|B', 'B|C', 'A', 'C', 'B|D|F')))
#    FOO
#1 A|B|C
#2   A|B
#3   B|C
#4     A
#5     C
#6 B|D|F

cSplit_e(df1, "FOO", "|", "value",  "character", TRUE, fill = " ")
#  FOO_A FOO_B FOO_C FOO_D FOO_F
#1     A     B     C            
#2     A     B                  
#3           B     C            
#4     A                        
#5                 C            
#6           B           D     F

Ответ 3

В базе R:

df <- data.frame(FOO = c('A|B|C', 'A|B', 'B|C', 'A', 'C'))

dummy <- strsplit(as.character(df$FOO), "[|]")
want <- data.frame(values = unlist(dummy),
                   ids = rep(1:length(dummy), unlist(lapply(dummy, length))), 
                   stringsAsFactors = FALSE)

library(reshape2)
want <- dcast(want, ids ~ values, value.var = "values", fill = " ")[, -1] # first col removed
names(want) <- paste0("X", seq_along(unique(unlist(dummy)))) 
want
# X1 X2 X3
#1  A  B  C
#2  A  B   
#3     B  C
#4  A      
#5        C

Ответ 4

Используйте unique и strsplit чтобы найти все уникальные значения (в данном случае A, B и C). Используйте grep для поиска уникальных значений и возвращайте значения, когда есть совпадение или character(0) противном случае. cbind получающиеся символы. Используйте apply и ifelse чтобы заменить character(0) на NA.

vals <- unique(unlist(sapply(a1, function(x) strsplit(x, '|', fixed = T))))

out <- NULL
for(i in vals){
  out <- cbind(out, as.character((lapply(df$FOO, function(x) grep(x, i, value=T)))))
}

apply(out, 2, function(x) ifelse(x=="character(0)", NA, x))

     [,1] [,2] [,3]
[1,] "A"  "B"  "C" 
[2,] "A"  "B"  NA  
[3,] NA   "B"  "C" 
[4,] "A"  NA   NA  
[5,] NA   NA   "C" 

Ответ 5

Вы также можете попробовать tidyverse

library(tidyverse)
df %>%
  rownames_to_column() %>% 
  separate_rows(FOO, sep="[|]") %>% 
  mutate(L=factor(FOO, labels = paste0("X",1:length(unique(FOO))))) %>% 
  spread(L, FOO) %>% 
  select(-1)
    X1   X2   X3
1    A    B    C
2    A    B <NA>
3 <NA>    B    C
4    A <NA> <NA>
5 <NA> <NA>    C

Он также обычно работает, например, df <- data.frame(FOO = c('A|B|C', 'A|B', 'B|C', 'A', 'C', 'B|D|F')). Кроме того, вы можете самостоятельно установить уровни, например B> C> A, используя levels = c("B", "C", "A") в фактор-функции на этапе мутанта.