4

我有一个包含两列的数据框,一个 ID 列和一个具有与相应 ID 相关的子 ID 的列。子 ID 可以再次具有子 ID(在这种情况下,以前的子 ID 现在是一个 ID)。

library(tibble)

df <- tibble(id = c(1, 1, 2, 2, 3, 7), sub_id = c(2, 3, 4, 5, 6, 8))

df

# A tibble: 6 x 2
     id sub_id
  <dbl>  <dbl>
1     1      2
2     1      3
3     2      4
4     2      5
5     3      6
6     7      8

我想编写一个函数来查找与 ID 相关的所有子 ID。它应该返回一个包含所有子 ID 的向量。

find_all_sub_ids <- function (data, id) {
data %>% ...
}

find_all_sub_ids(df, id = 1)

[1] 2 3 4 5 6

find_all_sub_ids(df, id = 2)

[1] 4 5

find_all_sub_ids(df, id = 9)

[1] NULL

这与我迄今为止在 R 中所做的一切都非常不同,我很难为这个问题写一个好的标题。因此,如果使用正确的措辞,我可能已经通过谷歌搜索找到了答案。

我解决这个问题的第一个直觉是 while 循环。由于我也不知道可能有多少子级别,因此该功能应继续执行,直到找到所有子级别。我从来没有使用过while循环,也不知道如何在这里实现它们。

也许有人知道解决这个问题的好方法。谢谢!

编辑:忘记将 tibble 分配给 df 并在函数调用中使用此参数。

4

3 回答 3

3

igraph

library(igraph)
g <- graph_from_data_frame(d, directed = TRUE)

find_all_subs <- function(g,id){
  #find child nodes, first one being origin
  r <- igraph::subcomponent(g,match(id, V(g)$name),"out")$name
  #remove origin
  as.numeric(r[-1])
}
find_all_subs(g,1)
[1] 2 3 4 5 6

find_all_subs(g,2)
[1] 5 6
于 2020-07-20T18:16:38.343 回答
2

我认为将其表述为图形问题最容易。
您的 data.frame 描述了一个有向图(从 id 到 sub_id 的顶点),并且您对从某个顶点可到达哪些节点感兴趣。

使用tidygraph,可以这样实现:

library(tidyverse)
library(tidygraph)

df <- tibble(id = c(1, 1, 2, 2, 3, 7), sub_id = c(2, 3, 4, 5, 6, 8))

find_all_sub_ids <- function (id) {
  if (!(id %in% df$id)) {
    return(NULL)
  }

  
  grph <- df %>% 
    as_tbl_graph(directed = TRUE)
  
  id <- which(grph %>% pull(name) == as.character(id))
  
  grph %>% 
    activate(nodes) %>% 
    mutate(reachable = !is.na(bfs_dist(id))) %>% 
    as_tibble() %>% 
    filter(reachable) %>% 
    pull(name) %>% 
    as.numeric()
}

我们看到哪些节点是可到达的(它们与您的给定节点有非 NA 距离),我们使用bfs_dist(参见此处进行解释)。
这给

> find_all_sub_ids(1)
[1] 1 2 3 4 5 6

> find_all_sub_ids(2)
[1] 2 4 5

> find_all_sub_ids(9)
NULL

这种方法的优点是它可以搜索很多层次,而无需显式地编写循环。

编辑 我的代码中有一个错误,tidygraph::bfs_dist使用的 id 与我预期的不同。现在修好了。
在新示例中:

> find_all_sub_ids(10)
[1]  10 200 300
于 2020-07-20T18:02:22.240 回答
0

我使用数据框完成了它。以下作品。

x= c(1,1,2,2,3,7)
y = c(2, 3, 4, 5, 6, 8)
df <- data.frame(cbind(x,y))
colnames(df) =c('id', 'sub_id')


find_all_sub_ids <- function (df, id_requested) {
  si <- df[df$id==id_requested,]$sub_id
  return(si)
}
find_all_sub_ids(df,id=2)
[1] 4 5
于 2020-07-20T18:08:36.450 回答