基于小时级起止时间的游戏时长占比统计Python需求
游戏会话时段时长统计实现思路
我跟踪了一整年的游戏会话数据,想借此学习Python并提取关注的统计数据。需要统计一天中0-23点每个时段的总游玩时长、日均游玩时长,还要计算每个小时的游玩时长占比(不是只标记是否游玩)。网上大多是按天/月计数事件的方案,没找到涉及时长占比计算的,求实现思路参考。
示例输入表
| session_id | game_id | start_datetime | end_datetime |
|---|---|---|---|
| 001 | 74 | 2023-02-22 13:15:00 | 2023-02-22 15:30:00 |
| 002 | 127 | 2023-02-23 13:30:00 | 2023-02-23 13:45:00 |
| 003 | 74 | 2023-02-24 14:40:00 | 2023-02-24 15:00:00 |
预期输出表
| hour_of_day | sum_hours_played | avg_hours_played_per_day | calculation |
|---|---|---|---|
| 13 | 1.00 | 0.33 | (0.75 + 0.25) / 3 days |
| 14 | 1.33 | 0.44 | (1.00 + 0.33) / 3 days |
| 15 | 0.50 | 0.17 | (0.50) / 3 days |
核心实现思路
1. 数据预处理
- 将
start_datetime和end_datetime转换为Python可识别的datetime对象,方便后续时间计算。用Pandas可直接调用pd.to_datetime(),原生Python用datetime.strptime()。 - 无需提前计算会话总时长,重点是处理跨小时的分段拆分。
2. 拆分跨小时会话(关键步骤)
单个游戏会话可能覆盖多个小时(比如示例中session001从13:15到15:30,覆盖13、14、15三个小时),必须把每个会话拆分成对应小时的分段时长:
- 对每个会话,先确定它覆盖的所有小时区间(从起始时间的小时到结束时间的小时)。
- 针对每个覆盖的小时:
- 起始小时:计算从会话开始时间到该小时结束的时长(比如13:15到14:00,时长为0.75小时)。
- 结束小时:计算从该小时开始到会话结束时间的时长(比如15:00到15:30,时长为0.5小时)。
- 中间小时:时长直接按1小时计算。
3. 按小时聚合统计
- 将拆分后的所有小时段数据,按
hour_of_day分组,求和得到sum_hours_played。 - 计算跟踪周期的总天数(从第一天到最后一天的天数,包含首尾)。
- 每个小时的
avg_hours_played_per_day= 该小时总时长 / 总天数。 - 时长占比:用每个小时的
sum_hours_played除以所有小时的总游玩时长之和,即可得到该时段的占比。
4. Python代码实现示例(基于Pandas)
import pandas as pd from datetime import datetime, timedelta # 加载示例数据(实际使用时替换为你的数据源) data = { 'session_id': ['001', '002', '003'], 'game_id': [74, 127, 74], 'start_datetime': ['2023-02-22 13:15:00', '2023-02-23 13:30:00', '2023-02-24 14:40:00'], 'end_datetime': ['2023-02-22 15:30:00', '2023-02-23 13:45:00', '2023-02-24 15:00:00'] } df = pd.DataFrame(data) # 转换时间列为datetime类型 df['start_datetime'] = pd.to_datetime(df['start_datetime']) df['end_datetime'] = pd.to_datetime(df['end_datetime']) # 定义函数:将单个会话拆分为各小时的时长片段 def split_to_hour_segments(row): start = row['start_datetime'] end = row['end_datetime'] segments = [] current_hour_start = start.replace(minute=0, second=0) while current_hour_start < end: current_hour_end = current_hour_start + timedelta(hours=1) # 计算当前片段的实际起止时间 segment_start = max(start, current_hour_start) segment_end = min(end, current_hour_end) # 转换为小时数 duration_hours = (segment_end - segment_start).total_seconds() / 3600 segments.append({ 'hour_of_day': current_hour_start.hour, 'duration': duration_hours, 'date': start.date() }) current_hour_start = current_hour_end return segments # 应用函数并展开所有小时片段 hour_segments = df.apply(split_to_hour_segments, axis=1).explode() hour_segments = pd.DataFrame(hour_segments.tolist()) # 计算跟踪周期的总天数 total_days = (hour_segments['date'].max() - hour_segments['date'].min()).days + 1 # 按小时聚合统计 result = hour_segments.groupby('hour_of_day').agg( sum_hours_played=('duration', 'sum'), daily_durations=('duration', list) # 保留每日时长用于生成计算说明 ).reset_index() # 计算日均时长 result['avg_hours_played_per_day'] = result['sum_hours_played'] / total_days # 生成计算说明文本 result['calculation'] = result.apply( lambda x: f"({'+'.join([f'{d:.2f}' for d in x['daily_durations']])}) / {total_days} days", axis=1 ) # 格式化数值保留两位小数 result['sum_hours_played'] = result['sum_hours_played'].round(2) result['avg_hours_played_per_day'] = result['avg_hours_played_per_day'].round(2) # 输出结果 print(result[['hour_of_day', 'sum_hours_played', 'avg_hours_played_per_day', 'calculation']])
内容的提问来源于stack exchange,提问作者Don_Andrej
相关产品推荐
相关产品推荐

