27  group

27.1 分类聚合

27.1.1 实例1:根据A列相同行合并B列的值

import pandas as pd

# 创建示例DataFrame
data = {'A': [1, 1, 2, 2, 3],
        'B': ['apple', 'banana', 'cherry', 'date', 'elderberry'],
        'C': [10, 20, 30, 40, 50],
        'D': [50, 40, 30, 20, 10]}
df = pd.DataFrame(data)

df
A B C D
0 1 apple 10 50
1 1 banana 20 40
2 2 cherry 30 30
3 2 date 40 20
4 3 elderberry 50 10

27.1.1.1 使用groupby和agg方法将列B内容合并

df_merged = df.groupby('A').agg({'B': ', '.join}).reset_index()

df_merged
A B
0 1 apple, banana
1 2 cherry, date
2 3 elderberry

说明:这种写法df.groupby('A').agg({'B': ', '.join}),聚合函数,如', '.join的参数是按A列分组后B列的各个值,如[apple, banana],其类型是Series,返回的结果是DataFrame。

27.1.1.2 使用groupby和agg方法将列B内容合并并保留列C第一行

df_merged = df.groupby('A').agg({'B': ', '.join, 'C': 'first'}).reset_index()   # 保留C列的第一行

df_merged
A B C
0 1 apple, banana 10
1 2 cherry, date 30
2 3 elderberry 50

27.1.1.3 使用groupby和agg方法将列B内容合并并保留所有其他列第一行

df_merged = df.groupby('A').agg(lambda x: ', '.join(x) if x.name=='B' else x.iloc[0]).reset_index()

df_merged
A B C D
0 1 apple, banana 10 50
1 2 cherry, date 30 30
2 3 elderberry 50 10

27.2 实例2:按A列分组,判断每组B列是否全部为空

# 创建示例数据框
data = {
    'A': ['group1', 'group1', 'group2', 'group2', 'group3'],
    'B': [1, None, None, None, None]
}
df = pd.DataFrame(data)

df
A B
0 group1 1.0
1 group1 NaN
2 group2 NaN
3 group2 NaN
4 group3 NaN
# 写法1
result1 = df.groupby('A')['B'].apply(lambda x: x.isna().all())

result1
A
group1    False
group2     True
group3     True
Name: B, dtype: bool

说明:这种写法df.groupby('A')['B'].apply(lambda x: x.isna().all()),聚合函数,如lambda x: x.isna().all()的参数是按A列分组后B列的各个值,如[1.0, NaN],其类型是Series,返回的是Series。

# 写法2

result2 = df.groupby('A').agg({'B': lambda x: x.isna().all()})

result2
B
A
group1 False
group2 True
group3 True

27.3 实例3:按A列分组,每组B列双向填充空值

data = {
    'A': ['group1', 'group1', 'group2', 'group2', 'group2', 'group3', 'group3'],
    'B': [1, None, None, 2, None, None, 5],
    'C': ['长嘱', '长嘱','长嘱','长嘱','长嘱','临嘱','临嘱',]
}
df = pd.DataFrame(data)

df
A B C
0 group1 1.0 长嘱
1 group1 NaN 长嘱
2 group2 NaN 长嘱
3 group2 2.0 长嘱
4 group2 NaN 长嘱
5 group3 NaN 临嘱
6 group3 5.0 临嘱
df1 = df.copy()

df1.B = df.groupby('A', group_keys = False)['B'].apply(
    lambda x : x.ffill().bfill()
)

df1
A B C
0 group1 1.0 长嘱
1 group1 1.0 长嘱
2 group2 2.0 长嘱
3 group2 2.0 长嘱
4 group2 2.0 长嘱
5 group3 5.0 临嘱
6 group3 5.0 临嘱
df2 = df.copy()

mask = df.C == '长嘱'

df2.loc[mask, 'B'] = df[mask].groupby('A', group_keys = False)['B'].apply(
    lambda x : x.ffill().bfill()
)

df2
A B C
0 group1 1.0 长嘱
1 group1 1.0 长嘱
2 group2 2.0 长嘱
3 group2 2.0 长嘱
4 group2 2.0 长嘱
5 group3 NaN 临嘱
6 group3 5.0 临嘱
#### 实例4 对载入包集合去重
import re
def deduplicate_import_set(import_set: set):
    '''
    import re
    import pandas as pd
    '''

    df = pd.DataFrame([re.split(r"(?<=[@|])", package) for package in import_set], columns = ['package', 'alias_or_subpackage'])

    # 填充空值为空字符串,后面处理的对象要求是字符串

    df.alias_or_subpackage = df.alias_or_subpackage.fillna('')


    # 分组聚合

    ## 聚合的结果要求是同组的alias_or_subpackage得到并集,防止重复导入

    ### 第一步:"+".join(x)将同一组的subpackage通过+连接成一个字符串

    ### 第二步: "+".join(x).split('+')再次将字符串分割成列表

    ### 第三步: set("+".join(x).split('+'))去除列表中的重复元素

    ### 第四步:"+".join(set("+".join(x).split('+')))再次将字符还原成原来通过+相连接的格式


    df = df.groupby('package').agg({'alias_or_subpackage': lambda x : "+".join(set("+".join(x).split('+')))}).reset_index()


    # 还原成import_set

    import_set = {f"{package}{alias_or_subpackage}" for package, alias_or_subpackage in df.values.tolist()}

    return import_set

import_set = {'flask|request', 'feffery_antd_components@fac', 'dash.dependencies|Output+Input+State', 'matplotlib.pyplot@plt', 'dash.dependencies|Output+Input', 'dash', 'shutil', 'uuid|uuid4', 'pandas@pd', 'os', 'dash|dcc+html'}

import_set_deduplicated = deduplicate_import_set(import_set)

import_set_deduplicated
{'dash',
 'dash.dependencies|Input+Output+State',
 'dash|dcc+html',
 'feffery_antd_components@fac',
 'flask|request',
 'matplotlib.pyplot@plt',
 'os',
 'pandas@pd',
 'shutil',
 'uuid|uuid4'}

27.4 分组筛选

27.4.1 实例1:按A列分组,筛选每组B列全部为空的行

df_colb_isna_all = (
    df
    .groupby('A')
    .filter(lambda group : group['B'].isna().all())   
    
)

df_colb_isna_all
A B C