Pandas DataFrame行两两比较并计算欧氏距离的Pythonic实现方法
高效实现DataFrame行两两欧氏距离计算
我有如下DataFrame:
region country val_a val_b reg1 cntr1 0.5 0.7 reg2 cntr1 1 2 reg3 cntr1 2 1.2 reg1 cntr2 3 0.3 reg44 cntr2 0.2 0.7
希望遍历该DataFrame,将每一行与其他行进行两两比较,计算每对行中val_a和val_b的欧氏距离,生成如下结构的新DataFrame:
source_region source_country dest_region dest_country distance reg1 cntr1 reg2 cntr1 0.9 reg1 cntr1 reg3 cntr1 1.5
(注:示例中distance为随机值,只需保证两两比较逻辑正确)
目前可以用嵌套循环实现,但有没有更Pythonic的方法?
更Pythonic的实现方案
不需要嵌套循环,我们可以利用Python标准库或科学计算库的工具来高效实现:
方案一:用itertools.combinations生成行对(直观易读)
先准备好原始DataFrame,然后用itertools.combinations生成所有不重复的行索引对(避免重复比较同一对行,比如行0和行1只计算一次),再批量提取信息并计算距离:
import pandas as pd import itertools import numpy as np # 构造原始DataFrame df = pd.DataFrame({ 'region': ['reg1', 'reg2', 'reg3', 'reg1', 'reg44'], 'country': ['cntr1', 'cntr1', 'cntr1', 'cntr2', 'cntr2'], 'val_a': [0.5, 1, 2, 3, 0.2], 'val_b': [0.7, 2, 1.2, 0.3, 0.7] }) # 生成所有不重复的行索引组合(i < j,避免重复计算) idx_pairs = itertools.combinations(df.index, 2) # 构建结果数据 result_rows = [] for i, j in idx_pairs: # 提取源和目标的区域、国家 src_reg, src_cntr = df.loc[i, ['region', 'country']] dest_reg, dest_cntr = df.loc[j, ['region', 'country']] # 计算欧氏距离 dist = np.sqrt((df.loc[i, 'val_a'] - df.loc[j, 'val_a'])**2 + (df.loc[i, 'val_b'] - df.loc[j, 'val_b'])**2) # 添加到结果列表 result_rows.append([src_reg, src_cntr, dest_reg, dest_cntr, round(dist, 2)]) # 转为目标格式的DataFrame result_df = pd.DataFrame( result_rows, columns=['source_region', 'source_country', 'dest_region', 'dest_country', 'distance'] )
方案二:用scipy批量计算(高效适合大数据)
如果你的DataFrame行数较多,推荐用scipy.spatial.distance.pdist批量计算所有两两距离,底层是优化过的C代码,比Python循环快得多:
import pandas as pd from scipy.spatial.distance import pdist, squareform # 构造原始DataFrame df = pd.DataFrame({ 'region': ['reg1', 'reg2', 'reg3', 'reg1', 'reg44'], 'country': ['cntr1', 'cntr1', 'cntr1', 'cntr2', 'cntr2'], 'val_a': [0.5, 1, 2, 3, 0.2], 'val_b': [0.7, 2, 1.2, 0.3, 0.7] }) # 提取用于计算距离的数值列 value_cols = df[['val_a', 'val_b']].values # 批量计算所有两两行的欧氏距离(返回压缩格式的结果) distances = pdist(value_cols, metric='euclidean') # 转为方阵,方便通过索引取对应距离 dist_matrix = squareform(distances) # 生成所有不重复的索引对 idx_pairs = [(i, j) for i in range(len(df)) for j in range(i+1, len(df))] # 组装结果DataFrame result_data = [] for i, j in idx_pairs: result_data.append({ 'source_region': df.loc[i, 'region'], 'source_country': df.loc[i, 'country'], 'dest_region': df.loc[j, 'region'], 'dest_country': df.loc[j, 'country'], 'distance': round(dist_matrix[i, j], 2) }) result_df = pd.DataFrame(result_data)
说明
- 两种方案都避免了嵌套循环,且只计算不重复的行对(比如不会同时计算行0→行1和行1→行0),符合常规的两两比较逻辑。
- 方案一代码直观,依赖标准库,适合中小规模数据;方案二效率更高,适合行数较多的场景。
内容的提问来源于stack exchange,提问作者user308827
相关产品推荐
相关产品推荐

