如何将一列字符串分成多列,每列包含一个字符串的单个字符,字符串长度不等且没有分隔符?

How to separate a column of strings into multiple columns, each containing a single char of a string, with strings of unequal length and no separator?

我的数据框是这样的:

data.frame(stringsAsFactors=FALSE,
       A = c("1234", "abc.", "e-2.1ad"),
       B = c("5-4", "1-0", "a,d")
)

我想将列分隔成包含单个字符的多个列。

我找到的其他答案,都涉及使用一些正则表达式或模式或分隔符,如你所见,我不能在这里这样做,或者使用 sapply 的复杂解决方案(使用位置,但对我来说它没有用)。 我确信那里有一个更优雅的解决方案,如果可能的话,我真的很喜欢使用 tidyr 的解决方案,但无论它做什么干净利落,我都非常感激。

这是应该的,毕竟说了算了:

 newdf <- data.frame(stringsAsFactors=FALSE,
      A1 = c("1", "a", "e"),
      A2 = c("2", "b", "-"),
      A3 = c("3", "c", "2"),
      A4 = c("4", ".", "."),
      A5 = c(NA, NA, 1),
      A6 = c(NA, NA, "a"),
      A7 = c(NA, NA, "d"),
      B1 = c("5", "1", "a"),
      B2 = c("-", "-", ","),
      B3 = c("4", "0", "d")
)

而且,如果答案不仅仅是抛出一两个函数,我将不胜感激,如果你能解释你是如何去做的,而不仅仅是解决方案本身。谢谢!

稍后编辑:我几乎可以使用 qdap 包来完成它,但我可以绕过它从一开始就用字符填充应该是 NA 的内容(因为字符串的长度不等)的字符串。文档中没有解释的非常奇怪的行为,否则是一个非常有前途的功能。

我在解决这个问题的蹩脚尝试中注意到的另一个奇怪行为是自动从字符转换为因子。但是,我无法确定它发生的位置。

这是我的 tidyverse 解决方案。编写函数对我来说是新的,任何改进建议将不胜感激。

library(tidyverse)
df <- data.frame(stringsAsFactors=FALSE,
        A = c("1234", "abc.", "e-2.1ad"),
        B = c("5-432", "1-0", "a,d"))    

a_split<- str_split(df$A, "")
b_split<- str_split(df$B, "")
f1 <- function(num, s)(c(s[[1]][num], s[[2]][num], s[[3]][num]))
x <- c(1:7)
all_a <- lapply(x, f1, a_split)
x <- c(1:5)
all_b <- lapply(x, f1, b_split)

我们可以使用 splitstackshape 中的 cSplit 并将列 AB 中的每个字符拆分为单独的列

df1 <- splitstackshape::cSplit(df, c('A', 'B'), sep = '', stripWhite = FALSE)
df1

#   A_1 A_2 A_3 A_4 A_5  A_6  A_7 B_1 B_2 B_3 B_4 B_5 B_6 B_7
#1:   1   2   3   4  NA <NA> <NA>   5   -   4   3   2  NA  NA
#2:   a   b   c   .  NA <NA> <NA>   1   -   0  NA  NA  NA  NA
#3:   e   -   2   .   1    a    d   a   ,   d  NA  NA  NA  NA

然而,这给了我一些额外的列 NA for B 可以使用 Filter

删除
Filter(function(x) any(!is.na(x)), df1)
#   A_1 A_2 A_3 A_4 A_5  A_6  A_7 B_1 B_2 B_3 B_4 B_5
#1:   1   2   3   4  NA <NA> <NA>   5   -   4   3   2
#2:   a   b   c   .  NA <NA> <NA>   1   -   0  NA  NA
#3:   e   -   2   .   1    a    d   a   ,   d  NA  NA

数据

df <- data.frame(stringsAsFactors=FALSE,
             A = c("1234", "abc.", "e-2.1ad"),
             B = c("5-432", "1-0", "a,d"))

有许多可能的选项,具体取决于您感兴趣的详细信息。请参阅上面@Elin 关于 5-432 中缺少 32 的评论。

要考虑的一种可能性是 str_split_fixed 来自 stringr 包:

str_split_fixed("1234", "", 7)

     [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] "1"  "2"  "3"  "4"  ""   ""   ""  

一个空模式“”将按字符拆分,在这种情况下尝试 return 7 个字符矩阵(最后 3 个空字符串)。现在,如果没有字符可用,它 return 是一个空字符串,而不是 NA。 (参见 github issue)。

如果列数基于 A 列和 B 列可能的最大字符数(例如 7 和 5),则可以执行以下操作:

as.data.frame(lapply(df, function(x) str_split_fixed(x, "", n=max(nchar(x)))))

  A.1 A.2 A.3 A.4 A.5 A.6 A.7 B.1 B.2 B.3 B.4 B.5
1   1   2   3   4               5   -   4   3   2
2   a   b   c   .               1   -   0        
3   e   -   2   .   1   a   d   a   ,   d        

注意:之后要用NA替换空字符串:

df[df==""] <- NA

  A.1 A.2 A.3 A.4  A.5  A.6  A.7 B.1 B.2 B.3  B.4  B.5
1   1   2   3   4 <NA> <NA> <NA>   5   -   4    3    2
2   a   b   c   . <NA> <NA> <NA>   1   -   0 <NA> <NA>
3   e   -   2   .    1    a    d   a   ,   d <NA> <NA>