Изменить значение, используя значение из другой строки в таблице
Я хочу рассчитать расстояние от узла до корня dtr
, Все, что у меня есть, это вектор, который содержит идентификатор родительского узла для каждого узла rel
(в этом примере id == 7
является корнем):
library(tidyverse)
tmp <- tibble(
id = 1:12,
rel = c(2,7,4,2,4,5,7,7,10,8,7,7)
)
В конце я ищу этот результат:
TMP $ DTR
[1] 2 1 3 2 3 4 0 1 3 2 1 1
До сих пор мне удавалось писать следующий алгоритм, пока я не застрял при попытке ссылаться на другую строку в моем коде.
Алгоритм должен работать так (псевдокод):
- Если не root, приращение
dtr
:if(!equals(tid,trel)): dtr = dtr+1
- + Изменить
tid
вtrel
:tid = trel
- + Изменить
trel
кrel
значение гдеid == trel
- Если есть
!equals(tid,trel)
GOTO 1., еще КОНЕЦ
Сначала я добавил 2 вспомогательных столбца для хранения временной информации:
tmp <- tmp %>%
mutate(
tid = id,
trel = rel,
dtr = 0
)
Первые два шага в алгоритме работают так:
tmp <- tmp %>%
mutate(
dtr = if_else(
!equals(tid,trel),
dtr + 1,
dtr
),
tid = trel
)
3-й шаг, в котором я не уверен.... Я пытался добиться этого с помощью следующего кода, но это не работает:
tmp <- tmp %>%
mutate(trel = rel[id == .$tid])
Результат (конечно) неправильный:
TMP $ отн
[1] 7 7 7 7 7 7 7 7 7 7 7 7
Но почему не это? (Должно быть правильным решением при запуске 3. в первый раз):
[1] 2 7 2 7 2 4 7 7 10 8 7 7
4-й шаг выполняется путем проверки наличия у меня более одного уникального значения в trel:
while(length(unique(tmp$trel)) > 1){
...
}
Таким образом, полный алгоритм должен выглядеть примерно так:
get_dtr <- function(tib){
tmp <- tib %>%
mutate(
tid = id,
trel = rel,
dtr = 0
)
while(length(unique(tmp$trel)) > 1){
tmp <- tmp %>%
mutate(
dtr = if_else(
!equals(tid,trel),
dtr + 1,
dtr
),
tid = trel
)
### Step 3
}
tmp
}
Любая идея, как решить это или более простое решение? Заранее спасибо!
2 ответа
Это в основном уже реализовано в tidygraph
пакет. Если вы собираетесь работать с графоподобными данными с помощью Tidyverse, вы должны сначала посмотреть туда. ты можешь сделать
library(tidygraph)
as_tbl_graph(tmp, directed=FALSE) %>%
activate(nodes) %>%
mutate(depth=bfs_dist(root=7)) %>%
as_tibble()
# name depth
# <chr> <int>
# 1 1 2
# 2 2 1
# 3 3 3
# 4 4 2
# 5 5 3
# 6 6 4
# 7 7 0
# 8 8 1
# 9 9 3
# 10 10 2
# 11 11 1
# 12 12 1
Если вы хотите написать функцию самостоятельно, вы можете использовать следующий код:
library(tidyverse)
tmp <- tibble(
id = 1:12,
rel = c(2,7,4,2,4,5,7,7,10,8,7,7)
)
calc_dtr <- function(id, tmp){
# find root
root <- tmp$id[tmp$id == tmp$rel]
# is this the root node?
if(id == root){return(0)}
# initialize counter
dtr <- 1
trel <- tmp$rel[tmp$id == id]
while(trel != root){
dtr <- dtr + 1
trel <- tmp$rel[tmp$id == trel]
}
return(dtr)
}
tmp %>%
mutate(
dtr = map_dbl(id, calc_dtr, tmp)
)
Это дает следующий вывод:
# A tibble: 12 x 3
id rel dtr
<int> <dbl> <dbl>
1 1 2 2
2 2 7 1
3 3 4 3
4 4 2 2
5 5 4 3
6 6 5 4
7 7 7 0
8 8 7 1
9 9 10 3
10 10 8 2
11 11 7 1
12 12 7 1