从数据框中选择所有未出现在另一个数据框中的行

19

我正在尝试解决一个棘手的R问题,通过关键字搜索仍未能解决。具体来说,我正在尝试从一个数据框中选择一个子集,其值不出现在另一个数据框中。这是一个例子:

> test
      number    fruit     ID1  ID2 
item1 "number1" "apples"  "22" "33"
item2 "number2" "oranges" "13" "33"
item3 "number3" "peaches" "44" "25"
item4 "number4" "apples"  "12" "13"
> test2
      number    fruit     ID1   ID2 
item1 "number1" "papayas" "22"  "33"
item2 "number2" "oranges" "13"  "33"
item3 "number3" "peaches" "441" "25"
item4 "number4" "apples"  "123" "13"
item5 "number3" "peaches" "44"  "25"
item6 "number4" "apples"  "12"  "13"
item7 "number1" "apples"  "22"  "33"

我有两个数据框test和test2,目标是选择test2中所有完整的行,在test中不存在,即使一些值是相同的。
我想要的输出应该像这样:
item1 "number1" "papayas" "22"  "33"
item2 "number3" "peaches" "441" "25"
item3 "number4" "apples"  "123" "13"

可能有任意数量的行或列,但在我的特定情况下,一个数据框是另一个数据框的直接子集。

我广泛使用了R中的subset()、merge()和which()函数,但无法弄清如何将它们组合使用,如果可能的话,以获得我想要的结果。

编辑:这是我用来生成这两个表格的R代码。

test <- data.frame(c("number1", "apples", 22, 33), c("number2", "oranges", 13, 33),
    c("number3", "peaches", 44, 25), c("number4", "apples", 12, 13))

test <- t(test)
rownames(test) = c("item1", "item2", "item3", "item4")
colnames(test) = c("number", "fruit", "ID1", "ID2")

test2 <- data.frame(data.frame(c("number1", "papayas", 22, 33), c("number2", "oranges", 13, 33),
    c("number3", "peaches", 441, 25), c("number4", "apples", 123, 13),c("number3", "peaches", 44, 25), c("number4", "apples", 12, 13)  ))

test2 <- t(test2)
rownames(test2) = c("item1", "item2", "item3", "item4", "item5", "item6")
colnames(test2) = c("number", "fruit", "ID1", "ID2")

提前感谢您!


你没有数据框,你有矩阵。不要在数据框上使用t。幸运的是,merge足够聪明,可以将你的矩阵转回数据框。但不幸的是,它无法将现在属于 factor 类的 id 转回数值类型。 - Hong Ooi
谢谢你的尝试,Hong!我的真实数据集是data.frame类的,不幸的是,你的代码对它也没有起作用 =\。对象Matches变成了一个NULL对象。 - so13eit
你所期望的输出结果中的行名与“test2”不匹配,这是一个错误吗? - Matthew Plourde
哦,好的。请忽略那些行名,那是一个假设输出! - so13eit
我希望有一种方法可以从 plyr 中否定 match_df - tumultous_rooster
6个回答

17

还有另一种方法:

x <- rbind(test2, test)
x[! duplicated(x, fromLast=TRUE) & seq(nrow(x)) <= nrow(test2), ]
#        number   fruit ID1 ID2
# item1 number1 papayas  22  33
# item3 number3 peaches 441  25
# item4 number4  apples 123  13

编辑:修改以保留行名称。


我正在尝试在我的真实数据上运行您的代码,它似乎工作得很好。我对它唯一的小问题是它删除了所有重复项,这可能不是理想的(因为有些记录实际上应该有重复项,例如如果一个客户购买了两个相同的产品)。尽管如此,对于我现在拥有的数据来说,它已经足够好了,谢谢! - so13eit
这是一个很好的解决方案,可以解决子集中所谓的“双重否定”问题。我试图通过一步子集来消除DF中具有两个列上的值组合的值,而我不想要这些值;例如,“subset(df, HIYA != "alpha" & BYA != "beta")”,其中我想要的是除了那些HIYA = alpha和BYA = beta的情况之外的所有情况。这个方法解决了这个问题! - WGray
看起来是个很好的答案,但老实说我看不懂这段代码。为什么我们需要一个序列? - Monica Heddneck
1
!dulpicated() 返回一个逻辑向量,其长度为x的长度,其中TRUE表示不重复。 fromLast 将TRUE推到向量的末尾(因为它们现在是“第一次”出现)。 seq()<= 创建一个向量,其中包含 length(test2) 个TRUE,后跟 'length(test)个FALSE,这将过滤掉末尾所有虚假的TRUE。 等效的构造方式是:x <- rbind(test, test2) x[! duplicated(x) & seq(nrow(x)) > nrow(test2), ]` - verbamour

