繁体   English   中英

如何将 append 中的行子集聚合到多索引 Pandas DataFrame?

[英]How to aggregate a subset of rows in and append to a MultiIndexed Pandas DataFrame?

问题设置和目标

我有一个看起来像这样的多索引 Pandas DataFrame:

import pandas as pd

df = pd.DataFrame({
    'Values':[1, 3, 4, 8, 5, 2, 9, 0, 2],
    'A':['A1', 'A1', 'A1', 'A1', 'A2', 'A2', 'A3', 'A3', 'A3'],
    'B':['foo', 'bar', 'fab', 'baz', 'foo', 'baz', 'qux', 'baz', 'bar']
})
df.set_index(['A','B'], inplace=True)
print(df.to_string())

        Values
A  B          
A1 foo       1
   bar       3
   fab       4
   baz       8
A2 foo       5
   baz       2
A3 qux       9
   baz       0
   bar       2

我的最终目标是以最简单、最规范的 Pandas 方式将 B 列中的所有“bar”和“baz”行替换为名为“other”(见下文)的总和行。

       Values
A  B          
A1 foo       1
   fab       4
   other    11
A2 foo       5
   other     2
A3 qux       9
   other     2

当前工作

我设法从一个类似的问题中弄清楚如何为 MultiIndex DataFrame 创建一个掩码,以突出显示我们最终要聚合的行,这些行位于 agg_list 中。

agg_list = ['bar', 'baz']
# Create a mask that highlights the rows in B that are in agg_list
filterFunc = lambda x: x.index.get_level_values('B') in agg_list
mask = df.groupby(level=['A','B']).apply(filterFunc)

这会产生预期的掩码:

print(mask.to_string())

A   B  
A1  bar     True
    baz     True
    fab    False
    foo    False
A2  baz     True
    foo    False
A3  bar     True
    baz     True
    qux    False

而且我知道如何删除我不再需要的行:

# Remove rows in B col that are in agg_list using mask
df_masked = df[[~mask.loc[i1, i2] for i1,i2 in df.index]]
print(df_masked.to_string())

    Values
A  B          
A1 foo       1
   fab       4
A2 foo       5
A3 qux       9

但我不知道如何对这些行进行实际聚合/求和,并将 append 对每个多索引行进行。

类似问题/解决方案

我见过的类似问题不涉及 Multindex DataFrame,所以我不能完全使用像这样的一些解决方案,它具有相同的一般思想,即创建一个掩码,然后是 append 一个总和行:

threshold = 6
m = df['value'] < threshold
df1 = df[~m].copy()
df1.loc['Z'] = df.loc[m, 'value'].sum()

或者

m = df['value'] < threshold
df1 = df[~m].append(df.loc[m, ['value']].sum().rename('Z'))

这是一种仅重置B的索引、执行替换并聚合值的方法。

agg_list = ['bar', 'baz']
(df.reset_index(level=1)
.replace({'B':{'|'.join(agg_list):'other'}},regex=True)
.groupby(['A','B']).sum())

另一种方法是创建一个新的 MultiIndex,其中barbaz被替换为other

(df.set_axis(pd.MultiIndex.from_arrays([df.index.get_level_values(0),
df.index.get_level_values(1).str.replace('|'.join(agg_list),'other')]))
.groupby(level=[0,1]).sum())

Output:

          Values
A  B            
A1 fab         4
   foo         1
   other      11
A2 foo         5
   other       2
A3 other       2
   qux         9

你可以做一些简单的事情如下

b_others = df.B.replace({'bar': 'other', 'baz': 'other'})
df.groupby(['A', b_others]).sum()

使用替换'bar''baz'值创建变量。 然后,只需使用它进行分组。

Output

          Values
A  B            
A1 fab         4
   foo         1
   other      11
A2 foo         5
   other       2
A3 other       2
   qux         9

一种选择是重命名axis=0level=1以便barbaz变为other然后执行标准groupby sum

df = df.rename(
    axis=0,
    level=1,
    mapper={'bar': 'other', 'baz': 'other'}
).groupby(level=['A', 'B']).sum()

更通用的解决方案可能如下所示:

from typing import List, Dict


def map_to_value(
        values_to_map: List[str],
        default_value: str
) -> Dict[str, str]:
    return {k: default_value for k in values_to_map}


df = df.rename(
    axis=0,
    level=1,
    mapper=map_to_value(['bar', 'baz'], 'other')
).groupby(level=['A', 'B']).sum()

任何一种方法都会导致:

          Values
A  B            
A1 fab         4
   foo         1
   other      11
A2 foo         5
   other       2
A3 other       2
   qux         9

或者,通过更多的工作,我们可以保留一组已知值:

from typing import List, Callable


def keep_and_default(
        keep_values: List[str], default_value: str
) -> Callable[[str], str]:
    mapping = {k: k for k in keep_values}

    def mapper(c: str) -> str:
        return mapping.get(c, default_value)

    return mapper


df = df.rename(
    axis=0,
    level=1,
    mapper=keep_and_default(['foo', 'fab', 'qux'], 'other')
).groupby(level=['A', 'B']).sum()

在这种情况下,这也会导致:

          Values
A  B            
A1 fab         4
   foo         1
   other      11
A2 foo         5
   other       2
A3 other       2
   qux         9

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM