基于多列条件生成DataFrame邻接CLLI列的高效Pandas方案
高效实现Pandas多条件邻接CLLI列生成
问题背景
现有如下Pandas DataFrame:
RING CLLI CIRCUIT SITE 0 N100 M200 Circuit1 M200 1 N100 M200 Circuit2 M200 2 N100 M201 Circuit3 M201 3 N101 M200 23 Circuit1 M200 4 N101 M300 Circuit1 M300 5 N101 M304 XK Circuit11 M304 6 N101 M147 Circuit10 M147 7 N102 M304E5 Circuit11 M304 8 N102 M874 Circuit114 M874 9 N102 M874 Circuit113 M874 10 N102 M874 Circuit112 M874 11 N104 M643 Circuit2 M643 12 N104 M643 Circuit234 M643 13 N104 M304 Circuit11 M304
需要新增NeigbourCLLI列,判断逻辑:
- 筛选相同CIRCUIT的行
- 在这些行中,筛选RING不同且SITE匹配的记录
- 将符合条件的CLLI值用逗号拼接填入对应行的
NeigbourCLLI,无匹配则为NaN
示例:
- Circuit1仅行0和3满足SITE匹配且RING不同,因此行0的
NeigbourCLLI为M200 23,行3的为M200,行4无匹配 - Circuit11的行5、7、13均满足条件,因此行5的
NeigbourCLLI为M304E5, M643,行7为M304 XK, M643,行13为M304 XK, M304E5
原实现代码在50万行以上的DataFrame中效率极低:
import numpy as np import pandas as pd grouped = df.groupby('CIRCUIT').apply(lambda x: x['RING'].nunique() > 1) def find_neighboring_cllis(row): circuit, ring, site = row['CIRCUIT'], row['RING'], row['site'] if grouped[circuit]: neighbors = df[(df['CIRCUIT'] == circuit) & (df['RING'] != ring) & (df['SITE'] == site)]['CLLI'].unique() if neighbors.size > 0: return ', '.join(neighbors) return np.nan df['NeigbourCLLI'] = df.apply(find_neighboring_cllis, axis=1)
高效实现方案
原代码效率低的核心原因是apply逐行遍历+每次重新过滤整个DataFrame,时间复杂度为O(n²)。以下是基于Pandas原生特性的O(n)级优化方案:
方案一:分组映射+合并
import pandas as pd # 1. 按CIRCUIT+SITE分组,预存每组内的RING-CLLI映射关系 grouped = df.groupby(['CIRCUIT', 'SITE']).apply( lambda x: x[['RING', 'CLLI']].set_index('RING')['CLLI'].to_dict() ).reset_index(name='ring_clli_map') # 2. 将分组结果合并回原表,让每行直接获取所在组的映射 df_merged = df.merge(grouped, on=['CIRCUIT', 'SITE'], how='left') # 3. 生成邻接CLLI:排除自身RING对应的CLLI后拼接 def get_neighbors(row): ring_clli = row['ring_clli_map'] current_ring = row['RING'] neighbors = [clli for ring, clli in ring_clli.items() if ring != current_ring] return ', '.join(neighbors) if neighbors else pd.NA df['NeigbourCLLI'] = df_merged.apply(get_neighbors, axis=1) # 清理临时列(可选) df.drop(columns=['ring_clli_map'], errors='ignore', inplace=True)
方案二:分组内直接处理(性能更优)
import pandas as pd def process_group(g): # 提取组内的RING和CLLI列表 ring_list = g['RING'].tolist() clli_list = g['CLLI'].tolist() # 对组内每个元素,生成排除自身RING的CLLI拼接字符串 neighbor_list = [] for i, ring in enumerate(ring_list): neighbors = [clli for r, clli in zip(ring_list, clli_list) if r != ring] neighbor_list.append(', '.join(neighbors) if neighbors else pd.NA) g['NeigbourCLLI'] = neighbor_list return g # 按CIRCUIT+SITE分组处理,直接生成结果列 df = df.groupby(['CIRCUIT', 'SITE']).apply(process_group).reset_index(drop=True)
优化逻辑说明
- 预分组减少重复计算:一次性按
CIRCUIT+SITE分组,把每组内的RING与CLLI的对应关系提前存储,避免逐行查询时重复过滤整个DataFrame - 内存内操作:所有匹配逻辑都在分组内部或预存的映射中完成,无需多次IO或全表扫描
- 避免逐行遍历的开销:利用Pandas分组的批量处理能力,替代低效的
apply(axis=1)逐行操作
内容的提问来源于stack exchange,提问作者user14073111
相关产品推荐
相关产品推荐

