基于条件在Python中连接两个Pandas数据表的实现方法
解决Pandas中基于IP地址范围的表连接问题
咱们得先明确核心需求:把15万条用户IP数据和13.9万条IP段-国家数据匹配,找到每个用户IP所属的国家。直接用普通的merge肯定不行,因为是范围匹配,得用更高效的方法,不然数据量这么大,暴力匹配会慢到离谱。
第一步:统一IP数据类型
先看你的df1里的IP有带小数的(比如732758368.8),df2的上下限应该也是数值类型。首先得把所有IP相关的列转成一致的数值类型(比如float64或者int64,如果小数部分是无效的,也可以处理成整数):
# 处理df1的IP列 df1['IP_Address'] = pd.to_numeric(df1['IP_Address'], errors='coerce') # 处理df2的IP范围列 df2['Lower_Bound_IP'] = pd.to_numeric(df2['Lower_Bound_IP'], errors='coerce') df2['Upper_Bound_IP'] = pd.to_numeric(df2['Upper_Bound_IP'], errors='coerce')
这里用
errors='coerce'是为了把无效的IP值转成NaN,后续可以根据情况处理这些异常值。
第二步:用merge_asof实现高效范围匹配
merge_asof是Pandas专门用来处理有序键的左连接的工具,非常适合这种范围匹配场景,效率比apply或者循环高几个数量级。不过用它有个前提:需要把用来匹配的键(这里是IP地址)排序。
具体步骤:
- 先对df2按
Lower_Bound_IP排序(因为merge_asof要求右表的匹配键是有序的) - 用
merge_asof左连接df1和df2,匹配条件是IP_Address在Lower_Bound_IP和Upper_Bound_IP之间
代码如下:
# 对df2按Lower_Bound_IP排序 df2_sorted = df2.sort_values('Lower_Bound_IP') # 执行范围匹配连接 result = pd.merge_asof( df1.sort_values('IP_Address'), # 左表也按IP排序,提升效率 df2_sorted, left_on='IP_Address', right_on='Lower_Bound_IP', direction='backward', # 找最大的Lower_Bound_IP <= IP_Address的行 allow_exact_matches=True ) # 过滤掉IP不在Upper_Bound_IP范围内的行(因为merge_asof只匹配了Lower,还要验证Upper) result = result[result['IP_Address'] <= result['Upper_Bound_IP']]
第三步:处理异常情况
如果有用户IP不在任何IP段里,会被过滤掉,你可以用左连接结合后续处理保留这些行,比如标记为Unknown:
# 先做左连接,保留所有用户 result_full = pd.merge_asof( df1.sort_values('IP_Address'), df2_sorted, left_on='IP_Address', right_on='Lower_Bound_IP', direction='backward', allow_exact_matches=True, how='left' ) # 标记不在范围内的IP所属国家为Unknown import numpy as np result_full['Country'] = np.where( (result_full['IP_Address'] <= result_full['Upper_Bound_IP']) & (~result_full['Upper_Bound_IP'].isna()), result_full['Country'], 'Unknown' )
为什么不用其他方法?
- 如果用
apply遍历每个IP去匹配df2的范围,15万条数据会非常慢,时间复杂度是O(n*m),完全不适合大数据量。 merge_asof的时间复杂度是O(n log m),处理几十万条数据秒级就能完成。
内容的提问来源于stack exchange,提问作者DS_Enthusiast
相关产品推荐
相关产品推荐

