首页 > 解决方案 > Pandas 数据框在一次运行中按不同键进行分区

问题描述

在 SQL 中,我们可以借助 OLAP 函数一次性按不同的键进行计数,从而提高 sql 性能:

select 
B, 
C,
D,
count(A) over (partition by B, C, D order by D) as by_BCD.
count(A) over (partition by B, C order by D) as by_BC,
count(A) over (partition by B order by D) as by_B,
count(A) over () as total,
from table;

我们可以在一次熊猫数据帧扫描中做同样的事情,而不是按数据帧分组 3 次吗?

Input dataset:

A   B   C   D
1   LZ  0   1
2   LZ  0   1
3   LZ  1   1
4   LZ  1   2 
5   LZ  1   2
6   SB  0   1
7   SB  0   1
8   SB  1   1
9   SB  1   2
10  SB  1   2
11   PZ  0   1


Output dataset:

A   B   C   D   by_BCD   by_BC   by_B   total
1   LZ  0   1     2        2      5      11
2   LZ  0   1     2        2      5      11
3   LZ  1   1     1        3      5      11 
4   LZ  1   2     2        3      5      11
5   LZ  1   2     2        3      5      11
6   SB  0   1     2        2      5      11
7   SB  0   1     2        2      5      11
8   SB  1   1     1        3      5      11
9   SB  1   2     2        3      5      11
10  SB  1   2     2        3      5      11
11  PZ  0   1     1        1      1      11

这是片段:

d = {'A': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
     'B': ['LZ', 'LZ', 'LZ', 'LZ', 'LZ', 'SB', 'SB', 'SB', 'SB', 'SB', 'PZ'],
     'C': [0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
     'D': [1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 1]}
df = pd.DataFrame(d)

标签: pythonpandas

解决方案


在我上面的评论中,我建议使用Multiindex

我的假设是,性能损失来自 group by 语句中的隐式索引。

按照 OP 的描述创建 df:

import pandas as pd

d = {'A': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
     'B': ['LZ', 'LZ', 'LZ', 'LZ', 'LZ', 'SB', 'SB', 'SB', 'SB', 'SB', 'PZ'],
     'C': [0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
     'D': [1, 1, 1, 2, 2, 1, 1, 1, 2, 2, 1]}
df = pd.DataFrame(d)

排序和创建多索引。很可能,为了获得更好的DataFrame.groupby排序性能就足够了。我没试过。

indexed = df.sort_values(['B', 'C', 'D']).set_index(['B', 'C', 'D'])

这产生:

         A                                                                                                                                                                                    
B  C D                                                                                                                                                                                        
LZ 0 1   1                                                                                                                                                                                    
     1   2                                                                                                                                                                                    
   1 1   3                                                                                                                                                                                    
     2   4                                                                                                                                                                                    
     2   5                                                                                                                                                                                    
PZ 0 1  11                                                                                                                                                                                    
SB 0 1   6                                                                                                                                                                                    
     1   7                                                                                                                                                                                    
   1 1   8                                                                                                                                                                                    
     2   9                                                                                                                                                                                    
     2  10    

为单行选择计数:

indexed.loc['LZ', 0, 1].count()  # 2

分组和计数,例如“BC”:

indexed.groupby(['B', 'C']).count()

产量:

      A                                                                                                                                                                                       
B  C                                                                                                                                                                                          
LZ 0  2                                                                                                                                                                                       
   1  3                                                                                                                                                                                       
PZ 0  1                                                                                                                                                                                       
SB 0  2                                                                                                                                                                                       
   1  3   

如前所述,我对性能的假设只是假设。


推荐阅读