如何将可变数量的串联标签的列拆分为每个标签的一列?

How to split a column of a variable number of concatenated tags into one column per tag?

考虑以下数据:

library(tibble)

key <- c("a", "b", "c", "d", "e")
tags <- c("A,B", "B", "A,E", "C,D", "")
data <- tibble(key, tags)

在这里,key 可能表示书名,tags 可能表示流派,或者 key 可能表示电子邮件发件人,而 tags 可能表示收件人。重要的是,列 tags 可以具有可变(可能为零)数量的不同子字符串。

为了拆分固定数量的连接标签(例如像数据)我可以使用 tidyr::spread,我可以使用字符串拆分来分隔 tags 列本身,但是如何组合两个?

我希望转换后的数据如下所示:

key  A     B     C     D     E
a    TRUE  TRUE  FALSE FALSE FALSE
b    FALSE TRUE  FALSE FALSE FALSE
c    TRUE  FALSE FALSE FALSE TRUE
d    FALSE FALSE TRUE  TRUE  FALSE
e    FALSE FALSE FALSE FALSE FALSE

我可以看到可以通过拆分 tags、确定唯一的子字符串并循环遍历每个子字符串并测试每一行的 tags 是否包含字符串来分几步完成此操作。但我更愿意 在使用 tidyverse 的管道中执行此操作。

问题:如何将可变数量的串联标签拆分为每个标签一列?

这是一个基本的 R 替代方法:

# get unique values in tags
x <- unique(unlist(strsplit(df$tags, ",", fixed=TRUE)))
# check for existence in the tags column
res <- sapply(paste0("(^|.*,)", x, "(,.*|$)"), grepl, df$tags)
# add sensible dimension names
dimnames(res) <- list(df$key, x)

生成的矩阵如下所示:

res
#      A     B     E     C     D
#a  TRUE  TRUE FALSE FALSE FALSE
#b FALSE  TRUE FALSE FALSE FALSE
#c  TRUE FALSE  TRUE FALSE FALSE
#d FALSE FALSE FALSE  TRUE  TRUE
#e FALSE FALSE FALSE FALSE FALSE

第三个基础 R 方法是

# get named list splitting by commas
myList <- setNames(strsplit(tags, split=",", fixed=TRUE), key)
# get unique elements from list
colTemp <- sort(unique(unlist(myList)))
# check each list element for the unique elements, return matrix
myMat <- t(sapply(myList, function(i) colTemp %in% i))
# add column names
colnames(myMat) <- colTemp

哪个returns

myMat
      A     B     C     D     E
a  TRUE  TRUE FALSE FALSE FALSE
b FALSE  TRUE FALSE FALSE FALSE
c  TRUE FALSE FALSE FALSE  TRUE
d FALSE FALSE  TRUE  TRUE FALSE
e FALSE FALSE FALSE FALSE FALSE

来自docendo discimus的方法,使用不同的粘贴方式

xx <- sort(unique(unlist(strsplit(data$tags,","))))

data1 <- sapply(paste(xx), grepl, data$tags)

data <- cbind(data[,1],data1)

  key     A     B     C     D     E
1   a  TRUE  TRUE FALSE FALSE FALSE
2   b FALSE  TRUE FALSE FALSE FALSE
3   c  TRUE FALSE FALSE FALSE  TRUE
4   d FALSE FALSE  TRUE  TRUE FALSE
5   e FALSE FALSE FALSE FALSE FALSE

tidyr 中的 separate_rows 函数可以帮助您到达所需位置。这会将 tags 中的字符串拆分为单独的行而不是单独的列,这使您可以使用 spread.

为了获得 TRUE/FALSE 结果,我创建了一个包含所有 TRUE 的新列作为值列,然后用 FALSE 填充缺失的列在 spread。最后,spread 将空白单元格保留为列名,我通过 select 将其删除。可能有更好的方法来做到这一点(也许转换为 NA?)。

library(tidyr)
library(dplyr)

data %>%
    separate_rows(tags) %>%
    mutate(tagslog = TRUE) %>%
    spread(tags, tagslog, fill = FALSE) %>%
    select(-one_of(""))

    key     A     B     C     D     E
* <chr> <lgl> <lgl> <lgl> <lgl> <lgl>
1     a  TRUE  TRUE FALSE FALSE FALSE
2     b FALSE  TRUE FALSE FALSE FALSE
3     c  TRUE FALSE FALSE FALSE  TRUE
4     d FALSE FALSE  TRUE  TRUE FALSE
5     e FALSE FALSE FALSE FALSE FALSE

您几乎可以通过 separate_rowstable 到达您想要的位置,但我仍然有需要删除的额外空白列。

data %>%
    separate_rows(tags) %>%
    with(., table(key, tags) == 1)

   tags
key           A     B     C     D     E
  a FALSE  TRUE  TRUE FALSE FALSE FALSE
  b FALSE FALSE  TRUE FALSE FALSE FALSE
  c FALSE  TRUE FALSE FALSE FALSE  TRUE
  d FALSE FALSE FALSE  TRUE  TRUE FALSE
  e  TRUE FALSE FALSE FALSE FALSE FALSE