在DataFrame中添加一个空列表列

58
类似于这个问题 如何向数据框添加一个空列?,我想知道最好的方法是如何向 DataFrame 添加一个空列表的列。
我想要做的事情基本上就是初始化一个列,并在迭代行以处理其中一些行时,在这个新列中添加一个填充列表以替换初始化值。
例如,如果以下是我的初始 DataFrame:
df = pd.DataFrame(d = {'a': [1,2,3], 'b': [5,6,7]}) # Sample DataFrame

>>> df
   a  b
0  1  5
1  2  6
2  3  7

我希望最终得到类似这样的结果,其中每一行都被单独处理(示例结果如下):

>>> df
   a  b          c
0  1  5     [5, 6]
1  2  6     [9, 0]
2  3  7  [1, 2, 3]
当然,如果我像对待其他常量一样尝试初始化如df['e'] = [],那么它会认为我正在尝试添加一个长度为0的项目序列,从而导致失败。
如果我尝试将新列初始化为NoneNaN,那么当我尝试将列表分配到该位置时,就会遇到以下问题。
df['d'] = None

>>> df
   a  b     d
0  1  5  None
1  2  6  None
2  3  7  None

问题1(如果我能让这种方法起作用就太完美了!也许我错过了一些微不足道的东西):

>>> df.loc[0,'d'] = [1,3]

...
ValueError: Must have equal len keys and value when setting with an iterable

问题2(此方法可以工作,但未经保证能够按预期工作,有警告提示):

>>> df['d'][0] = [1,3]

C:\Python27\Scripts\ipython:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
因此,我采用初始化空列表并根据需要扩展它们的方法。我能想到几种初始化方式,但是否有更直接的方式?
方法1:
df['empty_lists1'] = [list() for x in range(len(df.index))]

>>> df
   a  b   empty_lists1
0  1  5             []
1  2  6             []
2  3  7             []

方法2:

 df['empty_lists2'] = df.apply(lambda x: [], axis=1)

>>> df
   a  b   empty_lists1   empty_lists2
0  1  5             []             []
1  2  6             []             []
2  3  7             []             []

问题概要:

是否有一种轻微的语法更改可以在问题1中解决,从而允许将列表分配给None/NaN初始化的字段?

如果没有,那么最好的初始化新列的方法是什么?


在第1和第2期中,您开始提到一个列 d。那是指什么? - AZhao
就我个人而言,我喜欢第二种方法。在我看来,它相当简单明了。 - AZhao
列“d”只是一个由NoneNaN初始化值组成的列,正如问题之前所定义的。 - vk1011
有没有什么小的语法更改可以在问题1中解决,从而允许将列表分配给一个None/NaN初始化的字段? - vk1011
对于问题1:您可以使用df.at[0,'d'] = [1,3]将一个(空)列表分配给单元格。 - Frank_Coumans
3个回答

86

另一种方法是使用np.empty

df['empty_list'] = np.empty((len(df), 0)).tolist()
你也可以在尝试查找dflen时,从你的"方法1"中去掉.index
df['empty_list'] = [[] for _ in range(len(df))]
原来,np.empty 更快...
In [1]: import pandas as pd

In [2]: df = pd.DataFrame(pd.np.random.rand(1000000, 5))

In [3]: timeit df['empty1'] = pd.np.empty((len(df), 0)).tolist()
10 loops, best of 3: 127 ms per loop

In [4]: timeit df['empty2'] = [[] for _ in range(len(df))]
10 loops, best of 3: 193 ms per loop

In [5]: timeit df['empty3'] = df.apply(lambda x: [], axis=1)
1 loops, best of 3: 5.89 s per loop

谢谢。是的,np.empty方法看起来更快。而且len(df.index)实际上也比len(df)更快。 - vk1011
pd.np已经被弃用,请使用np进行导入和使用。 https://github.com/pandas-dev/pandas/pull/30489 - fogx

11

编辑:评论者在我的答案中发现了错误

s = pd.Series([[]] * 3)
s.iloc[0].append(1) #adding an item only to the first element
>s # unintended consequences:
0    [1]
1    [1]
2    [1]

因此,正确的解决方案是:

s = pd.Series([[] for i in range(3)])
s.iloc[0].append(1)
>s
0    [1]
1     []
2     []

OLD:

我在采纳答案中测试了所有三种方法,最快的一种在我的电脑上花费了216毫秒。然而,这种方法只需28毫秒:

df['empty4'] = [[]] * len(df)

注意:同样地,df['e5'] = [set()] * len(df) 也花费了28毫秒。


1
我已经试图解决这个问题两个小时了,这个解决方案是真正的解决办法。 - Joel Bondurant
5
所有这些列表都是同一个对象。设置一个单元格将同时设置它们所有的值。df['empty_list'] = [[] for _ in range(len(df))]更好。 - Joylove
这不是正确的,它将所有行分配给同一个列表引用,这意味着如果您附加到其中一个列表,则与一次性附加它们没有区别 - 您需要通过列表推导式初始化单独的空列表。 - cs95

4

经典解决方案:列表推导式,mapapply

必要的声明:尽可能避免在pandas列中使用列表,因为列表列处理速度较慢,因为它们是对象,本质上很难进行向量化操作。

既然这个问题已经解决了,那么这里有一些介绍创建一个空列表列的经典方法:

# List comprehension
df['c'] = [[] for _ in range(df.shape[0])]
df

   a  b   c
0  1  5  []
1  2  6  []
2  3  7  []

还有一些关于 applymap 的简写方式:

from collections import defaultdict
# map any column with defaultdict
df['c'] = df.iloc[:,0].map(defaultdict(list))
# same as,
df['c'] = df.iloc[:,0].map(lambda _: [])

# apply with defaultdict
df['c'] = df.apply(defaultdict(list), axis=1) 
# same as,
df['c'] = df.apply(lambda _: [], axis=1)

df

   a  b   c
0  1  5  []
1  2  6  []
2  3  7  []

不应该做的事情

一些人认为将空列表乘以一个数是正确的做法,但实际上这是错误的,通常会导致难以调试的问题。下面是一个MVP:

# WRONG
df['c'] = [[]] * len(df) 
df.at[0, 'c'].append('abc')
df.at[1, 'c'].append('def')
df

   a  b           c
0  1  5  [abc, def]
1  2  6  [abc, def]
2  3  7  [abc, def]

# RIGHT
df['c'] = [[] for _ in range(df.shape[0])]
df.at[0, 'c'].append('abc')
df.at[1, 'c'].append('def')
df

a  b      c
0  1  5  [abc]
1  2  6  [def]
2  3  7     []

在第一种情况下,将创建一个空列表并将其引用复制到所有行中,因此您会看到对其中一个列表所做的更新反映在它们所有列表中。而在后一种情况下,每一行都被赋予了自己的空列表,因此这不是一个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接