6.15 比较数据框

6.15.1 问题

你想要比较两个或多个数据框并找到在超过一个数据框中出现的行,或者仅在一个数据框中出现的行。

6.15.2 方案

6.15.2.1 一个例子

假设你有下面三个数据框,你想要知道那些至少在两个数据框中出现的行。

  1. dfA <- data.frame(Subject = c(1, 1, 2, 2), Response = c("X",
  2. "X", "X", "X"))
  3. dfA
  4. #> Subject Response
  5. #> 1 1 X
  6. #> 2 1 X
  7. #> 3 2 X
  8. #> 4 2 X
  9. dfB <- data.frame(Subject = c(1, 2, 3), Response = c("X",
  10. "Y", "X"))
  11. dfB
  12. #> Subject Response
  13. #> 1 1 X
  14. #> 2 2 Y
  15. #> 3 3 X
  16. dfC <- data.frame(Subject = c(1, 2, 3), Response = c("Z",
  17. "Y", "Z"))
  18. dfC
  19. #> Subject Response
  20. #> 1 1 Z
  21. #> 2 2 Y
  22. #> 3 3 Z

dfA 中,包括 (1,X) 的行同样出现在了 dfB,但是包含 (2,X) 的行没有出现在任何其他的数据框。相似地,dfB 包含的 (1,X) 出现在了 dfA(2,Y) 出现在了 dfC,但是 (3,X) 没有出现在其他数据框。

你可能想要标记在其他数据框中出现了的行,或者在每个数据框中都是唯一的行。

6.15.2.2 连接数据框

进一步地,我们首先用一个可以识别每一行来自哪里的列来连接数据框。这里称为 Coder 变量因为它可能是由三个不同的人编码的数据。在这个例子中,你可能想要找到编码者相同之处(至少出现在两个数据框中的行),或者它们不同之处。

  1. dfA$Coder <- "A"
  2. dfB$Coder <- "B"
  3. dfC$Coder <- "C"
  4. df <- rbind(dfA, dfB, dfC) # 把它们粘在一起
  5. df <- df[, c("Coder", "Subject", "Response")] # 重新排序
  6. df
  7. #> Coder Subject Response
  8. #> 1 A 1 X
  9. #> 2 A 1 X
  10. #> 3 A 2 X
  11. #> 4 A 2 X
  12. #> 5 B 1 X
  13. #> 6 B 2 Y
  14. #> 7 B 3 X
  15. #> 8 C 1 Z
  16. #> 9 C 2 Y
  17. #> 10 C 3 Z

如果你的数据一开始就是这种格式,那就不要将它们连接到一起啦。

6.15.2.3 dupsBetweenGroups() 函数

该函数用来寻找不同组别的重复行:

  1. dupsBetweenGroups <- function(df, idcol) {
  2. # df: the data frame idcol: the column which identifies
  3. # the group each row belongs to
  4. # Get the data columns to use for finding matches
  5. datacols <- setdiff(names(df), idcol)
  6. # Sort by idcol, then datacols. Save order so we can
  7. # undo the sorting later.
  8. sortorder <- do.call(order, df)
  9. df <- df[sortorder, ]
  10. # Find duplicates within each id group (first copy not
  11. # marked)
  12. dupWithin <- duplicated(df)
  13. # With duplicates within each group filtered out, find
  14. # duplicates between groups. Need to scan up and down
  15. # with duplicated() because first copy is not marked.
  16. dupBetween = rep(NA, nrow(df))
  17. dupBetween[!dupWithin] <- duplicated(df[!dupWithin,
  18. datacols])
  19. dupBetween[!dupWithin] <- duplicated(df[!dupWithin,
  20. datacols], fromLast = TRUE) | dupBetween[!dupWithin]
  21. # ============= Replace NA's with previous non-NA value
  22. # ============== This is why we sorted earlier - it was
  23. # necessary to do this part efficiently
  24. # Get indexes of non-NA's
  25. goodIdx <- !is.na(dupBetween)
  26. # These are the non-NA values from x only Add a leading
  27. # NA for later use when we index into this vector
  28. goodVals <- c(NA, dupBetween[goodIdx])
  29. # Fill the indices of the output vector with the indices
  30. # pulled from these offsets of goodVals. Add 1 to avoid
  31. # indexing to zero.
  32. fillIdx <- cumsum(goodIdx) + 1
  33. # The original vector, now with gaps filled
  34. dupBetween <- goodVals[fillIdx]
  35. # Undo the original sort
  36. dupBetween[sortorder] <- dupBetween
  37. # Return the vector of which entries are duplicated
  38. # across groups
  39. return(dupBetween)
  40. }

