pandas add item to a series of list data type

Viewed 1018

How to properly add a single item to a series of list data type? I tried to make a copy and add an item to the list but this method affects the original dataframe instead

This is my code:

df = pd.DataFrame({'num':[['one'],['three'],['five']]})

# make copy of original df
copy_df = df.copy()

# add 'thing' to every single list
copy_df.num.apply(lambda x: x.append('thing'))

# show results of copy_df
print(copy_df) # this will show [['one', 'thing'], ['three', 'things'], ...]

print(df) # this will also show [['one', 'thing'], ['three', 'things'], ...]
# WHY? 

My question is:

  1. why the method above adds element to original copy too?
  2. Is there any better way to add element to a Series of list?
3 Answers

Because you are copying the dataframe but not the list in dataframe so inner series still have reference of list from original dataframe.

Better way to achieve it;

copy_df.num = copy_df.num.apply(lambda x: x + ['thing'])

1- Pointers to the lists accessed through a dataframe, not the lists itself. So, when you modify one list in one dataframe, you modify it all implicilty (because it's a single object). You can check it - look at lists' ids:

copy_df = df.copy()

copy_df['num'].apply(id)
0    140262813220744
1    140262813299528
2    140262813298888
Name: num, dtype: int64

df['num'].apply(id)
0    140262813220744
1    140262813299528
2    140262813298888
Name: num, dtype: int64

2- Better not to store the lists in a dataframe, but instead of it use sort of a 'long' table, like this:

   list_index    num
0  0            "one"
0  1          "thing"
1  0          "three"
1  1         "things"
2  0           "five"
2  1         "things"  

You store the same data, but it's easier to deal with it via pandas methods.

Edit
If you'll use a

copy_df.num = copy_df.num.apply(lambda x: x + 'num')

it'll return a new dataframe with a brand new lists:

copy_df.num
Out:
0      [one, thing]
1    [three, thing]
2     [five, thing]

copy_df.num.apply(id)
Out:
0    140262813289352
1    140262794045256
2    140262794050504

id's just changed!

copy.deepcopy doesn't work, too:

import copy

deepcopy_df = copy.deepcopy(df)
deepcopy_df.num.apply(id)
Out:
0    140262813220744
1    140262813299528
2    140262813298888

deepcopy_df.apply(lambda x: x.append('things'))
df.num  # original DataFrame
Out:
0      [one, things]
1    [three, things]
2     [five, things]

Or a no-lambda version of Sunil's answer:

copy_df.num=copy_df.num.apply(['thing'].__add__)

If care about 'thing' being at first:

copy_df.num=copy_df.num.apply(['thing'].__add__).str[::-1]
Related