为Pandas DataFrame的value值匹配属性区间并标记对应列名
解决Pandas中根据区间匹配填充列的问题
这是个很典型的区间匹配场景,我来给你两种实现思路,从直观易懂到高效向量化都覆盖到:
方法1:逐行处理(直观易理解,适合小数据集)
如果你的数据量不大,用apply逐行处理最容易理清逻辑。我们先定义一个函数,专门处理每行的区间判断:
import pandas as pd import numpy as np # 生成你提供的测试数据 df2 = pd.DataFrame(np.random.randint(low=0, high=10, size=(5, 4)), columns=['value', 'a', 'b', 'c']) df2['low'] = '' # 定义每行的处理逻辑 def get_low_bound(row): # 把a/b/c的列名和值打包成字典,方便排序 abc_pairs = {'a': row['a'], 'b': row['b'], 'c': row['c']} # 按数值从小到大排序,得到[(列名, 值), ...]的有序列表 sorted_pairs = sorted(abc_pairs.items(), key=lambda x: x[1]) current_val = row['value'] # 遍历排序后的区间,找到对应的下界列名 for idx, (col_name, col_val) in enumerate(sorted_pairs): # 如果是最后一个值,且当前value大于它,直接返回这个列名 if idx == len(sorted_pairs)-1: if current_val > col_val: return col_name continue # 检查当前value是否落在当前列值和下一列值之间 next_val = sorted_pairs[idx+1][1] if col_val < current_val <= next_val: return col_name # 如果value比所有a/b/c都小,返回最小的那个列名(可根据需求调整) return sorted_pairs[0][0] # 把函数应用到每一行 df2['low'] = df2.apply(get_low_bound, axis=1)
拿你给的第一行例子验证:value=5,a=3、b=7、c=9,排序后是[(a,3), (b,7), (c,9)],5落在3和7之间,所以返回a,完全符合你的需求。
方法2:向量化处理(高效,适合大数据集)
如果你的DataFrame行数很多,逐行处理会变慢,这时候用向量化操作更高效:
# 提取a/b/c列,生成排序后的数值和对应列名的临时表 abc_df = df2[['a', 'b', 'c']] # 每行数值排序 sorted_values = abc_df.apply(sorted, axis=1, result_type='expand') # 每行对应列名按数值排序 sorted_columns = abc_df.apply(lambda x: sorted(x.index, key=lambda y: x[y]), axis=1, result_type='expand') # 定义四个区间的布尔掩码 mask_smallest = df2['value'] <= sorted_values[0] mask_mid_left = (df2['value'] > sorted_values[0]) & (df2['value'] <= sorted_values[1]) mask_mid_right = (df2['value'] > sorted_values[1]) & (df2['value'] <= sorted_values[2]) mask_largest = df2['value'] > sorted_values[2] # 批量赋值 df2.loc[mask_smallest, 'low'] = sorted_columns[0] df2.loc[mask_mid_left, 'low'] = sorted_columns[0] df2.loc[mask_mid_right, 'low'] = sorted_columns[1] df2.loc[mask_largest, 'low'] = sorted_columns[2]
这个方法通过批量判断和赋值,避免了逐行循环,速度能提升好几倍,适合十万行以上的数据集。
额外简化:如果a/b/c本身是递增的
如果你的数据保证每行都满足a < b < c,那可以直接用嵌套np.where跳过排序步骤:
df2['low'] = np.where(df2['value'] <= df2['a'], 'a', np.where((df2['value'] > df2['a']) & (df2['value'] <= df2['b']), 'a', np.where((df2['value'] > df2['b']) & (df2['value'] <= df2['c']), 'b', 'c')))
这个写法最简洁,但只适用于a/b/c已经有序的场景。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

