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