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

Pandas不规则时间序列分组后过滤DataFrame的技术问询

解决时序DataFrame分组后过滤指定时段及空分组的问题

看起来你在处理时序数据分组时遇到了两个头疼的问题:一是pd.Grouper生成了一堆不需要的空分组,二是尝试用apply过滤14点后时段时触发了KeyError: True。我来一步步帮你拆解解决。

先明确你的场景

先把你的示例数据用可运行的代码还原出来,方便后续验证:

import pandas as pd

data = {
    'timestamp': ['2018-11-12 14:03:53', '2018-11-12 14:03:58', 
                  '2018-11-12 14:04:09', '2018-11-12 14:04:19', 
                  '2018-11-12 14:04:29'],
    'A': [9.45, 73.8, 4.25, 62.39, 15.98],
    'B': [501.0, 108.0, 215.0, 551.0, 113.0]
}
df = pd.DataFrame(data)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)

先解答你的附带问题:为什么之前的apply写法会报错?

你尝试的代码:

df.groupby(pd.Grouper(freq='30Min')).apply(lambda x: x[x.index.min().hour >= 14])

触发KeyError: True的核心原因是**groupby.apply()对返回值的规则要求**:

  • 它要求每个分组的返回值要么是标量,要么是和输入分组同结构的DataFrame/Series,或者是能被组合成统一结果的对象。
  • 你的lambda逻辑里,当分组时段符合条件时返回整个分组DataFrame,不符合时返回x[False]——也就是空DataFrame。pandas在拼接这些结果时,空DataFrame的索引处理会混乱,最终抛出KeyError。简单说,这种写法不符合apply的返回规范。

正确的解决方案:两种高效过滤方式

方式1:先过滤原始数据,再分组(推荐!)

既然你只关注每日14点之后的数据,最高效的做法是先把原始数据中14点前的行过滤掉,再分组。这样分组时自然不会生成14点前的分箱,从根源避免空分组:

# 第一步:过滤出14点及之后的行
filtered_df = df[df.index.hour >= 14]

# 第二步:正常分组聚合
result = filtered_df.groupby(pd.Grouper(freq='30Min')).apply(custom_agg)

这种方法不需要处理后续的空分组,性能最优,优先推荐使用。

方式2:分组后再过滤(适合必须保留部分空分组的场景)

如果因为业务需求必须先分组再过滤(比如要保留14点后某些空分组,但排除其他时段的),可以用groupby对象的filter方法(注意这和DataFrame的filter不是同一个东西):

def filter_valid_groups(x):
    # 两个条件:分组非空,且分组的起始时段在14点之后
    # 用floor('30Min')确保取的是分箱的起始时间(比如14:03的数据属于14:00的分箱)
    return not x.empty and x.index.floor('30Min').hour.min() >= 14

# 先分组过滤,再重新分组聚合
result = df.groupby(pd.Grouper(freq='30Min')).filter(filter_valid_groups)\
           .groupby(pd.Grouper(freq='30Min')).apply(custom_agg)

或者更简单的:先完成聚合,再过滤掉空行和不符合时段的行:

# 先完成所有分组的聚合
agg_result = df.groupby(pd.Grouper(freq='30Min')).apply(custom_agg)

# 过滤:保留14点后且非全NaN的行
final_result = agg_result[(agg_result.index.hour >=14) & (~agg_result.isna().all(axis=1))]

这里~agg_result.isna().all(axis=1)用来排除所有列都是NaN的空分组行,agg_result.index.hour >=14确保只保留目标时段。


示例验证

假设你的自定义聚合函数是计算A列均值和B列总和:

def custom_agg(x):
    return pd.Series({
        'A_mean': x['A'].mean(),
        'B_sum': x['B'].sum()
    })

用方式1处理后,结果只会包含2018-11-12 14:00:00这个30分钟分箱的聚合值,完全不会出现其他空分组。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:31:32