You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

高效处理Pandas DataFrame跨行列重复手机号置为NaN的方法

Pandas 高效处理跨行列重复手机号

问题背景

我有个Pandas DataFrame,包含acct_id(账号ID)、home(家庭电话)、work(工作电话)、mobile(移动电话)字段,所有值都是字符串。需求是:

  • 同行内重复手机号:优先保留home,其次work,剩下的置为NaN
  • 跨行列重复手机号:只保留首次出现的实例,其余置为NaN

已经搞定了同行内的去重,现在需要高效处理跨行列的重复问题。

初始数据

import pandas as pd
import numpy as np

df_initial = pd.DataFrame({
    'acct_id': ['A1', 'A2', 'A3'],
    'home': ['123-456-7890', '987-654-3210', '123-456-7890'],
    'work': ['123-456-7890', '555-555-5555', '987-654-3210'],
    'mobile': ['444-444-4444', '987-654-3210', '555-555-5555']
})

目标结果

df_target = pd.DataFrame({
    'acct_id': ['A1', 'A2', 'A3'],
    'home': ['123-456-7890', '987-654-3210', np.nan],
    'work': [np.nan, '555-555-5555', np.nan],
    'mobile': ['444-444-4444', np.nan, np.nan]
})

已完成的同行去重代码

# 同行内按home→work→mobile优先级去重
for idx, row in df_initial.iterrows():
    seen = []
    for col in ['home', 'work', 'mobile']:
        phone = row[col]
        if phone not in seen:
            seen.append(phone)
        else:
            df_initial.loc[idx, col] = np.nan

df_interim = df_initial.copy()  # 同行去重后的中间结果

低效的暴力解法(仅作反面示例)

# 遍历所有单元格,记录已出现的手机号,重复就置NaN
seen_phones = set()
for col in ['home', 'work', 'mobile']:
    for idx in df_interim.index:
        phone = df_interim.loc[idx, col]
        if pd.notna(phone) and phone in seen_phones:
            df_interim.loc[idx, col] = np.nan
        else:
            seen_phones.add(phone)

高效跨行列去重方案

用Pandas的长格式转换+分组标记实现,全程向量化操作,比循环快N倍:

代码实现

# 基于同行去重后的中间df_interim处理
col_priority = {'home': 0, 'work': 1, 'mobile': 2}

# 1. 转长格式,同时记录行顺序和列优先级
df_melted = df_interim.melt(id_vars='acct_id', var_name='type', value_name='phone')
# 标记每一行的原始顺序,保证先出现的行优先
df_melted['row_idx'] = df_melted['acct_id'].map(df_interim.reset_index().set_index('acct_id')['index'])
# 标记列的优先级,保证同一行内home优先
df_melted['col_rank'] = df_melted['type'].map(col_priority)

# 2. 按手机号分组,标记每个手机号的首次出现项
# 首次出现的判断:行号最小,且同一行内列优先级最高
df_melted['is_first'] = df_melted.groupby('phone').apply(
    lambda x: (x['row_idx'] == x['row_idx'].min()) & (x['col_rank'] == x['col_rank'].min())
).reset_index(level=0, drop=True)

# 3. 非首次出现的手机号置为NaN
df_melted['phone'] = np.where((df_melted['is_first']) & pd.notna(df_melted['phone']), df_melted['phone'], np.nan)

# 4. 转回宽格式,恢复原列顺序
df_final = df_melted.pivot(index='acct_id', columns='type', values='phone').reset_index()
df_final.columns.name = None
df_final = df_final[['acct_id', 'home', 'work', 'mobile']]

为什么这个方案高效?

  • 避免了显式循环,用Pandas内置的分组、向量化操作处理,大数据集下性能碾压暴力循环
  • 严格遵循了行顺序和列优先级的规则,不会出现逻辑偏差
  • 代码结构清晰,后续调整优先级或字段时只需修改col_priority字典即可

内容的提问来源于stack exchange,提问作者Noah Petrasko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 10:36:04