代码之家  ›  专栏  ›  技术社区  ›  Shawn Janzen

找到重复的整行并标记单个R数据帧中的微小差异的更好方法?

  •  0
  • Shawn Janzen  · 技术社区  · 2 年前

    我有一个近200000行20列的大型数据集(数字和字符串数据的混合)。每一行都有一个唯一的标识符。少于100行具有重复标识符。我试图确定两件事:

    1. 如果具有重复标识符的每一行在所有20列或仅在某些列中具有完全相同的重复值(标识符至少有1列)。
    2. 对于每一组具有重复标识符的行,如果这些行在所有列中的值不相同,请确定哪些列具有不同的值。

    我看过其他几篇SO文章,但它们通常按列而不是按行讨论重复,和/或跨数据帧而不是在数据帧内进行比较。

    下面是一个小数据示例。
    注意,存在org_id值(a,b,c),其中a&b为副本。对于org_id a,列中的所有值都是重复的,但对于org_idb则不是。

    # load toy data
    df <- data.frame(org_id=c("a","a","b","b","b","c"),
                     thing=c("1","1","1","1","2","1"),
                     name=c("really_long_A_name_here", "really_long_A_name_here", "really_long_B_name_here", "really_long_B2_name_here", "really_long_B_name_here", "really_long_C_name_here"),
                     start=c("2020-10-31", "2020-10-31", "2022-09-17", "2022-09-17", "2022-09-17", "2023-05-11") )
    df
    
    组织id 分数 名称 开始
    1. 实际_长_名称_此处 2020-10-31
    1. 实际_长_名称_此处 2020-10-31
    b 1. 实际长度_名称_此处 2022-09-17
    b 1. really_long_B2_name_here 2022-09-17
    b 2. 实际长度_名称_此处 2022-09-17
    c 1. 实际长度_名称_此处 2023-05-11

    以下是我需要的示例: 首先,另一个数据帧告诉我哪个org_id具有重复的行值,例如:

    exact_dup 重复(_O)
    符合事实的
    错误的 b

    到目前为止,我将行数据组合成一个长字符串进行比较。 下面的代码可以工作,但看起来很笨拙。关于如何改进有什么建议吗?

    # create a long string for each row
    df$x <- apply(df, 1, paste0, collapse="|")
    # placeholder dataframe to identify which uplicate rows in the data are exact duplicates across the entire row
    review_dups <- data.frame(exact_dup = NA, dup_orgs = df |> filter(duplicated(org_id)==TRUE) |> distinct(org_id)|> pull(org_id) )
    # loop to find differences
    for(i in 1:nrow(review_dups)){
      n <- df |> filter(org_id == review_dups$dup_orgs[i]) |> select(x) |> count(x) |> pull(n) |> max()
      dup_rows <- df |> filter(org_id == review_dups$dup_orgs[i]) |> nrow()
      review_dups[i,1] <- n==dup_rows
      rm(n, dup_rows)
    }
    rm(i)
    # view results
    review_dups
    

    其次,我需要一种方法来报告那些与具有重复org_id的行的其余行不匹配的数据列。
    因此,输出应该告诉我org_idb的score列和name列不同。
    报告值最好可以作为第三个结果列出现在上面的重复检查数据框示例中,但我对不同的报告选项持开放态度。
    我还没有这部分的代码解决方案。

    谢谢

    1 回复  |  直到 2 年前
        1
  •  1
  •   r2evans    2 年前

    我推断,你只关心将第二行和随后的行与第一行进行比较,而不是一组完整的成对差异。

    这会将一列添加到原始框架中,该框架提供以逗号分隔的列名列表。

    df$dupe_differences <- unlist(by(df, df$org_id, function(dat) {
      if (nrow(dat) == 1) return(NA)
      c("", sapply(2:nrow(dat), function(i) {
        same <- mapply(Negate(`%in%`), dat[1,], dat[i,])
        paste(names(same[same]), collapse = ",")
      }))
    }))
    
    df
    #   org_id thing                     name      start dupe_differences
    # 1      a     1  really_long_A_name_here 2020-10-31                 
    # 2      a     1  really_long_A_name_here 2020-10-31                 
    # 3      b     1  really_long_B_name_here 2022-09-17                 
    # 4      b     1 really_long_B2_name_here 2022-09-17             name
    # 5      b     2  really_long_B_name_here 2022-09-17            thing
    # 6      c     1  really_long_C_name_here 2023-05-11             <NA>
    

    区别是明确的:

    • NA 表示没有重复项,该行是唯一的
    • "" (空字符串)表示其内容与该重复数据集的第一行相同
    • 任何其他内容都会列出与该重复集的第一行不同的列名(逗号分隔)

    从这里,您可以很容易地筛选出您想要使用的特定行 is.na(.) (为了避免重复), !is.na(.) & !nzchar(.) 对于与第一行(包括第一行)相同的行,以及 !is.na(.) & nzchar(.) 对于具有差异的重复行。