从多个变量中按比例减去
Proportional substraction from multiple variables
我有两个随时间变化的变量。加法是已知的,但是对于减法,我只知道需要减去的和,而我想从两个变量中按比例减去它。
这是一个数据集示例
df = data.frame(id = c(rep(1,5), rep(2,3)),
ord = c(1:5, 1:3),
a = c(10, NA, 20, 0, NA, 0, 15, NA),
b = c(0, NA, 0, 15, NA, 10, 0, NA),
substract = c(NA, -5, NA, NA, -10, NA, NA, -15)) %>%
rowwise() %>%
mutate(all = sum(c(a, b, substract), na.rm = TRUE)) %>%
arrange(id, ord) %>%
group_by(id) %>%
mutate(all = cumsum(all)) %>%
ungroup()
所以,我想用 substract
中的值替换 a
和 b
中的 NA
,乘以 a
和 b
分别除以 all
中 NA
之前的值。问题是,在每次替换之后,下一次替换应该考虑到所有之前的替换,因为 a
和 b
的累加和会在之后发生变化。
我有一个带有 while
循环的解决方案,该解决方案有效,但效率极低。原始数据集很大,所以它不是我的选择,但它可能会让我对我想要实现的目标有一些额外的了解。
test = df %>%
group_by(id)
while(any(is.na(test$a))){
test = test %>%
mutate(across(c("a", "b"), ~ ifelse(is.na(.x), lag(cumsum(.x)) / lag(all) * substract, .x)))
}
谁能提出更有效的解决方案?就像,如果有任何方法可以让 mutate
函数在每次突变后保存更改(因此不需要将其放入 while 循环)或其他什么?
编辑:user63230 suggested using 。这似乎确实是我想要的,但我仍然很难将它们应用到我的案例中。 accumulate2()
只接受 3 个参数函数并且似乎不适用于 lag()
(因为我不仅需要当前变量的先前值),所以它似乎还不够。也许有办法让它工作,但我还没有发现它。任何帮助将不胜感激。
使用与 类似的方法,我认为这会奏效,虽然不是很漂亮:
library(dplyr)
sp <- split(df, df$id)
list_of_dfs <- lapply(sp, function(x){
for(i in which(is.na(x$a))){
tmp <- x[seq_len(i), ]
x$a[i] <- tail(cumsum(tmp$a)[!is.na(cumsum(tmp$a))], 1)/tail(dplyr::lag(tmp$all), 1)*tail((tmp$substract), 1)
}
x
})
bind_rows(list_of_dfs)
# id ord a b substract all
# <dbl> <int> <dbl> <dbl> <dbl> <dbl>
# 1 1 1 10 0 NA 10
# 2 1 2 -5 NA -5 5
# 3 1 3 20 0 NA 25
# 4 1 4 0 15 NA 40
# 5 1 5 -6.25 NA -10 30
# 6 2 1 0 10 NA 10
# 7 2 2 15 0 NA 25
# 8 2 3 -9 NA -15 10
如果合适可以repeated/automated给b
吗?
我有两个随时间变化的变量。加法是已知的,但是对于减法,我只知道需要减去的和,而我想从两个变量中按比例减去它。
这是一个数据集示例
df = data.frame(id = c(rep(1,5), rep(2,3)),
ord = c(1:5, 1:3),
a = c(10, NA, 20, 0, NA, 0, 15, NA),
b = c(0, NA, 0, 15, NA, 10, 0, NA),
substract = c(NA, -5, NA, NA, -10, NA, NA, -15)) %>%
rowwise() %>%
mutate(all = sum(c(a, b, substract), na.rm = TRUE)) %>%
arrange(id, ord) %>%
group_by(id) %>%
mutate(all = cumsum(all)) %>%
ungroup()
所以,我想用 substract
中的值替换 a
和 b
中的 NA
,乘以 a
和 b
分别除以 all
中 NA
之前的值。问题是,在每次替换之后,下一次替换应该考虑到所有之前的替换,因为 a
和 b
的累加和会在之后发生变化。
我有一个带有 while
循环的解决方案,该解决方案有效,但效率极低。原始数据集很大,所以它不是我的选择,但它可能会让我对我想要实现的目标有一些额外的了解。
test = df %>%
group_by(id)
while(any(is.na(test$a))){
test = test %>%
mutate(across(c("a", "b"), ~ ifelse(is.na(.x), lag(cumsum(.x)) / lag(all) * substract, .x)))
}
谁能提出更有效的解决方案?就像,如果有任何方法可以让 mutate
函数在每次突变后保存更改(因此不需要将其放入 while 循环)或其他什么?
编辑:user63230 suggested using accumulate2()
只接受 3 个参数函数并且似乎不适用于 lag()
(因为我不仅需要当前变量的先前值),所以它似乎还不够。也许有办法让它工作,但我还没有发现它。任何帮助将不胜感激。
使用与
library(dplyr)
sp <- split(df, df$id)
list_of_dfs <- lapply(sp, function(x){
for(i in which(is.na(x$a))){
tmp <- x[seq_len(i), ]
x$a[i] <- tail(cumsum(tmp$a)[!is.na(cumsum(tmp$a))], 1)/tail(dplyr::lag(tmp$all), 1)*tail((tmp$substract), 1)
}
x
})
bind_rows(list_of_dfs)
# id ord a b substract all
# <dbl> <int> <dbl> <dbl> <dbl> <dbl>
# 1 1 1 10 0 NA 10
# 2 1 2 -5 NA -5 5
# 3 1 3 20 0 NA 25
# 4 1 4 0 15 NA 40
# 5 1 5 -6.25 NA -10 30
# 6 2 1 0 10 NA 10
# 7 2 2 15 0 NA 25
# 8 2 3 -9 NA -15 10
如果合适可以repeated/automated给b
吗?