Reputation: 712
I'd like to perform a function on a vector/matrix in the way a running total does.
Simply said
perform the function for each row
from the first row up and including the current one
I found various solutions for the running totals, basically with the cumsum function.cumsum1 dplyr cumsum2
But already the mean function did not work in the way I look for. And the rle also works only for whole vector.
Example
> df <- data.frame(value = df <- data.frame(value = c(1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1)))
> df$csum <- ave(df$value, FUN=cumsum)
> df$mean <- ave(df$value, FUN=mean)
> df
value csum mean
1 1 1 0.45
2 0 1 0.45
3 0 1 0.45
4 1 2 0.45
5 0 2 0.45
6 0 2 0.45
7 0 2 0.45
8 0 2 0.45
9 0 2 0.45
10 0 2 0.45
11 1 3 0.45
12 1 4 0.45
13 0 4 0.45
14 1 5 0.45
15 0 5 0.45
16 1 6 0.45
17 1 7 0.45
18 0 7 0.45
19 1 8 0.45
20 1 9 0.45
But I would like to get:
value csum mean run_mean
1 1 1 0.45 1
2 0 1 0.45 0,5
3 0 1 0.45 0,333333333
4 1 2 0.45 0,5
5 0 2 0.45 0,4
6 0 2 0.45 0,333333333
7 0 2 0.45 0,285714286
8 0 2 0.45 0,25
9 0 2 0.45 0,222222222
10 0 2 0.45 0,2
11 1 3 0.45 0,272727273
12 1 4 0.45 0,333333333
13 0 4 0.45 0,307692308
14 1 5 0.45 0,357142857
15 0 5 0.45 0,333333333
16 1 6 0.45 0,375
17 1 7 0.45 0,411764706
18 0 7 0.45 0,388888889
19 1 8 0.45 0,421052632
20 1 9 0.45 0,45
Now I know that I could use cumsum
and division to solve the mean-challenge. But I'd like to a general approach to solve something like the rle
> df$rle <- ave(df$value, FUN=rle)
> df
value csum mean rle
1 1 1 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
2 0 1 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
3 0 1 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
4 1 2 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
5 0 2 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
6 0 2 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
7 0 2 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
8 0 2 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
9 0 2 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
10 0 2 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
11 1 3 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
12 1 4 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
13 0 4 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
14 1 5 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
15 0 5 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
16 1 6 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
17 1 7 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
18 0 7 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
19 1 8 0.45 1, 2, 1, 6, 2, 1, 1, 1, 2, 1, 2
20 1 9 0.45 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
>
Any suggestions for a newbee?
EDIT: Made the example reproduceible (constants instead of sample
)
Upvotes: 1
Views: 1373
Reputation: 23788
You can try
df$run_mean <- cumsum(df$value)/seq_len(nrow(df))
#> head(df)
# value csum mean run_mean
#1 1 1 0.45 1.0000000
#2 0 1 0.45 0.5000000
#3 0 1 0.45 0.3333333
#4 1 2 0.45 0.5000000
#5 0 2 0.45 0.4000000
#6 0 2 0.45 0.3333333
Basically it is the cumulative sum of the value
divided by the row number.
As pointed out by @akrun the dplyr
package provides a function cummean()
that calculates just that. Hence an alternative could be:
df$run_mean <- dplyr::cummean(df$value)
data
df <- structure(list(value = c(1L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 0L,
0L, 1L, 1L, 0L, 1L, 0L, 1L, 1L, 0L, 1L, 1L), csum = c(1L, 1L,
1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 4L, 4L, 5L, 5L, 6L, 7L, 7L,
8L, 9L), mean = c(0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45,
0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45, 0.45,
0.45), run_mean = c(1, 0.5, 0.333333333333333, 0.5, 0.4, 0.333333333333333,
0.285714285714286, 0.25, 0.222222222222222, 0.2, 0.272727272727273,
0.333333333333333, 0.307692307692308, 0.357142857142857, 0.333333333333333,
0.375, 0.411764705882353, 0.388888888888889, 0.421052631578947,
0.45)), .Names = c("value", "csum", "mean", "run_mean"), row.names = c("1",
"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13",
"14", "15", "16", "17", "18", "19", "20"), class = "data.frame")
Upvotes: 2