Python实现无Pandas的按ID分组计算CSV办公时长
不用Pandas按ID分组计算2023年2月办公时长差值
核心思路
不需要嵌套多循环,用字典分组是更高效的方案:
- 以
(用户ID, 日期)作为字典键(比如(101, '2023-02-01')),确保同一人同一天的签到签退时间被归集到同一组 - 字典值存储该组的签到(
in)和签退(out)时间对象,方便后续计算差值
实现步骤
- 解析CSV并转换时间格式:将
time字段从字符串转为datetime对象,为时长计算做准备 - 归集签到签退数据:遍历已筛选的2月数据,填充分组字典
- 计算时长并输出结果:遍历分组字典,用签退时间减去签到时间得到当日时长,按需按ID汇总或按日期输出
完整代码示例
假设你的筛选后CSV样例如下:
id,type,time 101,in,2023-02-01 09:00:00 101,out,2023-02-01 18:30:00 102,in,2023-02-01 08:45:00 102,out,2023-02-01 17:15:00 101,in,2023-02-02 09:10:00 101,out,2023-02-02 18:00:00
对应的纯Python实现代码:
import csv from datetime import datetime # 初始化分组字典:键=(id, 日期), 值={'in': datetime对象, 'out': datetime对象} time_records = {} # 读取筛选后的2月CSV文件(替换为你的实际文件名) with open('filtered_feb_data.csv', 'r', encoding='utf-8') as f: reader = csv.DictReader(f) for row in reader: user_id = row['id'] record_type = row['type'] # 转换时间字符串为datetime对象 record_time = datetime.strptime(row['time'], '%Y-%m-%d %H:%M:%S') # 提取日期字符串作为分组键的一部分 date_str = record_time.strftime('%Y-%m-%d') # 初始化该用户当日的记录条目 key = (user_id, date_str) if key not in time_records: time_records[key] = {'in': None, 'out': None} # 填充签到/签退时间 if record_type == 'in': time_records[key]['in'] = record_time elif record_type == 'out': time_records[key]['out'] = record_time # 计算时长并整理结果 daily_duration = {} # 存储每个用户每日的办公时长 total_duration = {} # 存储每个用户2月总办公时长 for (user_id, date_str), times in time_records.items(): # 仅当签到和签退时间都存在时计算时长 if times['in'] and times['out']: # 计算当日时长(秒转小时,保留2位小数) duration_hours = (times['out'] - times['in']).total_seconds() / 3600 # 按用户ID和日期存储每日时长 daily_duration.setdefault(user_id, {})[date_str] = round(duration_hours, 2) # 累加用户总时长 total_duration[user_id] = total_duration.get(user_id, 0) + duration_hours # 输出每日时长结果 print("每日办公时长:") for user_id, dates in daily_duration.items(): print(f"用户ID {user_id}:") for date, hours in dates.items(): print(f" {date}: {hours}小时") # 输出2月总时长结果 print("\n2月总办公时长:") for user_id, total_hours in total_duration.items(): print(f"用户ID {user_id}: {round(total_hours, 2)}小时")
关键补充
- 若你还未完成2月数据筛选,可在读取行时添加判断:
# 仅处理2023年2月的数据 if record_time.year == 2023 and record_time.month == 2: # 执行后续分组逻辑 - 代码包含容错处理:跳过缺失签到或签退时间的记录,避免报错
- 时间复杂度为O(n),仅需两次线性遍历(一次读数据分组,一次计算时长),比嵌套循环更高效
内容的提问来源于stack exchange,提问作者mynameiszsofia
相关产品推荐
相关产品推荐

