Sorting by absolute value without changing the data

I'm looking for a simple way to sort a pandas dataframe by the absolute value of a particular column, but without actually changing the values within the dataframe. Something similar to sorted(df, key=abs). So if I had a dataframe like:

    a   b
0   1   -3
1   2   5 
2   3   -1
3   4   2
4   5   -9

The resultant sorted data when sorting on 'b' would look like:

    a   b
2   3   -1
3   4   2
0   1   -3
1   2   5 
4   5   -9

Solution 1:

UPDATE

Since 0.17.0 order and sort have been deprecated (thanks @Ruggero Turra), you can use sort_values to achieve this now:

In[16]:

df.reindex(df.b.abs().sort_values().index)
Out[16]: 
   a  b
2  3 -1
3  4  2
0  1 -3
1  2  5
4  5 -9

Solution 2:

Perfect Simple Solution with Pandas > V_1.1.0:

Use the parameter key in the sort_values() function:

import pandas as pd
ttt = pd.DataFrame({'A': ['a', 'b', 'c', 'd', 'e', 'f'], 'B': [-3, -2, -1, 0, 1, 2]})

ttt.sort_values(by='B', key=abs)

will yield:

    A   B
3   d   0
2   c   -1
4   e   1
1   b   -2
5   f   2
0   a   -3

Solution 3:

Towards more idiomatic pandas: Use argsort

A cleaner approach would be to call Series.argsort on the absolute values, and then index:

df.iloc[df['b'].abs().argsort()]

   a  b
2  3 -1
3  4  2
0  1 -3
1  2  5
4  5 -9

If you need to reset the index, use Series.reset_index,

df.iloc[df['b'].abs().argsort()].reset_index(drop=True)

   a  b
0  3 -1
1  4  2
2  1 -3
3  2  5
4  5 -9

Lastly, since argsort does not have an ascending parameter to specify ascending/descending order, you will need to negate df['b'].abs() to sort by descending order.

df.iloc[(-df['b'].abs()).argsort()]

   a  b
4  5 -9
1  2  5
0  1 -3
3  4  2
2  3 -1

You can do this with NumPy as well—use np.abs and ndarray.argsort.

df.iloc[np.abs(df['b'].values).argsort()]

   a  b
2  3 -1
3  4  2
0  1 -3
1  2  5
4  5 -9

Or, for descending order,

df.iloc[(-np.abs(df['b'].values)).argsort()]

   a  b
4  5 -9
1  2  5
0  1 -3
3  4  2
2  3 -1