You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pandas如何基于另一DataFrame高效创建新列计算订阅期总会话时长

解决方案

Pandas 向量化实现(性能比逐行apply提升10~100倍)

全程使用pandas原生向量化运算,避免Python层的逐行循环,适配绝大多数业务场景:

  • 第一步:预处理时间字段,统一转换为datetime类型,提前计算会话结束时间
  • 第二步:按user_id关联订阅表和会话表,得到同用户下所有订阅和会话的匹配记录
  • 第三步:过滤出时间重叠的有效记录,计算对应有效时长
  • 第四步:按维度分组聚合得到总时长

代码示例

首先做数据预处理:

import pandas as pd

# 转换所有时间字段为datetime类型
df_subscriptions['created'] = pd.to_datetime(df_subscriptions['created'])
df_subscriptions['ended'] = pd.to_datetime(df_subscriptions['ended'])
df_sessions['session_start'] = pd.to_datetime(df_sessions['session_start'])

# 提前计算会话结束时间,单位根据实际业务调整,秒用's',分钟用'm',小时用'h'
df_sessions['session_end'] = df_sessions['session_start'] + pd.to_timedelta(df_sessions['session_duration'], unit='s')

如果业务要求统计订阅时间段内的实际有效会话时长(跨订阅结束时间的会话仅统计订阅期内的部分):

# 按用户关联两个表
merged = df_subscriptions.merge(df_sessions, on='user_id', how='left')

# 计算重叠时间段的起止
merged['overlap_start'] = merged[['created', 'session_start']].max(axis=1)
merged['overlap_end'] = merged[['ended', 'session_end']].min(axis=1)

# 过滤有效记录,计算有效时长
merged['valid_duration'] = (merged['overlap_end'] - merged['overlap_start']).dt.total_seconds()
merged = merged[merged['valid_duration'] > 0]

# 聚合得到结果,按user_id分组得到每个用户所有订阅期的总会话时长
# 如果需要按每个订阅周期单独统计,分组字段改为 ['user_id', 'created', 'ended'] 即可
result = merged.groupby('user_id', as_index=False)['valid_duration'].sum()

如果业务规则为只要会话开始时间落在订阅期内,就统计整个会话的全部时长,可以简化为:

merged = df_subscriptions.merge(df_sessions, on='user_id', how='left')
# 过滤会话开始时间在订阅有效期内的记录
merged = merged[merged['session_start'].between(merged['created'], merged['ended'])]
# 聚合得到总时长
result = merged.groupby('user_id', as_index=False)['session_duration'].sum()

超大数据量下的提速方案

如果数据量超过千万级、单用户的订阅/会话记录非常多,pandas merge产生的笛卡尔积会占用过高内存,可以选择以下方案进一步优化:

  • 使用pd.merge_asof实现范围匹配:提前将两个表按user_id和时间字段排序,用merge_asof按时间范围匹配,避免生成大量无效匹配行,内存占用和运算速度均有明显提升
  • 切换到Polars库:语法与pandas高度兼容,底层为Rust实现的向量化运算,相同逻辑速度比pandas快3~10倍,内存占用更低
  • 数据库方案:将数据导入duckdb/sqlite,用SQL范围join运算,对内存要求更低,示例逻辑如下:
SELECT 
    user_id,
    SUM(MAX(0, MIN(ended, session_end) - MAX(created, session_start))) AS total_valid_duration
FROM df_subscriptions s
LEFT JOIN df_sessions se 
    ON s.user_id = se.user_id
    AND se.session_start <= s.ended
    AND se.session_end >= s.created
GROUP BY user_id

内容的提问来源于stack exchange,提问作者Eldar Vagapov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 10:30:01