需求:筛选间隔超20天且保留最小dif_to_forelast的有效日期列表
日期筛选需求与解决方案
需求背景
现有一个包含date和dif_to_forelast列的DataFrame,已通过带for循环的函数识别出dif_to_forelast≤-0.1的簇,并提取每个簇的首个日期存入first_date列表(列表长度为0-4,示例如下):
1: [] 2: [06/07/2017] 3: [23/05/2017, 24/06/2017] 4: [19/05/2018, 03/06/2018, 16/06/2018] 5: [18/05/2019, 23/06/2019, 01/07/2019, 06/07/2019]
核心筛选规则
对first_date列表需执行以下筛选逻辑:
- 列表中保留的日期需间隔超过20天;
- 若两个日期间隔不足20天,仅保留
dif_to_forelast值最小的日期; - 从第一对日期(
first_date[0]与first_date[1])开始逐对比较,后续比较需基于前一次决策的有效日期(即前一轮筛选后保留的最后一个日期)。
示例数据与预期结果
示例DataFrame片段
date dif_to_forelast 19/05/2018 -0.11 03/06/2018 -0.2 16/06/2018 -0.19 # End of cluster in list 4 18/05/2019 -0.15 23/06/2019 -0.14 01/07/2019 -0.11 06/07/2019 -0.13 # End of cluster in list 5
筛选逻辑示例
- 列表4:19/05/2018与03/06/2018间隔不足20天,保留
dif最小的03/06/2018;03/06/2018与16/06/2018间隔不足20天,仍保留03/06/2018,最终列表为[03/06/2018]。 - 列表5:18/05/2019与23/06/2019间隔超20天,均保留;23/06/2019与01/07/2019、06/07/2019间隔不足20天,保留
dif最小的23/06/2019,最终列表为[18/05/2019, 23/06/2019]。
现有代码问题
原尝试编写的代码逻辑混乱,无法优雅处理所有列表长度的情况,也无法正确实现连锁决策逻辑(代码片段如下):
# Handle false negatives too close to each other # Directly pass lists with one element or less if len(first_date) <= 1: results[year, field_id] = { 'num_cuts': len(first_date), 'cutting dates': first_date } # Begin analysis if list has two or more elements elif len(first_date) >= 2: # Initialise new list for valid elements valid_dates = [] # First two dates always need to be inspected first to populate valid_dates # So that valid_dates can be compared against following elements in first_date for lists of len >= 3 current_date = pd.to_datetime(first_date[1], dayfirst=True) prev_date = pd.to_datetime(first_date[0], dayfirst=True) days_difference = (current_date - prev_date).days # Branch for valid first pair of dates if days_difference > 20: # Append both dates if they have a valid distance valid_dates.append(prev_date) valid_dates.append(current_date) # Remove dates from the first_date list for further analysis in lists of len >= 3 first_date.pop(prev_date) first_date.pop(current_date) # End the analysis for lists of 2 if len(first_date) == 2: results[year, field_id] = { 'num_cuts': len(valid_dates), 'cutting dates': valid_dates } # Continue the analysis for lists of len >= 3 # Use the last date of valid_dates, compare with first remaining date in first_date else: for i in range(1, len(first_date)): # Branch for first pair of dates too close to each other else: prev_dif_to_forelast = dataframe[dataframe['date'] == prev_date]['dif_to_forelast'].iloc[0] current_dif_to_forelast = dataframe[dataframe['date'] == current_date]['dif_to_forelast'].iloc[0] # if - else conditions for if one or the other date are the valid ones # Analysed dates are removed from first_date in all cases for further analysis in lists of len >= 3 if current_dif_to_forelast < prev_dif_to_forelast: valid_dates.append(current_date) first_date.pop(prev_date) first_date.pop(current_date) else: valid_dates.append(prev_date) first_date.pop(prev_date) first_date.pop(current_date) # End the analysis for lists of 2 with valid spacing if len(first_date) == 2: results[year, field_id] = { 'num_cuts': len(valid_dates), 'cutting dates': valid_dates } # Continue the analysis for lists of len >= 3 # Use the last date of valid_dates, compare with first remaining date in first_date else:
解决方案代码
以下是实现上述逻辑的优雅代码,通过维护有效日期列表,逐一遍历处理每个日期,确保连锁决策逻辑正确:
import pandas as pd def filter_valid_dates(first_date_list, dataframe): # 处理空列表或单元素列表,直接返回 if len(first_date_list) <= 1: return { 'num_cuts': len(first_date_list), 'cutting dates': first_date_list.copy() } # 将first_date转换为带datetime和dif的DataFrame,方便后续操作 date_df = pd.DataFrame({ 'date_str': first_date_list, 'date': pd.to_datetime(first_date_list, dayfirst=True) }) # 关联原DataFrame的dif_to_forelast值 date_df = date_df.merge(dataframe[['date', 'dif_to_forelast']], left_on='date', right_on='date', how='left') valid_dates = [] # 初始化第一个候选日期 current_candidate = date_df.iloc[0] for idx in range(1, len(date_df)): next_date = date_df.iloc[idx] # 计算间隔天数 days_diff = (next_date['date'] - current_candidate['date']).days if days_diff > 20: # 间隔超过20天,保留当前候选,添加下一个日期为新候选 valid_dates.append(current_candidate['date_str']) current_candidate = next_date else: # 间隔不足20天,保留dif更小的那个作为候选 if next_date['dif_to_forelast'] < current_candidate['dif_to_forelast']: current_candidate = next_date # 循环结束后,添加最后一个候选日期 valid_dates.append(current_candidate['date_str']) return { 'num_cuts': len(valid_dates), 'cutting dates': valid_dates } # 使用示例 # 假设dataframe是你的原始数据,first_date是提取的日期列表 # results[(year, field_id)] = filter_valid_dates(first_date, dataframe)
代码说明
- 先处理边界情况(空列表或单元素列表),直接返回结果;
- 将
first_date列表转换为带datetime格式和对应dif_to_forelast值的DataFrame,避免多次查询原DataFrame; - 维护一个
current_candidate变量保存当前待保留的候选日期,遍历后续每个日期:- 若与候选日期间隔超20天,将候选日期加入有效列表,更新候选为当前日期;
- 若间隔不足20天,比较两者的
dif_to_forelast,保留值更小的作为新候选;
- 遍历结束后,将最后一个候选日期加入有效列表,返回结果字典。
这样的实现逻辑清晰,能处理0-4个元素的所有情况,且严格遵循逐对比较、连锁决策的规则。
内容的提问来源于stack exchange,提问作者Barbara Perez de Araújo
相关产品推荐
相关产品推荐

