R - 数据框中两组之间的差异

R - difference between 2 sets in data frame

我有 2 个因子列,我想创建第三列,告诉我第二列有什么,而第一列没有。 它与这个 post 非常相似,但我在从 df 到使用 setdiff() 函数时遇到了麻烦。
例如:

library(dplyr)
y1 <- c("a.b.","a.","b.c.d.")
y2 <- c("a.b.c.","a.b.","b.c.d.")
df <- data.frame(y1,y2)

y1 列有 a.b.,第 y2 列有 a.b.c.。我想要 return c. 或只是 c 的三分之一列。

> df
      y1     y2  col3
1   a.b.  a.b.c.  c.
2     a.    a.b.  b.
3 b.c.d.  b.c.d.  

我认为这应该是 strsplitsetdiff 的组合,但我无法让它工作。

我尝试将 factor 转换为 character,然后我尝试将 strsplit() 应用于结果,但输出对我来说似乎很奇怪。它似乎在列表中创建了一个列表,这使得它很难传递给 setdiff()

#convert factor to character
df <- df %>% mutate_if(is.factor, as.character)
lapply(df$y1,function(x)(strsplit(x,split = "[.]")))

> lapply(df$y1,function(x)(strsplit(x,split = "[.]")))
[[1]]
[[1]][[1]]
[1] "a" "b"


[[2]]
[[2]][[1]]
[1] "a"


[[3]]
[[3]][[1]]
[1] "b" "c" "d"

更新

当差异超过 1 个字符时出现问题,它创建了一个额外的行。为了克服这一点,我们 paste 将所有元素放在一起以解决每个差异。这也使我们免于 unlist 这一步。

df$col3 <- mapply(function(x, y) paste0(setdiff(y, x), collapse = ""),
   strsplit(as.character(df$y1), "\."), strsplit(as.character(df$y2), "\."))

原答案

我们可以使用 mapply 并在“.”上拆分两列。使用 strsplit 然后使用 setdiff.

计算它们之间的差异
df$col3 <- mapply(function(x, y) setdiff(y, x),
       strsplit(as.character(df$y1), "\."), strsplit(as.character(df$y2), "\."))

df
#     y1     y2 col3
#1   a.b. a.b.c.    c
#2     a.   a.b.    b
#3 b.c.d. b.c.d.     

如果我们不想 col3 作为列表,我们可以 unlist 但是,其中一个问题是如果我们 unlist 它从中删除 character(0) 值它。为了保留该值,我们需要对其执行额外的检查。摘自 .

unlist(lapply(df$col3,function(x) if(identical(x,character(0))) ' ' else x))

#[1] "c" "b" " "

你也可以使用purrr:map2:

df %>%
    mutate_if(is.factor, as.character) %>%
    mutate(col3 = map2(strsplit(y2, "\."), strsplit(y1, "\."), setdiff))
#      y1     y2 col3
#1   a.b. a.b.c.    c
#2     a.   a.b.    b
#3 b.c.d. b.c.d.    

说明:将factors 转换为character 向量,在"."-拆分列y2y1 上使用setdiff。请注意 col3list.


更新

unnest 似乎删除了 list 中的零长度 character 条目。因此,要将 col3list 转换为 character 向量,您可以执行以下操作:

df %>%
    mutate_if(is.factor, as.character) %>%
    mutate(col3 = map2(strsplit(y2, "\."), strsplit(y1, "\."), setdiff)) %>%
    rowwise() %>%
    mutate(col3 = paste(col3, collapse = "."))
## A tibble: 3 x 3
#  y1     y2     col3
#  <chr>  <chr>  <chr>
#1 a.b.   a.b.c. c
#2 a.     a.b.   b
#3 b.c.d. b.c.d. ""

这里的想法是字符串连接 col3 个条目(如果有多个);使用 rowwise() 确保按行 paste.

根据您的评论更新示例数据:

y1 <- c("a.b.","a.","b.c.d.")
y2 <- c("a.b.c.e.","a.b.","b.c.d.")
df <- data.frame(y1,y2)
df %>%
    mutate_if(is.factor, as.character) %>%
    mutate(col3 = map2(strsplit(y2, "\."), strsplit(y1, "\."), setdiff)) %>%
    rowwise() %>%
    mutate(col3 = paste(col3, collapse = "."))
## A tibble: 3 x 3
#  y1     y2       col3
#  <chr>  <chr>    <chr>
#1 a.b.   a.b.c.e. c.e
#2 a.     a.b.     b
#3 b.c.d. b.c.d.   ""

一个很简单但不严谨的方法就是将y1中的所有内容都替换为y2中的“”。 这不会处理订单不同的情况,或者如果 y1 有任何附加到 y2 而不是相反的情况。

df %>% rowwise() %>% mutate(col3 = gsub(y1,"",y2))