
本文介绍一种高效算法,用于在时间序列数据中动态计算滚动均值,要求同一姓名仅保留最新记录(去重),避免重复计算,适用于面试高频题与实际数据分析场景。
本文介绍一种高效算法,用于在时间序列数据中动态计算滚动均值,要求同一姓名仅保留最新记录(去重),避免重复计算,适用于面试高频题与实际数据分析场景。
在数据分析与算法面试中,常遇到一类“带条件的滚动统计”问题:需按时间维度累积计算统计量(如均值),但不能简单叠加所有历史数据,而要对关键维度(如用户ID、姓名)做去重处理——仅保留每个实体在当前时间窗口内的最新观测值。本例即典型代表:给定按 time 排序的记录,对每个时间点 t,需计算所有 time ≤ t 的记录中,每个 name 仅取其最后一次出现的 val,再求这些值的算术平均。
该问题的关键挑战在于避免暴力重算:若对每个时间点都重新扫描全部历史数据并去重求均值,时间复杂度为 O(n²),在大数据量下不可接受。理想解法应具备增量更新能力——利用前一时刻结果,仅处理新增时间点引入的变化。
以下提供一个清晰、高效且易于理解的 Pandas 实现方案(兼顾可读性与性能):
import pandas as pd
def rolling_mean_unique_name(df, time_col='time', name_col='names', val_col='val', keep='last'):
"""
计算按时间滚动的去重姓名均值(每姓名仅取当前窗口内最新值)
Parameters:
-----------
df : pd.DataFrame
输入数据框,必须包含 time_col, name_col, val_col 列
time_col : str
时间列名(用于定义滚动窗口边界)
name_col : str
实体标识列名(如姓名、用户ID),需去重
val_col : str
数值列名(用于计算均值)
keep : str, {'first', 'last'}
去重时保留策略,默认保留最新('last')
Returns:
--------
dict : {time_point: mean_value},按时间升序排列的滚动均值字典
"""
# 确保按时间排序(必要前提)
df = df.sort_values(time_col).reset_index(drop=True)
# 初始化存储结构
means = {}
# 维护一个动态字典:name -> 最新 val(随时间窗口扩展而更新)
latest_vals = {}
# 按时间点顺序遍历(天然有序滚动)
for t in df[time_col].unique():
# 获取截至时间 t 的所有记录
window_df = df[df[time_col] <= t]
# 更新 latest_vals:对 window_df 中每个 name,用其最后出现的 val 覆盖
# 注意:因 window_df 已按 time 排序,groupby(...).last() 即取最新
latest_update = window_df.groupby(name_col)[val_col].last()
latest_vals.update(latest_update.to_dict())
# 当前有效值列表(所有已知 name 的最新 val)
current_values = list(latest_vals.values())
# 计算均值(空情况防错)
means[t] = sum(current_values) / len(current_values) if current_values else 0.0
return means
# 示例数据
data = pd.DataFrame({
'time': [1, 1, 1, 2, 2, 2],
'names': ["Andy", "Bob", "Karen", "Andy", "Matt", "Sim"],
'val': [1, 2, 3, 5, 6, 8]
})
result = rolling_mean_unique_name(data)
print(result)
# 输出: {1: 2.0, 2: 4.8}✅ 算法优势说明:
- 时间复杂度优化:单次遍历时间点 + 分组聚合,整体接近 O(n),远优于嵌套循环的 O(n²);
- 空间友好:仅维护 latest_vals 字典(大小为不重复姓名数),不缓存中间 DataFrame;
- 逻辑清晰:latest_vals 显式表达“每个姓名当前最新值”的状态,符合人类直觉;
- 可扩展性强:只需修改 keep 参数即可切换去重策略(如首次出现 first),或轻松适配中位数、加权和等其他统计量。
⚠️ 注意事项:
- 输入数据必须按 time 严格升序排列,否则 groupby(...).last() 无法保证取到真正“最新”值;建议始终调用 sort_values(time_col) 预处理;
- 若存在同一 time 内多个同名记录,groupby(...).last() 会取该时间点内行序最后者——因此确保同 time 下的多条同名记录已按业务逻辑(如事件戳)二次排序;
- 对于超大规模流式数据,可进一步将 latest_vals 替换为 LRU Cache 或结合数据库物化视图实现近实时更新。
总结而言,解决此类“条件滚动统计”问题的核心思维是:将滚动过程建模为状态机——定义清晰的状态(如 latest_vals)、明确状态转移规则(新增时间点时如何更新)、并基于状态直接导出结果。这不仅适用于均值,也广泛适用于滚动去重计数、最新值提取、变化率追踪等工业级分析任务。