4
有两种方法可以解决这个问题,使用data.table和sqldf。
library(data.table)
test<- fread('
item number fruit ID1 ID2 
item1 "number1" "apples"  "22" "33"
item2 "number2" "oranges" "13" "33"
item3 "number3" "peaches" "44" "25"
item4 "number4" "apples"  "12" "13"
')
test2<- fread('
item number fruit ID1 ID2 
item1 "number1" "papayas" "22"  "33"
item2 "number2" "oranges" "13"  "33"
item3 "number3" "peaches" "441" "25"
item4 "number4" "apples"  "123" "13"
item5 "number3" "peaches" "44"  "25"
item6 "number4" "apples"  "12"  "13"
item7 "number1" "apples"  "22"  "33"
')

使用data.table方法,这将使您能够选择要比较的列。

setkey(test,item,number,fruit,ID1,ID2)
setkey(test2,item,number,fruit,ID1,ID2)
test[!test2]
item  number   fruit ID1 ID2
1: item1 number1  apples  22  33
2: item3 number3 peaches  44  25
3: item4 number4  apples  12  13

SQL方法

sqldf('select * from test except select * from test2')
item  number   fruit ID1 ID2
1: item1 number1  apples  22  33
2: item3 number3 peaches  44  25
3: item4 number4  apples  12  13

3
以下内容可以帮助您完成:
rows <- unique(unlist(mapply(function(x, y) 
          sapply(setdiff(x, y), function(d) which(x==d)), test2, test1)))
test2[rows, ]

这里所发生的事情是:
  • 使用mapply逐列比较两个数据集。
  • 它使用setdiff找出前者中没有而后者有的任何项。
  • which确定前者中不存在哪一行。
  • unique(unlist(....))获取所有唯一的行。

  • 然后我们将其用作前者(即test2)的过滤器。

结果:

       number   fruit ID1 ID2
item1 number1 papayas  22  33
item3 number3 peaches 441  25
item4 number4  apples 123  13

编辑:

请确保您的testtest2data.frames而不是matrices,因为mapply会迭代矩阵的每个元素,但是会迭代data.frame的每一列。

test  <- as.data.frame(test,  stringsAsFactors=FALSE)
test2 <- as.data.frame(test2, stringsAsFactors=FALSE)

不幸的是,这只输出一行,并且不是重复的行!我编辑了原帖以包含我用于生成表格的代码,也许你可以自己看看。 - so13eit
啊...你说你有data.frames,但实际上你有matrices。请查看编辑后的内容。将其转换为data.frame,一切都会好的 ;) - Ricardo Saporta
一切还不太好……目前为止!正如您所看到的结果,它们实际上并不正确。item2在“test”和“test2”中都重复出现,并且“test2”中的两行独特数据未显示在结果中。 - so13eit
我的错误,在mapply函数中出现了一个错误。然而,数据仍然需要是data.frames 并且字符串不能是因子。 - Ricardo Saporta

3

使用dplyr包,您也可以使用anti_join函数。

missing.species <- anti_join(test2, test, by = NULL)

它将返回test2中没有与test匹配的行。可以通过指定要连接的变量来实现,如果by参数为NULL,则函数将使用test和test2之间所有共同的变量。


1
在test2中创建一个新的行ID列,合并数据框,并选择那些ID不在合并结果中的行。
test2 <- cbind(test2, id=seq_len(nrow(test2)))

matches <- merge(test1, test2)$id

test2 <- test2[-matches, ]

这会产生一个错误:警告信息: 在Ops.factor(matches)中:- 对于因子无意义。 我已将我使用的代码编辑到了OP中。 - so13eit

1

这里还有另一种方法,但我不确定它的可扩展性如何。

test2[!apply(test2, 1, paste, collapse = "") %in% 
        apply(test, 1, paste, collapse = ""), ]
#       number    fruit     ID1   ID2 
# item1 "number1" "papayas" "22"  "33"
# item3 "number3" "peaches" "441" "25"
# item4 "number4" "apples"  "123" "13"

这不会删除所有的重复项。例如,如果test2有重复项,请进行比较:
test2 <- rbind(test2, test2[1:3, ])

## Matthew's answer: Duplicates dropped
x <- rbind(test2, test)
x[! duplicated(x, fromLast=TRUE) & seq(nrow(x)) <= nrow(test2), ]
#       number    fruit     ID1   ID2 
# item4 "number4" "apples"  "123" "13"
# item1 "number1" "papayas" "22"  "33"
# item3 "number3" "peaches" "441" "25"

## This one: Duplicates retained
test2[!apply(test2, 1, paste, collapse = "") %in%
  apply(test, 1, paste, collapse = ""), ]
#       number    fruit     ID1   ID2 
# item1 "number1" "papayas" "22"  "33"
# item3 "number3" "peaches" "441" "25"
# item4 "number4" "apples"  "123" "13"
# item1 "number1" "papayas" "22"  "33"
# item3 "number3" "peaches" "441" "25"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接