Переупорядочить вектор по порядку другого - PullRequest
3 голосов
/ 22 марта 2020

У меня есть два вектора:

vec1 <- c(0, 1, 2, 3, 4, 5, 6, 7, 9)
vec2 <- c(1, 2, 7, 5, 3, 6, 80, 4, 8)

Я бы хотел установить такой же порядок в vec1, как и в vec2. Например, в vec2 наибольшее число (позиция 9) находится в позиции 7, поэтому я хотел бы поместить наибольшее число в vec1 (позиция 9, номер 9) в положение 7.

Ожидается вывод:

vec1 <- c(0, 1, 6, 4, 2, 5, 9, 3, 7)

У меня нет дублированных значений ни в одном векторе.

Меня в первую очередь интересуют эффективные решения R cpp, но также приветствуется все, что связано с R.

Ответы [ 4 ]

9 голосов
/ 24 марта 2020

Другой вариант baseR - match

vec1[match(vec2, sort(vec2))]
# [1] 0 1 6 4 2 5 9 3 7

edit

Включая тест с большим размером выборки

set.seed(42)
n <- 1e6
vec1 <- seq_len(n)
vec2 <- sample(1:1e7, size = n)

benchmarks <- bench::mark(match = vec1[match(vec2, sort(vec2))],
                          rank = vec1[rank(vec2)],
                          frank = vec1[data.table::frank(vec2)],
                          order_order = vec1[order(order(vec2))],
                          rcpp_order_order = foo(vec1, vec2),
                          iterations = 25)
benchmarks[ , 1:3]

Результат

# A tibble: 5 x 3
#  expression            min   median
#  <bch:expr>       <bch:tm> <bch:tm>
#1 match             259.8ms    322ms
#2 rank              825.9ms    876ms
#3 frank              88.6ms    134ms
#4 order_order       110.6ms    139ms
#5 rcpp_order_order  793.5ms    893ms
6 голосов
/ 25 марта 2020

Мы можем адаптировать Rcpp версию order() из этого ответа (чтобы учесть тот факт, что вы не хотите проверять наличие дубликатов и добавлять функцию в заказ на порядок упорядочить), чтобы сделать следующее Rcpp решение:

#include <Rcpp.h>

Rcpp::IntegerVector order(const Rcpp::NumericVector& x) {
    return Rcpp::match(Rcpp::clone(x).sort(), x);
}

Rcpp::IntegerVector order(const Rcpp::IntegerVector& x) {
    return Rcpp::match(Rcpp::clone(x).sort(), x);
}

// [[Rcpp::export]]
Rcpp::NumericVector foo(const Rcpp::NumericVector x,
                        const Rcpp::NumericVector y) {
    return x[order(order(y))-1];
}

Тогда мы получим ожидаемые результаты:

library(Rcpp)
sourceCpp("foo.cpp")

vec1 <- c(0, 1, 2, 3, 4, 5, 6, 7, 9)
vec2 <- c(1, 2, 7, 5, 3, 6, 80, 4, 8)

foo(vec1, vec2)
# [1] 0 1 6 4 2 5 9 3 7

с приличной производительностью (сравнение с R-решениями, представленными другими ответами ):

benchmarks <- bench::mark(match = vec1[match(vec2, sort(vec2))],
                          rank = vec1[rank(vec2)],
                          order_order = vec1[order(order(vec2))],
                          rcpp_order_order = foo(vec1, vec2),
                          iterations = 10000)
benchmarks[ , 1:3]

# # A tibble: 4 x 3
#   expression            min   median
#   <bch:expr>       <bch:tm> <bch:tm>
# 1 match              28.4µs  31.72µs
# 2 rank               7.99µs   9.84µs
# 3 order_order       26.27µs  30.61µs
# 4 rcpp_order_order   2.51µs   3.23µs

Обратите внимание, что это решение работает только при отсутствии дубликатов. (Если вы можете столкнуться с дубликатами, добавление проверки показано в связанном ответе). Также обратите внимание, что эти тесты были только сделаны на этих данных; Я не знаю точно, как они меняются в масштабе.

4 голосов
/ 25 марта 2020

Мы могли бы использовать rank

vec1[rank(vec2)]
#[1] 0 1 6 4 2 5 9 3 7

Или с order

vec1[order(order(vec2))]
#[1] 0 1 6 4 2 5 9 3 7

Или, как @markus предложил вариант с frank из data.table

library(data.table)
vec1[frank(vec2)]
0 голосов
/ 22 марта 2020

Если я правильно вас понимаю, вы хотите, чтобы vec1 следовал тому же порядку vec1. То есть, vec2 увеличивается, так же как и значения vec1; если vec2 уменьшается, то и vec1 и т. д.

sort(vec1)[order(vec2)]
...