比 groupby 更快的替代方法,unstack 然后 fillna

Faster alternative to groupby, unstack then fillna

我目前正在根据由两列组成的数据框 (A) 执行以下操作,每列都有数千个唯一值。

>>> pd.DataFrame({
    'col1': ['foo', 'bar', 'bar', 'foo', 'baz', 'bar', 'baz'],
    'col2': ['abc', 'def', 'abc', 'abc', 'def', 'abc', 'ghi']
})

  col1 col2
0  foo  abc
1  bar  def
2  bar  abc
3  foo  abc
4  baz  def
5  bar  abc
6  baz  ghi

对该数据帧执行的操作是:

res = df.groupby(['col1', 'col2']).size().unstack().fillna(0)

输出是一个 table (B),行中的唯一值为 col1,列中的唯一值为 col2,每个单元格都是行数,来自原始数据框,匹配此唯一值组合。

>>> res
col2  abc  def  ghi
col1               
bar   2.0  1.0  0.0
baz   0.0  1.0  1.0
foo   2.0  0.0  0.0

每个操作花费的时间大约如下:

整个序列在真实数据集上可能需要大约 30 分钟(与上面的结构类似,只是更多行和更多唯一值)。

是否有 better/faster 替代方法从 (A) 原始数据帧到 (B) 最终结果 table?到目前为止,成本最高的操作是最后一个 fillna(0),因此我对这一点的替代方法特别感兴趣,但完全不同的方法也很好。

注意:将原始df中的字符串转换为整数可使groupby().size()运算速度提高约5倍,但并不真正影响后续运算

通过设置 fill_value:

,利用与 unstack 相同的步骤填充 NA
 >>> df.groupby(['col1', 'col2']).size().unstack(fill_value=0)

timeit 在 Google Colab 上:

%timeit df.groupby(['col1', 'col2']).size().unstack().fillna(0)
1000 loops, best of 5: 1.54 ms per loop

%timeit df.groupby(['col1', 'col2']).size().unstack(fill_value=0)
1000 loops, best of 5: 1.47 ms per loop

%timeit df.groupby(['col1','col2'])['col2'].count().unstack(fill_value=0)
1000 loops, best of 5: 1.43 ms per loop

%timeit pd.crosstab(index=df.col1, columns=df.col2)
100 loops, best of 5: 8.11 ms per loop

更新: 我已经包含了 rafaelc 的答案