3  函数定义 算术平均收益率计算

3.1 引言函数在金融编程中的重要性

函数是程序设计中的基本抽象单元,它将一系列操作封装起来,使代码更加模块化、可重用和易维护。在金融计算中,函数尤其重要,因为许多金融指标和模型(如收益率、波动率、VaR等)需要反复计算。

3.2 本章学习目标

通过本章学习,你将能够:

  1. 使用 deflambda 两种语法定义函数,并说明文档字符串、参数与返回值的作用
  2. 编写计算算术平均收益率的函数,并用 round() 控制输出精度
  3. 区分算术平均与几何平均的公式、适用场景与数值关系(\(\bar{r}_A \geq \bar{r}_G\))
  4. 为函数添加参数验证(空列表、非数值输入)与默认参数,提高金融计算函数的健壮性
  5. 将 lambda 与 map()filter()reduce() 组合,批量处理多只股票的收益率数据

先修:章节 1(第1章 常用基本数据类型)、章节 2(第2章 列表、字典和元组)

3.3 算术平均收益率

算术平均(Arithmetic Mean)是最常用的集中趋势度量,在金融中广泛用于计算平均收益率。

3.3.1 数学定义与性质

对于收益率序列 \(r_1, r_2, ..., r_n\), 算术平均定义为:

\[ \bar{r} = \frac{1}{n} \sum_{i=1}^{n} r_i = \frac{r_1 + r_2 + \cdots + r_n}{n} \]

数学性质:

  1. 线性性: \(\overline{aX + bY} = a\bar{X} + b\bar{Y}\)
  2. 最小二乘性: 算术平均使误差平方和最小
  3. 敏感性: 对极端值敏感,受异常值影响大

金融含义: 算术平均收益率适用于:

  • 单期期望收益率的估计
  • 投资组合绩效评估
  • 风险调整后收益率的基准

易混淆概念辨析:算术平均 vs. 几何平均

特性 算术平均 几何平均
公式 \(\bar{r}_A = \frac{1}{n}\sum r_i\) \(\bar{r}_G = \left[\prod(1+r_i)\right]^{1/n} - 1\)
应用场景 单期期望收益 多期复利增长
数值关系 \(\bar{r}_A \geq \bar{r}_G\) 波动率越大,差距越大
金融含义 期初财富的期望增长率 期末财富的实际增长率

关键理解: 如果你想知道”平均来说每期赚多少”,用算术平均;如果你想计算”长期复利增长”,用几何平均。

3.3.2 Python函数定义基础

理论背景:函数式编程思想

在动手写第一段函数代码之前,先明确函数这种机制解决了什么问题。从编程范式来看,函数体现了几种核心思想:

  1. 抽象(Abstraction): 隐藏实现细节,只暴露接口
  2. 分解(Decomposition): 将复杂问题分解为简单子问题
  3. 复用(Reuse): 一次编写,多次调用

Python支持多种编程范式,包括函数式编程。理解函数的数学定义(输入到输出的映射)对编写高质量金融代码至关重要。下面我们正式进入Python的函数定义语法:

Python提供多种定义函数的方式:

列表 3.1: Python函数定义的多种方式
# ==================== 方式1: 标准def定义 ====================
# def关键字用于定义函数,是Python中最常用的函数定义方式
# arithmetic_mean是函数名,遵循标识符命名规则(字母、数字、下划线,不能以数字开头)
# numbers是参数名,括号内是函数的参数列表
def arithmetic_mean(numbers):  # 定义函数名为arithmetic_mean,接收一个参数numbers
    """计算算术平均数"""  # 文档字符串(Docstring),用三引号包围,描述函数功能
    return sum(numbers) / len(numbers)  # return返回计算结果:sum求和,len求长度,/计算除法

# ==================== 方式2: lambda表达式(匿名函数) ====================
# lambda用于定义匿名函数,适合简单的单行函数
# 语法格式: lambda parameters: expression
# x是参数,sum(x) / len(x)是返回表达式
arithmetic_mean_lambda = lambda x: sum(x) / len(x)  # 定义lambda函数并赋值给变量

# ==================== 测试两种定义 ====================
# 创建测试数据列表,包含5个数值
data = [1, 2, 3, 4, 5]  # 方括号创建列表,元素用逗号分隔
print(arithmetic_mean(data))  # 调用def定义的函数,print输出结果: 3.0
print(arithmetic_mean_lambda(data))  # 调用lambda函数,输出: 3.0

