实际上,您的问题是组件的图形分离。在您的情况下,图的顶点是人。根据属性信息,即电子邮件和电话号码,您可以建立边缘关系。
看起来像paste 或duplicate 或group_by 这样的简单方法无效,因为您可以有相当复杂的路径。正如您所解释的那样,人 D 和人 E 具有完全不同的联系人,实际上他们是通过人 C 连接的,因此应该具有相同的 ID。
或者换句话说,有人在网站上注册了电子邮件A 和移动B。然后他把手机丢了。并在手机C上注册。然后他忘记了密码并使用电子邮件D注册。最后,我们有电子邮件D 和移动C 的人。不知什么原因,他注册了不同的名字。
你可能有更复杂的关系路径。
下面的算法使用igraph 根据您的条件创建的邻接矩阵制作无向图。在它识别出未连接的组件后,将其提取并与初始data.frame 合并。由于您的示例中没有足够的数据,因此使用了模拟。
模拟输入:
name tel email
1 AAA 222 F@xy.com
2 BBB 555 C@xy.com
3 CCC 333 E@xy.com
4 DDD 666 D@xy.com
5 EEE 666 A@xy.com
6 FFF 111 F@xy.com
7 GGG 444 B@xy.com
8 HHH 666 A@xy.com
9 III 444 B@xy.com
10 JJJ 333 F@xy.com
代码
library(igraph)
set.seed(123)
n <- 10
# simulation
df <- data.frame(
name = sapply(1:n, function(i) paste0(rep(LETTERS[i], 3), collapse = "")),
tel = sample(1:6, n, replace = TRUE) * 111,
email = paste0(sample(LETTERS[1:6], n, replace = TRUE), "@xy.com")
)
# adjacency matrix preparation
df1 <- expand.grid(df$name, df$name)
names(df1) <- c("name_x", "name_y")
df1 <- merge(df1, df, by.x = "name_x", by.y = "name")
df1 <- merge(df1, df, by.x = "name_y", by.y = "name")
df1$con <- ifelse(with(df1, tel.x == tel.y | email.x == email.y), 1, 0)
stats::reshape(df1[, c(1, 2, 7)], idvar = "name_x", timevar = "con", direction = "wide")
#v.names = , timevar = "numbers", direction = "wide")
library(igraph)
library(reshape2)
m <- dcast(df1[, c(1, 2, 7)], name_y ~ name_x)
rownames(m) <- m[, 1]
m[, 1] <- NULL
m <- as.matrix(m)
diag(m) <- 0
# graph creation
g1 <- graph_from_adjacency_matrix(m, mode = "undirected")
gcmps <- groups(components(g1))
# groups extraction
ids <- unlist(mapply(function(x, y) paste0(x, "_", y), seq_along(gcmps), gcmps))
df_ids <- as.data.frame(t(sapply(ids, function(x) unlist(strsplit(x, "_")))))
names(df_ids) <- c("id", "name")
# data merging
result <- merge(df, df_ids)
result
输出:
name tel email
1 AAA 222 F@xy.com
2 BBB 555 C@xy.com
3 CCC 333 E@xy.com
4 DDD 666 D@xy.com
5 EEE 666 A@xy.com
6 FFF 111 F@xy.com
7 GGG 444 B@xy.com
8 HHH 666 A@xy.com
9 III 444 B@xy.com
10 JJJ 333 F@xy.com
关系图(只取名字的首字母)