数据中的累积向量 table
Cumulative vector in data table
我有以下数据table:
library(data.table)
dat = data.table(j = c(3,8,9,11,10,28), gr = c(9,9,9,9,10,10))
> dat
j gr
1: 3 9
2: 8 9
3: 9 9
4: 11 9
5: 10 10
6: 28 10
有两组(由'gr'指定)并且它们是有序的。现在我想要实现的是为每个组的每一行创建一个 'j' 中值的累积向量。结果应该是 list
列,如下所示:
res_dat = data.table(j = c(3,8,9,11,10,28), gr = c(9,9,9,9,10,10),
res = list(3, c(3,8), c(3,8,9), c(3,8,9,11),
10, c(10, 28)))
> res_dat
j gr res
1: 3 9 3
2: 8 9 3,8
3: 9 9 3,8,9
4: 11 9 3, 8, 9,11
5: 10 10 10
6: 28 10 10,28
我尝试了以下方法:
首先,我创建了一个虚拟列,其中包含每组每行的编号。
dat[, tmp:= seq_len(.N), by = gr]
我的计划是使用该数字对 j 向量进行子集化,但我没有成功。 None 个有效:
dat[, res := list(j[1:tmp]), by = gr]
dat[, res := list(list(j[1:tmp])), by = gr] # based on
我收到以下错误:
Warning messages:
1: In 1:tmp : numerical expression has 4 elements: only the first used
2: In 1:tmp : numerical expression has 2 elements: only the first used
这确实有助于理解它是如何失败的,但我不知道如何让它成功。有什么想法吗?
这是一个简单的 tidyverse
解决方案,不幸的是我还没有开始学习 data.table
但在您获得相关解决方案之前它会很好:
library(dplyr)
library(purrr)
dat %>%
group_by(gr) %>%
mutate(res = accumulate(j[-1], .init = j[1], ~ c(.x, .y)))
# A tibble: 6 x 3
# Groups: gr [2]
j gr res
<dbl> <dbl> <list>
1 3 9 <dbl [1]>
2 8 9 <dbl [2]>
3 9 9 <dbl [3]>
4 11 9 <dbl [4]>
5 10 10 <dbl [1]>
6 28 10 <dbl [2]>
或者在 base R 中我们可以这样做:
do.call(rbind, lapply(unique(dat$gr), function(a) {
tmp <- subset(dat, gr == a)
tmp$res <- Reduce(c, tmp$j, accumulate = TRUE)
tmp
}))
甚至亲爱的@Henrik 提出的这个绝妙而简洁的建议:
do.call(rbind, by(dat, dat$gr, function(d){
cbind(d, res = I(Reduce(c, d$j, accumulate=TRUE)))}))
甚至这个也是亲爱的@Henrik:
dat$res = ave(dat$j, dat$gr, FUN = function(x) Reduce(c, x, accumulate=TRUE))
这是 Henrik 的回答(如果他们回来,我很乐意给他们这个答案......不知何故):
dat[, res := .(Reduce(c, j, accumulate=TRUE)), by = gr]
# j gr res
# <num> <num> <list>
# 1: 3 9 3
# 2: 8 9 3,8
# 3: 9 9 3,8,9
# 4: 11 9 3, 8, 9,11
# 5: 10 10 10
# 6: 28 10 10,28
Reduce
与sapply
类似,不同之处在于它对当前值和前一个操作的结果进行操作。例如,我们可以看到
sapply(1:3, function(z) z*2)
# [1] 2 4 6
这个展开后等于
1*2 # 2
2*2 # 4
3*2 # 6
即vector/list的一个元素的计算是完全独立的,永远不知道前面迭代的结果。
但是,Reduce
是明确给出前面计算的结果。默认情况下,它只会 return 最后一次计算,类似于 tail(sapply(...), 1)
:
Reduce(function(prev, this) prev + this*2, 11:13)
# [1] 61
这似乎有点晦涩...让我们看看所有的中间步骤,上面的答案是最后一个:
Reduce(function(prev, this) prev + this*2, 11:13, accumulate = TRUE)
# [1] 11 35 61
在这种情况下(不指定init=
,等待),第一个结果只是x=
中的第一个值,不是运行 通过函数。如果我们展开这个,我们会看到
11 # 11 is the first value in x
_________/
/
v
11 + 12*2 # 35
35 + 13*2 # 61
有时我们需要x=
中的第一个值是运行通过函数,有一个起始条件(当我们没有时prev
的第一个值要使用的先前迭代)。为此,我们可以使用 init=
;我们可以通过查看两个完全等价的调用来思考 init=
的用法:
Reduce(function(prev, this) prev + this*2, 11:13, accumulate = TRUE)
Reduce(function(prev, this) prev + this*2, 12:13, init = 11, accumulate = TRUE)
# [1] 11 35 61
(如果没有 init=
,Reduce 将取 x=
的第一个元素并将其分配给 init=
并从 x=
中移除它。)
现在假设我们希望起始条件(注入的“先前”值)为 0,那么我们会做
Reduce(function(prev, this) prev + this*2, 11:13, init = 0, accumulate = TRUE)
# [1] 0 22 46 72
### unrolled
0 # 0 is the init= value
________/
/
v
0 + 11*2 # 22
22 + 12*2 # 46
46 + 13*2 # 72
让我们回到这个问题和这个数据。我将注入一个 browser()
并稍微更改函数以便我们可以查看所有中间值。
> dat[, res := .(Reduce(function(prev, this) { browser(); c(prev, this); }, j, accumulate=TRUE)), by = gr]
Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=9`, row 2
[1] 3
Browse[2]> this
[1] 8
Browse[2]> c(prev, this)
[1] 3 8
Browse[2]> c # 'c'ontinue
Browse[2]> Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=9`, row 3
[1] 3 8
Browse[2]> this
[1] 9
Browse[2]> c(prev, this)
[1] 3 8 9
Browse[2]> c # 'c'ontinue
Browse[2]> Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=9`, row 4
[1] 3 8 9
Browse[2]> this
[1] 11
Browse[2]> c(prev, this)
[1] 3 8 9 11
Browse[2]> c # 'c'ontinue
Browse[2]> Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=10`, row 6
[1] 10
Browse[2]> this
[1] 28
Browse[2]> c(prev, this)
[1] 10 28
Browse[2]> c # 'c'ontinue
请注意我们如何没有“看到”第 1 行或第 5 行,因为它们是减少的 init=
条件(每组中看到的第一个 prev
值)。
Reduce
可能是一个难以可视化和使用的函数。当我使用它时,我几乎总是将 browser()
预先插入到匿名函数中并完成前三个步骤:第一步确保 init=
正确,第二步确保匿名-函数正在做我认为我想要的 init 和 next 值,第三个是确保它正确继续。这类似于“演绎证明”:第 n
次计算将是正确的,因为我们知道 (n-1)th
次计算是正确的。
我有以下数据table:
library(data.table)
dat = data.table(j = c(3,8,9,11,10,28), gr = c(9,9,9,9,10,10))
> dat
j gr
1: 3 9
2: 8 9
3: 9 9
4: 11 9
5: 10 10
6: 28 10
有两组(由'gr'指定)并且它们是有序的。现在我想要实现的是为每个组的每一行创建一个 'j' 中值的累积向量。结果应该是 list
列,如下所示:
res_dat = data.table(j = c(3,8,9,11,10,28), gr = c(9,9,9,9,10,10),
res = list(3, c(3,8), c(3,8,9), c(3,8,9,11),
10, c(10, 28)))
> res_dat
j gr res
1: 3 9 3
2: 8 9 3,8
3: 9 9 3,8,9
4: 11 9 3, 8, 9,11
5: 10 10 10
6: 28 10 10,28
我尝试了以下方法:
首先,我创建了一个虚拟列,其中包含每组每行的编号。
dat[, tmp:= seq_len(.N), by = gr]
我的计划是使用该数字对 j 向量进行子集化,但我没有成功。 None 个有效:
dat[, res := list(j[1:tmp]), by = gr]
dat[, res := list(list(j[1:tmp])), by = gr] # based on
我收到以下错误:
Warning messages:
1: In 1:tmp : numerical expression has 4 elements: only the first used
2: In 1:tmp : numerical expression has 2 elements: only the first used
这确实有助于理解它是如何失败的,但我不知道如何让它成功。有什么想法吗?
这是一个简单的 tidyverse
解决方案,不幸的是我还没有开始学习 data.table
但在您获得相关解决方案之前它会很好:
library(dplyr)
library(purrr)
dat %>%
group_by(gr) %>%
mutate(res = accumulate(j[-1], .init = j[1], ~ c(.x, .y)))
# A tibble: 6 x 3
# Groups: gr [2]
j gr res
<dbl> <dbl> <list>
1 3 9 <dbl [1]>
2 8 9 <dbl [2]>
3 9 9 <dbl [3]>
4 11 9 <dbl [4]>
5 10 10 <dbl [1]>
6 28 10 <dbl [2]>
或者在 base R 中我们可以这样做:
do.call(rbind, lapply(unique(dat$gr), function(a) {
tmp <- subset(dat, gr == a)
tmp$res <- Reduce(c, tmp$j, accumulate = TRUE)
tmp
}))
甚至亲爱的@Henrik 提出的这个绝妙而简洁的建议:
do.call(rbind, by(dat, dat$gr, function(d){
cbind(d, res = I(Reduce(c, d$j, accumulate=TRUE)))}))
甚至这个也是亲爱的@Henrik:
dat$res = ave(dat$j, dat$gr, FUN = function(x) Reduce(c, x, accumulate=TRUE))
这是 Henrik 的回答(如果他们回来,我很乐意给他们这个答案......不知何故):
dat[, res := .(Reduce(c, j, accumulate=TRUE)), by = gr]
# j gr res
# <num> <num> <list>
# 1: 3 9 3
# 2: 8 9 3,8
# 3: 9 9 3,8,9
# 4: 11 9 3, 8, 9,11
# 5: 10 10 10
# 6: 28 10 10,28
Reduce
与sapply
类似,不同之处在于它对当前值和前一个操作的结果进行操作。例如,我们可以看到
sapply(1:3, function(z) z*2)
# [1] 2 4 6
这个展开后等于
1*2 # 2
2*2 # 4
3*2 # 6
即vector/list的一个元素的计算是完全独立的,永远不知道前面迭代的结果。
但是,Reduce
是明确给出前面计算的结果。默认情况下,它只会 return 最后一次计算,类似于 tail(sapply(...), 1)
:
Reduce(function(prev, this) prev + this*2, 11:13)
# [1] 61
这似乎有点晦涩...让我们看看所有的中间步骤,上面的答案是最后一个:
Reduce(function(prev, this) prev + this*2, 11:13, accumulate = TRUE)
# [1] 11 35 61
在这种情况下(不指定init=
,等待),第一个结果只是x=
中的第一个值,不是运行 通过函数。如果我们展开这个,我们会看到
11 # 11 is the first value in x
_________/
/
v
11 + 12*2 # 35
35 + 13*2 # 61
有时我们需要x=
中的第一个值是运行通过函数,有一个起始条件(当我们没有时prev
的第一个值要使用的先前迭代)。为此,我们可以使用 init=
;我们可以通过查看两个完全等价的调用来思考 init=
的用法:
Reduce(function(prev, this) prev + this*2, 11:13, accumulate = TRUE)
Reduce(function(prev, this) prev + this*2, 12:13, init = 11, accumulate = TRUE)
# [1] 11 35 61
(如果没有 init=
,Reduce 将取 x=
的第一个元素并将其分配给 init=
并从 x=
中移除它。)
现在假设我们希望起始条件(注入的“先前”值)为 0,那么我们会做
Reduce(function(prev, this) prev + this*2, 11:13, init = 0, accumulate = TRUE)
# [1] 0 22 46 72
### unrolled
0 # 0 is the init= value
________/
/
v
0 + 11*2 # 22
22 + 12*2 # 46
46 + 13*2 # 72
让我们回到这个问题和这个数据。我将注入一个 browser()
并稍微更改函数以便我们可以查看所有中间值。
> dat[, res := .(Reduce(function(prev, this) { browser(); c(prev, this); }, j, accumulate=TRUE)), by = gr]
Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=9`, row 2
[1] 3
Browse[2]> this
[1] 8
Browse[2]> c(prev, this)
[1] 3 8
Browse[2]> c # 'c'ontinue
Browse[2]> Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=9`, row 3
[1] 3 8
Browse[2]> this
[1] 9
Browse[2]> c(prev, this)
[1] 3 8 9
Browse[2]> c # 'c'ontinue
Browse[2]> Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=9`, row 4
[1] 3 8 9
Browse[2]> this
[1] 11
Browse[2]> c(prev, this)
[1] 3 8 9 11
Browse[2]> c # 'c'ontinue
Browse[2]> Called from: f(init, x[[i]])
Browse[1]> debug at #1: c(prev, this)
Browse[2]> prev # group `gr=10`, row 6
[1] 10
Browse[2]> this
[1] 28
Browse[2]> c(prev, this)
[1] 10 28
Browse[2]> c # 'c'ontinue
请注意我们如何没有“看到”第 1 行或第 5 行,因为它们是减少的 init=
条件(每组中看到的第一个 prev
值)。
Reduce
可能是一个难以可视化和使用的函数。当我使用它时,我几乎总是将 browser()
预先插入到匿名函数中并完成前三个步骤:第一步确保 init=
正确,第二步确保匿名-函数正在做我认为我想要的 init 和 next 值,第三个是确保它正确继续。这类似于“演绎证明”:第 n
次计算将是正确的,因为我们知道 (n-1)th
次计算是正确的。