如何用np.where判断DataFrame中COL A数值是否在COL B嵌套列表区间内?
Pandas向量化判断数值是否在嵌套区间列表中的解决方案
错误原因分析
你之前的代码报错是因为test_df['COL B'][0][0]是取整列第一个元素的区间起始值,而非逐行读取对应行的区间参数。这种写法无法适配每行不同的嵌套列表,甚至当某行COL B为空列表时会直接触发索引越界错误。
向量化解决方案思路与代码
方法一:apply结合numpy向量化判断(推荐,灵活高效)
利用apply逐行传递数据,但内部用numpy的广播和any()实现向量化判断,比纯for循环效率提升明显,且适配任意数量的区间:
import pandas as pd import numpy as np # 构造测试数据 test_df = pd.DataFrame({ 'COL A': [5, 15, 25, 35], 'COL B': [ [[1, 10], [12, 20]], [[0, 5], [20, 30]], [[15, 25]], [[30, 40], [50, 60]] ] }) def check_in_ranges(val, ranges): # 将嵌套区间转为numpy数组,广播判断数值是否落在任意区间内 ranges_arr = np.array(ranges) return np.any((val >= ranges_arr[:, 0]) & (val <= ranges_arr[:, 1])) # 逐行调用判断函数 test_df['inRange'] = test_df.apply( lambda row: 'match' if check_in_ranges(row['COL A'], row['COL B']) else 'no match', axis=1 )
方法二:explode展开后批量聚合(纯向量化,大数据量首选)
通过explode将每行的嵌套区间展开为单独行,批量完成区间判断后再按原索引聚合结果,完全避免逐行循环:
# 展开嵌套区间并保留原索引 exploded_df = test_df.explode('COL B').reset_index() # 将区间拆分为起始、结束两列 exploded_df[['start', 'end']] = pd.DataFrame(exploded_df['COL B'].tolist(), index=exploded_df.index) # 标记当前区间是否匹配COL A的值 exploded_df['is_match'] = (exploded_df['COL A'] >= exploded_df['start']) & (exploded_df['COL A'] <= exploded_df['end']) # 按原索引聚合,只要有一个匹配则标记为match match_result = exploded_df.groupby('index')['is_match'].any().map({True: 'match', False: 'no match'}) test_df['inRange'] = match_result.values
方法三:numpy广播(适合区间数量固定的场景)
如果每行COL B的区间数量完全一致,可以直接用numpy广播实现纯向量化运算,效率最高:
# 将COL B的嵌套列表转为三维numpy数组(行数 × 区间数 × 起止值) ranges_arr = np.array(test_df['COL B'].tolist()) # 广播判断每行COL A是否落在任意区间内 is_match = (test_df['COL A'].values[:, np.newaxis] >= ranges_arr[:, :, 0]) & (test_df['COL A'].values[:, np.newaxis] <= ranges_arr[:, :, 1]) # 转换为最终结果 test_df['inRange'] = np.where(is_match.any(axis=1), 'match', 'no match')
方法选型建议
- 方法一:代码简洁,适配任意数量的区间,兼顾效率与灵活性,适合绝大多数场景
- 方法二:纯向量化操作,大数据量下性能最优,适合百万级以上数据处理
- 方法三:仅适用于每行区间数量固定的情况,性能最优但灵活性差
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

