【问题标题】:Create a mapping table of duplicated id / keys创建重复id/keys的映射表
【发布时间】:2017-08-03 13:17:36
【问题描述】:

我确实有一个统计例程,它不喜欢行精确重复(没有 ID)导致空距离。

所以我首先检测我删除的重复项,应用我的例程并合并回留下的记录。

为简单起见,假设我使用rownames 作为 ID/key。

我找到了以下方法来实现我在基础 R 中的结果:

data <- data.frame(x=c(1,1,1,2,2,3),y=c(1,1,1,4,4,3))

# check duplicates and get their ID -- cf. https://stackoverflow.com/questions/12495345/find-indices-of-duplicated-rows
dup1 <- duplicated(data)
dupID <- rownames(data)[dup1 | duplicated(data[nrow(data):1, ])[nrow(data):1]]

# keep only those records that do have duplicates to preveng running folowing steps on all rows
datadup <- data[dupID,]

# "hash" row
rowhash <- apply(datadup, 1, paste, collapse="_")

idmaps <- split(rownames(datadup),rowhash)
idmaptable <- do.call("rbind",lapply(idmaps,function(vec)data.frame(mappedid=vec[1],otherids=vec[-1],stringsAsFactors = FALSE)))

这给了我我想要的,即去重数据(简单)和映射表。

> (data <- data[!dup1,])
  x y
1 1 1
4 2 4
6 3 3
> idmaptable
      mappedid otherids
1_1.1        1        2
1_1.2        1        3
2_4          4        5

不知道有没有更简单或者更有效的方法(data.table/dplyr接受)。有什么替代方案可以提议吗?

【问题讨论】:

    标签: r dataframe duplicates data.table dplyr


    【解决方案1】:

    使用 data.table...

    library(data.table)
    setDT(data)
    
    # tag groups of dupes
    data[, g := .GRP, by=x:y]
    
    # do whatever analysis
    f = function(DT) Reduce(`+`, DT)
    resDT = unique(data, by="g")[, res := f(.SD), .SDcols = x:y][]
    
    # "update join" the results back to the main table if needed
    data[resDT, on=.(g), res := i.res ]
    

    OP 跳过了示例的中心部分(重复数据的使用),所以我只是弥补了f

    【讨论】:

    • 谢谢!令人印象深刻的是它是多么简洁。我按照我的意图验证这个,重写我的部分代码以使用data.table。如果我想要另一种方式来指定“by”列怎么办?我将有一个全局 ID 列(设置为键),我必须首先将其从进程中删除——因为我的重复映射过程显然必须在没有此 ID 列的情况下工作。
    • @Eric 当然。您可以执行cols=setdiff(names(data), "ID"),然后传递by=cols.SDcols=cols 之类的列。 ?data.table 中介绍了传递这些参数的各种选项。他们有很多。我的笔记franknarf1.github.io/r-tutorial/_book/… 在“指定列”下也有一个列表
    【解决方案2】:

    使用tidyverse 的解决方案。我通常不在行名中存储信息,所以我创建了IDID2 来存储信息。当然,您可以根据自己的需要进行更改。

    library(tidyverse)
    
    idmaptable <- data %>%
      rowid_to_column() %>%
      group_by(x, y) %>%
      filter(n() > 1) %>%
      unite(ID, x, y) %>%
      mutate(ID2 = 1:n()) %>%
      group_by(ID) %>%
      mutate(ID_type = ifelse(row_number() == 1, "mappedid", "otherids")) %>%
      spread(ID_type, rowid) %>%
      fill(mappedid) %>%
      drop_na(otherids) %>%
      mutate(ID2 = 1:n())
    
    idmaptable
    # A tibble: 3 x 4
    # Groups:   ID [2]
         ID   ID2 mappedid otherids
      <chr> <int>    <int>    <int>
    1   1_1     1        1        2
    2   1_1     2        1        3
    3   2_4     1        4        5
    

    【讨论】:

    • 谢谢。很适合锻炼!我将验证 data.table 选项,因为我最终打算使用这个包。
    • 请注意,操作很棘手,从某种意义上说,有很多步骤,而且逻辑不是那么容易阅读/分解/理解!
    • 感谢 cmets。棘手与否取决于用户的感受。在我的解决方案中,每一步都是一个功能,它只做一件事。如果你知道每个函数代表什么,你就可以“大声读出”。对我来说,有时那些简洁的方法太“紧凑”了。
    【解决方案3】:

    对基本 R 解决方案的一些改进,

    df <- data[duplicated(data)|duplicated(data, fromLast = TRUE),]
    
    do.call(rbind, lapply(split(rownames(df), 
                   do.call(paste, c(df, sep = '_'))), function(i) 
                                                      data.frame(mapped = i[1], 
                                                                 others = i[-1], 
                                                                 stringsAsFactors = FALSE)))
    

    这给了,

         mapped others
    1_1.1      1      2
    1_1.2      1      3
    2_4        4      5
    

    当然,

    unique(data)
    
     x y
    1 1 1
    4 2 4
    6 3 3
    

    【讨论】:

    • 确实更短。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 2019-12-14
    • 2013-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多