如何加快Pandas中每个groupby组的缺失值的替换?

时间:2016-03-29 14:08:49

标签: python performance pandas dataframe nan

我有一个非常大的pandas数据集,其数据看起来像

df = pd.DataFrame({'group1' : ['A', 'A', 'A', 'A',
                         'B', 'B', 'B', 'B'],
                   'group2' : ['C', 'C', 'C', 'D',
                         'E', 'E', 'F', 'F'],
                   'B' : ['one', np.NaN, np.NaN, np.NaN,
                        np.NaN, 'two', np.NaN, np.NaN],
                   'C' : [np.NaN, 1, np.NaN, np.NaN,
                        np.NaN, np.NaN, np.NaN, 4]})     




df
Out[64]: 
     B   C group1 group2
0  one NaN      A      C
1  NaN   1      A      C
2  NaN NaN      A      C
3  NaN NaN      A      D
4  NaN NaN      B      E
5  two NaN      B      E
6  NaN NaN      B      F
7  NaN   4      B      F

您可以在此处看到,对于group1group2的每个唯一组合,列BC最多只包含一个非缺失变量。

在每个groupby(['group1','group2'])中,如果该值存在,则使用该唯一的非缺失值(在该组中)替换所有缺失值。

为此,我使用first之后可用的groupby函数,该函数将每个组中B或C的第一个非缺失值传播到该组中的其余缺失值:

df[['B','C']]=df.groupby(['group1','group2']).transform('first')     



df
Out[62]: 
     B   C group1 group2
0  one   1      A      C
1  one   1      A      C
2  one   1      A      C
3  NaN NaN      A      D
4  two NaN      B      E
5  two NaN      B      E
6  NaN   4      B      F
7  NaN   4      B      F

不幸的是,在我非常大的数据集中,这是痛苦缓慢。你有没有办法提高速度?我在考虑fillna,但似乎我需要应用它两次(ffillbfill)......有什么想法吗?

更新下面ajcr提出的非常有效的解决方案是否适用于由多个列定义的组? map在这种情况下不起作用。也许merge

3 个答案:

答案 0 :(得分:3)

在我的计算机上,使用groupby然后map的速度提高近100倍:

g = df.groupby('group', sort=False).first()

df['B'] = df['group'].map(g['B'])
df['C'] = df['group'].map(g['C'])

这是一个包含1000组和10000行的测试DataFrame:

df = pd.DataFrame({'group': np.repeat(np.arange(1000), 10),
                    'B': np.nan,
                    'C': np.nan})

df.ix[4::10, 'B':'C'] = 5 # every 4th row of a group is non-null

时间安排:

%%timeit

df2 = df.copy()

g = df2.groupby('group', sort=False).first()

df2['B'] = df2['group'].map(g['B'])
df2['C'] = df2['group'].map(g['C'])

返回100 loops, best of 3: 2.29 ms per loop

transform方法慢了近100倍:

%%timeit

df3 = df.copy()

df3[['B','C']] = df3.groupby('group').transform('first')

返回1 loops, best of 3: 205 ms per loop

关于您关于使用多个群组的更新问题,@ jeff在以下评论中使用

的建议
df['B'] = df.groupby(['group1','group2']).B.transform('first')
df['C'] = df.groupby(['group1','group2']).C.transform('first')

比一次转换两列快约50倍。这是因为目前transform系列的性能要高得多,尽管newly-created issue也可以提高DataFrame上的操作速度。

答案 1 :(得分:1)

让我们加快一点:

In [130]: a = df.copy()

In [131]: %timeit a['B'],a['C'] = a.B.fillna(method='ffill'), a.C.fillna(method='bfill')
1000 loops, best of 3: 538 µs per loop

In [132]: a = df.copy()

In [133]: %timeit a[['B','C']]=a.groupby('A').transform('first')
100 loops, best of 3: 3 ms per loop

答案 2 :(得分:1)

如果你以不同的方式解决这个问题并且没有尝试填充,而是重建,那该怎么办?

unique_df = df.drop_duplicates()

bVal = unique_df.drop(['B'],axis = 1).dropna().set_index(['A'])
cVal = unique_df.drop(['C'],axis = 1).dropna().set_index(['A'])

colVals = pd.merge(bVal,cVal, how = 'outer',left_index = True, right_index = True)
output = pd.merge(df[['A']],colVals, how = 'left',left_on = 'A',right_index = True) 

将df缩小到唯一的部分,在较小的数据帧上找到唯一的行,然后重新合并以重建更大的帧 - 更快?