title: Ranking of numpy array with possible duplicates
body: <p>I have a numpy array of floats/ints and want to map its elements into their ranks.</p>
  
  <p>If an array doesn't have duplicates the problem can be solved by the following code</p>
  
  <pre><code>In [49]: a1
  Out[49]: array([ 0.1,  5.1,  2.1,  3.1,  4.1,  1.1,  6.1,  8.1,  7.1,  9.1])
  
  In [50]: a1.argsort().argsort()
  Out[50]: array([0, 5, 2, 3, 4, 1, 6, 8, 7, 9])
  </code></pre>
  
  <p>Now I want to extend this method to arrays with possible duplicates, so that duplicates are mapped to the same value. For example, I want array a</p>
  
  <pre><code>a2 = np.array([0.1, 1.1, 2.1, 3.1, 4.1, 1.1, 6.1, 7.1, 7.1, 1.1])
  </code></pre>
  
  <p>to be mapped to either</p>
  
  <pre><code>0 1 4 5 6 1 7 8 8 1
  </code></pre>
  
  <p>or to</p>
  
  <pre><code>0 3 4 5 6 3 7 9 9 3
  </code></pre>
  
  <p>or to </p>
  
  <pre><code>0 2 4 5 6 2 7 8.5 8.5 2
  </code></pre>
  
  <p>In the first/second case we map duplicates to the minimum/maximum rank among them if we just apply a2.argsort().argsort().
  The third case is just the average of first two cases.</p>
  
  <p>Any suggestions?</p>
  
  <p><strong>EDIT (efficiency requirements)</strong></p>
  
  <p>In the initial description I forgot to mention about <strong>time requirements</strong>. I am seeking for solution in terms of numpy/scipy functions which will let to avoid "pure python overhead". Just to make it clear, consider the solution proposed by Richard which actually solves the problem but quite slow:</p>
  
  <pre><code>def argsortdup(a1):
    sorted = np.sort(a1)
    ranked = []
    for item in a1:
      ranked.append(sorted.searchsorted(item))
    return np.array(ranked)
  
  In [86]: a2 = np.array([ 0.1,  1.1,  2.1,  3.1,  4.1,  1.1,  6.1,  7.1,  7.1,  1.1])
  
  In [87]: %timeit a2.argsort().argsort()
  1000000 loops, best of 3: 1.55 us per loop
  
  In [88]: %timeit argsortdup(a2)
  10000 loops, best of 3: 25.6 us per loop
  
  In [89]: a = np.arange(0.1, 1000.1)
  
  In [90]: %timeit a.argsort().argsort()
  10000 loops, best of 3: 24.5 us per loop
  
  In [91]: %timeit argsortdup(a)
  1000 loops, best of 3: 1.14 ms per loop
  
  In [92]: a = np.arange(0.1, 10000.1)
  
  In [93]: %timeit a.argsort().argsort()
  1000 loops, best of 3: 303 us per loop
  
  In [94]: %timeit argsortdup(a)
  100 loops, best of 3: 11.9 ms per loop
  </code></pre>
  
  <p>It is clear from the analysis above that argsortdup is 30-50 times slower than a.argsort().argsort(). The main reason is the use of python loops and lists.</p>