【问题标题】:Find unique set of identifiers/groups among several columns在几列中查找唯一的标识符/组集
【发布时间】:2019-10-26 03:40:22
【问题描述】:

我的数据包含两列(可能更多)标识符(通常是长字符串)。这些有时会有所不同,输入错误或随时间而变化。我想识别数据中的独特主题。这需要识别在某个级别通过其 id 连接的案例组。

一个例子

df <- data.frame(ida = c("A", "B", "C", "C", "D", "E"),
                 idb = c(1, 1, 3, 4, 4, 7),
                 trueid = c("id1", "id1", "id2", "id2", "id2", "id3"))
> df
  ida idb trueid
1   A   1    id1
2   B   1    id1
3   C   3    id2
4   C   4    id2
5   D   4    id2
6   E   7    id3

id1 的标识符是 "A", "B", 1id2 "C", "D", 3, 4id3 "E", 7

我不知道trueid,但需要使用idaidb 列中的信息找到它。

该解决方案需要扩展到具有数万个唯一 ID 的数百万个观测值。我已经在使用data.table

扩展:还有另一种情况,其中有两个以上的列,有些列可能对其他列提供信息,即具有相同的标识符。我不知道哪些栏可以提供哪些信息。我认为类型可以忽略,所有列都是字符串或者可以安全地转换。

另一个例子:

df <- data.frame(ida = c("A", "B", "C", "C", "D", "E"),
                 idb = c("1", "2", "3", "4", "4", "7"),
                 idc = c("1", "1", "2", "3", "4", "5"),
                 idd = c("1", "A", "2", "3", "4", "5"),
                 trueid = c("id1", "id1", "id1", "id1", "id1", "id2"))
> df
  ida idb idc idd trueid
1   A   1   1   1    id1
2   B   2   1   A    id1
3   C   3   2   2    id1
4   C   4   3   3    id1
5   D   4   4   4    id1
6   E   7   5   5    id2

编辑:正如评论者指出的那样,这本质上是在图中找到完整子图的集团问题。在阅读了更多之后,我知道这个问题可以通过library(igraph) 解决。我将问题悬而未决,因为我更喜欢依赖basedata.tabledplyr 的解决方案。我无法在我正在使用的服务器上轻松安装软件包,安装 igraph 涉及处理大量繁文缛节和延迟。

Edit2:对于任何阅读本文并面临类似问题的人:zx8754 使用 igraph 的答案在具有更多组的较大(模拟)数据上要快得多(几个数量级)。如果您有机会使用igraph,请这样做。

【问题讨论】:

  • 将案例视为唯一 ID 的规则是什么?
  • 通过某个 id 链接到任何其他案例的案例。我稍微改述了这个问题。在第一种情况下,id1 的标识符是 "A", "B", "1"id2 "C", "D", 3, 4id3 "E", 7
  • 这听起来像是一个识别“团”的网络分析问题,其中每个节点之间存在一条边,至少有一个公共变量,每个trueid是一个“完整子图”,其中节点可以通过有限的步骤连接起来。
  • 是的,本质上就是这样。我正在寻找不依赖 igraph 或更具体的包的解决方案。
  • 为什么不 igraph?

标签: r dataframe dplyr data.table igraph


【解决方案1】:

使用igraph

# example input, I removed "trueid" column
df <- data.frame(ida = c("A", "B", "C", "C", "D", "E"),
                 idb = c("1", "2", "3", "4", "4", "7"),
                 idc = c("1", "1", "2", "3", "4", "5"),
                 idd = c("1", "A", "2", "3", "4", "5"))
#trueid = c("id1", "id1", "id1", "id1", "id1", "id2")

library(igraph)

# set up connections
# Improved version suggested by @thelatemail in the comments
x <- cbind(df[ 1 ], unlist(df[ -1 ]))

# original clumsy version (do not use)
# x <- unique(do.call(rbind, lapply(1:(ncol(df) - 1), function(i) setNames(df[, c(i, i + 1) ], c("from", "to")))))

# convert to graph object
g <- graph_from_data_frame(x)        

# plot if you wish to visualise
plot(g)

# this is the solution, add membership ids to original input dataframe
merge(df, data.frame(grp = clusters(g)$membership),
      by.x = "ida", by.y = 0)
#   ida idb idc idd grp
# 1   A   1   1   1   1
# 2   B   2   1   A   1
# 3   C   3   2   2   1
# 4   C   4   3   3   1
# 5   D   4   4   4   1
# 6   E   7   5   5   2

【讨论】:

  • 获取x - do.call(rbind, Map(cbind.data.frame, x=df[1:3], y=df[2:4]))的另一种方式
  • 虽然这只是基于连接的,但我认为不必减少这么多——即使x &lt;- cbind(df[1], unlist(df[-1])) 也应该这样做。
  • @thelatemail 同意,'do.call' 有点不好,只是想发布一些有用的东西。如果您不介意,我会根据您的建议进行编辑。
  • 去吧——你欠我一杯虚拟啤酒 ;-)
  • @thelatemail 已编辑,cheers!
【解决方案2】:

这是使用data.table的递归方法:

#convert into a long format for easier processing
mDT <- melt(DT[, rn := .I], id.var="rn", variable.name="V", value.name="ID")[,
    tid := NA_integer_]

#the recursive function
link <- function(ids, label) {
    #identify the rows in DT containing ids and extract the IDs
    newids <- mDT[mDT[.(ID=ids), on=.(ID), .(rn=rn)], on=.(rn), allow.cartesian=TRUE,
        unique(ID)]

    #update those rows to the same group
    mDT[mDT[.(ID=ids), on=.(ID), .(rn=rn)], on=.(rn), tid := label]

    if (length(setdiff(newids, ids)) > 0L) {
        #call the recursive function if there are new ids
        link(newids, label)
    }
}

#get the first id that is not labelled yet
id <- mDT[is.na(tid), ID[1L]]
grp <- 1L
while(!is.na(id)) {
    #use recursive function to link them up
    link(id, grp)

    #repeat for next id that is not part of any group yet
    id <- mDT[is.na(tid), ID[1L]]
    grp <- grp + 1L
}

#update original DT with tid
DT[mDT, on=.(rn), tid := tid]

数据:

library(data.table)
DT <- data.table(ida = c("A", "B", "C", "C", "D", "E"),
    idb = c("1", "2", "3", "4", "4", "7"),
    idc = c("1", "1", "2", "3", "4", "5"),
    idd = c("1", "A", "2", "3", "4", "5"))

【讨论】:

  • 谢谢,这种递归解决方案正是我想要的。
猜你喜欢
  • 2021-11-19
  • 2012-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 2010-10-17
  • 1970-01-01
相关资源
最近更新 更多