title: NumPy - fast stable arg-sort of large array by frequency
body: <p>I have large 1D <a href="https://numpy.org/" rel="nofollow noreferrer">NumPy</a> array <code>a</code> of any comparable <code>dtype</code>, some of its elements may be repeated.</p>
  <p>How do I find sorting indexes <code>ix</code> that will stable-sort (stability in <a href="https://stackoverflow.com/q/64238462/941531">a sense described here</a>) <code>a</code> by frequencies of values in descending/ascending orders?</p>
  <p>I want to find fastest and simplest way to do this. Maybe there is existing standard numpy function to do that.</p>
  <p>There is another related <a href="https://stackoverflow.com/questions/51737245/how-to-sort-a-numpy-array-by-frequency">question here</a> but it was asking specifically to remove arrays duplicates, i.e. output only unique sorted values, I need all values of original array including duplicates.</p>
  <p>I've coded my first trial to do the task, but it is not the fastest (uses Python's loop) and probably not shortest/simplest possible form. This python loop can be very expensive if repeating of equal elements is not high and array is huge. Also would be nice to have short function for doing this all if available in NumPy (e.g. imaginary <code>np.argsort_by_freq()</code>).</p>
  <p><a href="https://tio.run/##dVHNSgMxEL7nKQY8dAKxVq0KhR4EH8FbkRJ3s@3Q3UmabLD15eukQYuge9gk8/3km0w4jlvP96cTDcHHETgP4Qg2AQfFYRott36YJudavNVqSwbYQOtSA0t4MnA3N/Aas1NWzhd@s/XUOJSClcrG4Za0AWQj/yBMVCBfb4f31p4LF2ldUBRG64WANxCmKQ@olUatVU4GmlQlmWmfHVoD0Y058rrxmccClkxa2a7yPl30CUvw8RhcrRGPj3OtOh8hiyMQwycFrPZ6cQ5ou5X0tYT8JppGUXfuvGJ0qD42bpI8HF7bzsCOuJXyJI32vXcTDVfwIgrHLfEGCk@5Prm/Hf4xeP6tD1GS4yT6j7QAko57nGl5JB9pQ2z7tZVBybmLbp/wrmyLzrUC3OuJ/jYoCX6gSp5/k@WyNR3w4UKXmBKp2WGd3GWwLBOV9y/Z7YoOb2VbVzoYJeBMZnY6fQE" rel="nofollow noreferrer" title="Python 3 – Try It Online">Try it online!</a></p>
  <pre><code>import numpy as np
  np.random.seed(1)
  hi, n, desc = 7, 24, True
  a = np.random.choice(np.arange(hi), (n,), p = (
      lambda p = np.random.random((hi,)): p / p.sum()
  )())
  us, cs = np.unique(a, return_counts = True)
  af = np.zeros(n, dtype = np.int64)
  for u, c in zip(us, cs):
      af[a == u] = c
  if desc:
      ix = np.argsort(-af, kind = 'stable') # Descending sort
  else:
      ix = np.argsort(af, kind = 'stable') # Ascending sort
  print('rows: i_col(0) / original_a(1) / freqs(2) / sorted_a(3)')
  print('    / sorted_freqs(4) / sorting_ix(5)')
  print(np.stack((
      np.arange(n), a, af, a[ix], af[ix], ix,
  ), 0))
  </code></pre>
  <p>outputs:</p>
  <pre><code>rows: i_col(0) / original_a(1) / freqs(2) / sorted_a(3)
      / sorted_freqs(4) / sorting_ix(5)
  [[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
   [ 1  1  1  1  3  0  5  0  3  1  1  0  0  4  6  1  3  5  5  0  0  0  5  0]
   [ 7  7  7  7  3  8  4  8  3  7  7  8  8  1  1  7  3  4  4  8  8  8  4  8]
   [ 0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  5  5  5  5  3  3  3  4  6]
   [ 8  8  8  8  8  8  8  8  7  7  7  7  7  7  7  4  4  4  4  3  3  3  1  1]
   [ 5  7 11 12 19 20 21 23  0  1  2  3  9 10 15  6 17 18 22  4  8 16 13 14]]
  </code></pre>