代码深度解析:

  1. def语句的解剖:

    def function_name(parameters):
        """文档字符串(Docstring)"""
        function_body
        return value
    • def: 函数定义关键字
    • function_name: 函数名,遵循标识符命名规则
    • parameters: 参数列表,多个参数用逗号分隔
    • return: 返回值,无return则返回None
  2. lambda表达式的限制:

    • 只能包含单个表达式
    • 不能包含语句(如if, for, while)
    • 自动返回表达式的值
    • 适合简单函数,提高代码简洁性
  3. Python中的函数是一等对象:

    • 可以赋值给变量
    • 可以作为参数传递
    • 可以作为返回值
    • 可以存储在数据结构中

3.3.3 任务实现计算股票的算术平均收益率

任务要求:用 lambda 表达式定义算术平均函数 A_mean=lambda x:sum(x)/len(x),对上证指数收益率列表 sh000001(5 个年度收益率)计算平均值,并用 round(...,2) 保留两位小数后输出。请将代码原样输入教学平台(注释除外),判定以平台为准。

列表 3.2: 平台原始代码
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
A_mean=lambda x:sum(x)/len(x)  # 定义匿名函数A_mean
sh000001=[26.468,-10.7081,2.8477,43.5348,13.4337]  # 定义列表sh000001
print(round(A_mean(sh000001),2))  # 输出四舍五入后的计算结果

预期输出(本机 Python 实际运行结果,具体以平台运行结果为准):15.12(即 75.5761/5 四舍五入到 2 位小数)。

代码逐步解析:

  1. lambda函数的拆解:

    lambda x: sum(x) / len(x)
    #      ^      ^^^^^^^^^^^^
    #      |           |
    #    参数      返回表达式
    • 参数x期望是一个可迭代对象(如列表)
    • sum(x)计算列表元素总和
    • len(x)获取列表长度
    • /执行浮点除法(Python 3特性)
  2. 数据处理流程:

    [26.468, -10.7081, 2.8477, 43.5348, 13.4337]
          ↓
    sum = 75.5761
    len = 5
          ↓
    mean = 75.5761 / 5 = 15.11522
          ↓
    round(15.11522, 2) = 15.12
  3. round函数的行为:

    • round(number, ndigits): 四舍五入到指定小数位
    • ndigits=2: 保留2位小数
    • 注意: Python 3采用”银行家舍入”(Round Half to Even)

金融实例分析:

上证指数在样本期内的平均年收益率为15.12%,这意味着:

  • 期望收益: 基于历史数据,投资者可期望每年获得约15.12%的回报
  • 风险评估: 需结合波动率(标准差)评估风险调整后收益
  • 基准比较: 可与无风险利率、其他市场指数进行比较

3.3.4 函数的进阶应用

在实际金融分析中,我们通常需要定义更复杂的函数:

列表 3.3: 带参数验证和文档的收益率计算函数
# ==================== 定义完整函数 ====================
# def关键字定义标准函数
# calculate_mean_return是函数名,采用snake_case命名规范
# returns是必选参数:收益率列表
# decimals=2是可选参数:保留小数位数,默认值为2
def calculate_mean_return(returns, decimals=2):  # 函数定义,包含两个参数
    """
    计算算术平均收益率

    参数:
        returns (list): 收益率序列
        decimals (int): 保留小数位数,默认为2

    返回:
        float: 算术平均收益率

    异常:
        ValueError: 如果输入为空列表或包含非数值
    """
    # ==================== 参数验证 ====================
    # if语句进行条件判断
    # not returns检查列表是否为空
    # raise用于抛出异常,中断程序执行
    if not returns:  # 如果returns列表为空
        raise ValueError("收益率列表不能为空")  # 抛出ValueError异常并提示

    # all()函数检查所有元素是否满足条件
    # isinstance(r, (int, float))检查r是否为整数或浮点数
    # for r in returns遍历returns列表中的每个元素
    # 这行代码确保所有收益率都是数值类型
    if not all(isinstance(r, (int, float)) for r in returns):  # 如果有非数值元素
        raise ValueError("收益率必须为数值类型")  # 抛出异常提示数据类型错误

    # ==================== 计算平均值 ====================
    # sum(returns)计算收益率总和
    # len(returns)获取收益率个数
    # / 执行除法,得到算术平均值
    mean = sum(returns) / len(returns)  # 计算算术平均收益率

    # ==================== 格式化返回 ====================
    # round函数四舍五入到指定小数位
    # mean是待舍入的数值
    # decimals是保留的小数位数
    # return将结果返回给调用者
    return round(mean, decimals)  # 返回四舍五入后的平均值

