【问题标题】:Permutation order in rcpprcpp中的排列顺序
【发布时间】:2017-01-18 02:52:43
【问题描述】:

我对@9​​87654321@ 进行了跟进。在R 中,您可以在辅助键之后对重复项进行排序。我怎样才能在Rcpp 中实现这一点?我正在尝试以下方法(灵感来自 Kevin Ushey 对原始问题的回答),这似乎适用于简单的情况。我该如何概括这一点?

// [[Rcpp::export]]
Rcpp::IntegerVector order_(Rcpp::NumericVector x, Rcpp::NumericVector y) {
  // sort x 
  Rcpp::NumericVector sorted_x = clone(x).sort();
  // find order of x only
  Rcpp::IntegerVector order = Rcpp::match(sorted_x, x);
  // find order of y for each order of x
  for (int i = 1; i <= Rcpp::max(order); ++i){
    Rcpp::LogicalVector duplicates = order == i;
    int k = which_max(duplicates);
    Rcpp::NumericVector duplicated_y = y[duplicates];
    Rcpp::NumericVector sorted_dy = clone(duplicated_y).sort();
    Rcpp::IntegerVector order_y = Rcpp::match(sorted_dy, duplicated_y);
    for (int j = 0; j < order_y.length(); ++j){
      order(k + j) = order(k + j) + order_y(j) - 1;
    }
  }
  return order;
}

【问题讨论】:

  • 您不限于 Rcpp 类型。我几乎可以肯定 Boost 或 C++ 标准库将有一个“带有二级键的订单”选项,所以我会寻找并使用它。 this answer to an old question 展示了 C++11 lambda 的使用,它允许您插入自定义比较器。

标签: r rcpp


【解决方案1】:

如果我理解您的要求,一种方法是利用 comparison operators of std::tuple,它应该“做正确的事”。

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

template <typename... Ts>
struct Point {
    std::size_t index;

    using tuple_t = std::tuple<Ts...>;
    tuple_t data;

    template <typename... Vs>
    Point(std::size_t i, const Vs&... vs)
        : index(i),
          data(std::forward<decltype(vs[i])>(vs[i])...)
    {}

    bool operator<(const Point& other) const {
        return data < other.data;
    }
};

template <typename... Ts>
class PointVector {
public:
    std::size_t sz;
    using vector_t = std::vector<Point<Ts...>>;
    vector_t data;

    template <typename... Vs>
    PointVector(const Vs&... vs)
        : sz(min_size(vs...))
    {
        data.reserve(sz);
        for (std::size_t i = 0; i < sz; i++) {
            data.emplace_back(i, vs...);
        }
    }

    Rcpp::IntegerVector sorted_index() const {
        vector_t tmp(data);
        std::stable_sort(tmp.begin(), tmp.end());

        Rcpp::IntegerVector res(sz);
        for (std::size_t i = 0; i < sz; i++) {
            res[i] = tmp[i].index + 1;
        }

        return res;
    }

private:
    template <typename V>
    std::size_t min_size(const V& v) {
        return v.size();
    }

    template <typename T, typename S, typename... Vs>
    std::size_t min_size(const T& t, const S& s, const Vs&... vs) {
        return t.size() < s.size() ?
            min_size(t, vs...) :
            min_size(s, vs...);
    }
};

using namespace Rcpp;

// [[Rcpp::export]]
IntegerVector order2(NumericVector x, NumericVector y) {
    PointVector<double, double> pv(x, y);
    return pv.sorted_index();
}

// [[Rcpp::export]]
IntegerVector order3(NumericVector x, NumericVector y, NumericVector z) {
    PointVector<double, double, double> pv(x, y, z);
    return pv.sorted_index();
}

用这个数据来演示,

