如何将Pandas DataFrame复制N次并分配新ID,保留原关联数据
问题描述
给定如下DataFrame:
import pandas as pd df = pd.DataFrame({'id':[1,3,500, 53, 1, 500], 'code1':['a0', 'b0', 'b0', 'c0', 'b0', 'a0'], 'code2':['aa', 'bb', 'cc', 'bb', 'cc', 'bb'], 'date':['2022-10-01', '2022-09-01', '2022-10-01', '2022-11-01', '2022-09-01', '2022-11-01']})
需要将该DataFrame复制N次,每次副本需分配全新的ID,同时保留原ID对应的code1/code2/date组合关系(即原ID=1对应的所有行组合,新ID也要完全复用这些组合)。
当N=1时,可通过以下代码实现:
df1 = df.loc[df.index.repeat(1)] # 等效于直接复制 ids = df.id.unique() # 获取原始唯一ID new_ids_start = df.id.max() + 1 # 新ID从原最大ID+1开始 nids = df.id.nunique() # 原始唯一ID的数量 new_ids = new_ids_start + range(0, nids) # 生成新ID列表 df1['id'] = df1['id'].replace(ids, new_ids) # 替换旧ID为新ID df_final = pd.concat([df, df1], axis=0) # 合并原数据和副本
但对于N≥2的场景,原本考虑用循环实现,想知道是否有更高效的非循环方案?核心要求是保留每个ID对应的组合关系,同时保留原ID数据。
解决方案
可以通过批量生成ID映射表 + 向量化操作实现,完全避免循环,效率更高,尤其适合数据量或N较大的场景:
向量化实现方案(推荐)
import pandas as pd import numpy as np # 原始数据 df = pd.DataFrame({'id':[1,3,500, 53, 1, 500], 'code1':['a0', 'b0', 'b0', 'c0', 'b0', 'a0'], 'code2':['aa', 'bb', 'cc', 'bb', 'cc', 'bb'], 'date':['2022-10-01', '2022-09-01', '2022-10-01', '2022-11-01', '2022-09-01', '2022-11-01']}) N = 2 # 设定需要复制的次数 # 1. 提取原始唯一ID及其索引映射 original_ids = df.id.unique() id_index_map = {id_val: idx for idx, id_val in enumerate(original_ids)} n_unique_ids = len(original_ids) max_original_id = df.id.max() # 2. 生成所有副本的ID偏移量:每轮副本的ID偏移为 轮次序号 * 唯一ID数量 offsets = np.arange(1, N+1) * n_unique_ids # 3. 为每个原始ID生成对应N轮副本的新ID # 公式:新ID = 原最大ID + 偏移量 + 原始ID的索引(保证组合关系不变) new_ids_matrix = max_original_id + offsets + np.arange(n_unique_ids)[:, None] # 4. 构建完整的ID映射字典:原始ID → [第1轮新ID, 第2轮新ID, ..., 第N轮新ID] full_id_map = {original_ids[i]: new_ids_matrix[i] for i in range(n_unique_ids)} # 5. 生成所有副本并替换ID # 重复原始数据N次,得到副本数据集 df_repeats = df.loc[df.index.repeat(N)].reset_index(drop=True) # 按原始ID分组,将每组的ID替换为对应的N轮新ID序列 df_repeats['id'] = df_repeats.groupby('id')['id'].transform( lambda x: np.tile(full_id_map[x.iloc[0]], len(x) // N) ) # 6. 合并原数据与所有副本,得到最终结果 df_final = pd.concat([df, df_repeats], axis=0).reset_index(drop=True)
方案说明
- 核心逻辑是保留原始ID与组合的对应关系:每个原始ID的索引固定,每轮副本的新ID基于原最大ID + 轮次偏移 + 原始ID索引生成,确保同一原始ID的所有组合在副本中对应同一个新ID。
- 全程使用numpy向量化操作和pandas分组变换,避免了循环带来的性能损耗,处理大规模数据时优势明显。
内容的提问来源于stack exchange,提问作者Andres
相关产品推荐
相关产品推荐

