【问题标题】:R - difference between 2 sets in data frameR - 数据框中两组之间的差异
【发布时间】:2018-09-28 02:18:53
【问题描述】:

我有 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.。我想要三分之一的列返回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"

【问题讨论】:

  • df %>%rowwise()%>% mutate(col3 = gsub(y1,"",y2)) 怎么样。问题是如果 y1 有额外的字符 y2 没有它就不起作用。但只是一个可能更简单的解决方案的想法
  • 实际上这会产生正确的结果。我实际上需要展示 y2 中没有的 y1 中的不同之处。我认为所有其他解决方案都做同样的事情。您可以将此作为解决方案而不是评论。
  • 使用df %&gt;%rowwise()%&gt;% mutate(col3 = gsub(y1,"",y2)) 的一个问题是,如果订单被更改,它将无法正常工作。考虑y1 是否有a.by2 是否有b.a.c

标签: r strsplit set-difference


【解决方案1】:

更新

当差异超过 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" " "

【讨论】:

  • 有什么方法可以将col3 转换为普通列吗?当我运行 str(df) 它返回 col3 作为 List of 3
  • @jmich738 添加在主要答案中。
  • 我正在尝试将此应用于我的整个数据集,但似乎col3 的输出产生的行数少于原始df。我仍然不确定问题出在哪里。
  • @jmich738 我希望您分两步进行。首先执行mapply 步骤,然后执行unlist 步骤。
  • 这似乎是导致问题的unlist()unlist 产生额外的行。我正在做的是说df$col3&lt;- unlist(...),但在我的实际数据集上。我仍在试图弄清楚我的示例数据与实际数据有何不同。
【解决方案2】:

你也可以使用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


更新

似乎unnestlist 中删除了零长度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.   ""

【讨论】:

  • 由于某种原因,当我运行此程序时,我没有得到第 3 行,即没有差异的行。你知道那是什么吗?
  • @jmich738 - unnest() 显然会删除列表中为空的所有行。
  • @thelatemail 好的,所以如果我在没有unnest() 的情况下运行它,我会得到所有行
  • @jmich738 和@thelatemail 你是对的!我没有意识到 unnest 会删除零长度 character 条目。请查看我更新的解决方案。
  • @MauritsEvers 非常接近,但似乎如果他的差异超过 1 个字符,那么结果很奇怪。如果你设置y2 &lt;- c("a.b.c.e.","a.b.","b.c.d."),那么输出看起来像c("c", "e")
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-18
    • 2022-12-09
    • 2014-01-04
    • 2021-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多