# ==================== 使用示例 ====================
# try-except用于捕获和处理异常
# try块包含可能抛出异常的代码
try:  # 尝试执行以下代码
    # 调用calculate_mean_return函数
    # sh000001是收益率数据
    # decimals=3指定保留3位小数
    result = calculate_mean_return(sh000001, decimals=3)  # 调用函数并接收返回值
    # f-string格式化输出
    # {result:.3f}表示格式化为浮点数,保留3位小数
    print(f"上证指数平均收益率: {result:.3f}%")  # 输出格式化的结果
# except块处理特定类型的异常
except ValueError as e:  # 捕获ValueError异常并赋值给e
    # e是异常对象,包含错误信息
    print(f"计算错误: {e}")  # 输出错误信息

代码质量要素:

  1. 文档字符串(Docstring):
    • 描述函数功能
    • 说明参数类型和含义
    • 说明返回值
    • 列出可能抛出的异常
  2. 参数验证:
    • 检查输入有效性
    • 提供清晰的错误信息
    • 防止程序崩溃
  3. 默认参数:
    • 提供合理默认值
    • 增强函数灵活性
    • 提高使用便利性

3.3.5 lambda表达式的高级用法

lambda表达式在金融数据处理中非常实用,特别是在结合map, filter, reduce等函数时:

列表 3.4: lambda表达式在金融数据处理中的应用
# ==================== 导入模块 ====================
# from...import语句从模块中导入特定函数
# functools是Python标准库,提供高阶函数工具
# reduce函数用于累积计算
from functools import reduce  # 导入reduce函数

# ==================== 创建示例数据 ====================
# portfolio_returns是一个字典
# 键(Key): 股票名称(Stock A, Stock B, Stock C)
# 值(Value): 收益率列表,每个列表包含5个交易日的收益率
portfolio_returns = {  # 花括号创建字典
    'Stock A': [0.05, 0.03, -0.02, 0.04, 0.06],  # 股票A的收益率
    'Stock B': [0.08, 0.02, 0.01, -0.03, 0.05],  # 股票B的收益率
    'Stock C': [-0.01, 0.04, 0.07, 0.02, 0.03]   # 股票C的收益率
}

# ==================== 使用map应用函数 ====================
# map(function, iterable)将函数应用于可迭代对象的每个元素
# lambda returns: sum(returns) / len(returns)定义匿名函数计算平均收益率
# portfolio_returns.values()获取字典的所有值(收益率列表)
# map返回迭代器,需要用list()转换为列表
mean_calculator = lambda returns: sum(returns) / len(returns)  # 定义计算均值的lambda函数
mean_returns = list(map(mean_calculator, portfolio_returns.values()))  # 对每只股票计算平均收益

# f-string格式化输出
# mean_returns是一个列表,包含3只股票的平均收益率
print(f"各股票平均收益率: {mean_returns}")  # 输出所有股票的平均收益率

# ==================== 使用filter筛选 ====================
# filter(function, iterable)根据函数条件筛选元素
# lambda r: r > 0定义条件:收益率大于0
# 中括号创建列表,包含多个收益率数据
# list()将filter对象转换为列表
positive_returns = lambda r: r > 0  # 定义判断是否为正收益的lambda函数
filtered_stocks = list(filter(positive_returns, [0.05, -0.02, 0.03, -0.01]))  # 筛选正收益
print(f"正收益股票: {filtered_stocks}")  # 输出筛选结果

# ==================== 使用reduce累积计算 ====================
# reduce(function, sequence, initial)对序列进行累积计算
# lambda x, y: x * (1 + y)定义累积函数:计算复利
# returns是收益率列表
# 1是初始值(代表初始本金1)
# -1在最后减去1,得到净收益率
returns = [0.05, 0.03, -0.02, 0.04]  # 创建收益率列表
cumulative_return = reduce(lambda x, y: x * (1 + y), returns, 1) - 1  # 计算累积收益率
# f-string格式化,.4f表示保留4位小数
print(f"累积收益率: {cumulative_return:.4f}")  # 输出累积收益率

