29

I set up a simple DataFrame in pandas:

a = pandas.DataFrame([[1,2,3], [4,5,6], [7,8,9]], columns=['a','b','c'])
>>> print a
   a  b  c
0  1  2  3
1  4  5  6
2  7  8  9

I would like to be able to alter a single element in the last row of. In pandas==0.13.1 I could use the following:

a.iloc[-1]['a'] = 77
>>> print a
    a  b  c
0   1  2  3
1   4  5  6
2  77  8  9

but after updating to pandas==0.14.1, I get the following warning when doing this:

SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead

The problem of course being that -1 is not an index of a, so I can't use loc. As the warning indicates, I have not changed column 'a' of the last row, I've only altered a discarded local copy.

How do I do this in the newer version of pandas? I realize I could use the index of the last row like:

a.loc[2,'a'] = 77

But I'll be working with tables where multiple rows have the same index, and I don't want to reindex my table every time. Is there a way to do this without knowing the index of the last row before hand?

Mike
  • 6,813
  • 4
  • 29
  • 50
  • 1
    you can use ``a.ix[-1,'a'] = 77`` or ``a.loc[a.index[-1],'a'] = 77`` – Jeff Aug 06 '14 at 12:28
  • @Jeff first code snippet `a.ix[-1,'a'] = 77` inserts a new row with index value -1 :`-1 77 NaN NaN`, I did find that this works though: `df.iloc[-1].ix['a'] = 77` – EdChum Aug 06 '14 at 12:30
  • @Jeff `a.index[-1]` returns the index of the last row, but If I have that index for multiple rows it will return those too. I get an error for `a.ix[-1, 'a']`. – Mike Aug 06 '14 at 12:31
  • @EdChum don't chain index! – Jeff Aug 06 '14 at 12:32
  • @MikeP. if you have duplicates then you need to either, use ``.append``, or do some sort of ``groupby`` to eliminate them – Jeff Aug 06 '14 at 12:32
  • @EdChum right you are, `a.iloc[-1].ix['a']` allows me to alter the contents. Thanks. – Mike Aug 06 '14 at 12:32
  • @Jeff didn't think that would chain, my bad. Is there a way to do this given the OP's errors? – EdChum Aug 06 '14 at 12:32
  • @MikeP.: It's best to avoid [chained assignments](http://stackoverflow.com/q/21463589/190597). – unutbu Aug 06 '14 at 12:35
  • @Jeff naive question you say `a.iloc[-1].ix['a']` is chained assignment, presumably this is also `a.iloc[-1].loc['a']`? – EdChum Aug 06 '14 at 12:38
  • @unutbu I'm not sure if this counts as chained assignment, but if I use `a.tail(1)['a'] = 77`, I get the result I want. – Mike Aug 06 '14 at 12:40
  • @MikeP. There is a section in the [docs](http://pandas.pydata.org/pandas-docs/dev/indexing.html#indexing-view-versus-copy) on this which should help clarify things – EdChum Aug 06 '14 at 12:43
  • @MikeP. I think after reading the docs again I would consider all these to be chained indexing/assigment apart from Jeff's suggestion, the fact it works seems incidental, it isn't guaranteed to work is the issue. – EdChum Aug 06 '14 at 12:45
  • @EdChum Agreed, it almost seems like the fact that some of these work is a bug. – Mike Aug 06 '14 at 12:54
  • @MikeP. Not sure I would say they are a bug, more accidental. – EdChum Aug 06 '14 at 12:55
  • These are ALL chained assignments. They will work because its a single dtyped frame (but even then is not guaranteed). – Jeff Aug 06 '14 at 13:23

3 Answers3

37

Taking elements from the solutions of @PallavBakshi and @Mike, the following works in Pandas >= 0.19:

a.loc[a.index[-1], 'a'] = 4.0

Just using iloc[-1, 'a'] won't work as 'a' is not a location.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
brunostuyts
  • 654
  • 5
  • 10
33

Alright I've found a way to solve this problem without chaining, and without worrying about multiple indices.

a.iloc[-1, a.columns.get_loc('a')] = 77
>>> a
   a  b  c
0  1  2  3
1  4  5  6
2 77  8  9

I wasn't able to use iloc before because I couldn't supply the column index as an int, but get_loc solves that problem. Thanks for the helpful comments everyone!

Mike
  • 6,813
  • 4
  • 29
  • 50
  • 2
    in pandas 0.19.1 this approach gives warning SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead – Alexander Reshytko Dec 17 '16 at 13:32
10

For pandas 0.22,

a.at[a.index[-1], 'a'] = 77

this is just one of the ways.

PallavBakshi
  • 522
  • 2
  • 12
  • 21