导读:本期聚焦于小伙伴创作的《Pandas中如何按org_id分组聚合多列并实现字符串去重拼接?》,敬请观看详情。在数据分析任务里,按机构编号归类后再把多个文本字段合并成不重复的集合,是报表加工的常见需求。如果直接用默认聚合函数,往往会出现重复值堆积或顺序混乱。本文围绕groupby与agg的组合用法,说明如何利用自定义函数配合set或dict.fromkeys保留首次出现顺序,完成多列字符串去重拼接。同时对比apply与transform在内存和速度上的差异,并给出空值处理建议,帮助你在生产脚本中写出稳定可维护的聚合逻辑。

在处理业务报表时,我们经常会遇到这样的原始数据:同一机构(用 org_id 标识)在多条记录里重复出现,而每一行又带有不同的负责人、地区标签或备注信息。如果要把同一个 org_id 下的多个文本列压缩成一行,并且去掉重复的人名或标签,就需要用到 Pandas 的分组与聚合机制。单纯使用 sum 或 list 拼接会导致结果中出现大量冗余字符串,既占用存储空间,也让后续展示变得混乱。因此,掌握按 org_id 分组后针对多列做去重拼接的方法,是数据清洗环节里非常实用的一项技能。

Pandas中如何按org_id分组聚合多列并实现字符串去重拼接?

基础分组与多列聚合语法

Pandas 的 groupby 方法允许我们指定一个或多个键进行拆分,随后通过 agg 对不同的列应用不同的聚合逻辑。当我们面对 org_id 这一列时,它可以作为分组的唯一依据。多列聚合的核心在于给 agg 传入一个字典,键为列名,值为函数或函数名。对于字符串去重拼接,标准的内置函数如 sumfirst 并不能满足要求,必须借助自定义函数将列值转为集合或利用字典保留顺序后再次连接。

下面展示一个最基础的框架:先构造示例数据,再按 org_id 分组,对 name 与 tag 两列分别使用去重拼接函数。注意在自定义函数中,我们要先丢弃缺失值,再考虑是否保持原有顺序。如果业务不关心顺序,用 set 最简洁;若需要保留首次出现顺序,则可以用 dict.fromkeys 技巧。这种写法既清晰又易于扩展到更多列,不需要为每一列单独写循环。

import pandas as pd

df = pd.DataFrame({
    'org_id': [1, 1, 2, 2, 2],
    'name': ['张三', '张三', '李四', '王五', '李四'],
    'tag': ['A', 'B', 'A', 'A', 'C']
})

def deduplicate_join(series, sep=','):
    # 去除空值并保持首次出现顺序
    seen = dict.fromkeys([x for x in series if pd.notna(x)])
    return sep.join(seen)

result = df.groupby('org_id').agg({
    'name': lambda s: deduplicate_join(s),
    'tag': lambda s: deduplicate_join(s)
}).reset_index()

print(result)

上述代码运行后,org_id 为 1 的 name 列会得到“张三”,tag 列得到“A,B”;org_id 为 2 的 name 列得到“李四,王五”,tag 列得到“A,C”。可以看到重复项已经被合并,且顺序遵循原始数据从上到下的首次出现规律。这种结构在处理成百上千个机构时依然稳定,因为分组操作底层使用了哈希表,时间复杂度接近线性。

顺序保持与性能权衡

很多初学者会用 set 来去重,例如 ','.join(set(series.dropna())),这在逻辑上没有错误,但集合是无序的,每次运行可能得到不同的拼接结果,对需要稳定输出的报表来说是个隐患。使用 dict.fromkeys 是利用字典键唯一且 Python 3.7 后保证插入顺序的特性,既去重又保序,是更严谨的写法。如果数据量极大,还可以考虑在数据库层先做 DISTINCT,再读入 Pandas 减少内存压力。

在性能方面,groupbyagg 中的 lambda 会比纯 Cython 实现的内置函数慢,因为 lambda 需要在 Python 层逐组调用。当列数不多、机构数在万级以内时,这种差异可以忽略。但若遇到千万行数据,建议将去重拼接逻辑改写为 groupby 配合 apply 返回 Series,或使用 transform 在原有行上标记后再做聚合,以减少函数调用次数。下面的示例展示了用 apply 一次性处理多列的方式,逻辑更集中。

def merge_cols(sub):
    return pd.Series({
        'name': deduplicate_join(sub['name']),
        'tag': deduplicate_join(sub['tag'])
    })

result2 = df.groupby('org_id').apply(merge_cols).reset_index()
print(result2)

这段代码把每个分组子框传给 merge_cols,在内部一次性算出两列结果,返回新的 Series。虽然 apply 仍有 Python 层开销,但函数只被调用机构数那么多次,而不是每列一次,在列多的时候更划算。你也可以把 deduplicate_join 换成支持分号、斜杠等其他分隔符的版本,只需修改默认参数即可,不需要动整体结构。

空值与异常数据处理

真实业务数据里,org_id 本身可能有空值,字符串列也常混有空串或空白字符。如果直接拼接,空串会留下多余的分隔符,影响阅读。因此在自定义函数开头,除了用 pd.notna 过滤,还应加上 str.strip 去除空格,并过滤掉长度为 0 的字符串。对于 org_id 的空值,可以在分组前用 fillna 赋予特定标记,或者先用 dropna(subset=['org_id']) 剔除,取决于报表是否允许“未知机构”归类。

另一个容易忽略的点是分隔符冲突:如果原始字符串里已经含有逗号,那么用逗号拼接会产生歧义。此时可改用管道符“|”或换行符,甚至将结果存为 Python 列表类型而非字符串,在导出 JSON 时更规范。下面的代码演示了带空白清洗与分隔符自定义的增强版函数,并处理了 org_id 为空的情形。

def clean_join(series, sep='|'):
    items = []
    for x in series:
        if pd.isna(x):
            continue
        x = str(x).strip()
        if x and x not in items:
            items.append(x)
    return sep.join(items)

df['org_id'] = df['org_id'].fillna('UNKNOWN')
result3 = df.groupby('org_id').agg({
    'name': lambda s: clean_join(s, ','),
    'tag': lambda s: clean_join(s, '|')
}).reset_index()
print(result3)

经过这样的处理,即便原始表中有“ 张三 ”、“”等脏数据,输出的聚合结果依然干净。如果后续要写回数据库,建议把去重后的列表直接以数组形式存储,避免再次解析字符串。整体来看,按 org_id 分组并聚合多列字符串去重拼接,关键就在于选对分组接口、写好保序去重函数、以及提前规整空值与分隔符,这三步做好,绝大多数报表需求都能稳妥交付。

Pandasgroupby字符串去重拼接修改时间:2026-08-15 22:08:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。