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

如何用更Pythonic的方式实现Pandas按年统计同符号连续值达标次数?

问题描述

我有一个由整数时间序列组成的pandas DataFrame,需要按年份对DataFrame分组,然后统计每年中同符号连续元素的绝对值之和≥5的次数。

示例数据与期望输出

>>> import pandas as pd
>>> l  = [1, -1, -4, 2, 2, 4, 5, 1, -3, -4]
>>> idx1 = pd.date_range('2019-01-01',periods=5)
>>> idx2 = pd.date_range('2020-01-01',periods=5)
>>> idx = idx1.union(idx2)
>>> df = pd.DataFrame(l, index=idx, columns=['a'])
>>> df
            a
2019-01-01  1
2019-01-02 -1
2019-01-03 -4  # 2019统计数=1:abs(-1)+abs(-4)≥5
2019-01-04  2
2019-01-05  2
2020-01-01  4
2020-01-02  5  # 2020统计数=1:abs(4)+abs(5)+abs(1)=10≥5
2020-01-03  1
2020-01-04 -3
2020-01-05 -4  # 2020统计数=2:abs(-3)+abs(-4)=7≥5

期望输出:

2019 1
2020 2

当前实现

我目前通过链式调用groupby和apply实现需求,编写了对应的分组函数和统计函数:

>>> import numpy as np
>>> def get_year(x):
        return x.year

>>> def count(group, t=5):
        c = 0 # 计数器
        s = 0 # 同符号连续元素的和                                                    
        for i in range(1,len(group)):
            if np.sign(group['a'].iloc[i-1]) == np.sign(group['a'].iloc[i]): 
                if s == 0:
                    s = group['a'].iloc[i-1] + group['a'].iloc[i]
                else:
                    s += group['a'].iloc[i]
                if i == (len(group) -1):
                   return c + 1
            elif (np.sign(group['a'].iloc[i-1]) != np.sign(group['a'].iloc[i])) and (abs(s) >= t):
                # 如果同符号连续序列中断且绝对值和≥t,计数器加1并重置s
                c += 1
                s = 0
            elif (np.sign(group['a'].iloc[i-1]) != np.sign(group['a'].iloc[i])) and (abs(s) < t):
                # 如果同符号连续序列中断且绝对值和<t,仅重置s
                s = 0
        return c

>>> by_year = df.groupby(get_year)
>>> by_year.apply(count)
2019 1
2020 2

我的问题是:是否存在更Pythonic的实现方式,能够得到相同结果且不依赖for循环?


更Pythonic的实现方案

可以利用pandas的矢量化操作和分组功能,完全避免显式for循环,代码更简洁高效:

步骤分解

  • 标记同符号连续组:通过比较当前元素与前一个元素的符号,生成分组标签,每个连续同符号的序列属于同一个组。
  • 计算每组的绝对值之和:按年份和同符号组分组,计算每组的绝对值总和。
  • 统计符合条件的组数量:筛选出绝对值和≥5的组,再按年份统计数量。

完整代码

import pandas as pd
import numpy as np

# 生成示例数据
l  = [1, -1, -4, 2, 2, 4, 5, 1, -3, -4]
idx1 = pd.date_range('2019-01-01',periods=5)
idx2 = pd.date_range('2020-01-01',periods=5)
idx = idx1.union(idx2)
df = pd.DataFrame(l, index=idx, columns=['a'])

# 1. 添加年份列和符号列
df['year'] = df.index.year
df['sign'] = np.sign(df['a'])

# 2. 标记同符号连续组:当符号变化时,组号+1
df['group'] = (df['sign'] != df['sign'].shift()).cumsum()

# 3. 按年份和同符号组分组,计算每组绝对值之和
grouped_sum = df.groupby(['year', 'group'])['a'].apply(lambda x: x.abs().sum())

# 4. 筛选出和≥5的组,再按年份统计数量
result = grouped_sum[grouped_sum >=5].groupby('year').count()

print(result)

输出结果

year
2019    1
2020    2
dtype: int64

代码解释

  • 标记同符号组:(df['sign'] != df['sign'].shift()).cumsum()会在符号变化时生成新的组号,确保每个连续同符号序列拥有唯一的组标识。
  • 分组求和:按年份和组号双重分组,计算每组的绝对值总和,准确获取每个连续同符号序列的总和。
  • 统计符合条件的组:筛选出总和≥5的组后,再按年份计数,得到每年的目标统计次数。

这种方法完全利用pandas的内置矢量化操作,避免了手动循环,代码更简洁易读,同时在大数据量下性能更优。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 12:01:20