比 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
每个操作花费的时间大约如下:
groupby().size()
-> 5%
unstack()
-> 15%
fillna(0)
-> 80%
整个序列在真实数据集上可能需要大约 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 的答案
我目前正在根据由两列组成的数据框 (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
每个操作花费的时间大约如下:
groupby().size()
-> 5%unstack()
-> 15%fillna(0)
-> 80%
整个序列在真实数据集上可能需要大约 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 的答案