
在数据分析和处理中,我们经常需要对数据中的连续模式进行识别和计数。例如,在股票交易数据中,我们可能需要统计连续上涨或下跌的天数。更进一步,如果希望这个计数在达到某个特定阈值后自动重置,传统的循环遍历方法可能会效率低下,尤其是在处理大型数据集时。
本教程的目标是展示如何使用Pandas的矢量化操作,高效地解决以下问题:给定一个DataFrame,其中包含一个表示信号(例如1代表上涨,-1代表下跌)的列,我们需要创建一个新的列来统计连续相同信号的序列长度。此外,这个计数必须在达到预设的阈值(本例中为5)时自动重置。
假设我们有以下初始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)
print("原始DataFrame:")
print(df) 期望得到的输出结果如下,其中count列表示连续序列的计数,并在达到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 10 14 1 2Pandas矢量化解决方案
解决此问题的关键在于巧妙地结合Pandas的几个核心功能:shift()、ne()、cumsum()、groupby()和cumcount(),并辅以模运算符(%)来实现计数重置。
核心概念分解-
识别连续块:df['sign'].ne(df['sign'].shift()).cumsum()
- df['sign'].shift(): 将sign列向下平移一位。这样,每一行的值都会与其前一行的值对齐。
- df['sign'].ne(df['sign'].shift()): ne (not equal) 操作会比较当前行sign的值与前一行sign的值。如果它们不相等,结果为True;如果相等,结果为False。这会生成一个布尔序列,标记出连续序列变化的边界。
- .cumsum(): 对布尔序列进行累积求和。True被视为1,False被视为0。这样,每当sign值发生变化时,累积和就会增加1。这个累积和的值可以作为一个唯一的组标识符,将所有连续相同的sign值分到同一个组中。
例如,对于sign列 [1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1]:
- shift(): [NaN, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1]
- ne(shift()): [True, False, True, False, True, False, False, False, False, False, False]
- cumsum(): [1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3] (NaN在cumsum中被忽略或视为0) 这个cumsum()的结果就是我们用来进行groupby的分组键。
-
组内累积计数:groupby(...).cumcount()
Teleporthq
一体化AI网站生成器,能够快速设计和部署静态网站
182
查看详情
- df.groupby(...): 使用上一步生成的组标识符对DataFrame进行分组。
- .cumcount(): 对每个分组内的元素进行累积计数。cumcount()从0开始计数,即组内的第一个元素计数为0,第二个为1,依此类推。
-
实现计数重置:% 5 + 1
- % 5: 模运算符用于实现计数重置。cumcount()生成的序列是0, 1, 2, 3, 4, 5, 6... 当对5取模时,序列会变为0, 1, 2, 3, 4, 0, 1... 这正是我们需要的重置逻辑。
- + 1: 由于我们希望计数从1开始而不是从0开始,所以将结果加1。
将上述概念组合起来,即可得到简洁高效的矢量化解决方案:
df['count'] = df.groupby(df['sign'].ne(df['sign'].shift()).cumsum()).cumcount() % 5 + 1
print("\n处理后的DataFrame:")
print(df) 输出结果:
处理后的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列完美地实现了对连续sign序列的计数,并在达到5时自动重置。
详细步骤解析为了更好地理解每一步的作用,我们可以将中间结果作为新列添加到DataFrame中进行观察:
df_detailed = df.assign(
consecutive_group=df['sign'].ne(df['sign'].shift()).cumsum(),
raw_cumcount=df.groupby(df['sign'].ne(df['sign'].shift()).cumsum()).cumcount(),
final_count=df.groupby(df['sign'].ne(df['sign'].shift()).cumsum()).cumcount() % 5 + 1
)
print("\n详细步骤解析的DataFrame:")
print(df_detailed) 输出结果:
详细步骤解析的DataFrame:
price sign consecutive_group raw_cumcount final_count
0 13 1 1 0 1
1 12 1 1 1 2
2 11 -1 2 0 1
3 12 -1 2 1 2
4 13 1 3 0 1
5 14 1 3 1 2
6 14 1 3 2 3
7 14 1 3 3 4
8 14 1 3 4 5
9 14 1 3 5 1
10 14 1 3 6 2 - consecutive_group: 这一列显示了如何将连续相同的sign值分组。例如,前两行sign都是1,所以它们的consecutive_group都是1。第三行sign变为-1,consecutive_group变为2,表示新的连续块。
- raw_cumcount: 这一列是每个consecutive_group内部的原始累积计数,从0开始。
- final_count: 这一列是raw_cumcount经过% 5 + 1处理后的最终结果,它实现了计数从1开始并在5时重置的逻辑。
- 性能优势: Pandas的矢量化操作(如本例所示)通常比使用for循环或apply配合自定义Python函数要快得多,尤其是在处理大型数据集时。
- 灵活性: 调整计数重置的阈值非常简单,只需修改模运算符后的数字即可(例如,% 10 + 1 会在计数达到10时重置)。
- 初始值处理: df.shift() 会在第一行引入NaN。ne() 操作会正确处理NaN,将其视为与任何实际值不相等,从而确保第一个连续块能被正确识别。cumsum()也会正确处理这些NaN。
- 适用性: 这种模式不仅适用于数字信号,也适用于任何需要按连续相同值进行分组和计数的场景,例如文本分类、状态变化分析等。
本教程详细展示了如何利用Pandas强大的矢量化能力,通过组合shift()、ne()、cumsum()、groupby()和cumcount()方法,高效地实现对DataFrame中连续相同值序列的计数,并引入了灵活的计数重置机制。掌握这种模式对于进行高效的数据清洗、特征工程和模式识别至关重要。通过理解每一步操作的原理,您可以将此方法应用于更复杂的序列分析任务中。
以上就是Pandas矢量化操作:实现连续序列计数与阈值重置的详细内容,更多请关注知识资源分享宝库其它相关文章!
相关标签: python app ai 数据清洗 python函数 Python pandas 运算符 count for 标识符 循环 数据分析 大家都在看: Python单元测试:正确Mock类方法中条件分支的内部函数调用 Python zip对象行为解析:迭代器的一次性遍历特性与多重使用策略 Python高效处理超大XML文件:使用ElementTree流式解析 python如何处理命令行选项和参数_python命令行参数处理模块argparse详解 Python处理超大型XML文件:使用ElementTree进行高效流式解析






发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。