6.15.2.4 寻找重复行

使用在前文定义的函数 dupsBetweenGroups(),我们可以找出在不同组别中重复的行。

  1. # 找出在不同组别中重复的行
  2. dupRows <- dupsBetweenGroups(df, "Coder")
  3. # 在数据框的旁边打印出来
  4. cbind(df, dup = dupRows)
  5. #> Coder Subject Response dup
  6. #> 1 A 1 X TRUE
  7. #> 2 A 1 X TRUE
  8. #> 3 A 2 X FALSE
  9. #> 4 A 2 X FALSE
  10. #> 5 B 1 X TRUE
  11. #> 6 B 2 Y TRUE
  12. #> 7 B 3 X FALSE
  13. #> 8 C 1 Z FALSE
  14. #> 9 C 2 Y TRUE
  15. #> 10 C 3 Z FALSE

注意这不会标记在同一组中的重复行,比如 Coder=A 时,有两行 Subject=2 以及 Response=X,但没有标记出来。

6.15.2.5 寻找唯一行

同样可以找出在每一组中唯一出现的行。

  1. cbind(df, unique = !dupRows)
  2. #> Coder Subject Response unique
  3. #> 1 A 1 X FALSE
  4. #> 2 A 1 X FALSE
  5. #> 3 A 2 X TRUE
  6. #> 4 A 2 X TRUE
  7. #> 5 B 1 X FALSE
  8. #> 6 B 2 Y FALSE
  9. #> 7 B 3 X TRUE
  10. #> 8 C 1 Z TRUE
  11. #> 9 C 2 Y FALSE
  12. #> 10 C 3 Z TRUE

6.15.2.6 拆分数据框

如果你想要把连接的数据框拆分为三个原始的数据框:

  1. # 保存df的结果
  2. dfDup <- cbind(df, dup = dupRows)
  3. dfA <- subset(dfDup, Coder == "A", select = -Coder)
  4. dfA
  5. #> Subject Response dup
  6. #> 1 1 X TRUE
  7. #> 2 1 X TRUE
  8. #> 3 2 X FALSE
  9. #> 4 2 X FALSE
  10. dfB <- subset(dfDup, Coder == "B", select = -Coder)
  11. dfB
  12. #> Subject Response dup
  13. #> 5 1 X TRUE
  14. #> 6 2 Y TRUE
  15. #> 7 3 X FALSE
  16. dfC <- subset(dfDup, Coder == "C", select = -Coder)
  17. dfC
  18. #> Subject Response dup
  19. #> 8 1 Z FALSE
  20. #> 9 2 Y TRUE
  21. #> 10 3 Z FALSE

6.15.2.7 忽略列

有可能需要通过移除数据框的列来忽略一个或者多个列,结果又可以把原始完整的数据框连接起来。

  1. # 忽略 Subject 列——仅使用 Response 列
  2. dfNoSub <- subset(df, select = -Subject)
  3. dfNoSub
  4. #> Coder Response
  5. #> 1 A X
  6. #> 2 A X
  7. #> 3 A X
  8. #> 4 A X
  9. #> 5 B X
  10. #> 6 B Y
  11. #> 7 B X
  12. #> 8 C Z
  13. #> 9 C Y
  14. #> 10 C Z
  15. # 检查重复行
  16. dupRows <- dupsBetweenGroups(dfNoSub, "Coder")
  17. # 把结果连接起来
  18. cbind(df, dup = dupRows)
  19. #> Coder Subject Response dup
  20. #> 1 A 1 X TRUE
  21. #> 2 A 1 X TRUE
  22. #> 3 A 2 X TRUE
  23. #> 4 A 2 X TRUE
  24. #> 5 B 1 X TRUE
  25. #> 6 B 2 Y TRUE
  26. #> 7 B 3 X TRUE
  27. #> 8 C 1 Z FALSE
  28. #> 9 C 2 Y TRUE
  29. #> 10 C 3 Z FALSE

6.15.3 注意

想要寻找单个数据框中的重复行,参见查找并移除重复记录