0

0

Pandas矢量化操作:实现带阈值重置的序列计数功能

心靈之曲

心靈之曲

发布时间:2025-09-24 10:06:40

|

574人浏览过

|

来源于php中文网

原创

Pandas矢量化操作:实现带阈值重置的序列计数功能

本文详细介绍了如何利用Pandas的矢量化操作,高效地对DataFrame中连续相同的数值序列进行计数,并实现当计数达到预设阈值时自动重置的功能。通过巧妙结合groupby、cumcount以及模运算,该方法能够避免低效的循环,显著提升数据处理性能,适用于股票信号、事件序列分析等场景。

问题背景与需求

在数据分析中,我们经常需要识别并统计数据序列中连续重复的模式。例如,在一个股票交易数据集中,我们可能需要计算连续上涨(信号为1)或连续下跌(信号为-1)的天数。更进一步,如果要求当连续计数达到某个特定阈值(比如5)时,计数器需要自动重置并重新开始计数,这就对传统的循环计数方法提出了挑战,尤其是在处理大规模数据时,循环操作效率低下。

考虑以下示例DataFrame,其中包含股票价格(price)和涨跌信号(sign):

import pandas as pd

data = {
    'price': [13, 12, 11, 12, 13, 14, 14, 14, 14, 14, 14],
    'sign': [1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1]
}
df = pd.DataFrame(data)
print("原始DataFrame:")
print(df)

期望的结果是在新列count中,对sign列的连续相同值进行计数,并在计数达到5时重置:

   price  sign  count
0     13     1      1
1     12     1      2
2     11    -1      1
3     12    -1      2
4     13     1      1
5     14     1      2
6     14     1      3
7     14     1      4
8     14     1      5
9     14     1      1  # 达到5后重置
10    14     1      2

Pandas 矢量化解决方案

为了高效地实现这一功能,我们可以利用Pandas的矢量化操作,特别是groupby、cumcount和模运算。核心思路是首先识别出sign列中所有连续相同的块,然后对每个块内部进行累积计数,最后通过模运算实现阈值重置。

1. 识别连续块

识别连续相同值的块是解决问题的关键第一步。我们可以通过比较当前值与其前一个值是否相等来判断连续性。当值发生变化时,就标志着一个新的连续块的开始。

  • df['sign'].shift(): 获取sign列的上一行值。
  • df['sign'].ne(df['sign'].shift()): 比较当前sign值是否不等于上一个sign值。这将生成一个布尔序列,True表示值发生了变化(即新块的开始),False表示值未变。
  • .cumsum(): 对布尔序列进行累积求和。由于True被视为1,False被视为0,cumsum()会在每次遇到True时加1,从而为每个连续块生成一个唯一的组标识符。
# 识别连续块
df['consecutive_group'] = df['sign'].ne(df['sign'].shift()).cumsum()
print("\n带有连续块标识的DataFrame:")
print(df)

输出如下:

   price  sign  consecutive_group
0     13     1                  1  # 第一个块 (sign=1)
1     12     1                  1
2     11    -1                  2  # 第二个块 (sign=-1)
3     12    -1                  2
4     13     1                  3  # 第三个块 (sign=1)
5     14     1                  3
6     14     1                  3
7     14     1                  3
8     14     1                  3
9     14     1                  3
10    14     1                  3

可以看到,consecutive_group列成功地为每个连续的sign值序列分配了一个唯一的整数ID。

2. 块内累积计数

有了连续块的标识后,我们就可以对每个块内部进行累积计数。Pandas的groupby()方法结合cumcount()可以非常方便地实现这一点。

  • df.groupby(df['consecutive_group']): 按照consecutive_group列进行分组。
  • .cumcount(): 对每个分组内部的行进行累积计数,从0开始。
# 对每个连续块进行累积计数(从0开始)
df['raw_count'] = df.groupby(df['consecutive_group']).cumcount()
print("\n带有原始累积计数的DataFrame:")
print(df)

输出如下:

   price  sign  consecutive_group  raw_count
0     13     1                  1          0
1     12     1                  1          1
2     11    -1                  2          0
3     12    -1                  2          1
4     13     1                  3          0
5     14     1                  3          1
6     14     1                  3          2
7     14     1                  3          3
8     14     1                  3          4
9     14     1                  3          5
10    14     1                  3          6

此时,raw_count列已经正确地显示了每个连续块内部从0开始的计数。

Background Eraser
Background Eraser

AI自动删除图片背景

下载

3. 应用重置阈值并调整为1开始计数