x <- rep(1:2 + 0.5, 10)
y <- rep(1:4 + 0.5, 5)
z <- rep(1:5 + 0.5, 4)
(df <- data.frame(x, y, z))
#      x   y   z
# 1  1.5 1.5 1.5
# 2  2.5 2.5 2.5
# 3  1.5 3.5 3.5
# 4  2.5 4.5 4.5
# 5  1.5 1.5 5.5
# 6  2.5 2.5 1.5
# 7  1.5 3.5 2.5
# 8  2.5 4.5 3.5
# 9  1.5 1.5 4.5
# 10 2.5 2.5 5.5
# 11 1.5 3.5 1.5
# 12 2.5 4.5 2.5
# 13 1.5 1.5 3.5
# 14 2.5 2.5 4.5
# 15 1.5 3.5 5.5
# 16 2.5 4.5 1.5
# 17 1.5 1.5 2.5
# 18 2.5 2.5 3.5
# 19 1.5 3.5 4.5
# 20 2.5 4.5 5.5

C++ 版本产生的结果与基本 R 等效:

all.equal(base::order(x, y, z), order3(x, y, z))
# [1] TRUE

df[order3(x, y, z),]
#      x   y   z
# 1  1.5 1.5 1.5
# 17 1.5 1.5 2.5
# 13 1.5 1.5 3.5
# 9  1.5 1.5 4.5
# 5  1.5 1.5 5.5
# 11 1.5 3.5 1.5
# 7  1.5 3.5 2.5
# 3  1.5 3.5 3.5
# 19 1.5 3.5 4.5
# 15 1.5 3.5 5.5
# 6  2.5 2.5 1.5
# 2  2.5 2.5 2.5
# 18 2.5 2.5 3.5
# 14 2.5 2.5 4.5
# 10 2.5 2.5 5.5
# 16 2.5 4.5 1.5
# 12 2.5 4.5 2.5
# 8  2.5 4.5 3.5
# 4  2.5 4.5 4.5
# 20 2.5 4.5 5.5

编辑:这是一个稍微好一点的版本,它允许您编写auto pv = MakePointVector(...),而不是指定所有类型参数:

// Point class as before 

template <typename... Vecs>
class PointVector {
public:
    std::size_t sz;
    using point_t = Point<typename std::remove_reference<decltype(Vecs{}[0])>::type...>;
    using vector_t = std::vector<point_t>;
    vector_t data;

    template <typename... Vs>
    PointVector(const Vecs&... vs)
        : sz(min_size(vs...))
    {
        data.reserve(sz);
        for (std::size_t i = 0; i < sz; i++) {
            data.emplace_back(i, vs...);
        }
    }

    Rcpp::IntegerVector sorted_index() const {
        vector_t tmp(data);
        std::stable_sort(tmp.begin(), tmp.end());

        Rcpp::IntegerVector res(sz);
        for (std::size_t i = 0; i < sz; i++) {
            res[i] = tmp[i].index + 1;
        }

        return res;
    }

private:
    template <typename V>
    std::size_t min_size(const V& v) {
        return v.size();
    }

    template <typename T, typename S, typename... Vs>
    std::size_t min_size(const T& t, const S& s, const Vs&... vs) {
        return t.size() < s.size() ?
            min_size(t, vs...) :
            min_size(s, vs...);
    }
};

template <typename... Vecs>
PointVector<Vecs...> MakePointVector(const Vecs&... vecs) {
    return PointVector<Vecs...>(vecs...);
}

using namespace Rcpp;

// [[Rcpp::export]]
IntegerVector order2(NumericVector x, NumericVector y) {
    auto pv = MakePointVector(x, y);
    return pv.sorted_index();
}

// [[Rcpp::export]]
IntegerVector order3(NumericVector x, NumericVector y, NumericVector z) {
    auto pv = MakePointVector(x, y, z);
    return pv.sorted_index();
}

/*** R

all.equal(base::order(x, y), order2(x, y))
# [1] TRUE

all.equal(base::order(x, y, z), order3(x, y, z))
# [1] TRUE

*/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多