我试图解决R中的数据管理问题。
假设我的数据如下:
id <- c("123", "414", "606")
next.up <- c("414", "606", "119")
is.cond.met <- as.factor(c("FALSE", "FALSE", "TRUE"))
df <- data.frame(id, next.up, is.cond.met)
> df
id next.up is.cond.met
1 123 414 FALSE
2 414 606 FALSE
3 606 119 TRUE
我想获得以下内容:
id <- c("123", "414", "606")
next.up <- c("414", "606", "119")
is.cond.met <- as.factor(c("FALSE", "FALSE", "TRUE"))
origin <- c("606", "606", "119")
df.result <- data.frame(id, next.up, is.cond.met, origin)
> df.result
id next.up is.cond.met origin
1 123 414 FALSE 606
2 414 606 FALSE 606
3 606 119 TRUE 119
换句话说:我想将每个ID与其#34;来源&#34;匹配。当给定条件(is.met)为真时。我遇到的困难是这是迭代的和分层的:找到原点我可能需要经历多个分离度。逻辑步骤如下所示。我真的不确定如何在R中解决这个问题。
更新
其中一条评论提出了一个data.frame解决方案,它适用于排序数据,如上面的最小例子。实际上,我的数据没有以这种方式排序。一个更好的例子如下:
id <- c("961980", "14788", "902460", "900748", "728912", "141726", "1041190", "692268")
next.up <- c("20090", "655036", "40375164", "40031850", "40368996", "961980", "141726", "760112")
is.cond.met <- c(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)
df <- data.frame(id, next.up, is.cond.met, stringsAsFactors = FALSE)
glimpse(df)
Observations: 8
Variables: 3
$ id <chr> "961980", "14788", "902460", "900748", "728912", "141726", "1041190", "692268"
$ next.up <chr> "20090", "655036", "40375164", "40031850", "40368996", "961980", "141726", "760112"
$ is.cond.met <lgl> TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
> df
id next.up is.cond.met
1 961980 20090 TRUE
2 14788 655036 FALSE
3 902460 40375164 FALSE
4 900748 40031850 FALSE
5 728912 40368996 FALSE
6 141726 961980 FALSE
7 1041190 141726 FALSE
8 692268 760112 FALSE
更新2 :最终结果应如下所示:
> df.end.result
id next.up is.cond.met origin
1 961980 20090 TRUE <NA>
2 14788 655036 FALSE <NA>
3 902460 40375164 FALSE <NA>
4 900748 40031850 FALSE <NA>
5 728912 40368996 FALSE <NA>
6 141726 961980 FALSE 961980
7 1041190 141726 FALSE 961980
8 692268 760112 FALSE <NA>
答案 0 :(得分:5)
我稍微扩展了您的示例数据,以显示TRUE
中更多is.cond.met
值会发生什么情况。使用data.table
包,您可以执行以下操作:
library(data.table)
setDT(df)[, grp := shift(cumsum(is.cond.met), fill=0)
][, origin := ifelse(is.cond.met, next.up, id[.N]), by = grp][]
给出:
> df
id next.up is.cond.met grp origin
1: 123 414 FALSE 0 606
2: 414 606 FALSE 0 606
3: 606 119 TRUE 0 119
4: 119 321 FALSE 1 321
5: 321 507 TRUE 1 507
6: 507 185 TRUE 2 185
说明:
shift(cumsum(is.cond.met), fill=0)
创建分组变量。ifelse(is.cond.met, next.up, id[.N])
,您可以将正确的值分配给origin
。 注意: id
和next.up
列应该是类字符,以便上述工作(因此我在构建扩展时使用了stringsAsFactors = FALSE
示例数据)。如果它们是因素,请先使用as.character
转换它们。如果is.cond.met
不合逻辑,请将其转换为as.logical
。
在更新的示例数据上,上面的代码给出了:
id next.up is.cond.met grp origin
1: 961980 20090 TRUE 0 20090
2: 14788 655036 FALSE 1 692268
3: 902460 40375164 FALSE 1 692268
4: 900748 40031850 FALSE 1 692268
5: 728912 40368996 FALSE 1 692268
6: 141726 961980 FALSE 1 692268
7: 1041190 141726 FALSE 1 692268
8: 692268 760112 FALSE 1 692268
使用过的数据:
id <- c("123", "414", "606", "119", "321", "507")
next.up <- c("414", "606", "119", "321", "507", "185")
is.cond.met <- c(FALSE, FALSE, TRUE, FALSE, TRUE, TRUE)
df <- data.frame(id, next.up, is.cond.met, stringsAsFactors = FALSE)
答案 1 :(得分:3)
所以,imho,我认为如果没有交互式更新,你就无法解决它。
与@ procrastinatus-maximus类似,这是一个带有dplyr
的迭代解决方案
library(dplyr)
dfIterated <- data.frame(df, cond.origin.node = id,
cond.update = is.cond.met, stringsAsFactors = F)
initial.cond <- dfIterated$is.cond.met
while(!all(dfIterated$is.cond.met %in% c(TRUE, NA))) {
dfIterated <- dfIterated %>%
mutate(cond.origin.node = if_else(is.cond.met,
cond.origin.node,
next.up),
parent.match = match(next.up, id),
cond.update = (cond.update[parent.match] | cond.update),
cond.origin.node = if_else(!is.cond.met & cond.update,
next.up[parent.match],
next.up),
is.cond.met = cond.update)
}
# here we use ifelse instead of if_else since it is less type strict
dfIterated %>%
mutate(cond.origin.node = ifelse(initial.cond,
yes = NA,
no = cond.origin.node))
编辑:添加了开始条件;已被ifelse
dplyr::if_else
解释:我们迭代地更新dfIterated
以包含已建议的所有next.up
个节点。在这里,我们并行地为每个id
执行此操作。
cond.origin.node
并将其替换为id cond.is.met == TRUE
和next.up
“否则”NA
中的cond.is.met
值将返回{{1}重视自己,这在我们的案例中是非常实际的。
NA
我们与cond.update
列中的父级匹配的位置。 (将返回NA的值,即id
中没有匹配项,将被id
替换。)我们使用NA
(或)运算符,fortunetaley将返回{{1}如果|
TRUE == (TRUE | NA)
条目
TRUE
条件的原始节点。cond.update
TRUE
仅由is.cond.met
或is.cond.met
s组成。 orgin将包含TRUE
以上示例的输出如下所示:
NA
希望这有帮助!正向查找将以类似的方式工作。进一步的改进取决于您想要保留的结果类型(例如,您真的想要覆盖cond.is.met == TRUE
吗?)
答案 2 :(得分:3)
我希望我能正确理解你的问题,这符合我的观点。您似乎尝试根据数据表解决网络问题。我建议采用以下方法。
我们有一个网络,定义为一组边(列id
和next.up
对应vertex_from
和vertex_to
)。网络是一组树。列is.cond.met
映射作为端点或树根的顶点。未考虑根的树木不会被考虑在内。
我稍微修改了您的MRE,使其更具说明性。
id <- c("961980", "14788", "902460", "900748", "728912", "141726", "1041190", "692268", "40368996", "555555", "777777")
next.up <- c("20090", "655036", "40375164", "40031850", "40368996", "961980", "141726", "760112", "692268", "760112", "555555")
is.cond.met <- c(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE)
dt <- data.table(id, next.up, is.cond.met, stringsAsFactors = FALSE)
现在让我们将所有内容翻译成图形语言。
library(data.table)
library(magrittr)
library(igraph)
graph_from_edgelist(as.matrix(dt[, 1:2, with = F])) -> dt_graph
V(dt_graph)$color <- ifelse(V(dt_graph)$name %in% dt[is.cond.met == T]$next.up, "green", "yellow")
E(dt_graph)$arrow.size <- .7
E(dt_graph)$width <- 2
plot(dt_graph, edge.color = "grey50")
绿色顶点是映射根 - 让我们将它们命名为treeroots。他们的fisrt秩序的根源是每棵树的主要分支的根源 - 让它们成为分支根。问题是初始数据的id
列中的每个顶点都找到相应的branchroot。
treeroots <- dt[is.cond.met == T]$next.up %>% unique
lapply(V(dt_graph)[names(V(dt_graph)) %in% treeroots],
function(vrtx) neighbors(dt_graph, vrtx, mode = "in")) -> branchroots
我们可以在ego
包的igraph
函数的帮助下找到所有顶点到每个branchroot的顶点。
lapply(seq_along(branchroots), function(i) {
data.table(tree_root = names(branchroots[i]), branch_root = branchroots[[i]]$name)
}) %>% rbindlist() -> branch_dt
branch_dt[, trg_vertices := ego(dt_graph, order = 1e9,
V(dt_graph)[names(V(dt_graph)) %in% branch_dt$branch_root],
mode = "in", mindist = 1) %>% lapply(names)]
branch_dt
# tree_root branch_root trg_vertices
# 1: 20090 961980 141726,1041190
# 2: 760112 692268 40368996,728912
# 3: 760112 555555 777777
之后我们可以创建origin
列。
sapply(seq_along(branch_dt$branch_root),
function(i) rep(branch_dt$branch_root[i],
length(branch_dt$trg_vertices[[i]]))) %>% unlist -> map_vertices
branch_dt$trg_vertices %>% unlist() -> map_names
names(map_vertices) <- map_names
dt[, origin := NA_character_]
dt[id %in% map_names, origin := map_vertices[id]]
dt
# id next.up is.cond.met origin
# 1: 961980 20090 TRUE NA
# 2: 14788 655036 FALSE NA
# 3: 902460 40375164 FALSE NA
# 4: 900748 40031850 FALSE NA
# 5: 728912 40368996 FALSE 692268
# 6: 141726 961980 FALSE 961980
# 7: 1041190 141726 FALSE 961980
# 8: 692268 760112 TRUE NA
# 9: 40368996 692268 FALSE 692268
# 10: 555555 760112 FALSE NA
# 11: 777777 555555 FALSE 555555
为方便起见,我已将结果代码安排到一个函数中。
add_origin <- function(dt) {
require(data.table)
require(magrittr)
require(igraph)
setDT(dt)
graph_from_edgelist(as.matrix(dt[, .(id, next.up)])) -> dt_graph
treeroots <- dt[is.cond.met == T]$next.up %>% unique
lapply(V(dt_graph)[names(V(dt_graph)) %in% treeroots],
function(vrtx) neighbors(dt_graph, vrtx, mode = "in")) -> branchroots
lapply(seq_along(branchroots), function(i) {
data.table(tree_root = names(branchroots[i]), branch_root = branchroots[[i]]$name)
}) %>% rbindlist() -> branch_dt
branch_dt[, trg_vertices := rep(list(NA), nrow(branch_dt))][]
vertices_on_branch <- ego(dt_graph, order = 1e9,
V(dt_graph)[names(V(dt_graph)) %in% branch_dt$branch_root],
mode = "in", mindist = 1) %>% lapply(names)
set(branch_dt, j = "trg_vertices", value = list(vertices_on_branch))
sapply(seq_along(branch_dt$branch_root),
function(i) rep(branch_dt$branch_root[i],
length(branch_dt$trg_vertices[[i]]))) %>% unlist -> map_vertices
branch_dt$trg_vertices %>% unlist() -> map_names
names(map_vertices) <- map_names
dt[, origin := NA_character_]
dt[id %in% map_names, origin := map_vertices[id]]
dt[]
}
对于您的MRE,它会产生所需的输出。
df0 <- data.frame(id = c("961980", "14788", "902460", "900748", "728912", "141726", "1041190", "692268"),
next.up = c("20090", "655036", "40375164", "40031850", "40368996", "961980", "141726", "760112"),
is.cond.met = c(TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE), stringsAsFactors = FALSE)
df0 %>% add_origin
# id next.up is.cond.met origin
# 1: 961980 20090 TRUE NA
# 2: 14788 655036 FALSE NA
# 3: 902460 40375164 FALSE NA
# 4: 900748 40031850 FALSE NA
# 5: 728912 40368996 FALSE NA
# 6: 141726 961980 FALSE 961980
# 7: 1041190 141726 FALSE 961980
# 8: 692268 760112 FALSE NA
所描述的方法应该明显快于循环内data.frame
的迭代更新。