R - 数据帧中2组之间的差异

时间:2018-04-18 01:00:40

标签: r strsplit set-difference

我有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)

y1a.b.,列y2a.b.c.。我希望第三列返回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"

3 个答案:

答案 0 :(得分:5)

<强>更新

当差异超过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)值,则会出现一个问题。为了保留该值,我们需要对其进行额外检查。取自here

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

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

答案 1 :(得分:4)

您还可以使用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.    

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

更新

unnest似乎从character中删除了零长度list条目。因此,要将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.   ""

答案 2 :(得分:3)

非常简单但不严谨的是用y2中的“”替换y1中的所有内容。 这不会处理订单不同或者y1除了y2之外还有其他任何东西的情况。

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