pandas 包含间隔的列中的查找值

pandas lookup value in column containing Intervals

我正在尝试根据连续值落入的范围将一列连续浮点值映射到一些离散(分桶)值

例如

df_lookup = pd.DataFrame(data=[[0.0, 0.3, 10.1],
                               [0.3, 0.65, 30.3],
                               [0.65, 1.0, 50.5]], 
                        columns=['start', 'end', 'mapped_value'])
# create intervals
df_lookup['interval'] = df_lookup.apply(lambda x: 
                                             pd.Interval(x['start'], 
                                                         x['end'], 
                                                         closed='both' if x['end']==1.0 else 'left'), axis=1)

df_lookup

输出:

start end mapped_value interval
0 0.00 0.30 10.1 [0.0, 0.3)
1 0.30 0.65 30.3 [0.3, 0.65)
2 0.65 1.00 50.5 [0.65, 1.0]
df_data=pd.DataFrame(data=[['A', 0.3],
                           ['B', 0.65],
                           ['C', 0.6],
                           ['D', 0.75],
                           ['E', 0.4]], 
                     columns=['ID', 'original_value'])
df_data
ID original_value
0 A 0.30
1 B 0.65
2 C 0.60
3 D 0.75
4 E 0.40

此时我使用 pandas.DataFrame.apply 来获取我的查找值,但是

df_data['mapped_value'] = df_data.apply(
        lambda x: df_lookup.loc[x['original_value'] in df_lookup['interval']]['mapped_value'], 
            axis=1)

但这爆炸告诉我 KeyError: 'False: boolean label can not be used without a boolean index'

进一步调查表明,我遇到的问题是,当我执行 in 时,我只得到一个布尔值 return 而不是布尔值列表,例如,数据 ID= 'A' 其中 original value 是 0.3,我希望 x['original_value'] in df_lookup['interval'] 会 return [False, True, False] 但实际上它 returning False

我希望能就如何实现此“查找”映射提供一些指导。谢谢

如果间隔可能不相交,您可以使用 pandas.merge_asof 对上限进行可选检查:

df_data['mapped_value'] = (pd
 .merge_asof(df_data.sort_values(by='original_value'),
             df_lookup,
             left_on='original_value', right_on='start')
 # assign command below is only required if the intervals are disjoint
 .assign(mapped_value=lambda d: d['mapped_value']
                                .mask(d['end'].lt(d['original_value'])))
 ['mapped_value']
)

输出:

  ID  original_value  mapped_value
0  A            0.30          30.3
1  B            0.65          30.3
2  C            0.60          30.3
3  D            0.75          50.5
4  E            0.40          50.5

Series.map

的解决方案

无需创建中间 interval 列,您应该创建一个 IntervalIndex 可用于替换 df_data

中的值
i = pd.IntervalIndex.from_arrays(df_lookup.start, df_lookup.end, closed='left')
df_data['mapped_value'] = df_data['original_value'].map(df_lookup.set_index(i)['mapped_value'])

结果

print(df_data)

  ID  original_value  mapped_value
0  A            0.30          30.3
1  B            0.65          50.5
2  C            0.60          30.3
3  D            0.75          50.5
4  E            0.40          30.3