共享单车项目空闲时间计算算法异常问题排查求助
共享单车空闲时间计算异常排查
我们正在开展一项高校项目,任务是优化共享单车的维修维护计划。单车仅能在停靠站点租借和归还,需计算空闲时间:单车x在站点y归还后,到该站点任意单车被租用的时间段。
我们的实现代码运行后出现部分异常结果(比如负空闲时间)。虽然已按start_time对DataFrame排序,但仍存在后续行交易的start_time早于之前交易的end_time的情况。原本需要在满足以下两个条件时更新空闲时间,但该逻辑未生效:
- 待计算空闲时间的交易的
end_station_id与当前循环交易的start_station_id一致; - 当前循环交易的
start_time晚于待计算交易的end_time。
原实现代码
import pandas as pd csv_file = '../Data_Cleanup/outCSV/Clean_Metro_Set.csv' metro = pd.read_csv(csv_file) metro['start_time'] = pd.to_datetime(metro['start_time']) metro ['end_time'] = pd.to_datetime(metro['end_time']) metro = metro.sort_values(by='start_time') metro['idle_time'] = None BigDict = { # station_id: { # bike_id: (transaction_id ,end_time) # } } for i, row in metro.iterrows(): current_start_time = row["start_time"] current_end_time = row["end_time"] current_end_station_id = row["end_station_id"] current_start_station_id = row["start_station_id"] current_bike_id = row["bike_id"] current_index = i if current_start_station_id in BigDict: for bike in list(BigDict[current_start_station_id]): # Create a copy of the keys idle_time = current_start_time - BigDict[current_start_station_id][bike][1] metro.at[BigDict[current_start_station_id][bike][0], "idle_time"] = idle_time if idle_time.total_seconds() >= 0: del BigDict[current_start_station_id][bike] if current_end_station_id not in BigDict: BigDict[current_end_station_id] = {current_bike_id: (current_index, current_end_time)} BigDict[current_end_station_id][current_bike_id] = (current_index, current_end_time) metro.to_csv('../Data_Cleanup/outCSV/Metro_Set_with_IdleTime.csv')
问题原因分析
- 时间判断逻辑顺序错误:原代码先计算所有该站点已归还单车的空闲时间并赋值,再判断时间是否合法。这就导致即使
current_start_time早于单车的end_time(即空闲时间为负),也会把错误值写入idle_time字段。 - 无效覆盖操作:最后两行关于
BigDict的更新逻辑冗余,虽不影响功能,但核心的时间校验时机错误,是异常值产生的直接原因。
修复后的代码
import pandas as pd csv_file = '../Data_Cleanup/outCSV/Clean_Metro_Set.csv' metro = pd.read_csv(csv_file) metro['start_time'] = pd.to_datetime(metro['start_time']) metro['end_time'] = pd.to_datetime(metro['end_time']) metro = metro.sort_values(by='start_time') metro['idle_time'] = None # 结构:station_id -> {bike_id: (transaction_index, end_time)} BigDict = {} for i, row in metro.iterrows(): current_start_time = row["start_time"] current_end_time = row["end_time"] current_end_station_id = row["end_station_id"] current_start_station_id = row["start_station_id"] current_bike_id = row["bike_id"] current_index = i # 处理当前租借站点的已归还单车 if current_start_station_id in BigDict: # 遍历站点内的所有已归还单车 for bike in list(BigDict[current_start_station_id].keys()): bike_end_time = BigDict[current_start_station_id][bike][1] # 先判断时间条件:当前租借时间晚于单车归还时间 if current_start_time > bike_end_time: idle_time = current_start_time - bike_end_time # 仅满足条件时才更新空闲时间 metro.at[BigDict[current_start_station_id][bike][0], "idle_time"] = idle_time # 移除已计算完成的单车记录 del BigDict[current_start_station_id][bike] # 更新当前单车的归还记录到字典 if current_end_station_id not in BigDict: BigDict[current_end_station_id] = {} BigDict[current_end_station_id][current_bike_id] = (current_index, current_end_time) metro.to_csv('../Data_Cleanup/outCSV/Metro_Set_with_IdleTime.csv')
关键修改点
- 调换时间判断与赋值顺序:先检查当前租借时间是否晚于单车归还时间,只有符合条件时才计算并写入空闲时间,彻底避免负数值的产生。
- 保留未满足条件的记录:对于当前租借时间早于归还时间的单车,继续保留在
BigDict中,等待后续时间更晚的租借交易来匹配计算。 - 简化字典更新逻辑:将站点字典的初始化与单车记录更新合并,代码更简洁易读。
内容的提问来源于stack exchange,提问作者Yusuf Shehadeh
相关产品推荐
相关产品推荐

