将数据分组到 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 多索引的主要内容,如果未能解决你的问题,请参考以下文章