HOWTO · R

R 中按列删除重复行

这篇文章演示了如何在 R 中按列删除重复的行

本页内容

若要在 R 数据框中为每个所选键保留一行,当您需要其他列时,请使用 dplyr 的 distinct() 并设置 .keep_all = TRUE。以下每种方法都会按照数据当前的顺序保留最早的匹配行,因此当另一个记录应优先时,请先排序。这些示例使用一个具有 id、group 和 score 列的小型数据框。

使用 dplyr 包的 distinct 函数按列删除 R 中的重复行

当 id 定义了唯一记录,且您希望结果包含每个原始列时,请使用 distinct(df, id, .keep_all = TRUE)。distinct() 会比较所选的键列,并为每个键值保留第一行。使用 .keep_all = TRUE 时,其他列会被带入结果中,但不会影响两行是否匹配。若没有该参数,结果仅包含所选的列。

对于复合键,请列出每一列:distinct(df, id, group, .keep_all = TRUE)。这样,具有相同 id 但不同 group 值的行便会保持分离。下面的完整示例使用相同的示例数据,并展示了两种键的选择。它需要安装 dplyr;其结果在下文中说明,且此示例不附带运行时的输出。

df <- data.frame(
  id = c(1, 2, 2, 3, 3),
  group = c("A", "A", "B", "A", "B"),
  score = c(8, 5, 9, 4, 7)
)

if (requireNamespace("dplyr", quietly = TRUE)) {
  unique_by_id <- dplyr::distinct(df, id, .keep_all = TRUE)
  unique_by_pair <- dplyr::distinct(df, id, group, .keep_all = TRUE)
  print(unique_by_id)
  print(unique_by_pair)
} else {
  cat("Install dplyr to run these distinct() examples.\n")
}

单一键的结果保留了 (1, A, 8)、(2, A, 5) 和 (3, A, 4)。复合键的结果保留了这些行以及 (2, B, 9) 和 (3, B, 7),因为每个 id 与 group 的配对都是唯一的。下面的另一个 Base R 示例会打印这两个结果。

df <- data.frame(
  id = c(1, 2, 2, 3, 3),
  group = c("A", "A", "B", "A", "B"),
  score = c(8, 5, 9, 4, 7)
)

unique_by_id <- df[!duplicated(df$id), ]
unique_by_pair <- df[!duplicated(df[c("id", "group")]), ]

cat("By id:\n")
cat(paste(unique_by_id$id, unique_by_id$group, unique_by_id$score, sep = ":"), sep = "\n")
cat("\nBy id and group:\n")
cat(paste(unique_by_pair$id, unique_by_pair$group, unique_by_pair$score, sep = ":"), sep = "\n")
By id:
1:A:8
2:A:5
3:A:4

By id and group:
1:A:8
2:A:5
2:B:9
3:A:4
3:B:7

在这里,!duplicated(df$id) 标记了每个 id 后续出现的位置;将键列的数据框传递给 duplicated() 会对其组合应用相同的首次出现规则。两种选择都保留了每个所选行的所有列。

使用 group_by、filter 和 duplicated 函数按列删除 R 中的重复行

对于单一全局 id 键,filter(!duplicated(id)) 会保留其首次出现。如果输入可能已经分组,请先调用 ungroup(),以便 filter() 对整个数据评估重复检查:

df %>% ungroup() %>% filter(!duplicated(id))

管道 %>% 将其左侧的值作为第一个参数传递给其右侧的函数。在 filter(!duplicated(id)) 之前按 id 分组不会删除后面的行:在每个 id 组内,该键是恒定的,因此 duplicated(id) 找不到后续出现。按另一列分组在 ID 可能在每个组内独立重复时非常有用;此时,duplicated(id) 会保留每个组中每个 ID 的首次出现。对于单一全局键,未分组的表达式与上面的 Base R 选择具有相同的首行行为。

例如,按 group 分组会在每个组内分别应用相同的检查:

df %>%
  group_by(group) %>%
  filter(!duplicated(id)) %>%
  ungroup()

此管道按照当前的行顺序,保留每个 group 内每个 id 的第一行。在示例数据中,每个 id 与 group 的配对仅出现一次,因此每一行都是其在组内该 id 的首次出现,五行全部保留;若再有一行重复同一组中已出现过的 id,则会被丢弃。

使用 group_by 和 slice 函数按列删除 R 中的重复行

dplyr 管道 df %>% group_by(id) %>% slice(1) %>% ungroup() 从每个 id 组中选取位置 1。当前的行顺序决定了哪个记录排在第一位;slice(1) 不会自动选取最高分或最新记录。在分组和切片之前,请按照您希望的偏好排列数据,然后在后续操作应将结果视为普通数据框时取消分组。

对于示例数据,此方法保留了与单一键的 distinct() 和 duplicated() 方法相同的行:ID 1、2 和 3 的第一行。当任务是选取唯一的键组合时,请使用 distinct();当按每个组内的位置选取一行是更广泛的分组工作流程的一部分时,请使用 slice(1)。这些 dplyr 表达式仅供说明,且需要 dplyr 包;上面的 Base R 示例无需该依赖即可运行。