3.4 算术平均的局限性与改进

3.4.1 局限性分析

算术平均虽然简单直观,但在金融应用中存在重要局限:

  1. 极端值敏感: 一个极端值会显著影响平均值

    # 示例:极端值的影响
    normal_returns = [0.05, 0.03, 0.04, 0.06, 0.02]
    with_outlier = normal_returns + [0.50]  # 添加极端收益率
    
    print(f"正常平均: {sum(normal_returns)/len(normal_returns):.4f}")
    print(f"含极端值: {sum(with_outlier)/len(with_outlier):.4f}")
  2. 不适用于多期增长: 算术平均会高估长期复利增长

  3. 分布假设: 隐含假设收益率对称分布,但实际金融收益常呈偏态分布(Skewed Distribution)

3.4.2 改进方案稳健统计量

列表 3.5: 稳健统计量的计算与比较
# ==================== 导入必要的库 ====================
# numpy是Python科学计算基础库,提供高性能数组运算
# as np给numpy起别名,简化代码书写
import numpy as np  # 导入numpy库并简写为np

# ==================== 创建示例数据 ====================
# returns列表包含收益率数据,其中包含极端值0.50和-0.10
# 这种数据在金融中很常见,突发事件会造成极端收益
returns = [0.05, 0.03, 0.04, 0.50, 0.02, -0.10, 0.06]  # 包含极端值的收益率数据

# ==================== 1. 算术平均 ====================
# np.mean()计算算术平均值
# arithmetic_mean对极端值敏感,会被0.50拉高
arithmetic_mean = np.mean(returns)  # 计算算术平均值

# ==================== 2. 中位数 ====================
# np.median()计算中位数
# 中位数对极端值不敏感,是稳健的统计量
# 中位数是将数据排序后位于中间位置的值
median = np.median(returns)  # 计算中位数

# ==================== 3. 截尾平均 ====================
# scipy是科学计算库,stats模块提供统计函数
from scipy import stats  # 从scipy导入stats模块

# stats.trim_mean(data, proportion)计算截尾平均
# 0.1表示从两端各截去10%的数据
# 这样可以消除极端值的影响
trimmed_mean = stats.trim_mean(returns, 0.1)  # 去掉两端各10%后计算平均

# ==================== 4. 缩尾平均 ====================
# stats.mstats.winsorize将极端值替换为分位数值
# limits=[0.1, 0.1]表示将最低10%和最高10%的值替换
# 这比直接删除数据更保守
winsorized_mean = stats.mstats.winsorize(np.array(returns), limits=[0.1, 0.1]).mean()  # 转为数组后缩尾计算平均

# ==================== 输出比较结果 ====================
# f-string格式化输出,.4f保留4位小数
# 可以看到不同统计量的差异
print(f"算术平均: {arithmetic_mean:.4f}")  # 输出算术平均
print(f"中位数: {median:.4f}")  # 输出中位数
print(f"截尾平均: {trimmed_mean:.4f}")  # 输出截尾平均
print(f"缩尾平均: {winsorized_mean:.4f}")  # 输出缩尾平均

选择建议:

  • 正常分布数据: 使用算术平均
  • 含极端值: 使用中位数或截尾平均
  • 投资组合基准: 使用算术平均(与CAPM一致)
  • 长期绩效评估: 使用几何平均

实际应用指南:

在实际金融分析中,选择合适的集中趋势度量至关重要。对于简单一次性函数,使用lambda可以提高代码简洁性;对于复杂逻辑,应使用def定义并添加完整文档字符串;同时始终验证输入参数的有效性,确保函数的健壮性。理解算术平均与几何平均的区别——前者用于单期期望收益估计,后者用于多期复利增长计算——是正确应用这些统计量的基础。

下一步学习: 在下一章中,我们将学习Python的内置函数,这些函数提供了丰富的基础功能,可以大大提高编程效率。

3.5 本章小结

要点:

  • def 适合复杂逻辑(可写文档字符串与多条语句),lambda 限于单个表达式,二者在简单场景可互换
  • 算术平均 \(\bar{r}=\frac{1}{n}\sum r_i\) 用于估计单期期望收益;几何平均用于度量多期复利增长
  • 函数是一等对象:可以赋值给变量、作为参数传递、作为返回值
  • 参数验证加 raise 能对空列表、非数值输入给出明确错误,而不是让程序莫名崩溃
  • map()/filter()/reduce() 分别完成批量映射、条件筛选与累积(复利)计算

