Sort tenors in finance notation
<p>I have an array of tenors</p>

<pre><code>Tenors = np.array(['10Y', '15Y', '1M', '1Y', '20Y', '2Y', '30Y', '3M', '5Y', '6M', '9M'])
</code></pre>

<p>where <code>M</code> stands for month and <code>Y</code> stands for years. The correctly sorted order (ascending) would then be</p>

<pre><code>['1M', '3M', '6M', '9M', '1Y', '2Y', '5Y', '10Y', '15Y', '20Y', '30Y']
</code></pre>

<p>How do I achieve that using python with scipy/numpy? As the <code>tenors</code> originate from a <code>pandas</code> dataframe a solution based on <code>pandas</code> would be fine as well.</p>

<p><strong>Approach #1</strong> Here's a NumPy based approach using <a href="http://docs.scipy.org/doc/numpy/reference/generated/numpy.core.defchararray.replace.html#numpy.core.defchararray.replace" rel="nofollow"><code>np.core.defchararray.replace</code></a> -</p>

<pre><code>repl = np.core.defchararray.replace
out = Tenors[repl(repl(Tenors,'M','00'),'Y','0000').astype(int).argsort()]
</code></pre>

<hr>

<p><strong>Approach #2</strong> If you are working with strings like <code>'18M'</code>, we need to do a bit more of work, like so -</p>

<pre><code>def generic_case_vectorized(Tenors):
    # Get shorter names for functions
    repl = np.core.defchararray.replace
    isalph = np.core.defchararray.isalpha

    # Get scaling values
    TS1 = Tenors.view('S1')
    scale = repl(repl(TS1[isalph(TS1)],'Y','12'),'M','1').astype(int)

    # Get the numeric values
    vals = repl(repl(Tenors,'M',''),'Y','').astype(int)

    # Finally scale numeric values and use sorted indices for sorting input arr
    return Tenors[(scale*vals).argsort()]
</code></pre>

<p><strong>Approach #3</strong> Here's another approach, though a loopy one to again handle generic cases -</p>

<pre><code>def generic_case_loopy(Tenors):
    arr = np.array([[i[:-1],i[-1]] for i in Tenors])
    return Tenors[(arr[:,0].astype(int)*((arr[:,1]=='Y')*11+1)).argsort()]
</code></pre>

<p>Sample run -</p>

<pre><code>In [84]: Tenors
Out[84]: 
array(['10Y', '15Y', '1M', '1Y', '20Y', '2Y', '30Y', '3M', '25M', '5Y',
       '6M', '18M'], 
      dtype='|S3')

In [85]: generic_case_vectorized(Tenors)
Out[85]: 
array(['1M', '3M', '6M', '1Y', '18M', '2Y', '25M', '5Y', '10Y', '15Y',
       '20Y', '30Y'], 
      dtype='|S3')

In [86]: generic_case_loopy(Tenors)
Out[86]: 
array(['1M', '3M', '6M', '1Y', '18M', '2Y', '25M', '5Y', '10Y', '15Y',
       '20Y', '30Y'], 
      dtype='|S3')
</code></pre>

python sorting pandas numpy finance