样本数据:
Bilagstoptekst <- c("A", "A", "A", "A", "A","B","B","C","C","C","C","C","C","C")
AKT <- c("80","80","80","25","25","25","25","80","80","80","80","80","80","80")
IA <- c("HUVE", "HUVE", "HUBO", "BILÅ", "BILÅ", "BILÅ","BILÅ", "HUBO","HUBO","HUBO","HUBO","HUBO","HUBO","HUBO")
Belob <- c(100,100,50,75,40,60,400,100,100,100,100,100,333,333)
FPT8 <- data.frame(Bilagstoptekst, AKT, IA, Belob)
> FPT8
Bilagstoptekst AKT IA Belob
A 80 HUVE 100
A 80 HUVE 100
A 80 HUBO 50
A 25 BILÅ 75
A 25 BILÅ 40
B 25 BILÅ 60
B 25 BILÅ 400
C 80 HUBO 100
C 80 HUBO 100
C 80 HUBO 100
C 80 HUBO 100
C 80 HUBO 100
C 80 HUBO 333
C 80 HUBO 333
Bilagstoptekst <- c("A", "A", "A", "A", "B", "C", "C")
AKT <- c("80", "80", "25", "25", "25", "80", "80")
IA <- c("HUVE", "HUBO", "BILÅ", "BILÅ", "BILÅ", "HUBO", "HUBO")
RegKonto <- c(4,5,7,1,6,3,9)
Psteksnr <- c(1,6,8,2,5,7,9)
Belob_sum <- c(200,50,75,40,460,500,666)
G69 <- data.frame(Bilagstoptekst, AKT, IA, RegKonto, Psteksnr, Belob_sum)
> G69
Bilagstoptekst AKT IA RegKonto Psteksnr Belob_sum
A 80 HUVE 4 1 200
A 80 HUBO 5 6 50
A 25 BILÅ 7 8 75
A 25 BILÅ 1 2 40
B 25 BILÅ 6 5 460
C 80 HUBO 3 7 500
C 80 HUBO 9 9 666
现在,我的真实数据集非常大。
我要做的是将 RegKonto 和 Psteksnr 从 G69 合并到 FPT8 。
我有三个关键列,应该在两个数据框中相互匹配:
爱荷华州AKT的Bilagstoptekst 。
但是我不能仅仅使用left_join,因为还有另一条规则。 FPT8 $ Belob 应该与 G69 $ Belob_sum 相匹配。有时它确实匹配(在我的示例数据第3行中为fx)。有时,我可以通过将所有 FPT8 $ Belob 加在一起并将该数字(结合我的3个关键列)与 G69 $ Belob_sum (第1行中的fx)进行匹配来找到匹配项和2)。
但是有时将哪些行加在一起以找到正确的匹配项是随机的(实际上,它不是随机的,但确实感觉像这样!)。像最后一个带有bilagstoptekst == C的行。
我要问的是,是否有一种方法可以将不同的组合加起来并用于合并。
预期输出:
> FPT8
Bilagstoptekst AKT IA Belob RegKonto Psteksnr
A 80 HUVE 100 4 1
A 80 HUVE 100 4 1
A 80 HUBO 50 5 6
A 25 BILÅ 75 7 8
A 25 BILÅ 40 1 2
B 25 BILÅ 60 6 5
B 25 BILÅ 400 6 5
C 80 HUBO 100 3 7
C 80 HUBO 100 3 7
C 80 HUBO 100 3 7
C 80 HUBO 100 3 7
C 80 HUBO 100 3 7
C 80 HUBO 333 9 9
C 80 HUBO 333 9 9
我已经尝试过的方法:
我已经展开了-对于每一行键-FPT8 $ Belob有什么不同的值。
dt <- as.data.table(FPT8)
dt[, idx := rowid(Bilagstoptekst, AKT, IA)] # creates the timevar
out <- dcast(dt,
Bilagstoptekst + AKT + IA~ paste0("Belob", idx),
value.var = "Belob")
然后我对展开的FPT8 $ Belob的总和进行了不同的组合:
# Adding together two different FPT8$Belob - all combinations
output <- as.data.frame(combn(ncol(out[,-c(1:3)]), m=2, FUN =function(x) rowSums(out[,-c(1:3)][x])))
names(output) <- paste0("sum_", combn(names(out[,-c(1:3)]), 2, FUN = paste, collapse="_"))
在此之后,我来回合并,我真的不想进入这一部分,因为当我每个键有4个以上不同的FPT8 $ Belob(3列)时,这真是一团糟。因此,我绝对需要一种更流畅的方法来执行此操作。
希望有人可以帮助我。
编辑:行如何配对以及更多说明
所以我的FPT8数据是一笔付款(Belob表示金额)。 G69数据就是账单。我需要找到正确的匹配项,但是我的问题是,有时人们会选择将帐单分成较小的部分。因此,FPT8数据大于G69数据。
让我解释一下。
我有4个要匹配的关键列:Bilagstoptekst,AKT,IA和Belob。前三个应该始终在FPT8数据中找到完全匹配的内容。有时Belob确实与G69中的Belob_sum相匹配(逐行),有时我们需要FPT8 Belob行的总和与Bilagstoptekst,AKT和IA 中的相同,以与G69中的Belob_sum相匹配。让我尝试用下面的示例数据来显示它。
FPT8:
基于我的3个关键列** Bilagstoptekst *, AKT 和 IA ,前两行“相同”(即同一笔账单已支付了两次)。我添加了ID列作为第一列,而我的真实数据中没有。这仅是为了说明。因此,这两行我称为ID = 1。
第3行(ID = 2)与我的示例FPT8数据中的其他行不匹配,因为键列组合没有其他任何内容(即,该人一次付清了全部账单-这一个将很容易与G69账单信息匹配。
在底部,所有Bilagstoptekst == C具有三个关键列(C,80和HUBO)的相同组合。那是同一张账单。但是这些不是同一张账单。在这种情况下,我可以在G69数据中找到两个匹配项。我怎么知道哪一个是正确的?我看一下FPT8 $ Belob和G69 $ Belob_sum列。
G69:
因此,如果我手动执行此操作,则尝试在FPT8 $ Belob中找到与G69 $ Belob_sum和其他3个关键列匹配的总和的不同组合。 Fx我可以看到,Belob中的最后两行总计为666,与G69中的最后一行相匹配。由于100 * 5 = 500,另一个Bilagstoptekst == C,AKT = 80和IA = HUBO匹配G69中的倒数第二行。
所需的输出:
我已经添加了一些颜色,所以希望现在更容易理解。
答案 0 :(得分:1)
嘿!
我看不到任何一站式解决方案,但是通过一些简单的规则,我们可以逐步进行匹配。
此外,最终输出不能匹配您的精美图像,因为您没有包括该数据(reg.nr列)。
首先,最简单的方法是:Belob
与Belob_sum
直接匹配,因为只有一行:
library(dplyr)
# Rule 1: Easy matching -----
s1 <- inner_join(FPT8, G69, by=c('Bilagstoptekst','AKT','IA','Belob'='Belob_sum'))
not_matched1 <- anti_join(FPT8, s1,by=c('Bilagstoptekst','AKT','IA'))
最后一行检查不匹配的内容。因此,我们应用规则2,使用分组总和:
# Rule 2: Calculate Belob_sum to match by ---------------
s2 <- not_matched1 %>% group_by(Bilagstoptekst, AKT, IA) %>%
mutate(Belob_sum=sum(Belob)) %>%
inner_join(G69, by=c('Bilagstoptekst','AKT','IA','Belob_sum'))
matched <- bind_rows(s1, s2)
not_matched2 <- anti_join(FPT8, matched, by=c('Bilagstoptekst','AKT','IA'))
同样,我们检查不匹配的内容并将两者合并。然后,规则3。现在这很棘手,并且仅在付款均分的假设下有效。
# Rule 3: More gætværk ---------------
# We assume the payed amounts are divided *equally*
s3 <- not_matched2 %>% group_by(Bilagstoptekst, AKT, IA, Belob) %>%
mutate(Belob_sum=sum(Belob)) %>%
inner_join(G69, by=c('Bilagstoptekst','AKT','IA','Belob_sum'))
matched <- bind_rows(matched, s3)
not_matched3 <- anti_join(FPT8, matched, by=c('Bilagstoptekst','AKT','IA'))
# not_matched3 is now empty!
> matched
Bilagstoptekst AKT IA Belob RegKonto Psteksnr Belob_sum
1 A 80 HUBO 50 5 6 NA
2 A 25 BILÅ 75 7 8 NA
3 A 25 BILÅ 40 1 2 NA
4 A 80 HUVE 100 4 1 200
5 A 80 HUVE 100 4 1 200
6 B 25 BILÅ 60 6 5 460
7 B 25 BILÅ 400 6 5 460
8 C 80 HUBO 100 3 7 500
9 C 80 HUBO 100 3 7 500
10 C 80 HUBO 100 3 7 500
11 C 80 HUBO 100 3 7 500
12 C 80 HUBO 100 3 7 500
13 C 80 HUBO 333 9 9 666
14 C 80 HUBO 333 9 9 666
现在,如果您的“ C”组没有分成大组,则您会有点不高兴,您将不得不手动整理数据以识别相同的组分组,或应用其他算法尝试在给定的限制下匹配最佳分组。
**最后的提示:**
R和dplyr 可以使用丹麦字母,但这是小便。当将一列称为字符串时,很简单:
FPT8[,'Beløb']
但是,如果您在dplyr 中将它们用作变量名,请使用反斜线:
FPT8 %>% summarise(`Beløb_sum`=sum(`Beløb`))
更新:
我已经找到了一种解决方案,可以基于迭代方法将各种不相等的分组求和。这是一个示例,您必须将其重构为解决方案的第四步。但是,如果您可以将搜索范围限制为匹配“ Bilagstoptekst”,“ AKT”和“ IA”的内容,那么我想您应该很好。
groups <- data.frame(name=letters[1:4], sumsize=c(100,130, 80,99), stringsAsFactors = FALSE)
subpayments <- data.frame(paid=c(50,40,10,50,43,37,20,25,20,15,42,57))
stopifnot(sum(groups$sumsize) == sum(subpayments$paid))
subpayments$id <- 1:nrow(subpayments)
groups <- groups[order(groups$sumsize, decreasing=TRUE),]
subpayments <- subpayments[order(subpayments$paid, decreasing=TRUE),]
subpayments$group <- NA
for (g in seq_along(groups$name)) {
sumsize <- 0
#subpayments$tried <- FALSE
maxsize <- groups$sumsize[g]
path <- c()
attemptspath <- list()
attempts <- vector('logical', nrow(subpayments))
#attempts[1] <- TRUE
#attemptspath <- list(1)
i <- 0
while (sumsize < maxsize) {
#browser()
last_i <- i
i <- min(which(subpayments$paid <= (maxsize - sumsize) & !attempts & is.na(subpayments$group)))
if (is.infinite(i)) {
# current path did not succed, backpeddle and try another route
#cat('is infinite.', i, 'path', path, '\n')
#cat('attempts:', attempts, '\n')
if (length(path) == 0) {
# at the beginning again and exhausted our attempts
break
}
if (is.infinite(last_i)) {
attempts[attemptspath[[length(path)+1]]] <- FALSE
attemptspath[[length(path)+1]] <- logical(0)
#last <- path[length(path)]
#path <- path[-length(path)]
#sumsize <- sumsize - subpayments$paid[last]
}
# backpeddle; remove last attempt and retry
last <- path[length(path)]
path <- path[-length(path)]
sumsize <- sumsize - subpayments$paid[last]
print(cbind(subpayments, attempts))
next
}
#cat('i:', i, 'path before:', path, ' -- ')
path <- c(path, i)
sumsize <- sumsize + subpayments$paid[i]
#cat('path after:', path, 'sumsize:', sumsize, '\n')
attemptspath[[length(path)]] <- c(unlist(attemptspath[length(path)]) %||% integer(0), i)
attempts[i] <- TRUE
#print(attemptspath)
#print(cbind(subpayments, attempts))
}
if (length(path) > 0)
subpayments$group[path] <- groups$name[g]
}
print(subpayments)