如何基于多列数据生成结构化文本新列

本文介绍如何在pandas中按逻辑分组并组合多列信息,动态生成符合业务语义的文本新列(如个性化邀请消息),重点解决重复`head`值需分段聚合、排除同名成员、保留原始顺序等关键问题。

在数据分析与报表生成中,常需将结构化数据(如姓名、分组标识)转化为自然语言文本。本例中,目标是为每个连续的 head 分组生成一条定制化邀请消息,格式为:
“Hi [头名], we invite you, [其他成员列表]. Please use "[完整head]" when arriving.”

难点在于:

  • head 列存在重复值(如 "Abba As" 出现两次),但需视为两个独立邀请批次(因数据已按业务逻辑排序,相同 head 的连续块代表不同场景);
  • 每组内需提取 head 的首名(空格分割取第一部分);
  • members 中若存在与该 head 首名相同的成员(如 "Abba" 与 "Abba As"),应排除自身,仅列出其他受邀人
  • 成员间用 " and " 连接(非逗号),且需保持原始顺序。

✅ 正确实现步骤

首先,通过比较相邻行识别 head 的连续变化点,构造唯一分组标识:

group = df['head'].ne(df['head'].shift()).cumsum()

此操作为每个连续的 head 块分配一个递增整数标签(如 [0,0,0,1,1,1,2,2,2]),确保 "Abba As" 的两段被分别处理。

接着,使用 groupby(['head', group], sort=False) 进行双重分组(sort=False 保留原始顺序),并应用自定义函数:

def message(g):
    head_full = g.name[0]  # 当前组的 head 值(如 "Abba As")
    head_first = head_full.split()[0]  # 提取首名:"Abba"
    # 过滤出非首名的 members,并用 " and " 连接
    others = ' and '.join([m for m in g['members'] if m != head_first])
    return f'Hi {head_first}, we invite you, {others}. Ple

ase use "{head_full}" when arriving.' out = (df.groupby(['head', group], sort=False) .apply(message) .droplevel(1) # 移除辅助分组 level(即 group 标签) .reset_index(name='message') )
? 关键细节说明: g.name[0] 获取 groupby 的元组键中第一个元素(head 值); 列表推导式 [m for m in g['members'] if m != head_first] 精准排除同名成员,避免 "Abba" 被误邀自己; droplevel(1) 是必需的——因为分组键含两个维度,apply 返回的 Series 默认以双层索引存储,需降维才能 reset_index。

⚠️ 常见误区提醒

  • ❌ 直接 groupby('head') 会合并所有 "Abba As" 行,导致 Ally, Apo, Abba, Arra, Alya 全部混入一条消息,违背业务要求;
  • ❌ 使用 str.contains() 或模糊匹配判断同名易出错(如 "Abba" 与 "Abbas" 冲突),应严格用 == 比较首名;
  • ❌ 忽略 sort=False 可能触发 Pandas 默认重排序,打乱原始分组逻辑。

最终输出严格匹配预期:三行独立消息,每行对应一个连续 head 块,成员列表准确、语法规范、引用完整 head 字符串。此模式可扩展至邮件模板、通知文案、报告摘要等场景,核心在于用 cumsum() 捕捉连续性 + groupby.apply 实现上下文感知的文本合成