【问题标题】:create id variable from table of duplicates从重复表创建 id 变量
【发布时间】:2018-08-09 18:58:39
【问题描述】:

我有一个数据框,其中每一行都有一个唯一标识符,但有些行实际上是重复的。

fdf <- data.frame(name = c("fred", "ferd", "frad", 'eric', "eirc", "george"),
                  id = 1:6)
fdf
#>     name id
#> 1   fred  1
#> 2   ferd  2
#> 3   frad  3
#> 4   eric  4
#> 5   eirc  5
#> 6 george  6

我已确定哪些行是重复的,并且此信息作为唯一 ID 对存储在第二个数据帧中。所以关键告诉我第 1 行与第 2 行和第 3 行是同一个人,等等。

key <- data.frame(id1 = c(1,1,2,4), id2 = c(2,3,3,5))
key
#>   id1 id2
#> 1   1   2
#> 2   1   3
#> 3   2   3
#> 4   4   5

我正在努力想出一种简单的方法来使用密钥在我的原始数据框中创建一个 id 变量。期望的输出是:

fdf$realid <- c(1,1,1,2,2,3)
fdf
#>     name id realid
#> 1   fred  1      1
#> 2   ferd  2      1
#> 3   frad  3      1
#> 4   eric  4      2
#> 5   eirc  5      2
#> 6 george  6      3

为清楚起见进行编辑

这里的键是 data.frame fdf 中行之间的一组真实连接。因此,您可以想象从所有可行连接的集合开始:

#  id1 id2
#   1   2
#   1   3
#   1   4
#   ...
#   6   4
#   6   5

确定哪些是真正的联系(基于每个观察中的其他变量)。

#  id1 id2 match
#   1   2   match
#   1   3  no match
#   1   4   match
#   ...
#   6   4   no match
#   6   5   no match

以及对匹配的案例进行子设置。

【问题讨论】:

  • 数据中的名字真的一样吗?
  • 不,这只是一个玩具示例。真实数据有更多变量,包括有拼写错误的长字符串。
  • 您能更详细地解释一下您的密钥对吗?例如。 1等于2、1等于3、2等于3怎么加起来?
  • 键对是fdf data.frame 中行之间的连接。由于fdf 的前三个观察值(id = 1 到 3)是重复的,因此键表示 1 与 2 相关联,1 与 3 相关联,当然 2 与 3 相关联。2 必须等于 3如果 1 等于 2 和 3,因为“等于”是可传递的

标签: r data-manipulation


【解决方案1】:

最简单的方法是将 key 数据框重新创建为以下格式(即哪个 id 属于哪个 realid)

key <- data.frame(id     = c(1, 2, 3, 4, 5, 6), 
                  realid = c(1, 1, 1, 2, 2, 3))

那么只需将fdfkey合并到一起merge

fdf <- merge(fdf, key_table, by.x = "id")
fdf
  id   name realid
1  1   fred      1
2  2   ferd      1
3  3   frad      1
4  4   eric      2
5  5   eirc      2
6  6 george      3

【讨论】:

  • 如果 OP 需要帮助从他们的 key 生成这个 key - 这个问题相当于一个图论问题。如果我们将它们的键视为图中的边列表,则此答案的关键是将realid 分配给每个集团。 igraph 包可以帮助找到所有的派系。
  • 是的@Gregor这正是问题所在。这个答案对我来说不可行,因为我有成千上万的匹配项。
【解决方案2】:

我没有找到“直截了当的方法”,但它似乎运作良好。

首先检查哪些 ID 在一个组中,通过检查是否存在“重叠”,即key 中两行之间的交集是否非空:

check_overlap <- function(pair1, pair2){
  newset <- intersect(pair1, pair2)
  length(newset) != 0
}

然后我们可以将此函数应用于key 中的行与其他行。如果已经匹配了一行,它会自动从key 中删除,如下所示:

check_overlaps <- function(key){
  cont <- data.frame()
  i <- 1
  while(nrow(key) > 0){
    ids  <- apply(key, 1, check_overlap, key[1, ])
    vals <- unique(unlist(key[ids, ]))
    key  <- key[!ids, ]
    cont <- rbind(cont, cbind(vals, rep(i, length(vals))))
    i <- i+1
  }
  return(cont)
}

new_ids <- check_overlaps(key)
#    vals V2
# 1    1  1
# 2    2  1
# 3    3  1
# 4    4  2
# 5    5  2

但是合并fdfnew_ids的问题是key中可能不会出现一些旧的ID,但是应该按照新的顺序映射到新的ID。您可以先验地操纵key 并执行以下操作:

for(val in unique(fdf$id)){
  if(!(val %in% unlist(key))){
    key <- rbind(key, c(val, val))
  }
}

new_ids2 <- check_overlaps(key)
     vals V2
# 1    1  1
# 2    2  1
# 3    3  1
# 4    4  2
# 5    5  2
# 6    6  3

这很容易与fdf 合并喜欢:

merge(fdf, new_ids2, by.x = "id", by.y = "vals")
    id   name V2
# 1  1   fred  1
# 2  2   ferd  1
# 3  3   frad  1
# 4  4   eric  2
# 5  5   eirc  2
# 6  6 george  3

【讨论】:

  • 这符合我的要求,所以我接受了你的回答。最终它太慢了,但是考虑到我的数据大小,所以我找到了使用 igraph 的更快解决方案(按照上面的建议):g &lt;- graph_from_data_frame(key, directed=FALSE); new_ids &lt;- components(g)$membership 让我到达那里。
【解决方案3】:

如果我正确理解您的问题,则可以通过创建匹配 id 组并从这些组中创建一个新的(真实)id 来解决:

# determine the groups of ids
id_groups <- list()
i = 1
for (id in unique(key$id1)) {
  if (!(id %in% unlist(id_groups))) {
    id_groups[[i]] <- c(id, key$id2[key$id1 == id])
    i = i + 1
  }
}

# add ids without match
id_groups <- c(id_groups, setdiff(fdf$id, unlist(id_groups)))

# for every id in fdf, set real_id to index in id_groups to which id belongs
fdf$real_id <- sapply(fdf$id, function(id) {
  which(sapply(id_groups, function(group) id %in% group))
})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-10
    • 2020-09-13
    • 2016-02-18
    • 2020-10-17
    相关资源
    最近更新 更多