如何过滤干簧管事件数据中时间差小于阈值的行并保持1/0状态交替
干簧管事件数据过滤需求与优化方案
问题与背景
我正在处理电子设备通过干簧管采集的事件数据,设备会记录干簧管断开(记为0)和闭合(记为1)的时间,设备默认状态为常闭,有时会生成不符合实际时间尺度的虚假测量数据。
需求目标
过滤掉时间差小于60秒的状态变化记录,同时兼容连续多组虚假开闭事件的场景,最终输出的DataFrame必须保证事件按1、0、1、0……交替排列,符合设备只能先断开、再闭合、再断开的运行逻辑。
已尝试方案
我已经写出可运行的代码,但逻辑冗长繁琐,希望能找到更简洁高效的实现方式,欢迎给出相关建议。
示例原始DataFrame
构造数据的代码:
import numpy as np import pandas as pd df = pd.DataFrame({'date' : ['2019-05-10 12:20:31', '2019-05-10 12:25:43', '2019-05-10 12:25:57', '2019-05-10 12:28:00', '2019-05-10 12:30:01', '2019-05-10 12:35:00', '2019-05-10 12:35:30', '2019-05-10 12:35:45', '2019-05-10 12:37:00', '2019-05-10 12:40:00', '2019-05-10 12:45:00'], 'event' : [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]}, index=range(11)) df['date'] = pd.to_datetime(df['date'], errors='raise', dayfirst=True) df['event'] = pd.to_numeric(df['event'])
原始数据输出:
df Out[21]: date event 0 2019-05-10 12:20:31 1 1 2019-05-10 12:25:43 0 2 2019-05-10 12:25:57 1 3 2019-05-10 12:28:00 0 4 2019-05-10 12:30:01 1 5 2019-05-10 12:35:00 0 6 2019-05-10 12:35:30 1 7 2019-05-10 12:35:45 0 8 2019-05-10 12:37:00 1 9 2019-05-10 12:40:00 0 10 2019-05-10 12:45:00 1
已实现的函数代码
def filter_event_filter_dups(input_data, input_variable_name): """ 主函数filter_event的内部函数,用于迭代移除重复状态 """ df_tmp = input_data.copy() df_tmp = df_tmp.dropna() df_tmp = df_tmp.reset_index() df_tmp_final = pd.DataFrame(columns=['date', input_variable_name], index=range(len(df_tmp))) for index, row in df_tmp.iterrows(): if index == 0: print('') df_tmp_final['date'].loc[index] = df_tmp['date'].loc[index] df_tmp_final[input_variable_name].loc[index] = df_tmp[input_variable_name].loc[index] else: if index + 1 == len(df_tmp): print('---------------------------------------------------------------') print('Index = ', index) print('End!') else: print('---------------------------------------------------------------') print('Index = ', index) if df_tmp[input_variable_name].loc[index] == df_tmp[input_variable_name].loc[index-1]: # 仅保留第一条,删除第二条 df_tmp_final['date'].loc[index-1] = df_tmp['date'].loc[index-1] df_tmp_final[input_variable_name].loc[index-1] = df_tmp[input_variable_name].loc[index-1] elif df_tmp[input_variable_name].loc[index] != df_tmp[input_variable_name].loc[index-1]: df_tmp_final['date'].loc[index] = df_tmp['date'].loc[index] df_tmp_final[input_variable_name].loc[index] = df_tmp[input_variable_name].loc[index] return(df_tmp_final) def filter_event(input_data, input_variable_name, input_delta_time_threshold_seconds): """ 用于过滤时间差过小的事件的主函数 """ df_tmp = input_data.reset_index() df_tmp[input_variable_name] = pd.to_numeric(df_tmp[input_variable_name]) del df_tmp['index'] output_df = pd.DataFrame(columns=['date', input_variable_name], index=range(len(df_tmp))) for index, row in df_tmp.iterrows(): if index+1 == len(df_tmp): print('---------------------------------------------------------------') print('Index = ', index) print('End!') else: print('---------------------------------------------------------------') print('Index = ', index) if ((df_tmp['date'].iloc[index+1] - df_tmp['date'].iloc[index]).total_seconds())<input_delta_time_threshold_seconds: # 小于阈值的情况 print('This is LESS than threshold - Threshold = ', input_delta_time_threshold_seconds, 'seconds') print('Time difference to next 0 = ', df_tmp['date'].loc[index+1]-df_tmp['date'].loc[index]) print('Ignore') if ((df_tmp['date'].iloc[index+1] - df_tmp['date'].iloc[index]).total_seconds())>input_delta_time_threshold_seconds: # 大于阈值的情况 print('This is MORE than threshold - Threshold = ', input_delta_time_threshold_seconds, 'seconds') print('Time difference to next 0 = ', df_tmp['date'].loc[index+1]-df_tmp['date'].loc[index]) output_df['date'].loc[index] = df_tmp['date'].loc[index] output_df[input_variable_name].loc[index] = df_tmp[input_variable_name].loc[index] output_df = output_df.dropna() output_df = output_df.reset_index() output_df[input_variable_name] = pd.to_numeric(output_df[input_variable_name]) output_df_final = filter_event_filter_dups(input_data = output_df, input_variable_name = input_variable_name) output_df_final = output_df_final.dropna() output_df_final[input_variable_name] = pd.to_numeric(output_df_final[input_variable_name]) output_df_final['date'] = output_df_final['date'].apply(pd.to_datetime) return(output_df_final) test = filter_event(input_data = df , input_variable_name = 'event' , input_delta_time_threshold_seconds = 60 )
预期最终输出的DataFrame
final_df Out[20]: date event 0 2019-05-10 12:20:31 1 1 2019-05-10 12:28:00 0 2 2019-05-10 12:30:01 1 3 2019-05-10 12:40:00 0 4 2019-05-10 12:45:00 1
简洁高效实现方案
直接用pandas内置方法替代原循环逻辑,代码量缩减70%以上,性能提升明显,输出完全符合要求:
def filter_events(df, event_col='event', time_col='date', threshold=60): # 先保证数据按时间正序排列 df = df.sort_values(time_col).reset_index(drop=True) # 第一步:过滤和上一个有效事件间隔小于阈值的虚假事件 keep_idx = [0] last_valid_time = df.iloc[0][time_col] for i in range(1, len(df)): if (df.iloc[i][time_col] - last_valid_time).total_seconds() >= threshold: keep_idx.append(i) last_valid_time = df.iloc[i][time_col] df_filtered = df.iloc[keep_idx].copy() # 第二步:移除连续重复的事件,保证1、0交替排列 df_filtered = df_filtered[df_filtered[event_col].diff().fillna(1) != 0].reset_index(drop=True) return df_filtered[[time_col, event_col]] # 测试调用 final_df = filter_events(df, threshold=60)
内容的提问来源于stack exchange,提问作者Cairan Van Rooyen
相关产品推荐
相关产品推荐

