学过概率统计的孩子都知道,统计里最基本的概念就是样本的均值,方差,或者再加个标准差。首先我们给你一个含有n个样本的集合,依次给出这些概念的公式描述,这些高中学过数学的孩子都应该知道吧,一带而过。
很显然,均值描述的是样本集合的中间点,它告诉我们的信息是很有限的,而标准差给我们描述的则是样本集合的各个样本点到均值的距离之平均。以这两个集合为例,[0,8,12,20]和[8,9,11,12],两个集合的均值都是10,但显然两个集合差别是很大的,计算两者的标准差,前者是8.3,后者是1.8,显然后者较为集中,故其标准差小一些,标准差描述的就是这种“散布度”。之所以除以n-1而不是除以n,是因为这样能使我们以较小的样本集更好的逼近总体的标准差,即统计上所谓的“无偏估计”。而方差则仅仅是标准差的平方。
为什么需要协方差?
上面几个统计量看似已经描述的差不多了,但我们应该注意到,标准差和方差一般是用来描述一维数据的,但现实生活我们常常遇到含有多维数据的数据集,最简单的大家上学时免不了要统计多个学科的考试成绩。面对这样的数据集,我们当然可以按照每一维独立的计算其方差,但是通常我们还想了解更多,比如,一个男孩子的猥琐程度跟他受女孩子欢迎程度是否存在一些联系啊,嘿嘿~协方差就是这样一种用来度量两个随机变量关系的统计量,我们可以仿照方差的定义:
来度量各个维度偏离其均值的程度,标准差可以这么来定义:
协方差的结果有什么意义呢?如果结果为正值,则说明两者是正相关的(从协方差可以引出“相关系数”的定义),也就是说一个人越猥琐就越受女孩子欢迎,嘿嘿,那必须的~结果为负值就说明负相关的,越猥琐女孩子越讨厌,可能吗?如果为0,也是就是统计上说的“相互独立”。
从协方差的定义上我们也可以看出一些显而易见的性质,如:
协方差多了就是协方差矩阵
上一节提到的猥琐和受欢迎的问题是典型二维问题,而协方差也只能处理二维问题,那维数多了自然就需要计算多个协方差,比如n维的数据集就需要计算 n! / ((n-2)!*2) 个协方差,那自然而然的我们会想到使用矩阵来组织这些数据。给出协方差矩阵的定义:
这个定义还是很容易理解的,我们可以举一个简单的三维的例子,假设数据集有三个维度,则协方差矩阵为
可见,协方差矩阵是一个对称的矩阵,而且对角线是各个维度上的方差。
Matlab协方差实战
上面涉及的内容都比较容易,协方差矩阵似乎也很简单,但实战起来就很容易让人迷茫了。必须要明确一点,协方差矩阵计算的是不同维度之间的协方差,而不是不同样本之间的。这个我将结合下面的例子说明,以下的演示将使用Matlab,为了说明计算原理,不直接调用Matlab的cov函数(蓝色部分为Matlab代码)。
首先,随机产生一个10*3维的整数矩阵作为样本集,10为样本的个数,3为样本的维数。
mysample = fix(rand(10,3)*50)
根据公式,计算协方差需要计算均值,那是按行计算均值还是按列呢,我一开始就老是困扰这个问题。前面我们也特别强调了,协方差矩阵是计算不同维度间的协方差,要时刻牢记这一点。样本矩阵的每行是一个样本,每列为一个维度,所以我们要按列计算均值。为了描述方便,我们先将三个维度的数据分别赋值:
>> dim1 = mysample(:,1);
>> dim2 = mysample(:,2);
>> dim3 = mysample(:,3);
计算dim1与dim2,dim1与dim3,dim2与dim3的协方差:
>> sum((dim1 - mean(dim1)) .* (dim2 - mean(dim2))) / (size(mysample, 1) - 1) %得到 -147.0667
>> sum((dim1 - mean(dim1)) .* (dim3 - mean(dim3))) / (size(mysample, 1) - 1) %得到 -82.2667
>> sum((dim2 - mean(dim2)) .* (dim3 - mean(dim3))) / (size(mysample, 1) - 1) %得到 76.5111
搞清楚了这个后面就容易多了,协方差矩阵的对角线就是各个维度上的方差,下面我们依次计算:
>> var(dim1) %得到 227.8778
>> var(dim2) %得到 179.8222
>> var(dim3) %得到 156.7111
这样,我们就得到了计算协方差矩阵所需要的所有数据,调用Matlab自带的cov函数进行验证:
>> cov(mysample)
把我们计算的数据对号入座,是不是一摸一样?
Update
今天突然发现,原来协方差矩阵还可以这样计算,先让样本矩阵中心化,即每一维度减去该维度的均值,使每一维度上的均值为0,然后直接用新的到的样本矩阵乘上它的转置,然后除以(N-1)即可。其实这种方法也是由前面的公式推导而来,只不过理解起来不是很直观,但在抽象的公式推导时还是很常用的!同样给出Matlab代码实现:
>> temp = mysample - repmat(mean(mysample), 10, 1);
>> result = temp' * temp ./ (size(mysample, 1) - 1)
总结
理解协方差矩阵的关键就在于牢记它计算的是不同维度之间的协方差,而不是不同样本之间,拿到一个样本矩阵,我们最先要明确的就是一行是一个样本还是一个维度,心中明确这个整个计算过程就会顺流而下,这么一来就不会迷茫了~
在概率论和统计学中,协方差用于衡量两个变量的总体误差。而方差是协方差的一种特殊情况,即当两个变量是相同的情况。其定义的数学形式是:Cov(X,Y)=E[(X−E(X))(Y−E(Y))]=E[XY]−E[X]E[Y]Cov(X,Y)=E[(X-E(X))(Y-E(Y))] =E[XY]-E[X]E[Y]Cov(X,Y)=E[(X−E(X))(Y−E(Y))]=E[XY]−E[X]E[Y]
np.cov详解
协方差数学形式
协方差代码形式
函数原型:def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None,aweights=None)
m:一维或则二维的数组,默认情况下每一行代表一个变量(属性),每一列代表一个观测
y:与m具有一样的形式的一组数据
rowvar:默认为True,此时每一行代表一个变量(属性),每一列代表一个观测;为False时,则反之
bias:默认为False,此时标准化时除以n-1;反之为n。其中n为观测数
ddof:类型是int,当其值非None时,bias参数作用将失效。当ddof=1时,将会返回无偏估计(除以n-1),即使指定了fweights和aweights参数;当ddof=0时,则返回简单平均值。
frequency weights:一维数组,代表每个观测要重复的次数(相当于给观测赋予权重)
analytic weights:一维数组,代表观测矢量权重。对于被认为“重要”的观察,这些相对权重通常很大,而对于被认为不太重要的观察,这些相对权重较小。如果ddof = 0,则可以使用权重数组将概率分配给观测向量。
代码示例
基本使用
import numpy as np
# 计算协方差的时候,一行代表一个特征
# 下面计算cov(T, S, M)
T = np.array([9, 15, 25, 14, 10, 18, 0, 16, 5, 19, 16, 20])
S = np.array([39, 56, 93, 61, 50, 75, 32, 85, 42, 70, 66, 80])
M = np.asarray([38, 56, 90, 63, 56, 77, 30, 80, 41, 79, 64, 88])
X = np.vstack((T, S, M))
# X每行代表一个属性
# 每列代表一个示例,或者说观测
print(np.cov(X))
# [[ 47.71969697 122.9469697 129.59090909]
# [122.9469697 370.08333333 374.59090909]
# [129.59090909 374.59090909 399. ]]
重点:协方差矩阵计算的是不同维度之间的协方差,而不是不同样本之间。拿到一个样本矩阵,首先要明确的就是行代表什么,列代表什么。
fweights
frequency weights:一维数组,代表每个观测要重复的次数(相当于给观测赋予权重)
T = np.array([9, 15, 25, 14, 10, 18, 0, 16, 5, 19, 16, 20])
S = np.array([39, 56, 93, 61, 50, 75, 32, 85, 42, 70, 66, 80])
M = np.asarray([38, 56, 90, 63, 56, 77, 30, 80, 41, 79, 64, 88])
X = np.vstack((T, S, M))
print(np.cov(X, None, True, False, fweights=[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]))
# 和上面例子结果一样
# [[ 47.71969697 122.9469697 129.59090909]
# [122.9469697 370.08333333 374.59090909]
# [129.59090909 374.59090909 399. ]]
T = np.array([9, 15, 25, 14, 10, 18, 0, 16, 5, 19, 16, 20])
S = np.array([39, 56, 93, 61, 50, 75, 32, 85, 42, 70, 66, 80])
M = np.asarray([38, 56, 90, 63, 56, 77, 30, 80, 41, 79, 64, 88])
X = np.vstack((T, S, M))
print(np.cov(X, None, True, False, fweights=[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]))
# 结果变了,就是因为相当于在X中增加了一列[9,39,38].T
# [[ 45.6025641 121.55769231 128.43589744]
# [121.55769231 381.42307692 389.30769231]
# [128.43589744 389.30769231 415.76923077]]
T = np.array([9, 9, 15, 25, 14, 10, 18, 0, 16, 5, 19, 16, 20])
S = np.array([39, 39, 56, 93, 61, 50, 75, 32, 85, 42, 70, 66, 80])
M = np.asarray([38, 38, 56, 90, 63, 56, 77, 30, 80, 41, 79, 64, 88])
X = np.vstack((T, S, M))
print(np.cov(X, None, True, False))
# 这样就验证了上面的话咯~
# [[ 45.6025641 121.55769231 128.43589744]
# [121.55769231 381.42307692 389.30769231]
# [128.43589744 389.30769231 415.76923077]]
aweights
很遗憾,暂时不知道它的计算方式,等有时间我仔细看看源码怎么算的,再修正!
注意事项
参数y
T = np.array([9, 15, 25, 14, 10, 18, 0, 16, 5, 19, 16, 20])
S = np.array([39, 56, 93, 61, 50, 75, 32, 85, 42, 70, 66, 80])
M = np.asarray([38, 56, 90, 63, 56, 77, 30, 80, 41, 79, 64, 88])
X = np.vstack((T, S, M))
# 你会惊奇发现,这个结果和上面的结果一致,这就是参数 m, y,不知道为什么要设置这样一个参数,hhh
print(np.cov(X[0:1], X[1:]))
# [[ 47.71969697 122.9469697 129.59090909]
# [122.9469697 370.08333333 374.59090909]
# [129.59090909 374.59090909 399. ]]
和方差的区别
>>> a = [1,2,3,4] # 当a是一维向量时
>>> import numpy as np
>>> np.cov(a) # 计算样本方差
array(1.66666667)
>>> np.var(a) # 计算总体方差
1.25
下面是 cov(a) 和 var(a)的区别
>>> 1.666666666666666667*3/4
1.25
>>>
协方差矩阵实例:
import numpy as np
x = np.array([2,4,5,3,6,9,40,25,32])
print(np.mean(x))
print(x-np.mean(x))
print((x-np.mean(x))**2)
print(np.sum((x-np.mean(x))**2))
print(np.cov(x),np.cov(x)*8)
print(np.var(x)*9)
y = np.array([[1,5,6],[4,3,9],[4,2,9],[4,7,2]])
print(y.shape)
print(np.cov(y,rowvar=False))
#其中rowvar是布尔类型。默认为true是将行作为独立的变量、如果是flase的话,则将列作为独立的变量。
输出结果:
14.0
[-12. -10. -9. -11. -8. -5. 26. 11. 18.]
[144. 100. 81. 121. 64. 25. 676. 121. 324.]
1656.0
207.0 1656.0
1656.0
(4, 3)
[[ 2.25 -0.75 0.5 ]
[-0.75 4.91666667 -7.16666667]
[ 0.5 -7.16666667 11. ]]
以下使用鸢尾花数据集计算数据的协方差矩阵,由于数据包含4个特征,因此这个协方差矩阵一定是一个4*4的矩阵的代码
import numpy as np
from sklearn import datasets
iris = datasets.load_iris()
# print(iris.data)
print(iris.data.shape)
print(np.cov(iris.data,rowvar=True).shape)
print(np.cov(iris.data,rowvar=False))
输出结果:
(150, 4)
(150, 150)
[[ 0.68569351 -0.042434 1.27431544 0.51627069]
[-0.042434 0.18997942 -0.32965638 -0.12163937]
[ 1.27431544 -0.32965638 3.11627785 1.2956094 ]
[ 0.51627069 -0.12163937 1.2956094 0.58100626]]