一个条目中多个值的累计和
Cumulative sum for more values in one entry
假设我有这个数据框(“数字”变量也来自原始数据框中的字符类型):
df <- data.frame(
id = c(1,2,2,1,2),
number = c(30.6, "50.2/15.5", "45/58.4", 80, "57/6"))
df$number <- as.character(df$number)
现在我想添加另一列,其中包含每个 ID 的累计总和,我使用 df %>% mutate(csum = ave(number, id, FUN=cumsum))
完成了此操作,它适用于单个数字,但当然不适用于以“/”分隔的数字。我该如何解决这个问题?
最终的数据帧应该是这样的:
df2 <- data.frame(
id = c(1,2,2,1,2),
number = c(30.6, "50.2/15.5", "45/58.4", 80, "57/6"),
csum = c(30.6, "50.2/15.5", "95.2/73.9", 110.6, "152.2/79.9"))
df2
一种方法可以是:
- 组与
group_by
separate
列 a
和 b
mutate
跨越 a
和 b
并应用 cumsum
unite
来自 tidyr
包使用 na.rm=TRUE
参数
library(dplyr)
library(tidyr)
df %>%
group_by(id) %>%
separate(number, c("a", "b"), sep="/", remove = FALSE, convert = TRUE) %>%
mutate(across(c(a,b), ~cumsum(.))) %>%
unite(csum, c(a,b), sep = '/', na.rm = TRUE)
id number csum
<dbl> <chr> <chr>
1 1 30.6 30.6
2 2 50.2/15.5 50.2/15.5
3 2 45/58.4 95.2/73.9
4 1 80 110.6
5 2 57/6 152.2/79.9
你可以使用极快的matrixStats::colCumsums
。
res <- do.call(rbind, by(df, df$id, \(x) {
cs <- matrixStats::colCumsums(do.call(rbind, strsplit(x$number, '/')) |>
type.convert(as.is=TRUE))
r <- do.call(paste, c(as.list(as.data.frame(cs)), sep='/'))
data.frame(id=x$id, number=x$number, csum=r)
}))
注: R version 4.1.2 (2021-11-01)
.
给出:
res
# id number csum
# 1.1 1 30.6 30.6
# 1.2 1 80 110.6
# 2.1 2 50.2/15.5 50.2/15.5
# 2.2 2 45/58.4 95.2/73.9
# 2.3 2 57/6 152.2/79.9
我们可以使用 base R
- 使用 read.table
读取 'number' 列,将其拆分为两列,创建一个没有 NAs
子集的逻辑向量'd1' 行,遍历列,得到累积和 (cumsum
) 和 paste
,然后将其分配给原始数据集 [=17] 中的新列 'csum' =]
d1 <- read.table(text = df$number, sep = "/", fill = TRUE, header = FALSE)
i1 <- !rowSums(is.na(d1)) > 0
df$csum[i1] <- do.call(paste, c(lapply(d1[i1,], cumsum), sep = "/"))
-输出
> df
id number csum
1 1 30.6 <NA>
2 2 50.2/15.5 50.2/15.5
3 2 45/58.4 95.2/73.9
4 1 80 <NA>
5 2 57/6 152.2/79.9
假设我有这个数据框(“数字”变量也来自原始数据框中的字符类型):
df <- data.frame(
id = c(1,2,2,1,2),
number = c(30.6, "50.2/15.5", "45/58.4", 80, "57/6"))
df$number <- as.character(df$number)
现在我想添加另一列,其中包含每个 ID 的累计总和,我使用 df %>% mutate(csum = ave(number, id, FUN=cumsum))
完成了此操作,它适用于单个数字,但当然不适用于以“/”分隔的数字。我该如何解决这个问题?
最终的数据帧应该是这样的:
df2 <- data.frame(
id = c(1,2,2,1,2),
number = c(30.6, "50.2/15.5", "45/58.4", 80, "57/6"),
csum = c(30.6, "50.2/15.5", "95.2/73.9", 110.6, "152.2/79.9"))
df2
一种方法可以是:
- 组与
group_by
separate
列a
和b
mutate
跨越a
和b
并应用cumsum
unite
来自tidyr
包使用na.rm=TRUE
参数
library(dplyr)
library(tidyr)
df %>%
group_by(id) %>%
separate(number, c("a", "b"), sep="/", remove = FALSE, convert = TRUE) %>%
mutate(across(c(a,b), ~cumsum(.))) %>%
unite(csum, c(a,b), sep = '/', na.rm = TRUE)
id number csum
<dbl> <chr> <chr>
1 1 30.6 30.6
2 2 50.2/15.5 50.2/15.5
3 2 45/58.4 95.2/73.9
4 1 80 110.6
5 2 57/6 152.2/79.9
你可以使用极快的matrixStats::colCumsums
。
res <- do.call(rbind, by(df, df$id, \(x) {
cs <- matrixStats::colCumsums(do.call(rbind, strsplit(x$number, '/')) |>
type.convert(as.is=TRUE))
r <- do.call(paste, c(as.list(as.data.frame(cs)), sep='/'))
data.frame(id=x$id, number=x$number, csum=r)
}))
注: R version 4.1.2 (2021-11-01)
.
给出:
res
# id number csum
# 1.1 1 30.6 30.6
# 1.2 1 80 110.6
# 2.1 2 50.2/15.5 50.2/15.5
# 2.2 2 45/58.4 95.2/73.9
# 2.3 2 57/6 152.2/79.9
我们可以使用 base R
- 使用 read.table
读取 'number' 列,将其拆分为两列,创建一个没有 NAs
子集的逻辑向量'd1' 行,遍历列,得到累积和 (cumsum
) 和 paste
,然后将其分配给原始数据集 [=17] 中的新列 'csum' =]
d1 <- read.table(text = df$number, sep = "/", fill = TRUE, header = FALSE)
i1 <- !rowSums(is.na(d1)) > 0
df$csum[i1] <- do.call(paste, c(lapply(d1[i1,], cumsum), sep = "/"))
-输出
> df
id number csum
1 1 30.6 <NA>
2 2 50.2/15.5 50.2/15.5
3 2 45/58.4 95.2/73.9
4 1 80 <NA>
5 2 57/6 152.2/79.9