如何计算含NaN/0的Pandas Series三值间的pct_change?
问题描述
场景
我有一个包含3个值的Pandas Series,值可能为NaN、0或任意正数,需要尽可能计算该序列的百分比变化(pct_change),无法计算时输出0。
示例输入序列
[0,nan,50] [0,0,0] [0,0,50] [nan,nan,50] [nan,nan,0] [0,0,nan] [0,nan,0]
尝试过的方案
参考相关问题尝试了忽略NaN或位移的方法,但要么得到空值,要么结果不符合预期。理想情况是无法计算时输出0。
测试代码
import pandas as pd series_test = pd.Series([0, None, 50]) # 仅输出NaN或inf series_test.pct_change().where(series_test.notna()) # 输出空结果 series_test.pct_change(fill_method=None)[series_test.shift(2).notnull()].dropna()
预期输出
[0,nan,50] - 0 (未定义场景) [0,0,0] - 0 (未定义场景) [0,0,50] - 0 (未定义场景) [nan,nan,50] - 0 (未定义场景) [nan,nan,0] - 0 (未定义场景) [0,0,nan] - 0 (未定义场景) [0,nan,0] - 0 (未定义场景) [1,nan,5] - 400% [0,1,5] - 400% [1,2,nan] - 100% [1,1.3,1.8] - 80%
解决方案
根据预期输出反推的核心规则:仅当序列中存在至少两个非NaN的正数时,取第一个正数和最后一个正数计算百分比变化;其他所有无法计算的场景返回0。
实现代码如下:
import pandas as pd import numpy as np def calculate_pct_change(series): # 提取所有非NaN的有效数值 valid_vals = series.dropna().values # 有效数值不足2个,返回0 if len(valid_vals) < 2: return 0 # 筛选出所有大于0的数值 positive_vals = valid_vals[valid_vals > 0] # 正数不足2个,返回0 if len(positive_vals) < 2: return 0 # 取第一个和最后一个正数 first_pos = positive_vals[0] last_pos = positive_vals[-1] # 计算百分比变化并转成百分比格式 pct = ((last_pos - first_pos) / first_pos) * 100 # 格式化输出,整数百分比显示为整数,否则保留一位小数 return f"{int(pct)}%" if pct.is_integer() else f"{pct:.1f}%" # 测试所有示例输入 test_cases = [ ([0, np.nan, 50], "0"), ([0, 0, 0], "0"), ([0, 0, 50], "0"), ([np.nan, np.nan, 50], "0"), ([np.nan, np.nan, 0], "0"), ([0, 0, np.nan], "0"), ([0, np.nan, 0], "0"), ([1, np.nan, 5], "400%"), ([0, 1, 5], "400%"), ([1, 2, np.nan], "100%"), ([1, 1.3, 1.8], "80%") ] for input_list, expected in test_cases: series = pd.Series(input_list) result = calculate_pct_change(series) print(f"{input_list} - {result} {'(符合预期)' if str(result) == expected else '(不符合预期)'}")
代码解释
- 提取有效数值:过滤序列中的NaN,得到非空数值列表;
- 基础校验:有效数值不足2个时直接返回0;
- 筛选正数:从有效数值中筛选大于0的数,避免0作为除数导致计算错误;
- 正数校验:正数数量不足2个时返回0;
- 计算与格式化:用第一个和最后一个正数计算百分比变化,转成易读的百分比格式,整数百分比显示为整数,非整数保留一位小数。
内容的提问来源于stack exchange,提问作者DGMS89
相关产品推荐
相关产品推荐

