You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

按组查找ini.1落在ini.2与fin.2区间内的对应行号

如何在分组DataFrame中匹配ini.1到ini.2/fin.2区间并获取对应行号?

嘿,我来帮你解决这个问题!看起来你需要在每个Grp分组里,把每个观测的ini.1值匹配到同组内ini.2和fin.2构成的有效区间里,然后拿到对应行的行号(就是你示例里的1-11那种序号对吧?)。我给你两种可行的方案,你可以根据自己的数据规模和特点选择:

首先先还原你的示例DataFrame(方便代码测试):

import pandas as pd
import numpy as np

data = {
    'Grp': ['A','A','A','A','A','A','B','B','B','B','B'],
    'Grp.ind': ['A.1','A.2','A.3','A.4','A.5','A.6','B.1','B.2','B.3','B.4','B.5'],
    'ini.1': [0,5,10,15,26,28,0,np.nan,15,20,31],
    'fin.1': [5,10,15,26,28,30,15,np.nan,20,31,50],
    'ini.2': [0,5,np.nan,np.nan,np.nan,25,0,10,20,25,30],
    'fin.2': [5,25,np.nan,np.nan,np.nan,30,10,20,25,30,50]
}
df = pd.DataFrame(data)
# 生成1-based的行号,和你示例里的行号对应
df['row_num'] = df.index + 1

方案1:分组自定义匹配(灵活通用)

这个方案适合所有情况,不管区间是否重叠、有序,都能精准匹配。核心思路是对每个分组单独处理,先筛选出该组内ini.2和fin.2都非空的有效区间,再逐个检查每个ini.1落在哪个区间里:

def match_interval_in_group(group):
    # 提取当前组的有效区间(排除ini.2或fin.2为NA的行)
    valid_intervals = group.dropna(subset=['ini.2', 'fin.2'])[['ini.2', 'fin.2', 'row_num']]
    
    # 定义单个ini.1值的匹配逻辑
    def get_matching_row(ini_val):
        if pd.isna(ini_val):
            return np.nan
        # 筛选满足 ini.2 <= ini_val <= fin.2 的行
        matched = valid_intervals[(valid_intervals['ini.2'] <= ini_val) & (ini_val <= valid_intervals['fin.2'])]
        # 如果有多个匹配,这里返回第一个;如果要返回所有,改成matched['row_num'].tolist()
        return matched['row_num'].iloc[0] if not matched.empty else np.nan
    
    # 给当前组添加匹配结果列
    group['matching_row'] = group['ini.1'].apply(get_matching_row)
    return group

# 应用到整个DataFrame,得到结果
result_df = df.groupby('Grp', group_keys=False).apply(match_interval_in_group)

运行后,result_df['matching_row']就是你要的结果:

  • 第3行(A.3)的ini.1=10,落在A组第2行的区间(5,25)里,所以matching_row=2
  • 第6行(A.6)的ini.1=28,落在自身的区间(25,30)里,所以matching_row=6
  • 第9行(B.3)的ini.1=15,落在B组第8行的区间(10,20)里,所以matching_row=8

方案2:用merge_asof高效匹配(适合有序区间)

如果你的分组内区间是不重叠且按ini.2升序排列的,用merge_asof会更高效,尤其是处理大数据量的时候:

# 第一步:提取有效区间并按分组和ini.2排序
interval_df = df.dropna(subset=['ini.2', 'fin.2']).sort_values(['Grp', 'ini.2'])
# 第二步:提取需要匹配的ini.1数据并排序
target_df = df.dropna(subset=['ini.1']).sort_values(['Grp', 'ini.1'])

# 用merge_asof匹配:找每个ini.1对应的最大的ini.2 <= ini.1的区间
merged = pd.merge_asof(
    target_df,
    interval_df[['Grp', 'ini.2', 'fin.2', 'row_num']],
    on='ini.2',
    by='Grp',
    direction='backward'
)

# 检查匹配到的区间是否满足ini.1 <= fin.2,不满足的设为NA
merged['matching_row'] = np.where(merged['ini.1'] <= merged['fin.2'], merged['row_num'], np.nan)

# 把匹配结果合并回原DataFrame
result_df = df.merge(merged[['Grp', 'Grp.ind', 'matching_row']], on=['Grp', 'Grp.ind'], how='left')

这个方法的优势是速度快,但依赖区间的有序性,如果你的区间有重叠或者乱序,还是方案1更稳妥。

注意事项

  • 如果一个ini.1匹配多个区间,方案1可以轻松改成返回所有匹配的行号(把return matched['row_num'].iloc[0]换成return matched['row_num'].tolist())
  • 示例里用的是1-based行号(和你给出的示例行号一致),如果需要原DataFrame的0-based索引,去掉df['row_num'] = df.index +1里的+1即可
  • 对于ini.1为NA的观测,两种方案都会返回NA,符合预期

内容的提问来源于stack exchange,提问作者Javmi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:56:50