使用 separate 拆分列中不均匀数量的变量

Using separate to split uneven number of variables in a column

我有这样的数据:

x <- c("France:4|Morroco:8|Italy:2", "Scotland:6|Mexico:2", "Scotland:2")
> player_country_info <- data.frame(x)
> setnames(player_country_info, "player_country_data")
> names(player_country_info)
[1] "player_country_data"
> is.data.frame(player_country_info)
[1] TRUE
> head(player_country_info)
                country_data
1 France:4|Morocco:8|Italy:2
2        Scotland:6|Mexico:2
3                 Scotland:2

我想要一个如下所示的中间数据框:

player_country_data.1   player_country_data.2   player_country_data.3
France:4                Morocco:8               Italy:2
Scotland:6              Mexico:2                NA
Scotland:2              NA                      NA

我计划然后使用 dplyr::separate 函数将上面的内容分隔成这样,对每一列使用此命令。

player_country_info %>% separate( col=player_country_data.1, into=c("country_name.1","player_count.1), sep=":")

country_name.1  player_count.1  country_name.2  player.2    country_name.3 player.3
France          4               Morocco         8           Italy           2
Scotland        6               Mexico          2
Scotland        2           

是否有更有效的方法来完成上述操作?也许是一步完成的命令?或者我应该在 while 循环之外用 for 循环处理它?

谢谢

我们可以使用 cSplit

一步完成
library(splitstackshape)
cSplit(country_info, 'country_data', ':|\|', fixed = FALSE)

如果我们只需要中间步骤

cSplit(country_info, 'country_data', '|')

或者使用 tidyr,我们使用 outer 在预期输出中创建列名向量,然后在 [=] 中使用 'nm1' 指定 into 列19=].

library(tidyr)
nm1 <- c(outer(c('country_name.', 'player_count.'), 1:3, FUN = paste0))
separate(country_info, country_data, into = nm1, sep="[:|]")
#  country_name.1 player_count.1 country_name.2 player_count.2 country_name.3 player_count.3
#1         France              4        Morroco              8          Italy              2
#2       Scotland              6         Mexico              2           <NA>           <NA>
#3       Scotland              2           <NA>           <NA>           <NA>           <NA>

更新

根据OP在评论中展示的新数据

separate(player_country_info2, player_country_data, into = nm1, sep="[:|]", convert= TRUE)
#  country_name.1 player_count.1 country_name.2 player_count.2 country_name.3 player_count.3
#1         France              4        Morocco             NA          Italy              2
#2       Scotland              6         Mexico              2           <NA>             NA
#3       Scotland              2           <NA>             NA           <NA>             NA

如果这是关于效率,另一种选择是 tstrsplit 来自 data.table

library(data.table)
setnames(setDT(country_info)[, tstrsplit(country_data, '[:|]', type.convert = TRUE)], nm1)[]
#  country_name.1 player_count.1 country_name.2 player_count.2 country_name.3 player_count.3
#1:         France              4        Morroco              8          Italy              2
#2:       Scotland              6         Mexico              2             NA             NA
#3:       Scotland              2             NA             NA             NA             NA

使用 tidyr 包中的 separate

library(tidyr)
country_info %>% 
  separate(country_data, 
           into = sprintf('%s.%s', rep(c('country','player.count'),3), rep(1:3, each=2)))

结果:

  country.1 player.count.1 country.2 player.count.2 country.3 player.count.3
1    France              4   Morroco              8     Italy              2
2  Scotland              6    Mexico              2      <NA>           <NA>
3  Scotland              2      <NA>           <NA>      <NA>           <NA>

Separate 自动将 :| 识别为必须分隔的字符。如果要分隔特定字符,则需要使用 sep 参数指定。在这种情况下,您可以使用 sep = '[:|]'。这也可以防止在存在缺失值时自动检测的不当行为(请参阅评论中的讨论)。

使用 sprintf 将两个向量 rep(c('country','player.count'),3)rep(1:3, each=2) 粘贴到一个列名向量中,其中 %s.%s 告诉 sprintf 处理这两个向量向量是字符串向量,并用点作为分隔符将它们粘贴在一起。有关详细信息,请参阅 ?sprintfeach 参数告诉 rep 不要将整个向量重复多次,而是将向量的每个元素重复多次。