2

Suppose I have a vector, and I want to get the ranks of the elements if they were sorted.

So if I have the vector:

0.5
1.5
3.5
0.1

and I need returned the ranks of each element:

2
3
4
1

Is there a way to do this in Armadillo? This is different than the previous post since we are getting the ranks and not the indices before sorting.

Community
  • 1
  • 1
Cauchy
  • 1,677
  • 2
  • 21
  • 30
  • make a std::map, take a copy of your vector, sort it, attach the index of the key "every original number" as its value in the map, then iterate the original vector and get its value from the map – Khalil Khalaf Mar 13 '16 at 04:14
  • Possible duplicate of [C++ sorting and keeping track of indexes](http://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes) – yuyoyuppe Mar 13 '16 at 04:15
  • @yuyoyuppe Not a duplicate – Cauchy Mar 13 '16 at 04:23
  • @Cauchy is [that](http://arma.sourceforge.net/docs.html#sort_index) what you're looking for? – yuyoyuppe Mar 13 '16 at 04:26
  • @yuyoyuppe already tried that, doesn't do what i want. – Cauchy Mar 13 '16 at 04:36

3 Answers3

2

Here, check this out:

#include<iostream>
#include<vector> // std:: vector
#include <algorithm> // std::sort
#include <map> // std::map

using namespace std;


int main() {
    vector<double> myVector = { 0.5, 1.5, 3.5, 0.1 };
    vector<double> Sorted = myVector;

    std::sort(Sorted.begin(), Sorted.end());

    map<double, int> myMap;

    for (int i = 0; i < Sorted.size() ; i++)
    {
        myMap.insert(make_pair(Sorted[i],i));
    }

    for (int i = 0; i < myVector.size() ; i++)
    {
        auto it = myMap.find(myVector[i]);
        cout << it->second + 1 << endl;
    }

    return 0;
};

Output:

enter image description here

Khalil Khalaf
  • 9,259
  • 11
  • 62
  • 104
  • How can you handle the repeated values? – drbombe Dec 03 '17 at 15:29
  • @drbombe if you want all duplicate numbers to have the same rank, then you will need remove duplicate from the initial vector – Khalil Khalaf Dec 03 '17 at 16:42
  • But in that case, the largest rank will not be the same as the size of the vector right? I provided an alternative solution below. Please feel free to comment if it can be improved in complexity. – drbombe Dec 03 '17 at 17:35
  • @drbombe Right, and I assume ranks do not have to be the same size as the vector. Since you have the `map`, then any value you access on the vector, can retrieve its rank (from the map) – Khalil Khalaf Dec 03 '17 at 19:01
2

Here is my codes using STL to get the rankings in a concise way

template <typename T>
vector<size_t> calRank(const vector<T> & var) {
    vector<size_t> result(var.size());
    //sorted index
    vector<size_t> indx(var.size());
    iota(indx.begin(),indx.end(),0);
    sort(indx.begin(),indx.end(),[&var](int i1, int i2){return var[i1]<var[i2];});
    //return ranking
    for(size_t iter=0;iter<var.size();++iter){
        //it may cause overflow for a really long vector, in practice it should be ok.
        result[indx[iter]]=iter+1;
    }
    return result;
}
drbombe
  • 609
  • 7
  • 15
0

Here is a solution in pure Armadillo code, using the function arma::sort_index().

The function arma::sort_index() calculates the permutation index to sort a given vector into ascending order.

Applying the function arma::sort_index() twice: arma::sort_index(arma::sort_index())}, calculates the reverse permutation index to sort the vector from ascending order back into its original unsorted order. The ranks of the elements are equal to the reverse permutation index.

Below is Armadillo code wrapped in RcppArmadillo that defines the function calc_ranks(). The function calc_ranks() calculates the ranks of the elements of a vector, and it can be called from R.

#include <RcppArmadillo.h>
using namespace Rcpp;
using namespace arma;
// [[Rcpp::depends(RcppArmadillo)]]

// Define the function calc_ranks(), to calculate 
// the ranks of the elements of a vector.
//
// [[Rcpp::export]]
arma::uvec calc_ranks(const arma::vec& da_ta) {
  return (arma::sort_index(arma::sort_index(da_ta)) + 1);
}  // end calc_ranks

The above code can be saved to the file calc_ranks.cpp, so it can be compiled in R using the function Rcpp::sourceCpp().

Below is R code to test the function calc_ranks() (after it's been compiled in R):

# Compile Rcpp functions
Rcpp::sourceCpp(file="C:/Develop/R/Rcpp/calc_ranks.cpp")

# Create a vector of random data
da_ta <- runif(7)
# Calculate the ranks of the elements
calc_ranks(da_ta)

# Compare with the R function rank()
all.equal(rank(da_ta), drop(calc_ranks(da_ta)))
algoquant
  • 1,087
  • 1
  • 11
  • 15