58 绘制散点图
本章定位(复习与补充训练层):本章对应正课第20章《常用图形绘制》(章节 20,“相关性用散点图”的选型要点),并衔接第21章(章节 21)与第34章(章节 34)的相关性分析主线,并与第49章《数据框可视化》(章节 50)互为补充训练(本章为分项版),用于复习、补缺与额外练习。建议先不看讲解,直接尝试下方平台任务,再对照解析补弱项。本章不属于必修主线。先做本章『动手与思考』第 1 题与平台任务自测,通过即可跳过本章。
58.1 引言相关性的可视化
散点图(Scatter Plot)是展示两个变量关系最直观的工具,帮助我们识别相关方向与强度(正、负、无相关)、发现线性或非线性模式、检测远离主群体的异常点,并验证”风险—收益”这类假设关系。
58.2 本章学习目标
通过本章复习,你将能够:
- 写出Pearson相关系数公式,并把 \(\rho\approx1\)、\(\rho\approx-1\)、\(\rho\approx0\) 与散点图的视觉形态一一对应
- 复述平台任务的数据流:读入股指数据 →
pd.to_datetime(..., format='%Y%m%d')转日期 →set_index→df/df.shift(1)-1计算周涨跌幅 →dropna→corr()输出相关系数矩阵 → 两个共用坐标轴的子图分别绘制创业板指对上证综指、深证成指的散点 - 用
s、alpha、color、edgecolors控制散点的可读性,用气泡大小与颜色编码第三维变量,并用散点图矩阵一次查看多变量两两关系 - 用
np.polyfit拟合并叠加趋势线,解读斜率、截距与 \(R^2\),说明CAPM特征线中 \(\beta\) 的含义
58.3 散点图的数学基础
散点图展示两个变量 \((X, Y)\) 的关系:
\[ \{(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)\} \]
相关系数(Pearson):
\[ \rho_{XY} = \frac{\sum(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum(x_i - \bar{x})^2}\sqrt{\sum(y_i - \bar{y})^2}} \]
相关性的视觉特征:
- \(\rho \approx 1\): 点从左下到右上,直线上升
- \(\rho \approx -1\): 点从左上到右下,直线下降
- \(\rho \approx 0\): 点散乱,无明显方向
58.4 基础散点图
任务要求(归纳自块内任务注释与代码,同一代码块含两问):
- 任务一:从给定地址读取股指收盘价 Excel,把”日期”列转为 datetime 并设为索引,用
df/df.shift(1)-1计算涨跌幅(题面注释表述为周涨跌幅,数据为逐日频率),dropna后用print输出各指数涨跌幅的相关系数矩阵 - 任务二:在 1 行 2 列、共用坐标轴的子图中,分别绘制创业板指(横轴)对上证综指、创业板指对深证成指(纵轴)的散点(青色圆点),设置轴标签、标题与网格,保存为
1.png
请将代码原样输入教学平台(注释除外),判定以平台为准。
平台任务(平台原始代码)
以下代码与教学平台任务要求完全一致:
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#任务一
import pandas as pd
index_closeprice = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726722771060.xlsx") #导入数据
index_closeprice["日期"] = pd.to_datetime(index_closeprice["日期"] , format='%Y%m%d') # 转换为日期时间格式
index_closeprice.set_index("日期",inplace=True) # 将日期列设为index_closeprice数据框的索引
index_return = index_closeprice/index_closeprice.shift(1)-1 #计算股指的周涨跌幅
index_return = index_return.dropna() #删除缺失值
print(index_return.corr()) #计算指数周涨跌幅的相关系数
#任务二
import matplotlib.pyplot as plt
import pandas as pd # 导入Pandas数据分析库
plt.rcParams['font.sans-serif']=['SimHei'] # 用黑体显示中文
plt.rcParams['axes.unicode_minus']=False # 正常显示负号
index_closeprice = pd.read_excel("https://huoran.oss-cn-shenzhen.aliyuncs.com/1726722771060.xlsx") #导入数据
index_closeprice["日期"] = pd.to_datetime(index_closeprice["日期"] , format='%Y%m%d') # 转换为日期时间格式
index_closeprice.set_index("日期",inplace=True) # 将日期列设为index_closeprice数据框的索引
index_return = index_closeprice/index_closeprice.shift(1)-1 #计算股指的周涨跌幅
index_return = index_return.dropna() #删除缺失值
plt.figure(figsize=(11,6)) # 创建图形画布
plt.subplot(1,2,1) #第1行、第1列子图
plt.scatter(x=index_return.iloc[:,-1],y=index_return.iloc[:,0],c="c",marker="o") #创业板指与上证综指的散点图
plt.xlabel(u"创业板指",fontsize=13) # 设置X轴标签
plt.ylabel(u"上证综指",fontsize=13,rotation=90) # 设置Y轴标签
plt.xticks(fontsize=13) # 设置X轴刻度标签
plt.yticks(fontsize=13) # 设置Y轴刻度标签
plt.title(u"创业板指与上证综指的散点图",fontsize=15) # 设置图表标题
plt.grid(True) # 显示网格线
plt.subplot(1,2,2,sharex=plt.subplot(1,2,1),sharey=plt.subplot(1,2,1)) #与第一个子图共用X轴和Y轴
plt.scatter(x=index_return.iloc[:,-1],y=index_return.iloc[:,1],c="c",marker="o") # 绘制散点图
plt.xlabel(u"创业板指",fontsize=13) # 设置X轴标签
plt.ylabel(u"深证成指",fontsize=13,rotation=90) # 设置Y轴标签
plt.xticks(fontsize=13) # 设置X轴刻度标签
plt.yticks(fontsize=13) # 设置Y轴刻度标签
plt.title(u"创业板指与深证成指的散点图",fontsize=15) # 设置图表标题
plt.grid(True) # 显示网格线
plt.savefig("1.png") # 保存图形至文件预期输出(数据来自教学平台的 OSS 直链,OSS 直链本机实测;以平台运行结果为准):
任务一先输出相关系数矩阵(作者实测;注意数据文件自身的列名为”深圳成指”,与任务二注释中的”深证成指”为同一指数):
上证综指 深圳成指 创业板指
上证综指 1.000000 0.886014 0.774132
深圳成指 0.886014 1.000000 0.946785
创业板指 0.774132 0.946785 1.000000
三个指数两两均为强正相关,其中深证系内部(深证成指—创业板指,0.9468)高于各自与上证综指的相关(0.8860、0.7741)。任务二无控制台输出,生成文件 1.png——两个共用坐标轴的散点子图:左图创业板指对上证综指、右图创业板指对深证成指,右图点云更贴近对角线,与 0.9468>0.7741 的数值相互印证。
# =============================================================================
# 题目: 基础散点图——风险与收益的关系
# =============================================================================
# 本代码展示如何绘制基础散点图,可视化金融学中风险与收益的正相关关系
# 包含数据生成、散点绘制、趋势线拟合和相关系数计算
# ==================== 导入必要的库 ====================
import matplotlib.pyplot as plt # 绘图库
import pandas as pd # 数据分析库
import numpy as np # 数值计算库
# ==================== 设置中文字体 ====================
plt.rcParams['font.sans-serif'] = ['Source Han Serif SC', 'SimHei'] # 规范字体在前,SimHei 仅作后备 # 设置中文字体为黑体,解决中文显示问题
plt.rcParams['axes.unicode_minus'] = False # 解决负号'-'显示为方块的问题
# ==================== 创建风险-收益数据 ====================
np.random.seed(42) # 设置随机种子,确保结果可复现
n_stocks = 50 # 股票数量
# 模拟:风险越高,收益越高(正相关)
risk = np.random.uniform(0.1, 0.4, n_stocks) # 生成50个0.1到0.4之间的均匀分布随机数,代表标准差(风险)
return_mean = 0.03 + 0.5 * risk + np.random.normal(0, 0.02, n_stocks) # 期望收益 = 3%基础收益 + 0.5×风险 + 随机噪声
# ==================== 绘制散点图 ====================
plt.figure(figsize=(10, 6)) # 创建画布,宽度10英寸,高度6英寸,适合学术展示
plt.scatter(risk, return_mean, s=100, alpha=0.7, color='#2E86AB', edgecolors='white')
# s=100: 点的大小(面积),适合多数场景
# alpha=0.7: 透明度0.7,重叠点仍可见
# color='#2E86AB': 蓝色,专业的学术配色
# edgecolors='white': 点边缘为白色,使重叠点更清晰
# ==================== 添加拟合线 ====================
z = np.polyfit(risk, return_mean, 1) # 1阶多项式拟合(线性回归),返回[斜率,截距]
p = np.poly1d(z) # 将拟合系数转换为多项式函数,便于计算
plt.plot(risk, p(risk), 'r--', linewidth=2, label=f'拟合线: y={z[0]:.2f}x+{z[1]:.3f}')
# 'r--': 红色虚线
# linewidth=2: 线宽2像素,清晰可见
# label: 图例标签,显示回归方程
# ==================== 添加坐标轴标签和标题 ====================
plt.xlabel('风险(标准差)', fontsize=12) # X轴标签,字体大小12
plt.ylabel('期望收益率', fontsize=12) # Y轴标签,字体大小12
plt.title('风险与收益的关系', fontsize=16, fontweight='bold') # 标题,字体大小16,加粗
plt.legend(fontsize=11) # 显示图例,字体大小11
plt.grid(True, alpha=0.3) # 添加网格,透明度0.3,不喧宾夺主
plt.tight_layout() # 自动调整布局,防止标签被截断
plt.show() # 显示图形
# ==================== 计算相关系数 ====================
correlation = np.corrcoef(risk, return_mean)[0, 1] # 计算Pearson相关系数,[0,1]取非对角线元素
print(f'风险与收益的相关系数: {correlation:.4f}') # 打印相关系数,保留4位小数
print(f'解释: 正相关,风险越高收益越高') # 解释相关系数的含义scatter关键参数:
s: 点大小,s=100适合大多数场景alpha: 透明度,重叠点可见color: 点颜色edgecolors: 点边缘颜色,'white'使点更清晰
58.5 多组散点图对比
# =============================================================================
# 题目: 多组散点图——不同行业公司的对比
# =============================================================================
# 本代码展示如何在同一散点图中绘制多组数据,使用不同颜色和图例区分
# 应用于金融场景:对比不同行业的风险-收益特征
# ==================== 创建三个行业的数据 ====================
np.random.seed(42) # 设置随机种子,确保每次运行结果相同
industries = {
'银行': {'risk': np.random.uniform(0.1, 0.2, 15), # 银行业风险较低,10%-20%标准差
'return': np.random.normal(0.04, 0.01, 15)}, # 收益率4%,波动1%
'科技': {'risk': np.random.uniform(0.25, 0.45, 15), # 科技业风险较高,25%-45%标准差
'return': np.random.normal(0.08, 0.02, 15)}, # 收益率8%,波动2%
'公用事业': {'risk': np.random.uniform(0.08, 0.15, 15), # 公用事业风险最低,8%-15%标准差
'return': np.random.normal(0.03, 0.008, 15)} # 收益率3%,波动0.8%
}
# ==================== 定义各行业的颜色 ====================
colors = {'银行': '#E3120B', '科技': '#008080', '公用事业': '#F0A700'}
# 使用高对比度配色方案,便于区分
# ==================== 绘制多组散点图 ====================
plt.figure(figsize=(10, 6)) # 创建画布
for industry, data in industries.items(): # 遍历每个行业
plt.scatter(data['risk'], data['return'], # 绘制散点
s=120, alpha=0.7, color=colors[industry], # 点大小120,透明度0.7,对应颜色
edgecolors='white', label=industry, # 白色边缘,标签为行业名
linewidths=1.5) # 边缘线宽1.5
# ==================== 添加坐标轴标签和标题 ====================
plt.xlabel('风险(标准差)', fontsize=12) # X轴标签
plt.ylabel('收益率', fontsize=12) # Y轴标签
plt.title('不同行业风险-收益特征', fontsize=16, fontweight='bold') # 标题
plt.legend(fontsize=11, loc='upper left') # 图例,位置左上角
plt.grid(True, alpha=0.3) # 网格
plt.tight_layout() # 调整布局
plt.show() # 显示
# ==================== 计算各行业统计 ====================
print('各行业统计:') # 打印标题
for industry, data in industries.items(): # 遍历每个行业
avg_risk = data['risk'].mean() # 计算平均风险
avg_return = data['return'].mean() # 计算平均收益
sharpe = avg_return / avg_risk # 计算简化的夏普比率 = 收益/风险
print(f'{industry}: 风险={avg_risk:.3f}, 收益={avg_return:.3f}, 夏普比率={sharpe:.2f}')
# 打印各行业的统计指标,保留3位小数可视化策略:
- 颜色区分:每个类别用不同颜色
- 图例清晰:标注每个颜色的含义
- **点大小一致`:便于比较密度
- 边缘线:使重叠点更清晰
58.6 气泡图
# =============================================================================
# 题目: 气泡图——三维数据展示
# =============================================================================
# 本代码展示如何绘制气泡图,在散点图基础上用气泡大小表示第三维数据
# 应用于金融场景:同时展示风险、收益、市值三个维度
# ==================== 创建三维数据(风险、收益、市值) ====================
n = 30 # 公司数量
np.random.seed(42) # 设置随机种子,确保每次运行结果相同
data = pd.DataFrame({ # 创建DataFrame存储数据
'风险': np.random.uniform(0.1, 0.4, n), # 风险:10%-40%标准差
'收益': np.random.normal(0.05, 0.02, n), # 收益:均值5%,标准差2%
'市值': np.random.uniform(50, 500, n) # 市值:50-500亿元
})
# ==================== 绘制气泡图 ====================
plt.figure(figsize=(10, 6)) # 创建画布
scatter = plt.scatter(data['风险'], data['收益'], # 绘制散点
s=data['市值']/2, # 气泡大小 = 市值/2,避免过大
c=data['市值'], # 颜色也映射市值,双重编码
cmap='Blues', # 使用Blues色阶,浅蓝到深蓝
alpha=0.6, # 透明度0.6
edgecolors='white', # 白色边缘
linewidths=1.5) # 边缘线宽1.5
# ==================== 添加颜色条 ====================
cbar = plt.colorbar(scatter) # 添加颜色条,显示颜色与市值的映射关系
cbar.set_label('市值(亿元)', fontsize=11) # 设置颜色条标签
# ==================== 添加标签(标注市值最大的3个公司) ====================
top3 = data.nlargest(3, '市值') # 找出市值最大的3家公司
for idx, row in top3.iterrows(): # 遍历这3家公司
plt.annotate(f'市值{row["市值"]:.0f}亿', # 添加标注文本,显示市值
(row['风险'], row['收益']), # 标注位置
xytext=(5, 5), textcoords='offset points', # 文本偏移5个点
fontsize=9, bbox=dict(boxstyle='round,pad=0.3', # 圆角边框
facecolor='yellow', alpha=0.3)) # 黄色背景,透明度0.3
# ==================== 添加标题和标签 ====================
plt.xlabel('风险(标准差)', fontsize=12) # X轴标签
plt.ylabel('收益率', fontsize=12) # Y轴标签
plt.title('风险-收益-市值关系', fontsize=16, fontweight='bold') # 标题
plt.grid(True, alpha=0.3) # 网格
plt.tight_layout() # 调整布局
plt.show() # 显示
print('市值最大的3家公司:') # 打印标题
print(top3) # 打印市值最大的3家公司数据气泡图的设计要点:
- 第三维度:气泡大小表示第三个变量
- 大小比例:避免气泡过大遮挡,过小看不见
- 颜色映射:可用颜色强化第三维信息
- 标注:只标注关键点,避免拥挤
58.7 散点图矩阵
# =============================================================================
# 题目: 散点图矩阵——多变量关系探索
# =============================================================================
# 本代码展示如何绘制散点图矩阵,快速探索多个变量两两之间的关系
# 应用于金融场景:分析PE、PB、ROE等多个财务指标的相关性
# ==================== 创建多变量数据 ====================
from pandas.plotting import scatter_matrix # 导入散点图矩阵函数
np.random.seed(42) # 设置随机种子,确保每次运行结果相同
df_multi = pd.DataFrame({ # 创建包含多个财务指标的DataFrame
'PE': np.random.uniform(10, 60, 100), # 市盈率:10-60倍
'PB': np.random.uniform(0.5, 10, 100), # 市净率:0.5-10倍
'ROE': np.random.uniform(0.05, 0.35, 100), # 净资产收益率:5%-35%
'收益率': np.random.normal(0.05, 0.02, 100) # 收益率:均值5%,标准差2%
})
# ==================== 绘制散点图矩阵 ====================
fig = plt.figure(figsize=(12, 12)) # 创建12×12英寸的画布,适合显示4×4矩阵
axes = scatter_matrix(df_multi, alpha=0.6, diagonal='kde', # 绘制散点图矩阵
c='#2E86AB', edgecolors='white', # 蓝色点,白色边缘
figsize=(12, 12))
# alpha=0.6: 透明度
# diagonal='kde': 对角线显示核密度估计图,展示变量分布
# ==================== 调整布局 ====================
plt.suptitle('多变量散点图矩阵', fontsize=16, fontweight='bold', y=1.02) # 总标题
# y=1.02: 标题位置略高于1,避免重叠
plt.tight_layout() # 自动调整布局
plt.show() # 显示
# ==================== 计算相关系数矩阵 ====================
print('相关系数矩阵:') # 打印标题
print(df_multi.corr().round(3)) # 计算并打印相关系数矩阵,保留3位小数
# .corr()计算Pearson相关系数矩阵散点图矩阵的应用:
- 探索性数据分析(EDA):快速发现变量关系
- 特征选择:识别高度相关的特征
- 多元正态性检验:联合分布是否为多元正态
58.8 回归线与置信区间
# =============================================================================
# 题目: 散点图与回归线——包含置信区间
# =============================================================================
# 本代码展示如何绘制带回归线和置信区间的散点图
# 应用于金融场景:验证两个变量之间的线性关系,评估拟合优度
# ==================== 导入seaborn库 ====================
import seaborn as sns # seaborn是基于matplotlib的统计绘图库
# ==================== 创建数据 ====================
np.random.seed(42) # 设置随机种子
x = np.random.uniform(0, 10, 100) # X变量:0到10之间的均匀分布,100个样本
y = 2 + 1.5 * x + np.random.normal(0, 2, 100) # Y变量 = 2 + 1.5×X + 噪声
# ==================== 绘制带回归线的散点图 ====================
plt.figure(figsize=(10, 6)) # 创建画布
sns.regplot(x=x, y=y, # 绘制带回归线的散点图
scatter_kws={'s': 80, 'alpha': 0.6, 'color': '#2E86AB'}, # 散点参数:大小80,透明度0.6,蓝色
line_kws={'color': '#E3120B', 'linewidth': 2.5}, # 回归线参数:红色,线宽2.5
ci=95) # 95%置信区间,半透明阴影显示
# ==================== 添加标签和标题 ====================
plt.xlabel('X变量', fontsize=12) # X轴标签
plt.ylabel('Y变量', fontsize=12) # Y轴标签
plt.title('线性回归与置信区间', fontsize=16, fontweight='bold') # 标题
plt.grid(True, alpha=0.3) # 网格
plt.tight_layout() # 调整布局
plt.show() # 显示
# ==================== 计算回归统计 ====================
from scipy import stats # 导入scipy的统计模块
slope, intercept, r_value, p_value, std_err = stats.linregress(x, y)
# 线性回归,返回:斜率、截距、相关系数、p值、标准误差
print(f'回归方程: y = {slope:.2f}x + {intercept:.2f}') # 打印回归方程
print(f'R² = {r_value**2:.4f}') # 打印R平方(决定系数)
print(f'p值 = {p_value:.4e}') # 打印p值(科学计数法)
print(f'结论: {"线性关系显著" if p_value < 0.05 else "线性关系不显著"}')
# 如果p值<0.05,则线性关系显著置信区间的含义:
- 95%置信区间:有95%的把握真实回归线在此区间内
- 形状:两端较宽(数据少时不确定性大),中间较窄
- 金融应用:预测区间、风险度量
58.9 金融应用CAPM模型
# =============================================================================
# 题目: 证券特征线——CAPM模型可视化
# =============================================================================
# 本代码展示如何可视化CAPM(资本资产定价模型)的证券特征线
# 应用于金融场景:计算股票的Beta和Alpha,评估系统性风险
# ==================== 模拟市场收益率和个股收益率 ====================
n_obs = 60 # 观测期数量(60个交易日,约3个月)
np.random.seed(42) # 设置随机种子,确保每次运行结果相同
market_return = np.random.normal(0.0008, 0.015, n_obs) # 市场收益率:均值0.08%,标准差1.5%
beta = 1.2 # 个股Beta系数,衡量对市场波动的敏感度
alpha = 0.0002 # Alpha(超额收益),0.02%每天
stock_return = alpha + beta * market_return + np.random.normal(0, 0.01, n_obs)
# 个股收益率 = Alpha + Beta×市场收益率 + 特质风险
# ==================== 绘制证券特征线 ====================
plt.figure(figsize=(10, 6)) # 创建画布
plt.scatter(market_return, stock_return, # 绘制散点
s=80, alpha=0.6, color='#2E86AB', edgecolors='white') # 点大小80,蓝色,白色边缘
# ==================== 添加拟合线(SML) ====================
z = np.polyfit(market_return, stock_return, 1) # 1阶多项式拟合
p = np.poly1d(z) # 转换为多项式函数
x_line = np.linspace(market_return.min(), market_return.max(), 100)
# 生成100个X值,用于绘制平滑的拟合线
plt.plot(x_line, p(x_line), 'r-', linewidth=2.5, label='证券特征线(SML)')
# 绘制拟合线,红色实线
# ==================== 添加理论线(CAPM预测,假设alpha=0) ====================
plt.plot(x_line, beta * x_line, 'g--', linewidth=2, alpha=0.7, label='理论线(α=0)')
# 绘制理论线(经过原点,斜率为Beta),绿色虚线
# ==================== 添加坐标轴标签和标题 ====================
plt.xlabel('市场收益率 $R_m$', fontsize=12) # X轴标签,使用LaTeX公式
plt.ylabel('股票收益率 $R_i$', fontsize=12) # Y轴标签,使用LaTeX公式
plt.title(f'CAPM模型: β={beta:.2f}, α={alpha:.4f}', fontsize=16, fontweight='bold') # 标题显示参数
plt.legend(fontsize=11) # 图例
plt.grid(True, alpha=0.3) # 网格
plt.axhline(y=0, color='k', linestyle='-', linewidth=0.5) # 添加Y=0水平线,黑色
plt.axvline(x=0, color='k', linestyle='-', linewidth=0.5) # 添加X=0垂直线,黑色
plt.tight_layout() # 调整布局
plt.show() # 显示
# ==================== 计算统计量 ====================
correlation = np.corrcoef(market_return, stock_return)[0, 1] # 相关系数
r_squared = correlation ** 2 # R平方(决定系数)
print(f'CAPM统计:') # 打印标题
print(f'Beta (β): {beta:.2f}') # 打印Beta
print(f'Alpha (α): {alpha:.4f}') # 打印Alpha
print(f'R²: {r_squared:.4f}') # 打印R平方
print(f'解释: Beta>{1:.1f},股票波动大于市场') # 解释Beta含义
# Beta>1表示股票波动性大于市场(进攻型)
# Beta<1表示股票波动性小于市场(防御型)58.10 本章小结
要点:
- 散点图把观测对 \(\{(x_i,y_i)\}\) 直接画在平面上,是判断两变量关系方向与强度的第一工具;Pearson相关系数是其数值概括
- 平台任务的数据处理链:
pd.to_datetime(df['日期'], format='%Y%m%d')→set_index('日期')→df/df.shift(1)-1→dropna→corr()与plt.scatter sharex/sharey让多个子图共用坐标轴,便于横向比较不同散点图的斜率与离散程度- 气泡图用
s(面积)与颜色共同编码第三维(如市值);散点图矩阵(scatter_matrix)一次性查看多变量两两关系,对角线放各变量的分布 np.polyfit(x, y, 1)返回[斜率, 截距],叠加成趋势线可直观展示线性关系;CAPM特征线的斜率即 \(\beta\),衡量个股对市场波动的敏感度,\(R^2\) 衡量市场因子能解释的波动比例
易错点:
- 相关系数只度量线性关系:\(\rho=0\) 不等于两变量无关,强非线性关系(如抛物线)的相关系数可能接近0
- 单个离群点可以显著改变相关系数与拟合线,先看散点图再报告数字
s是点的面积而非半径,放大倍数按平方生效;alpha过大时密集散点会糊成色块np.polyfit返回数组的第0个元素是斜率、第1个是截距,与”先截距后斜率”的书写习惯相反- 散点图矩阵的行列顺序对应变量顺序,读图时先看对角线确认每个变量的分布再读两两关系
- 本地缺少SimHei等中文字体时,中文标签显示为方框并伴随findfont警告,属环境问题,以平台渲染结果为准
58.11 动手与思考
以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。
自测回忆:不看正文,写出Pearson相关系数的公式,并描述 \(\rho=0.9\)、\(\rho=-0.9\)、\(\rho=0\) 三张散点图各自的形态;再回答”相关系数为零”能否推出”两变量没有关系”。
参考答案(点开前请先独立完成)
解题思路:公式为 \(\rho = \text{Cov}(X,Y)/(\sigma_X\sigma_Y)\),样本口径下即协方差除以两个标准差的乘积。形态:\(\rho=0.9\) 点云紧密围绕一条正斜率直线分布,仅在中部略散开;\(\rho=-0.9\) 同样紧密但围绕负斜率直线;\(\rho=0\) 无线性趋势,点云成团(或呈明显的非线性形态)。“相关系数为零”不能推出”两变量没有关系”——\(\rho\) 只度量线性关系,对强非线性关系(如对称取值的抛物线 \(y=x^2\))相关系数可以接近 0,判断关系要先看散点图再报告数字。
回扣主线:相关系数的定义与”只度量线性关系”的告诫见本章”散点图的数学基础”一节与第 章节 34 章。
输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。
import numpy as np x = np.array([1, 2, 3, 4, 5]) y = np.array([2, 4, 6, 8, 10]) z = np.polyfit(x, y, 1) print(np.round(z, 2)) print(round(np.corrcoef(x, y)[0, 1], 4))参考答案(先写下你的预测再点开)
解题思路:y 恰为 x 的 2 倍(y = 2x),一元线性拟合的斜率是 2、截距是 0;注意
np.polyfit返回数组的第 0 个元素是斜率、第 1 个才是截距,与”先截距后斜率”的书写习惯相反。完全正线性关系的相关系数为 1.0。# 验证脚本:完全线性关系的拟合与相关 import numpy as np # 导入NumPy库 x = np.array([1, 2, 3, 4, 5]) # 自变量 y = np.array([2, 4, 6, 8, 10]) # y=2x z = np.polyfit(x, y, 1) # 一次多项式拟合,返回[斜率,截距] print(np.round(z, 2)) # 输出斜率与截距 print(round(np.corrcoef(x, y)[0, 1], 4)) # 完全正线性关系的相关系数预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
[2. 0.] 1.0回扣主线:
np.polyfit返回顺序的易错点见本章”要点”第 5 条与”易错点”第 4 条。输出预测:线性变换不改变相关系数的绝对值,只可能改变符号。先预测输出,再上机检验。
import numpy as np a = np.array([1, 2, 3, 4]) b = 3 * a + 1 c = -2 * a + 5 print(round(np.corrcoef(a, b)[0, 1], 4)) print(round(np.corrcoef(a, c)[0, 1], 4))参考答案(先写下你的预测再点开)
解题思路:相关系数对正的仿射变换(乘正数、加常数)不变——协方差与两个标准差同比例放大,比值不变,故 corr(a, b) 仍为 1.0;乘负数使协方差变号、标准差不变(取正),相关系数变号为 −1.0。一般结论:\(\text{corr}(a, \alpha x + \beta) = \text{sign}(\alpha)\,\text{corr}(a, x)\)。
# 验证脚本:仿射变换对相关系数的作用 import numpy as np # 导入NumPy库 a = np.array([1, 2, 3, 4]) # 原序列 b = 3 * a + 1 # 正系数仿射变换 c = -2 * a + 5 # 负系数仿射变换 print(round(np.corrcoef(a, b)[0, 1], 4)) # 正变换:不变 print(round(np.corrcoef(a, c)[0, 1], 4)) # 负变换:变号预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
1.0 -1.0回扣主线:相关系数的标准化(除以标准差)正是它对量纲与线性变换稳健的原因,见本章”散点图的数学基础”一节与第 章节 34 章。
变式任务(平台任务同型改造):仿照平台任务”先算相关系数、再画散点”的结构,在本地选取你熟悉的两只指数(或股票)与某一基准指数的日收盘价,计算日收益率后,在一张图上并列两个共用坐标轴的散点子图,分别绘制它们对基准的收益率散点并叠加
np.polyfit趋势线,比较两组 \(\rho\) 的大小。参考答案(点开前请先独立完成)
解题思路:取数路径——读者可用 tushare 的
pro.index_daily(ts_code=..., start_date=..., end_date=...)依次拉取基准指数(如 000001.SH 上证综指)与两只对比指数(如 399006.SZ 创业板指、000300.SH 沪深300)的日线收盘价(需读者自己的 tushare token),或从本地行情文件读取对应列;数据处理与平台任务同链:pd.to_datetime(..., format='%Y%m%d')→set_index→df/df.shift(1)-1→dropna→corr()与散点。下面用固定种子的模拟数据演示完整结构(模拟仅用于演示代码结构,结论以真实数据为准):股票 A 以 1.1 倍市场敏感度生成,股票 B 仅 0.4 倍——实测 \(\rho_A=0.8566\) 大于 \(\rho_B=0.2731\),A 图点云紧贴红色趋势线、B 图点云松散且趋势线平缓。# 模拟演示:两只股票对基准的收益率散点与趋势线 import matplotlib.pyplot as plt # 导入绘图库 import pandas as pd # 导入pandas库 import numpy as np # 导入NumPy库 np.random.seed(2024) # 固定随机种子,结果可复现 n_days = 250 # 模拟250个交易日 benchmark_return = np.random.normal(0.0005, 0.012, n_days) # 基准指数日收益率 stock_a_return = 0.0008 + 1.1 * benchmark_return + np.random.normal(0, 0.008, n_days) # 高敏感度个股 stock_b_return = 0.0006 + 0.4 * benchmark_return + np.random.normal(0, 0.015, n_days) # 低敏感度个股 df_return = pd.DataFrame({'基准指数': benchmark_return, '股票A': stock_a_return, '股票B': stock_b_return}) # 组装收益率数据框 print(round(df_return['基准指数'].corr(df_return['股票A']), 4), round(df_return['基准指数'].corr(df_return['股票B']), 4)) # 两组相关系数 slope_a, intercept_a = np.polyfit(benchmark_return, stock_a_return, 1) # A的趋势线 slope_b, intercept_b = np.polyfit(benchmark_return, stock_b_return, 1) # B的趋势线 print(round(slope_a, 4), round(slope_b, 4)) # 两条趋势线斜率 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 5), sharex=True, sharey=True) # 共用坐标轴双子图 xs = np.linspace(benchmark_return.min(), benchmark_return.max(), 50) # 趋势线横坐标网格 ax1.scatter(benchmark_return, stock_a_return, s=12, alpha=0.6) # 左图散点 ax1.plot(xs, slope_a * xs + intercept_a, 'r--', lw=2) # 左图趋势线 ax2.scatter(benchmark_return, stock_b_return, s=12, alpha=0.6, c='c') # 右图散点 ax2.plot(xs, slope_b * xs + intercept_b, 'r--', lw=2) # 右图趋势线 fig.savefig('two_scatter.png') # 保存图形预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
0.8566 0.2731 1.1912 0.3449图形:左图点云沿正斜率趋势线紧凑分布,右图点云明显更发散、趋势线更平缓,与 0.8566 大于 0.2731 相互印证;两子图共用坐标轴便于直接比较斜率与离散程度。
注意:以上为本地演练代码;列表 58.1 的平台任务仍须按原始代码原样输入教学平台。
回扣主线:“先算相关系数、再画散点”的结构与
sharex/sharey的用法见本章平台任务解析与”要点”第 3 条,真实指数相关性的口径见第 章节 21 章与第 章节 34 章。思考题:某分析师发现广告支出与销售额的相关系数高达0.95,于是建议”继续加大广告预算,销售额会按比例上升”。请分别从相关与因果、遗漏变量、样本范围三个角度各给出一条质疑。
参考答案(点开前请先独立完成)
解题思路:第一,相关不等于因果——0.95 只说明广告支出与销售额同步变动,方向可能是反的(销售额上涨的公司更有钱投广告),也可能是双向驱动,“加大预算导致销售上升”的因果链并未被相关系数证明。第二,遗漏变量——季节性旺季、新品上市、竞品退出、渠道扩张等第三因素可能同时推高广告与销售,两者的高相关只是共同受驱动,据此加预算可能只是”花钱陪涨”。第三,样本范围——若样本期恰处销量上升通道、或只覆盖了高投放区间,外推到”继续加大”就超出了数据支持的取值范围;且广告的边际效应通常递减,“按比例上升”的线性假设在预算继续扩大后未必成立。稳妥做法是以投放量为控制变量做因果推断设计(或至少分区段观察弹性),而非直接把相关系数当作投放指南。
回扣主线:“相关不等于因果、高相关可能来自共同因子”的告诫见本章”易错点”第 3 条与第 章节 34 章。