3

I would like to swap (exchange) only column names in pandas, preferably with one-liner. The additional problem is the fact that I have about 100 columns, which causes that I can't reassign all column names, so I would like to replace pairs of selected column names. I don't know the indexes of columns (it will vary), so I must use column names.

I tried following code:

import pandas as pd

probes = {'Spam': [0.0,1.0],
        'Ham': [1.0,0.0],
        'PT011': [0.11,0.21],
        'PT012': [0.12,0.22],
        'PT021': [0.21,0.11],
        'PT022': [0.22,0.12]}

df = pd.DataFrame(probes,columns= ['Spam','Ham','PT011', 'PT012','PT021','PT022'])
print("Before renaming:\n",df)
df.rename(columns={'PT011':'PT021', 'PT012':'PT022','PT021':'PT011','PT022':'PT012'}, inplace=True)
print("After renaming:\n",df)

And I got:

Before renaming:
    Spam  Ham  PT011  PT012  PT021  PT022
0   0.0  1.0   0.11   0.12   0.21   0.22
1   1.0  0.0   0.21   0.22   0.11   0.12
After renaming:
    Spam  Ham  PT021  PT022  PT011  PT012
0   0.0  1.0   0.11   0.12   0.21   0.22
1   1.0  0.0   0.21   0.22   0.11   0.12

But I would expect some simply one-liner to work allowing to swap column names, with no necessity to define both pairs of column names to swap, but to define only one pair of column names, retaining data, with some loc or iloc attribute, like:

df['PT011','PT012']=df['PT021','PT022']

with expected output (also proposed order is desired):

   Spam  Ham   PT011  PT012  PT021  PT022
0   0.0  1.0   0.21   0.22   0.11   0.12
1   1.0  0.0   0.11   0.12   0.21   0.22

The answers shown in: Renaming columns in pandas, in example:

df.columns = df.columns.str.replace('PT011','PT021')

is not suitable for my case, because it still needs to give both pairs of column names or needs to reassign names of all columns and doesn't give desired order of columns.

Tomasz
  • 528
  • 7
  • 21

2 Answers2

6

I the pandas documentation: Indexing and selecting data I found an easy and simple approach to do this:

df[['PT011','PT021']]=df[['PT021','PT011']]
df[['PT012','PT022']]=df[['PT022','PT012']]

which gives output in desired order:

After renaming:
    Spam  Ham  PT011  PT012  PT021  PT022
0   0.0  1.0   0.21   0.22   0.11   0.12
1   1.0  0.0   0.11   0.12   0.21   0.22
Tomasz
  • 528
  • 7
  • 21
4

If you have a dict that you can use to map stuff then this works fine.

df_map = {'PT011':'PT021', 'PT012':'PT022'}
df.columns = [{**df_map, **{v:k for k,v in df_map.items()}}.get(x, x) for x in df.columns]

>>> df
   Spam  Ham  PT021  PT022  PT011  PT012
0   0.0  1.0   0.11   0.12   0.21   0.22
1   1.0  0.0   0.21   0.22   0.11   0.12

Or you could use rename() to be safer.

df.rename(columns={**df_map, **{v:k for k,v in df_map.items()}}, inplace=True)

I'm not sure what you mean when you say you don't want to give it two names to flip. How would you know which columns to swap otherwise?

As for your followup question:

df_map = {'PT011':'PT021', 'PT012':'PT022'}
df_column_order = df.columns.tolist()
df.rename(columns={**df_map, **{v:k for k,v in df_map.items()}}, inplace=True)
df = df.reindex(df_column_order, axis=1)
CJR
  • 3,916
  • 2
  • 10
  • 23
  • While saying I don't want to swap pairs, I meant that I din't want approach: `'PT011':'PT021', 'PT012':'PT022','PT021':'PT011','PT022':'PT012'` but rather: `'PT011':'PT021', 'PT012':'PT022'` the next question is how to retain previous order of columns, so the columns will be in order: `PT011 PT012 PT021 PT022` – Tomasz Aug 11 '19 at 09:17
  • Ahh, that makes more sense. – CJR Aug 11 '19 at 16:46