我相信这可以通过igraph 以与“recursive” self join in data.table 类似的方式解决,但无需计算。
这里的困难在于每个Item 都有单独的图表。我的方法是将数据框拆分为图表列表。可能有更简洁的解决方案使用type 顶点属性。
但是,下面的代码会产生预期的结果:
library(igraph)
library(data.table)
library(magrittr)
lapply(
lapply(split(lctolc, lctolc$Item), function(x) graph.data.frame(x[, 2:3])),
function(x) lapply(
V(x)[degree(x, mode = "in") == 0],
function(s) all_simple_paths(x, from = s,
to = V(x)[degree(x, mode = "out") == 0]) %>%
lapply(
function(y) as.data.table(t(names(y))) %>% setnames(paste0("LC", seq_along(.)))
) %>%
rbindlist(fill = TRUE)
) %>% rbindlist(fill = TRUE)
) %>% rbindlist(fill = TRUE, idcol = "Item")
Item LC1 LC2 LC3 LC4
1: 8T4121 MN12 AB12 BC34 <NA>
2: 8T4121 MW92 WK14 RS11 OY01
3: 8T4121 MW92 WK14 RM11 <NA>
4: AB7651 MW92 RS11 OY01 <NA>
说明
igraph 包是解决此类问题的不错选择。
但是,我们需要分别处理每个Item 的图。这是通过拆分 data.frame 并通过
创建图表列表来实现的
lg <- lapply(split(lctolc, lctolc$Item), function(x) graph.data.frame(x[, 2:3]))
返回
lg
$`8T4121`
IGRAPH 8eb2bcc DN-- 8 6 --
+ attr: name (v/c)
+ edges from 8eb2bcc (vertex names):
[1] AB12->BC34 MN12->AB12 MW92->WK14 WK14->RM11 WK14->RS11 RS11->OY01
$AB7651
IGRAPH 7cd75e7 DN-- 3 2 --
+ attr: name (v/c)
+ edges from 7cd75e7 (vertex names):
[1] MW92->RS11 RS11->OY01
或者,通过两个单独的图进行可视化。
lapply(seq_along(lg), function(i) plot(lg[[i]], main = names(lg)[i]))
现在,函数all_simple_paths() 列出了从一个源顶点到另一个顶点的简单路径,或者如果顶点被访问一次,则路径是简单的顶点。要使用该功能,我们需要确定起始节点和所有结束节点。这是通过
实现的
V(x)[degree(x, mode = "in") == 0] # start nodes
V(x)[degree(x, mode = "out") == 0] # end nodes
degree() 函数分别返回传入或传出边的数量。
对于我们的示例数据集,我们得到
lapply(lg, function(x) V(x)[degree(x, mode = "in") == 0]) # start nodes
$`8T4121`
+ 2/8 vertices, named, from 8eb2bcc:
[1] MN12 MW92
$AB7651
+ 1/3 vertex, named, from 7cd75e7:
[1] MW92
lapply(lg, function(x) V(x)[degree(x, mode = "out") == 0]) # end nodes
$`8T4121`
+ 3/8 vertices, named, from 8eb2bcc:
[1] BC34 RM11 OY01
$AB7651
+ 1/3 vertex, named, from 7cd75e7:
[1] OY01
现在,我们遍历每个图的所有起始节点并确定所有简单路径。结果又是一个列表。对于每个列表项,节点名称被提取并重新调整为宽格式的 data.table。列重命名为LC1、LC2等。
在每一步中,我们都会得到一个由rbindlist() 组合的data.tables 列表。 fill 参数是必需的,因为列数可能会有所不同。对 rbindlist() 的最终调用使用idcol 参数来标记与Item 关联的行。
数据
样本数据集已经过修改,以包含来自 OP 的 cmets here 和 here 的案例。
library(data.table)
lctolc <- fread("
Item LC ToLC
8T4121 AB12 BC34
8T4121 MN12 AB12
8T4121 MW92 WK14
8T4121 WK14 RM11
8T4121 WK14 RS11
8T4121 RS11 OY01
AB7651 MW92 RS11
AB7651 RS11 OY01",
data.table = FALSE)