迭代地和分层地循环遍历行直到满足条件

时间:2016-07-13 17:25:49

标签: r loops dplyr data-manipulation tidyr

我试图解决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中解决这个问题。

logical steps


更新
其中一条评论提出了一个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>

3 个答案:

答案 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

说明:

  1. 首先使用shift(cumsum(is.cond.met), fill=0)创建分组变量。
  2. 使用ifelse(is.cond.met, next.up, id[.N]),您可以将正确的值分配给origin
  3. 注意: idnext.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执行此操作。

  1. 我们改变cond.origin.node并将其替换为id cond.is.met == TRUEnext.up“否则”NA中的cond.is.met值将返回{{1}重视自己,这在我们的案例中是非常实际的。
    • 然后我们计算匹配的父索引
  2. 我们更新NA我们与cond.update列中的父级匹配的位置。 (将返回NA的值,即id中没有匹配项,将被id替换。)我们使用NA(或)运算符,fortunetaley将返回{{1}如果|
  3. 中有先前的TRUE == (TRUE | NA)条目
  4. 然后我们需要计算TRUE条件的原始节点。
  5. 然后在cond.update
  6. 中提升条件
  7. 重复所有内容,直到TRUE仅由is.cond.metis.cond.met s组成。 orgin将包含TRUE
  8. 的节点

    以上示例的输出如下所示:

    NA

    希望这有帮助!正向查找将以类似的方式工作。进一步的改进取决于您想要保留的结果类型(例如,您真的想要覆盖cond.is.met == TRUE吗?)

答案 2 :(得分:3)

我希望我能正确理解你的问题,这符合我的观点。您似乎尝试根据数据表解决网络问题。我建议采用以下方法。

我们有一个网络,定义为一组边(列idnext.up对应vertex_fromvertex_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")

我们有以下图表。 enter image description here

绿色顶点是映射根 - 让我们将它们命名为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的迭代更新。