Partial row sums along non-square block diagonal

Viewed 85

There must be a simple solution to this with dplyr or maybe with sapply(seq....?

I want to create a column vector of partial row sums across my data frame, where, say, the first 3 rows will sum across the first 2 columns, the next 3 rows will some across the next 2 columns, etc.

So for example with this randomly generated dataset:

df <- data.frame(replicate(expr=rnorm(9),n = 6))

X1 X2 X3 X4 X5 X6
0.1964241 0.03964263 -0.01284166 0.2151990 -0.07765504 -0.92258542
1.7902662 -0.63973304 -0.25093688 -1.6119364 0.82503876 0.40736909
0.5831977 2.03465322 -0.96911364 -2.9498718 1.12056903 0.26401143
0.6503336 -0.55889760 -0.30331195 1.8801063 -0.12216360 0.09128355
1.2450301 -0.35577941 -0.65526714 0.7572617 1.94533708 -0.68068743
0.1611066 1.32441739 0.85744732 0.6347286 0.98836270 -0.23196950
0.7254907 -0.02062223 -0.29098358 -2.2365083 1.22438538 1.98511178
-0.2744802 1.16812643 -0.08278120 0.6771291 -0.31511761 -0.92915936
0.1248361 -0.60735712 -0.82784469 0.5198143 -0.13112286 -0.40609023

I would have a straightforward command where I'd get back a vector of the row sums of the first two columns only for the first three rows, the middle two columns only for the next three rows, and the last two columns only for the last three rows.

This would be the output I'm looking for:

New vector
0.2360667
1.1505332
2.6178509
1.5767943
0.1019946
1.4921759
3.2094972
-1.2442770
-0.5372131

What would be the most straightforward command to apply this kind of thing to a large data frame? I thought I could convert the data frame into a matrix and extract the non-square block diagonal, which would then let me just do normal row sums if the off diagonals are 0, but the fatdiag command in the diagonals package -- fatdiag(df, size = c(3,2)) -- returns the following error:

Error in 1:steps : argument of length 0
In addition: Warning messages:
1: In split_vector(1:dx[1], steps = steps) :
  Both steps and size parameters are NULL, setting step size to 1 (one). 
2: In split_vector(1:dx[2], steps = steps) :
  Both steps and size parameters are NULL, setting step size to 1 (one).
2 Answers

Here is a way to go about it:

n <- 3 # Number of rows to be considered
p <- 2 # Number of columns to sum
A <- list(matrix(1, n, p))
B <- as.matrix(Matrix::bdiag(rep(A,nrow(df)/n)))
rowSums(df * B)

[1]  0.2360667  1.1505332  2.6178509  1.5767944  0.1019946  1.4921759
[7]  3.2094972 -1.2442770 -0.5372131

Split twice: column-wise then row-wise, then rowSums as usual:

sapply(split.default(df, rep(1:3, each = 2)), function(i){
  sapply(split(i, gl(nrow(df)/3, 3)), function(j){
    rowSums(j)
  })
})

#               1          2           3
# [1,]  0.2360667  0.2023573 -1.00024046
# [2,]  1.1505332 -1.8628733  1.23240785
# [3,]  2.6178509 -3.9189854  1.38458046
# [4,]  0.0914360  1.5767944 -0.03088005
# [5,]  0.8892507  0.1019946  1.26464965
# [6,]  1.4855240  1.4921759  0.75639320
# [7,]  0.7048685 -2.5274919  3.20949716
# [8,]  0.8936462  0.5943479 -1.24427697
# [9,] -0.4825210 -0.3080304 -0.53721309

We can use unlist to convert into a vector.

Related