易错点:

  • 函数体内忘记 return 时,函数默认返回 None
  • 对空列表调用 sum(x)/len(x) 会抛 ZeroDivisionError
  • 用算术平均回答”长期平均每年赚多少”,会系统性高估多期复利增长
  • map()/filter() 返回迭代器,须转成 list() 才能查看内容;reduce() 需从 functools 导入

3.6 动手与思考

以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。

  1. 输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。

    f = lambda x: sum(x) / len(x)
    print(f([1, 2, 3]))
    print(list(map(lambda r: r * 100, [0.05, -0.02])))
    returns = [0.05, 0.03, -0.02, 0.06]
    print(round(sum(returns) / len(returns), 2))

    参考答案(先写下你的预测再点开)

    解题思路:逐行推演。第 1 行定义 lambda 函数 f,功能是求和除以个数。第 2 行 f([1, 2, 3])sum = 6len = 36 / 3 是浮点除法,得到 2.0(注意输出是 2.0 而不是 2)。第 3 行 map(lambda r: r * 100, [0.05, -0.02]) 把每个元素乘 100,map 返回迭代器,必须套 list() 才能看到内容,得到 [5.0, -2.0]。第 4—5 行:returns 的和为 0.05 + 0.03 - 0.02 + 0.06 = 0.12,除以 4 得 0.03round(0.03, 2) 仍为 0.03

    # 验证脚本:逐行输出三个表达式的求值结果
    f = lambda x: sum(x) / len(x)  # 定义求算术平均的匿名函数
    print(f([1, 2, 3]))  # 6除以3,浮点除法得2.0
    print(list(map(lambda r: r * 100, [0.05, -0.02])))  # 批量乘100后转列表
    returns = [0.05, 0.03, -0.02, 0.06]  # 定义收益率列表
    print(round(sum(returns) / len(returns), 2))  # 均值0.03,保留两位小数

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    2.0
    [5.0, -2.0]
    0.03

    回扣本章:对应本章小结“易错点”第 4 条——map() 返回迭代器,须转成 list() 才能查看内容。

  2. 概念辨析:某基金连续两年收益率分别为 +50% 与 -50%。其算术平均是多少?几何平均 \((1.5\times0.5)^{1/2}-1\) 约为多少?若要回答”这笔钱两年间平均每年实际增值多少”,应选哪一个统计量,为什么?

    参考答案(点开前请先独立完成)

    解题思路:先算两个统计量,再作选择。算术平均为 \((0.50-0.50)/2=0\),即 0%;几何平均为 \((1.5\times0.5)^{1/2}-1=\sqrt{0.75}-1\approx 0.8660-1=-0.1340\),约 -13.40%。回答“这笔钱两年间平均每年实际增值多少”应选几何平均。原因:1 元本金第一年末变为 1.5 元,第二年末变为 \(1.5\times0.5=0.75\) 元,两年实际亏损 25%,把它折算成等价的年化增速正是 \(\sqrt{0.75}-1\approx-13.4\%\)(几何平均复利还原出期末财富);而算术平均的 0% 意味着“两年后资金回到原点”,与事实不符。算术平均回答的是“单期期望收益率的估计”,几何平均回答的是“多期复利增长的实际速度”,这正是本章强调的 \(\bar{r}_A \geq \bar{r}_G\) 且波动越大差距越大的具体体现。

    # 验证脚本:分别计算算术平均与几何平均年收益率
    import math  # 导入数学模块,提供sqrt开方
    fund_returns = [0.50, -0.50]  # 连续两年的收益率
    arith_mean = sum(fund_returns) / len(fund_returns)  # 算术平均收益率
    geo_mean = math.sqrt(1.5 * 0.5) - 1  # 几何平均:期末财富开方后减1
    print(arith_mean)  # 输出算术平均(0.0)
    print(round(geo_mean, 4))  # 输出保留4位小数的几何平均

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    0.0
    -0.134

    回扣本章:对应本章小结“要点”第 2 条与“易错点”第 3 条——算术平均用于估计单期期望收益,几何平均度量多期复利增长,用算术平均回答“长期平均每年赚多少”会系统性高估。

  3. 变式任务(平台任务同型改造):用 lambda 定义 A_mean,计算列表 [3.5, -1.2, 4.8, 2.0, 0.9] 的算术平均,保留 2 位小数输出;再仿照本章 calculate_mean_return 把它改写为带参数验证的 def 函数(输入空列表时抛出 ValueError),并用 try-except 捕获调用该函数传入空列表时产生的异常。

    参考答案(点开前请先独立完成)

    解题思路:列表 5 个元素之和为 3.5-1.2+4.8+2.0+0.9=10.0,除以 5 得 2.0,保留两位小数输出 2.0def 版本在函数体开头用 if not returns: 检测空列表(空列表真值为假,not 后为真),命中即 raise ValueError(...) 主动抛出异常并给出明确提示;调用方用 try-except ValueError 捕获,程序不崩溃并打印错误信息。参数验证让“数据为空”这类错误在入口处就被拦下,而不是等到 len(returns) 为 0 时抛出难以定位的 ZeroDivisionError

    # 变式程序:lambda 与带参数验证的 def 两种实现
    A_mean = lambda x: sum(x) / len(x)  # 用lambda定义算术平均函数
    returns_list = [3.5, -1.2, 4.8, 2.0, 0.9]  # 待计算的收益率列表
    print(round(A_mean(returns_list), 2))  # 保留2位小数输出平均值
    
    def calculate_mean_return(returns):  # 改写为带参数验证的标准函数
        if not returns:  # 空列表真值为假,not后为真,即检测到空输入
            raise ValueError('收益率列表不能为空')  # 主动抛出带提示的异常
        return round(sum(returns) / len(returns), 2)  # 返回保留2位小数的平均值
    
    print(calculate_mean_return(returns_list))  # 正常调用,输出2.0
    try:  # 尝试传入空列表
        print(calculate_mean_return([]))  # 该调用会抛出ValueError
    except ValueError as error_msg:  # 捕获特定类型的异常
        print('捕获异常:', error_msg)  # 打印异常信息,程序不中断

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    2.0
    2.0
    捕获异常: 收益率列表不能为空

    注意:以上为本题变式的独立代码;列表 3.2 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。

    回扣本章:对应本章小结“要点”第 4 条与“易错点”第 2 条——参数验证加 raise 能对空列表给出明确错误,而不是让 sum(x)/len(x) 抛出 ZeroDivisionError

  4. 动手验证:取 normal_returns = [0.05, 0.03, 0.04, 0.06, 0.02],先计算其平均值;再构造加入极端值 0.50 后的列表,计算新平均值与两组数据的中位数,说明极端值对均值与中位数的影响有何不同。

    参考答案(点开前请先独立完成)

    解题思路:原 5 个数据之和为 0.20,均值为 0.04;加入极端值 0.50 后 6 个数据之和为 0.70,均值升到约 0.1167,一个极端值就把均值拉高了近两倍——因为均值对每个数值线性求和,极端值按其绝对大小计入。中位数则只看排序后的中间位置:原数据排序为 [0.02, 0.03, 0.04, 0.05, 0.06],中位数 0.04;加入 0.50 后排序为 [0.02, 0.03, 0.04, 0.05, 0.06, 0.50],偶数个取中间两值平均,中位数为 (0.04+0.05)/2=0.045,几乎没被拉动。结论:均值对极端值敏感、会被显著拉动,中位数只依赖次序、对极端值稳健,这正是本章“含极端值时应改用中位数或截尾平均”结论的依据。

    # 验证脚本:对比极端值加入前后均值与中位数的变化
    import statistics  # 导入统计模块,提供median中位数
    normal_returns = [0.05, 0.03, 0.04, 0.06, 0.02]  # 原始收益率数据
    with_outlier = normal_returns + [0.50]  # 拼接极端值构成新列表
    print(round(sum(normal_returns) / len(normal_returns), 4))  # 原数据均值
    print(round(sum(with_outlier) / len(with_outlier), 4))  # 含极端值的均值
    print(statistics.median(normal_returns))  # 原数据中位数
    print(statistics.median(with_outlier))  # 含极端值的中位数

    预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):

    0.04
    0.1167
    0.04
    0.045

    回扣本章:对应本章小结“要点”第 2 条与本章“算术平均的局限性”一节——算术平均对极端值敏感,含极端值时应改用中位数等稳健统计量。