Во-первых, пусть
original_transactions
# row start change end
# 1 1 100.00 2.33 102.33
# 2 2 102.33 -6.52 95.81
# 3 3 95.81 -4.20 91.61
# 4 4 91.61 -3.56 88.05
# 5 5 88.05 7.92 95.97
# 6 6 95.97 3.61 99.58
transactions
# row start change end
# 1 1 100.00 2.33 102.33
# 2 2 91.61 -3.56 88.05
# 3 3 95.81 -4.20 91.61
# 4 4 102.33 -6.52 95.81
# 5 5 88.05 7.92 95.97
# 6 6 95.97 3.61 99.58
и
diffs <- outer(transactions$start, transactions$start, `-`)
matches <- abs(sweep(diffs, 2, transactions$change, `-`)) < 1e-3
Я предполагаю, что вычисление diffs
является самой дорогой вычислительной частью во всем решении.diffs
имеет все возможные различия между start
вашего transactions
.Затем, сравнивая их со столбцом change
в matches
, мы узнаем, какие пары строк transactions
должны идти вместе.Если бы не было проблем с числовой точностью, мы могли бы использовать функцию match
и сделать это быстро.В этом случае, однако, у нас есть следующие два варианта .
Во-первых, мы можем использовать igraph
.
library(igraph)
(g <- graph_from_adjacency_matrix(t(matches) * 1))
# IGRAPH 45d33f0 D--- 6 5 --
# + edges from 45d33f0:
# [1] 1->4 2->5 3->2 4->3 5->6
То есть мыу нас есть скрытый граф путей: 1-> 4-> 3-> 2-> 5-> 6, который мы хотим восстановить.Он задается самым длинным путем из вершины, у которой нет входящих ребер (1
):
transactions[as.vector(tail(all_simple_paths(g, from = which(rowSums(matches) == 0)), 1)[[1]]), ]
# row start change end
# 1 1 100.00 2.33 102.33
# 4 4 102.33 -6.52 95.81
# 3 3 95.81 -4.20 91.61
# 2 2 91.61 -3.56 88.05
# 5 5 88.05 7.92 95.97
# 6 6 95.97 3.61 99.58
Другой вариант является рекурсивным.
fun <- function(x, path = x) {
if(length(xNew <- which(matches[, x])) > 0)
fun(xNew, c(path, xNew))
else path
}
transactions[fun(which(rowSums(matches) == 0)), ]
# row start change end
# 1 1 100.00 2.33 102.33
# 4 4 102.33 -6.52 95.81
# 3 3 95.81 -4.20 91.61
# 2 2 91.61 -3.56 88.05
# 5 5 88.05 7.92 95.97
# 6 6 95.97 3.61 99.58
Itиспользует ту же уникальную идею самого длинного графа путей, что и в предыдущем подходе.
Нет явных циклов ... И, конечно, вы можете переписать все с помощью %>%
, но это будет не так красиво, как выхочу;на самом деле это не традиционная задача преобразования данных, где dplyr
лучше всего.