将空列表列添加到DataFrame

时间:2015-07-17 00:37:45

标签: python pandas

与此问题类似How to add an empty column to a dataframe?,我很想知道将一列空列表添加到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初始化字段的次要语法更改?

如果没有,那么用空列表初始化新列的最佳方法是什么?

3 个答案:

答案 0 :(得分:19)

另一种方法是使用np.empty

df['empty_list'] = np.empty((len(df), 0)).tolist()

在尝试查找.index的{​​{1}}时,您也可以在“方法1”中取消len

df

事实证明,df['empty_list'] = [[] for _ in range(len(df))] 更快......

np.empty

答案 1 :(得分:5)

我在接受的答案中计算了所有三种方法,最快的一种方法在我的机器上花了216毫秒。但是,这只花了28毫秒:

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

注意:同样,df['e5'] = [set()] * len(df)也需要28毫秒。

答案 2 :(得分:0)

规范解决方案:列表理解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     []

在第一种情况下,将创建一个空列表,并将其 reference 复制到所有行中,因此您会看到对所有行的更新。在后一种情况下,每行都分配有自己的空列表,因此不必担心。