Search code examples
c++arraysc++11rcpp

Is there a way to exclude rows and columns in a C++ array?


Suppose I have an array in C++ such as the following:

1  2  3  4 = arr
5  6  7  8
9  9  8  9
7  6  1  3

Is there a concise way to exclude any row and/or column?

For example, suppose I want to do an operation on the following array:

1  3  4
5  7  8
7  1  3

In other programming languages, I can obtain the above array fairly easy with arr[-3,-2] to exclude the third row and second column. However, I have been unable to find a concise way to exclude rows and columns in C++. How would you go about it?

Update:

I suppose this is an XY problem. Let me tell you why I want to do this.

I am running a statistical model, specifically a conditional autoregressive (CAR) model. In this Gaussian model, we need the mean function and covariance matrix.

We obtain the mean function as

mean = mu + Sig(i,-i) * inv(Sig(-i,-i)) * (v(i,-i) - mu)

and the covariance matrix as

s2 = Sig(i,i) - Sig(i,-i) * inv(Sig(-i,-i)) * Sig(-i,i)

So, I need to obtain three variants of the matrix Sig: Sig(l,-l), Sig(-l,-l), Sig(-l,l). This is why I'm hoping to find a simple way to exclude rows and columns. I would usually program this in R, but it's taking so long. So, I'm hoping to get it working in Rcpp.

Next Update:

I think I'm figuring it out, so thank you to the commenters. This is what I'm thinking. I need a vector that stores the indices that I want to keep in my submatrix. I plan on using Rcpp's X.submat() function.

Suppose the I want to obtain the submatrix of Sig that excludes the ith row and ith column. Then I must have a vector of indices that contains {0,1,...,(i-2),i,...,(L-1)}, since C++ indexing starts at 0. To obtain this vector of indices, I have the following code:

// We need to get the vector of indices excluding i
  arma::vec vece = arma::zeros(L-1); // vector to exclude the ith index
  for(int k = 0; k < (L-1); k++){ // we have a vector of length L-1
    if(k < (i-1)){
      vece(k)=k;
    }
    else if(k == (i-1)){
      // do not add the ith index
    }
    else{ // k > (i-1)
      vece(k-1) = k;
    }
  }
  
  // We need to make Sig(-i,-i)
  arma::mat Sigee = arma::zeros(L-1,L-1); // ee for exclude,exclude
  Sigee = Sig.submat(vece,vece)

However, this does not appear to work when i = 0. I have this code within the following for-loop, so I need this to work when i=0.

for(int l = 0; l < L; l++){                     }

Solution

  • To me, it seems a more straightforward approach is to fill an n-1 length uvec with sequential integers, just skipping i, like so:

    // [[Rcpp::depends(RcppArmadillo)]]
    #include <RcppArmadillo.h>
    
    // [[Rcpp::export]]
    arma::mat exclude_one_row_and_col(const arma::mat& X, arma::uword i) {
        arma::uword n = X.n_rows; // X should be square so only need # rows
        arma::uvec idx(n-1); // vector of indices to subset by
        arma::uword ii = 0; // the integer we'll add at each elem of idx
        for ( arma::uword j = 0; j < (n-1); ++j ) { // for each elem of idx
            if ( ii == i ) { // if ii equals i, we need to skip i
                ii += 1;     // (i.e., add 1 to ii)
            }
            idx[j] = ii;     // then we store ii for this elem
            ii += 1;         // and increment ii
        }
        return X.submat(idx, idx); // finally we can subset the matrix
    }
    

    A simple demonstration shows this works as expected:

    X <- diag(1:3)
    X
    #      [,1] [,2] [,3]
    # [1,]    1    0    0
    # [2,]    0    2    0
    # [3,]    0    0    3
    
    exclude_one_row_and_col(X, 0)
    #      [,1] [,2]
    # [1,]    2    0
    # [2,]    0    3
    
    exclude_one_row_and_col(X, 1)
    #      [,1] [,2]
    # [1,]    1    0
    # [2,]    0    3
    
    exclude_one_row_and_col(X, 2)
    #      [,1] [,2]
    # [1,]    1    0
    # [2,]    0    2