计算指定列数的累积和列
Calculate a cumulative sum columwise with a specified number of columns
下面你可以看到一个更大的例子 table 我有
library(data.table)
input <- data.table(ID = c("A", "B"),
Para = c(2.8, 5),
Value1 = c(50, 80),
Value2 = c(80, 40),
Value3 = c(80, 100),
Value4 = c(60, 10),
Value5 = c(40, 80))
我想要实现的是添加一个列,其中包含在 Para 列中指定的下一个 x 列的累计总和。但如果逗号后有数字,则列中的值应按数字调整。
所以对于第一行 (Para = 2.8),结果应该是
1*50 + 1*80 + 0.8*80 = 194
第二行 (Para = 5) 的结果应该是
1*80 + 1*40 + 1*100 + 1*10 + 1*80 = 310
最后的 table 应该是这样的
output <- cbind(input, Result = c(194, 310))
我想到的是将 Para 值 2.8 拆分为 5 个数字的百分比向量,因此整个范围。
c(1, 1, .8, 0, 0)
将列 Value1:Value5 与该向量相乘,然后对所有 Value1:Value5 求和。但我不知道如何将 2.8 拆分成这样的向量,也许还有我不知道的更好的解决方案。谢谢。
您可以使用模除法 %/%
和除法的其余部分来创建乘法向量,然后在 apply
-调用中使用所有内容:
apply(input, MARGIN = 1, function(x) {
multiplier <- as.numeric(x["Para"])
multiplier_long <- c(rep(1, multiplier %/% 1), multiplier %% 1)[1:5]
multiplier_long[is.na(multiplier_long)] <- 0
sum(as.numeric(x[-c(1, 2)]) * multiplier_long)
})
# [1] 194 310
如果您想将 Para
拆分为一个 5 值向量,您可以尝试这样的操作:
input %>%
select(ID,Para) %>%
slice(rep(1:n(), each = 5)) %>%
group_by(ID) %>%
mutate(rn = 1:n()) %>%
mutate(Para = if_else( (Para - rn)>0, 1.0,
if_else(Para - rn > -1, Para - lag(rn), 0))) %>%
select(-rn)
给出:
ID Para
<chr> <dbl>
1 A 1
2 A 1
3 A 0.800
4 A 0
5 A 0
6 B 1
7 B 1
8 B 1
9 B 1
10 B 1
这是一个解决方案,它以宽格式保存数据并使用 Reduce()
计算 "weighted row sums":
library(data.table)
input[, Cumul := {
tmp <- c(rep(1, Para), Para %% 1)
mul <- replace(rep(0, ncol(.SD)), seq_along(tmp), tmp)
Reduce(sum, .SD * mul)
}, .SDcols = Value1:Value5, by = ID]
input[]
ID Para Value1 Value2 Value3 Value4 Value5 Cumul
1: A 2.8 50 80 80 60 40 194
2: B 5.0 80 40 100 10 80 310
这将适用于 .SDcols
指定的任意数量的列,或者如果 Para
更大。
# example data
input <- data.frame(ID = c("A", "B"),
Para = c(2.8, 5),
Value1 = c(50, 80),
Value2 = c(80, 40),
Value3 = c(80, 100),
Value4 = c(60, 10),
Value5 = c(40, 80))
library(tidyverse)
# function that creates a vector of multipliers based on Para column
# assumes that you have ID, Para and rest columns are Value 1,2...,N
# if Para is larger than the corresponding values it keeps first x multipliers
f_create_vector = function(x) {
y = if(x %% 1 > 0) c(rep(1, x), x %% 1) else rep(1, x)
z = rep(0, ncol(input)-2)
c(y, z[-seq_along(y)])[1:(ncol(input)-2)]
}
input %>%
group_by(ID, Para) %>% # for each combination
nest() %>% # nest data
group_by(ID) %>% # for each ID
mutate(vec = list(f_create_vector(Para))) %>% # create a column of multipliers in a list
mutate(CumSum = map2(data, vec, ~sum(.x * .y))) %>% # get the cumsum using multipliers and the value columns
ungroup() %>% # forget the grouping
unnest(data, CumSum) %>% # unnest those columns
select(-vec) # remove that column
# # A tibble: 2 x 8
# ID Para CumSum Value1 Value2 Value3 Value4 Value5
# <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 A 2.8 194 50 80 80 60 40
# 2 B 5 310 80 40 100 10 80
下面你可以看到一个更大的例子 table 我有
library(data.table)
input <- data.table(ID = c("A", "B"),
Para = c(2.8, 5),
Value1 = c(50, 80),
Value2 = c(80, 40),
Value3 = c(80, 100),
Value4 = c(60, 10),
Value5 = c(40, 80))
我想要实现的是添加一个列,其中包含在 Para 列中指定的下一个 x 列的累计总和。但如果逗号后有数字,则列中的值应按数字调整。
所以对于第一行 (Para = 2.8),结果应该是
1*50 + 1*80 + 0.8*80 = 194
第二行 (Para = 5) 的结果应该是
1*80 + 1*40 + 1*100 + 1*10 + 1*80 = 310
最后的 table 应该是这样的
output <- cbind(input, Result = c(194, 310))
我想到的是将 Para 值 2.8 拆分为 5 个数字的百分比向量,因此整个范围。
c(1, 1, .8, 0, 0)
将列 Value1:Value5 与该向量相乘,然后对所有 Value1:Value5 求和。但我不知道如何将 2.8 拆分成这样的向量,也许还有我不知道的更好的解决方案。谢谢。
您可以使用模除法 %/%
和除法的其余部分来创建乘法向量,然后在 apply
-调用中使用所有内容:
apply(input, MARGIN = 1, function(x) {
multiplier <- as.numeric(x["Para"])
multiplier_long <- c(rep(1, multiplier %/% 1), multiplier %% 1)[1:5]
multiplier_long[is.na(multiplier_long)] <- 0
sum(as.numeric(x[-c(1, 2)]) * multiplier_long)
})
# [1] 194 310
如果您想将 Para
拆分为一个 5 值向量,您可以尝试这样的操作:
input %>%
select(ID,Para) %>%
slice(rep(1:n(), each = 5)) %>%
group_by(ID) %>%
mutate(rn = 1:n()) %>%
mutate(Para = if_else( (Para - rn)>0, 1.0,
if_else(Para - rn > -1, Para - lag(rn), 0))) %>%
select(-rn)
给出:
ID Para
<chr> <dbl>
1 A 1
2 A 1
3 A 0.800
4 A 0
5 A 0
6 B 1
7 B 1
8 B 1
9 B 1
10 B 1
这是一个解决方案,它以宽格式保存数据并使用 Reduce()
计算 "weighted row sums":
library(data.table)
input[, Cumul := {
tmp <- c(rep(1, Para), Para %% 1)
mul <- replace(rep(0, ncol(.SD)), seq_along(tmp), tmp)
Reduce(sum, .SD * mul)
}, .SDcols = Value1:Value5, by = ID]
input[]
ID Para Value1 Value2 Value3 Value4 Value5 Cumul 1: A 2.8 50 80 80 60 40 194 2: B 5.0 80 40 100 10 80 310
这将适用于 .SDcols
指定的任意数量的列,或者如果 Para
更大。
# example data
input <- data.frame(ID = c("A", "B"),
Para = c(2.8, 5),
Value1 = c(50, 80),
Value2 = c(80, 40),
Value3 = c(80, 100),
Value4 = c(60, 10),
Value5 = c(40, 80))
library(tidyverse)
# function that creates a vector of multipliers based on Para column
# assumes that you have ID, Para and rest columns are Value 1,2...,N
# if Para is larger than the corresponding values it keeps first x multipliers
f_create_vector = function(x) {
y = if(x %% 1 > 0) c(rep(1, x), x %% 1) else rep(1, x)
z = rep(0, ncol(input)-2)
c(y, z[-seq_along(y)])[1:(ncol(input)-2)]
}
input %>%
group_by(ID, Para) %>% # for each combination
nest() %>% # nest data
group_by(ID) %>% # for each ID
mutate(vec = list(f_create_vector(Para))) %>% # create a column of multipliers in a list
mutate(CumSum = map2(data, vec, ~sum(.x * .y))) %>% # get the cumsum using multipliers and the value columns
ungroup() %>% # forget the grouping
unnest(data, CumSum) %>% # unnest those columns
select(-vec) # remove that column
# # A tibble: 2 x 8
# ID Para CumSum Value1 Value2 Value3 Value4 Value5
# <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 A 2.8 194 50 80 80 60 40
# 2 B 5 310 80 40 100 10 80