Pandas计算时序数据谷底及前后峰值:解决pre_peak空值问题
解决Pandas时序数据限定谷底范围后前峰值(pre_peak)空值问题
首先,我得先理清你遇到的核心问题:你把nadir(谷底)的计算范围限定在第6列之后(也就是Q22001及以后),但计算pre_peak(谷底前的最大值)时出现了空值,这大概率是因为你混淆了谷底限定区域的局部索引和全局列索引,导致pre_peak的计算范围被错误缩小了。
先重现你的场景,用代码构造示例数据:
import pandas as pd import numpy as np # 构造示例DataFrame data = [ [4085280.0, 4114911.0, 4108089.0, 4111713.0, 4055699.0, 4076430.0, 4043219.0, 4039370.0, 4201158.0, 4243119.0, 4231823.0, 4254681.0], [21226.0, 21566.0, 21804.0, 22072.0, 21924.0, 23232.0, 22748.0, 22258.0, 22614.0, 22204.0, 22500.0, 22660.0], [96400.0, 102000.0, 98604.0, 97086.0, 96354.0, 103054.0, 97824.0, 95958.0, 115938.0, 123064.0, 120406.0, 120648.0], [23820.0, 24116.0, 24186.0, 23726.0, 23504.0, 23574.0, 23162.0, 23078.0, 22306.0, 22334.0, 22152.0, 22080.0], [7838.0, 7906.0, 7714.0, 7676.0, 7480.0, 7520.0, 7102.0, 6722.0, 8324.0, 8166.0, 8208.0, 8326.0] ] cols = ['Q12000', 'Q22000', 'Q32000', 'Q42000', 'Q12001', 'Q22001', 'Q32001', 'Q42001', 'Q12002', 'Q22002', 'Q32002', 'Q42002'] df = pd.DataFrame(data, columns=cols)
问题根源分析
你之前的代码可能犯了这个错误:在限定区域找到nadir的局部索引(比如限定区域内的第0位)后,直接用这个局部索引去切分整行数据,导致pre_peak的计算范围变成了row.iloc[:0]——也就是空数据,自然会返回空值。
正确的逻辑应该是:
- 在限定区域内找到nadir及其对应的全局季度列名
- pre_peak是该季度之前所有时序数据的最大值(而不是限定区域内nadir之前的部分)
修正后的代码实现
下面是完整的处理函数,完美解决空值问题:
def calculate_peaks_nadir(row): # 定义谷底计算的起始列索引(对应Q22001,1-based的第6列) nadir_start_col = 'Q22001' nadir_start_idx = row.index.get_loc(nadir_start_col) # 1. 计算nadir(限定在Q22001及以后) nadir_segment = row.iloc[nadir_start_idx:] nadir = nadir_segment.min() nadir_qtr = nadir_segment.idxmin() # 2. 计算pre_peak:nadir所在季度之前的所有数据的最大值 pre_nadir_segment = row.iloc[:row.index.get_loc(nadir_qtr)] if not pre_nadir_segment.empty: pre_peak = pre_nadir_segment.max() pre_peak_qtr = pre_nadir_segment.idxmax() else: # 理论上不会触发,因为nadir限定在Q22001及以后,前面必有数据 pre_peak = np.nan pre_peak_qtr = None # 3. 计算post_peak:nadir所在季度之后的所有数据的最大值 post_nadir_segment = row.iloc[row.index.get_loc(nadir_qtr)+1:] if not post_nadir_segment.empty: post_peak = post_nadir_segment.max() post_peak_qtr = post_nadir_segment.idxmax() else: post_peak = np.nan post_peak_qtr = None return pd.Series({ 'nadir': nadir, 'nadir_qtr': nadir_qtr, 'pre_peak': pre_peak, 'pre_peak_qtr': pre_peak_qtr, 'post_peak': post_peak, 'post_peak_qtr': post_peak_qtr }) # 应用到每行并合并结果 result_df = df.apply(calculate_peaks_nadir, axis=1) final_df = pd.concat([df, result_df], axis=1)
关键修正点
- 用
row.index.get_loc(nadir_qtr)获取nadir的全局列索引,确保pre_peak的计算范围是时序上真正在谷底之前的所有数据 - 直接用列名
Q22001来定义nadir的起始范围,比硬编码索引更直观,也避免列顺序变化导致的错误 - 增加了空值判断逻辑,虽然在你的场景下不会触发,但让代码更健壮
验证第二行数据
第二行的nadir限定在Q22001及以后,最小值是22204.0(对应Q22002),pre_peak会正确计算为Q22001的23232.0,不会出现空值。
内容的提问来源于stack exchange,提问作者CJ H
相关产品推荐
相关产品推荐

