Поиск уникальных строк в arma :: mat

В R мы можем использовать уникальный метод для поиска уникальных строк

> data <- matrix(c(1,1,0,1,1,1,0,1),ncol = 2)
> data
     [,1] [,2]
[1,]    1    1
[2,]    1    1
[3,]    0    0
[4,]    1    1

> unique(data)
     [,1] [,2]
[1,]    1    1
[2,]    0    0

Как мы можем сделать это для arma::mat в Rcpp? Здесь уникальная функция возвращает уникальные элементы, а не уникальные строки.


person cryptomanic    schedule 10.05.2016    source источник


Ответы (2)


Я не думаю, что в библиотеке Armadillo есть встроенный способ сделать это, но вот простой подход:

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

template <typename T>
inline bool rows_equal(const T& lhs, const T& rhs, double tol = 0.00000001) {
    return arma::approx_equal(lhs, rhs, "absdiff", tol);
}

// [[Rcpp::export]]
arma::mat unique_rows(const arma::mat& x) {
    unsigned int count = 1, i = 1, j = 1, nr = x.n_rows, nc = x.n_cols;
    arma::mat result(nr, nc);
    result.row(0) = x.row(0);

    for ( ; i < nr; i++) {
        bool matched = false;
        if (rows_equal(x.row(i), result.row(0))) continue;

        for (j = i + 1; j < nr; j++) {
            if (rows_equal(x.row(i), x.row(j))) {
                matched = true;
                break;
            }
        }

        if (!matched) result.row(count++) = x.row(i);
    }

    return result.rows(0, count - 1);
}

/*** R

data <- matrix(c(1,1,0,1,1,1,0,1), ncol = 2)
all.equal(unique(data), unique_rows(data))
#[1] TRUE

data2 <- matrix(1:9, nrow = 3)
all.equal(unique(data2), unique_rows(data2))
#[1] TRUE

data3 <- matrix(0, nrow = 3, ncol = 3)
all.equal(unique(data3), unique_rows(data3))
#[1] TRUE

data4 <- matrix(c(0, 0, 0, 1, 1, 0, 1, 1), ncol = 2)
all.equal(unique(data4), unique_rows(data4))
#[1] TRUE

*/

Как было предложено mtall в комментариях, rows_equal использует arma::approx_equal для проверки равенства, а не operator==, чтобы избежать некоторых проблем сравнения, присущих числам с плавающей запятой. Параметры, используемые в этой функции, были выбраны несколько произвольно и, конечно, могут быть изменены по мере необходимости; но значение tol примерно равно допуску по умолчанию, используемому R all.equal, который равен .Machine$double.eps^0.5 (~ 0.00000001490116 на моей машине).

person nrussell    schedule 10.05.2016
comment
Числа с плавающей запятой (float и double) являются приближениями, поэтому использование сравнения с ними на равенство, как правило, является плохой идеей. Базовый тип хранилища для класса arma::mat - double. Лучше использовать about_equal () вместо ==. - person mtall; 11.05.2016
comment
@mtall Хороший улов; Спасибо. Я не знал об этой функции. - person nrussell; 11.05.2016

Тот же подход, вдохновленный @nrussell, немного короче:

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

template <typename T>
inline bool approx_equal_cpp(const T& lhs, const T& rhs, double tol = 0.00000001) {
  return arma::approx_equal(lhs, rhs, "absdiff", tol);
}

// [[Rcpp::export]]
arma::mat unique_rows(const arma::mat& m) {

  arma::uvec ulmt = arma::zeros<arma::uvec>(m.n_rows);

  for (arma::uword i = 0; i < m.n_rows; i++) {
    for (arma::uword j = i + 1; j < m.n_rows; j++) {
      if (approx_equal_cpp(m.row(i), m.row(j))) { ulmt(j) = 1; break; }
    }
  }

  return m.rows(find(ulmt == 0));

}

// [[Rcpp::export]]
arma::mat unique_cols(const arma::mat& m) {

  arma::uvec vlmt = arma::zeros<arma::uvec>(m.n_cols);

  for (arma::uword i = 0; i < m.n_cols; i++) {
    for (arma::uword j = i + 1; j < m.n_cols; j++) {
      if (approx_equal_cpp(m.col(i), m.col(j))) { vlmt(j) = 1; break; }
    }
  }

  return m.cols(find(vlmt == 0));

}

/*** R

data <- matrix(c(1,1,0,1,1,1,0,1), ncol = 2)
all.equal(unique(data), unique_rows(data))
#[1] TRUE

data2 <- matrix(1:9, nrow = 3)
all.equal(unique(data2), unique_rows(data2))
#[1] TRUE

data3 <- matrix(0, nrow = 3, ncol = 3)
all.equal(unique(data3), unique_rows(data3))
#[1] TRUE

data4 <- matrix(c(0, 0, 0, 1, 1, 0, 1, 1), ncol = 2)
all.equal(unique(data4), unique_rows(data4))
#[1] TRUE

*/
person y g    schedule 03.11.2016