如何删除MultiIndex DataFrame中所有上层索引分组下全为0的子索引行
问题:删除MultiIndex DataFrame中所有level0分组下全为0的子索引行
我有一个结构如下的MultiIndex DataFrame:
| level0 | level1 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| idx1 | name1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| name2 | 1.0 | 2.0 | 5.0 | 4.0 | 2.0 | |
| name3 | 1.0 | 4.0 | 2.0 | 6.0 | 8.0 | |
| idx2 | name1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| name2 | 4.0 | 2.0 | 9.0 | 5.0 | 5.0 | |
| name3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
| idx3 | name1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| name2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
| name3 | 4.0 | 7.0 | 6.0 | 5.0 | 1.0 |
需求是删除那些在所有level0分组下,对应子索引(level1)的所有数值行均为0的行。比如示例中的(idx1, name1)、(idx2, name1)、(idx3, name1)这些行需要全部删除。
我尝试过这段代码:
tmp.loc[~(tmp==0).all(axis=1)]
但它会删除所有单条全0的行,不符合需求,比如会误删(idx2, name3)、(idx3, name2)这些仅在部分分组下全0的行。
解决方案
核心思路是:先找出哪些level1子索引在所有level0分组下对应的行都是全0的,再过滤掉这些子索引的所有行。
步骤1:标记每行是否为全0行
先对每行判断是否所有数值列都是0:
is_all_zero = (tmp == 0).all(axis=1)
步骤2:按level1分组,判断该子索引是否在所有分组下都是全0
对is_all_zero按level1分组,检查每组的所有值是否都是True(即该子索引在所有level0分组下都是全0行):
to_drop = is_all_zero.groupby(level='level1').all() # 得到需要删除的level1索引列表 drop_names = to_drop[to_drop].index
步骤3:过滤DataFrame,保留不在删除列表中的子索引行
用~tmp.index.get_level_values('level1').isin(drop_names)作为掩码过滤:
result = tmp[~tmp.index.get_level_values('level1').isin(drop_names)]
完整代码
# 标记每行是否全0 is_all_zero = (tmp == 0).all(axis=1) # 找出所有分组下都全0的level1索引 to_drop = is_all_zero.groupby(level='level1').all() drop_names = to_drop[to_drop].index # 过滤DataFrame result = tmp[~tmp.index.get_level_values('level1').isin(drop_names)]
最终结果
处理后的DataFrame如下:
| level0 | level1 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| idx1 | name2 | 1.0 | 2.0 | 5.0 | 4.0 | 2.0 |
| name3 | 1.0 | 4.0 | 2.0 | 6.0 | 8.0 | |
| idx2 | name2 | 4.0 | 2.0 | 9.0 | 5.0 | 5.0 |
| name3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
| idx3 | name2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| name3 | 4.0 | 7.0 | 6.0 | 5.0 | 1.0 |
内容的提问来源于stack exchange,提问作者Xavier
相关产品推荐
相关产品推荐

