将数据分组到 Pandas 多索引

Posted

技术标签:

【中文标题】将数据分组到 Pandas 多索引【英文标题】:Grouped data to Pandas Multi-Index 【发布时间】:2022-01-10 00:34:11 【问题描述】:

我在 Excel 中有一些数据已经分组排列(这是来自某些会计软件的现金流量表),我正在使用 read_excel() 方法将这些数据读入 Pandas。我想在前 4 列上创建一个索引,但我不知道如何维护帐户的层次结构。层次结构如下所示:

当我尝试从前 4 列创建多索引时,或者如果我对它们执行 ffill(),Pandas(实际上)会这样做...

突出显示的单元格不符合我的需要,它们应该是空白/NaN 以保持层次结构。当第 2 层的子类别 B2 开始时,不应填写第 3 和第 4 层的任何类别。

这是我想要实现的目标:

最终,这些是多年的现金流量表,其科目表略有不同,所以我希望将它们导入 Pandas DataFrames 并合并它们,以便账户在整个时间段内都排成一行……这个只是这个过程的第一步。我可以手动将其编码到字典中,但想知道在 Pandas 中是否更简单?

【问题讨论】:

【参考方案1】:

我有一个简单的实用功能:

def fillright(row, cols=None, text='DUMMY'):
    """ If empty or NaN, fill given columns to the right with 'text'. """ 
    filling = False
    for c in cols:
        if row[c] is np.nan or row[c]=='':
            if filling==True:
                row[c] = text
        else:
            filling = True
    return row

levels = ['Level 1','Level 2','Level 3','Level 4']
df.apply(fillright, cols=levels, axis=1 )

中间结果:

  Level 1 Level 2 Level 3 Level 4 DataCol1 DataCol2
0      A1   DUMMY   DUMMY   DUMMY                  
1              B1   DUMMY   DUMMY                  
2                      C1   DUMMY                  
3                              D1   Value1   Value2
4                              D2   Value3   Value4
5              B2   DUMMY   DUMMY                  
6                      C2   DUMMY                  
7                              D3   Value5   Value6

现在我们做一个常规的填充,然后去掉虚拟文本:

df[levels] = ( df[levels]
    .replace('', np.nan)
    .fillna( method='ffill' )
    .replace('DUMMY', '')
)

这会产生预期的结果。

【讨论】:

【参考方案2】:

这里有一种方法是颠倒列顺序和cumprod 上的isna

给定df,

df = pd.DataFrame('Level 1':['A1']+['']*7,
                  'Level 2':['']+['B1']+['']*3+['B2']+['']*2,
                  'Level 3':['']*2+['C1']+['']*3+['C2']+[''],
                  'Level 4':['']*3+['D1','D2']+['']*2+['D3'],
                  'DataCol1':['']*3+['Value1', 'Value3']+['']*2+['Value5'],
                  'DataCol2':['']*3+['Value2', 'Value4']+['']*2+['Value6'])

输入数据框:

  Level 1 Level 2 Level 3 Level 4 DataCol1 DataCol2
0      A1                                          
1              B1                                  
2                      C1                          
3                              D1   Value1   Value2
4                              D2   Value3   Value4
5              B2                                  
6                      C2                          
7                              D3   Value5   Value6

用 np.nan 替换 '':

df_nans = df.replace('', np.nan)

创建一个布尔数组,反转列,cumprod on isna;对于所有 NaN,这将是 1,直到第一个非 NaN 值,然后将变为 0。并再次反转以重新排序列。

mask_frame = df_nans.loc[:,::-1].isna().cumprod(axis=1).loc[:, ::-1].astype(bool)
print(mask_frame)

布尔数据框:

   Level 1  Level 2  Level 3  Level 4  DataCol1  DataCol2
0    False     True     True     True      True      True
1    False    False     True     True      True      True
2    False    False    False     True      True      True
3    False    False    False    False     False     False
4    False    False    False    False     False     False
5    False    False     True     True      True      True
6    False    False    False     True      True      True
7    False    False    False    False     False     False

转发填充df_nans和掩码值:

df_out = df_nans.ffill().mask(mask_frame)

输出:

  Level 1 Level 2 Level 3 Level 4 DataCol1 DataCol2
0      A1     NaN     NaN     NaN      NaN      NaN
1      A1      B1     NaN     NaN      NaN      NaN
2      A1      B1      C1     NaN      NaN      NaN
3      A1      B1      C1      D1   Value1   Value2
4      A1      B1      C1      D2   Value3   Value4
5      A1      B2     NaN     NaN      NaN      NaN
6      A1      B2      C2     NaN      NaN      NaN
7      A1      B2      C2      D3   Value5   Value6

而且,用 '' 填充,

df_out.fillna('')

输出:

      Level 1 Level 2 Level 3 Level 4 DataCol1 DataCol2
0      A1                                          
1      A1      B1                                  
2      A1      B1      C1                          
3      A1      B1      C1      D1   Value1   Value2
4      A1      B1      C1      D2   Value3   Value4
5      A1      B2                                  
6      A1      B2      C2                          
7      A1      B2      C2      D3   Value5   Value6

【讨论】:

我已经用 Python 编程大概 8 年了,我为自己在那段时间只需要问一个 SO 问题而感到自豪;我一直都能弄明白。但我对 Pandas 有点陌生,这个让我很困惑。巧妙的答案 - 我不知道 cumprod 或面具。我知道我们不应该写“谢谢” cmets,但到底是什么 - 谢谢!!

以上是关于将数据分组到 Pandas 多索引的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Pandas 将多索引系列加入单个索引数据框?

Pandas:在多索引数据帧中重新索引和插值

在写入Excel时,“解析”一个pandas多索引

Pandas 多索引数据框 - 从多索引中的一个索引中选择最大值

pandas:在多索引数据框中转换索引类型

将 Pandas 多索引变成列