highBandWidth
highBandWidth

Reputation: 17296

Named indexing for a matrix

I am trying to index a matrix with names. The usual method gives errors:

NumericMatrix mytest(NumericVector v) {
  NumericMatrix ans(v.length(), v.length());
  rownames(ans) = v;
  float y = ans("1",0);
  NumericVector x = ans.row("1");
  return (ans);
}

I looked over in Matrix.h and the matrix unit tests in rcpp and couldn't find a similar example. Also this mailing list question didn't provide a method to do it.

Can I write my own code to index the matrix, perhaps using R's internal C interface?

Upvotes: 1

Views: 125

Answers (2)

nrussell
nrussell

Reputation: 18612

This is by no means a robust solution, but hopefully a jumping off point for you, where operator() is overloaded to handle different combinations of int and string passed as row & column indices:

#include <Rcpp.h>
// [[Rcpp::plugins(cpp11)]]

class MyMat : public Rcpp::NumericMatrix {
  public:
    MyMat(const Rcpp::NumericMatrix& data_,
          const std::vector<std::string>& rnames_,
          const std::vector<std::string>& cnames_)
    : data(data_),
      rnames(rnames_),
      cnames(cnames_) {}

    double operator()(const std::string& i, const std::string& j) {
      typedef std::vector<std::string>::const_iterator cit;
      cit it_i = std::find(rnames.begin(), rnames.end(), i);
      cit it_j = std::find(cnames.begin(), cnames.end(), j);

      int idx_i, idx_j;

      if (it_i != rnames.end() ) {
        idx_i = it_i - rnames.begin();
      } else {
        idx_i = rnames.size();
      }

      if (it_j != cnames.end() ) {
        idx_j = it_j - cnames.begin();
      } else {
        idx_j = cnames.size();
      }

      return data(idx_i, idx_j);
    }

    double operator()(const std::string& i, const size_t j) {
      typedef std::vector<std::string>::const_iterator cit;
      cit it_i = std::find(rnames.begin(), rnames.end(), i);

      int idx_i, idx_j;

      if (it_i != rnames.end() ) {
        idx_i = it_i - rnames.begin();
      } else {
        idx_i = rnames.size();
      }

      if (j <= cnames.size() ) {
        idx_j = j;
      } else {
        idx_j = cnames.size();
      }

      return data(idx_i, idx_j);
    }

    double operator()(const size_t i, const std::string& j) {
      typedef std::vector<std::string>::const_iterator cit;
      cit it_j = std::find(cnames.begin(), cnames.end(), j);

      int idx_i, idx_j;

      if (i <= rnames.size() ) {
        idx_i = i;
      } else {
        idx_i = rnames.size();
      }

      if (it_j != cnames.end() ) {
        idx_j = it_j - cnames.begin();
      } else {
        idx_j = cnames.size();
      }

      return data(idx_i, idx_j);
    }

    double operator()(const int& i, const int& j) {
      return data(i, j);
    }

  private:
    Rcpp::NumericMatrix data;
    std::vector<std::string> rnames;
    std::vector<std::string> cnames;
};

// [[Rcpp::export]]
void test_MyMat(Rcpp::NumericMatrix m)
{
  std::vector<std::string> rnames = { "a", "b", "c" };
  std::vector<std::string> cnames = { "A", "B", "C" };
  MyMat mmObj(m,rnames,cnames);

  Rcpp::Rcout << "(Row 1, Column 1)" <<
    std::endl;
  Rcpp::Rcout << "(b,B) = " << mmObj("b","B") << 
    std::endl << "(b,1) = " << mmObj("b",1) <<
    std::endl << "(1,B) = " << mmObj(1,"B") << 
    std::endl << "(1,1) = " << mmObj(1,1) <<
    std::endl;
}

/*** R
x <- matrix(1:9,nrow=3)

test_MyMat(x)
#(Row 1, Column 1)
#(b,B) = 5
#(b,1) = 5
#(1,B) = 5
#(1,1) = 5

x[2,2]
#[1] 5
*/

Upvotes: 1

Dirk is no longer here
Dirk is no longer here

Reputation: 368389

We only support numeric (row) indices.

You could add a "names" attribute and look up the index in that, and/or add your own accessor methods.

Upvotes: 1

Related Questions