23

I am aware of these two similar questions:

Pandas replace values

Pandas: Replacing column values in dataframe

I used a different approach for substituting values from which I think it should be the cleanest one. But it does not work. I know how to work around it, but I would like to understand why it does not work:

In [108]: df=pd.DataFrame([[1, 2, 8],[3, 4, 8], [5, 1, 8]], columns=['A', 'B', 'C']) 

In [109]: df
Out[109]: 
   A  B  C
0  1  2  8
1  3  4  8
2  5  1  8

In [110]: df.loc[:, ['A', 'B']].replace([1, 3, 2], [3, 6, 7], inplace=True)

In [111]: df
Out[111]: 
   A  B  C
0  1  2  8
1  3  4  8
2  5  1  8

In [112]: df.loc[:, 'A'].replace([1, 3, 2], [3, 6, 7], inplace=True)

In [113]: df
Out[113]: 
   A  B  C
0  3  2  8
1  6  4  8
2  5  1  8

If I slice only one column In [112] it works different to slicing several columns In [110]. As I understand the .loc method it returns a view and not a copy. In my logic this means that making an inplace change on the slice should change the whole DataFrame. This is what happens at line In [110].

Community
  • 1
  • 1
mcocdawc
  • 1,748
  • 1
  • 12
  • 21
  • you have a typo in your df ctor, you need to pass `columns=['A','B','C']` – EdChum Jan 07 '16 at 10:51
  • I think this is because your syntax is wrong: `df.loc[:, 'A': 'B'].replace([1, 3, 2], [3, 6, 7], inplace=True)` works, it seems to raise a `A value is trying to be set on a copy of a slice from a DataFrame ` warning for some reason – EdChum Jan 07 '16 at 10:55
  • Actually I think this is a bug now, it should work as `df.loc[:, 'A': 'B'].replace` is the same as `df.loc[:, ['A', 'B']].replace` – EdChum Jan 07 '16 at 11:08
  • Thank you very much! Since I want to apply this on several columns that are separated your slicing solution does not work for me. I will use `df.loc[:, ['A', 'B']] = df.loc[:, ['A', 'B']].replace([1, 3, 2], [3, 6, 7])` and write this to pandas bug report. – mcocdawc Jan 07 '16 at 11:12

2 Answers2

36

Here is the answer by one of the developers: https://github.com/pydata/pandas/issues/11984

This should ideally show a SettingWithCopyWarning, but I think this is quite difficult to detect.

You should NEVER do this type of chained inplace setting. It is simply bad practice.

idiomatic is:

In [7]: df[['A','B']] = df[['A','B']].replace([1, 3, 2], [3, 6, 7])

In [8]: df
Out[8]: 
   A  B  C
0  3  7  8
1  6  4  8
2  5  3  8

(you can do with df.loc[:,['A','B']] as well, but more clear as above.

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83
mcocdawc
  • 1,748
  • 1
  • 12
  • 21
3
to_rep = dict(zip([1, 3, 2],[3, 6, 7]))
df.replace({'A':to_rep, 'B':to_rep}, inplace = True)

This will return:

   A  B  C
0  3  7  8
1  6  4  8
2  5  3  8
user41855
  • 917
  • 8
  • 15