现在我们需要实现计数达到阈值(例如5)时重置,并且最终的计数是从1开始而不是从0开始。这可以通过模运算(%)和加1操作来实现。

  • raw_count % 5: 对raw_count进行模5运算。当raw_count达到0, 1, 2, 3, 4时,结果分别为0, 1, 2, 3, 4。当raw_count达到5时,结果变为0,实现了重置。
  • + 1: 由于我们希望计数从1开始,所以对模运算的结果加1。这样,0, 1, 2, 3, 4就变成了1, 2, 3, 4, 5。

将以上所有步骤整合到一行代码中:

# 完整的矢量化解决方案
threshold = 5
df['count'] = df.groupby(df['sign'].ne(df['sign'].shift()).cumsum()).cumcount() % threshold + 1

print("\n最终结果DataFrame:")
print(df[['price', 'sign', 'count']])

最终输出:

最终结果DataFrame:
   price  sign  count
0     13     1      1
1     12     1      2
2     11    -1      1
3     12    -1      2
4     13     1      1
5     14     1      2
6     14     1      3
7     14     1      4
8     14     1      5
9     14     1      1
10    14     1      2

可以看到,count列完美地实现了连续计数并在达到5时重置为1的功能。

详细步骤解析(中间列展示)

为了更清晰地理解整个过程,我们可以将中间步骤的列也添加到DataFrame中进行观察:

import pandas as pd

data = {
    'price': [13, 12, 11, 12, 13, 14, 14, 14, 14, 14, 14],
    'sign': [1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1]
}
df = pd.DataFrame(data)
threshold = 5

df_detailed = df.assign(
    # 步骤1: 识别连续块的起始点 (True表示变化)
    is_new_block=df['sign'].ne(df['sign'].shift()),
    # 步骤2: 为每个连续块生成唯一ID
    consecutive_group=df['sign'].ne(df['sign'].shift()).cumsum(),
    # 步骤3: 在每个块内进行0-based累积计数
    cum_counter_0based=df.groupby(df['sign'].ne(df['sign'].shift()).cumsum()).cumcount(),
    # 步骤4: 应用模运算实现重置
    cum_counter_mod_threshold=df.groupby(df['sign'].ne(df['sign'].shift()).cumsum()).cumcount() % threshold,
    # 步骤5: 最终的1-based计数
    count=df.groupby(df['sign'].ne(df['sign'].shift()).cumsum()).cumcount() % threshold + 1
)

print("\n详细步骤解析DataFrame:")
print(df_detailed)

输出:

详细步骤解析DataFrame:
    price  sign  is_new_block  consecutive_group  cum_counter_0based  cum_counter_mod_threshold  count
0      13     1          True                  1                   0                          0      1
1      12     1         False                  1                   1                          1      2
2      11    -1          True                  2                   0                          0      1
3      12    -1         False                  2                   1                          1      2
4      13     1          True                  3                   0                          0      1
5      14     1         False                  3                   1                          1      2
6      14     1         False                  3                   2                          2      3
7      14     1         False                  3                   3                          3      4
8      14     1         False                  3                   4                          4      5
9      14     1         False                  3                   5                          0      1
10     14     1         False                  3                   6                          1      2

通过观察is_new_block、consecutive_group、cum_counter_0based、cum_counter_mod_threshold和count列,可以清晰地看到每一步的逻辑如何协同工作,最终生成期望的计数结果。

注意事项与总结

  • 性能优势: 这种矢量化方法比使用Python循环(如for循环或apply结合自定义函数)在处理大型数据集时效率更高,因为Pandas底层是C语言实现,优化了这类操作。
  • 通用性: 这里的threshold值可以根据具体需求进行调整。例如,如果希望每3次重置,则将% 5改为% 3即可。
  • 适用场景: 这种技术不仅适用于股票信号分析,还可以应用于任何需要对连续事件或状态进行计数并在达到特定条件时重置的场景,例如日志分析、传感器数据处理等。
  • 初始值: df['sign'].shift()在第一行会产生NaN。ne()操作会自动处理NaN,将其与任何非NaN值比较都视为不相等,因此is_new_block在第一行通常是True,这符合我们对新序列开始的预期。

通过上述方法,我们能够利用Pandas强大的矢量化能力,简洁而高效地解决复杂的序列计数与重置问题,极大地提升了数据处理的效率和代码的可读性。

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

772

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

661

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

764

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

679

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1345

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

549

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

579

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

730

2023.08.11

菜鸟裹裹入口以及教程汇总
菜鸟裹裹入口以及教程汇总

本专题整合了菜鸟裹裹入口地址及教程分享,阅读专题下面的文章了解更多详细内容。

0

2026.01.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 13.4万人学习

Django 教程
Django 教程

共28课时 | 3.4万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号