参考:
1. Directional statistics(Mardia)P14-19&29-30&47-50;
2. Biostatistical Analysis(2009 Zar)-5th edition P614-617;
3. CircStat: A MATLAB Toolbox for Circular Statistics(Philipp Berens);(这是一个MATLAB中实现的计算方向数据的一个工具包)
以上文献如有需要,可留下邮箱分享与你。
正文:
最近因为做风的研究,需要衡量风向的一致性,就想转化到求角度的标准差。但是,当你去算得时候你就会发现,角度的标准差与平常的我们所算的标准差是不一样的。为行文方便,在本文中我把类似角度这样的数据(多是周期数据,如时间等)姑且称作——方向数据(directional statistics/ circular data),把其他数据(中学时就接触的那些算标准差的数据)称为——线性数据(data on the line)。
为了直观表示方向数据,最简单的就是把它放在圆上:
当我们想得到它的一些描述性数据(比如:均值、中位数、标准差等)时,最初的想法可能是在圆上的适当位置把圆断开,然后用惯常的线性数据方法去求取。这种方法非常依赖于断点的选择,为什么这样讲呢?比如,我们有一个数据集是:1°和359°,在0°处断开——得到均值是180°,标准差179°;在180°处断开——得到均值是0°,标准差1°。
从上面例子中,我们看到这个断点的选择对我们描述性数据的影响是多么大。当然,我认为这种选断点来处理数据的方法,在一些情况下可能是简单而有用的——比如上面的例子中,我想大家都会同意断点在180°处是合理的。
在Directional Statistics(Mardia&jupp, 2000)一书中,提出了一个适用更为广泛的方法,来求取方向数据的描述性数据。具体做法是将圆上的点,转化成平面上的单位向量,再进行后续处理。
数据预处理
方向可以被视作单位向量x,或者等同于单位圆上的点。还有另外两种表示法:角度和单位复数。为单位圆指定初始方向和走向,就可以将单位圆上的点转化成角度θ或者单位复数z,以下是就是单位向量x的相关表示:
(参看图3)
方向用角度来表示可视作固有方法,因为方向本身就被当作圆上的点;方向用复数来表示可视作一种嵌入式的方法,因为这时方向被当作了平面上特殊的点。在嵌入式的方法中,原本在圆上的数据分布被认为是集中分布在平面的单位圆上。
值得注意的是,方向用角度或者单位复数来表示时,数据的值依赖于初始方向和走向。一般情况下没有特别选定初始方向和走向,所以当你在处理同一数据集时,若选择了特殊的初始方向和走向,要注意数据的一致性。即是说,在方向数据领域,数据本身的属性——如离散性——与坐标无关。
方向数据的均值
假设我们有单位向量,来表示对应角度。那么的平均方向就是的中心所指的方向。因为笛卡尔坐标系中的坐标是,因此数据集的中心就是,其中:
因此就是下式的结果:
其中平均值长度是:
值得注意的是时,是不存在的,或者说此种情况下我们没有定义样本的均值,当>0时,可这样求得:
其中‘arctan'是正切函数的反函数,取值范围是[-π/2, π/2],注意这里不等于
角度坐标系的问题
罗盘的角度坐标系建立如图a,在Mardia的书中坐标系建立如图b,下面以分别以x,y指代a、b中的角度值:
在两坐标系中,x和y有如下关系:
上面Mardia坐标系下求得的,其值的范围是(-π/2, -3π/2),所以在求得时候要注意下这个问题。下面是我在python中编写的求均值的程序:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
'''
@author: liucheng
created on 2018-10-16
Discription: This program is to calculate the discriptive measures --like variance, standard deviation, mean direction and so on-- of directional statistics
'''
import math
# import pandas as pd
# calculate standard deviation of angles
def calc_angles_stddev(data_list):
m_sin, m_cos = calc_mean_sin_cos(data_list)
stddev = math.sqrt(-math.log(m_sin**2 + m_cos**2))#calculate standard deviation
return stddev
# mean angle calculation in Mardia's coordinates
def calc_angles_mean1(data_list):
m_sin, m_cos = calc_mean_sin_cos(data_list)
mean_angle = math.atan(m_sin/m_cos)*(180/math.pi) #calculate mean angle
arctan1 = math.atan(m_sin/m_cos)*(180/math.pi)
if (m_cos >= 0 ):
mean_angle = mean_angle
elif (m_cos < 0):
mean_angle = mean_angle + 180
if mean_angle < 0 :
mean_angle = 360 + mean_angle # 将 (-pi/2, 0)这一段的值转换成正值
return mean_angle
# # mean angle calculation in compass coordinates
def calc_angles_mean2(data_list):
lst = []
for elem in data_list: # 坐标系转换,将罗盘坐标系的角度值,转换到Mardia的坐标系下
if elem > 90:
elem = 450 - elem
else:
elem = 90 - elem
lst.append(elem)
m_sin, m_cos = calc_mean_sin_cos(lst) # 算 sin 和 cos 均值
mean_angle = math.atan(m_sin/m_cos)*(180/math.pi) # 计算角度均值,
arctan2 = math.atan(m_sin/m_cos)*(180/math.pi)
if (m_cos >= 0 ): # 依据Mardia的方法,均值转换
mean_angle = mean_angle
elif (m_cos < 0):
mean_angle = mean_angle + 180
if mean_angle < 0: # 上面计算得到的均值范围(-pi/2, 3pi/2),这里将负值转换成正值
mean_angle = 360 + mean_angle
if mean_angle < 90: # 将均值再转换回风向本身坐标系下
mean_angle = 90 - mean_angle
else:
mean_angle = 450 - mean_angle
return mean_angle
# calculation of mean sine and cosine for a set of angles
def calc_mean_sin_cos(data_list):
m_sin = 0
m_cos = 0
for data_elmt in data_list:
m_sin += math.sin(math.radians(data_elmt))
m_cos += math.cos(math.radians(data_elmt))
m_sin /= len(data_list) #average of list sin
m_cos /= len(data_list)
return m_sin, m_cos
if __name__ == '__main__':
data_list = [43, 45, 52, 61, 75, 88, 88, 297, 357] # a data set of angles
a1 = calc_angles_mean1(data_list) # calculate mean angles in Mardia's coordinates
a2 = calc_angles_mean2(data_list) # calculate mean angles in Compass coordinates
dev = calc_angles_stddev(data_list)
print('a1 = ', a1, '\na2 = ', a2, '\ndev = ', dev)
举个例子,以罗盘坐标系为例,称作例1吧:
现有数据:[43°,45°,52°,61°,75°,88°,88°,297°,357°],按照以上方法可以得到平均值和平均值长度:
方向数据的中位数
中位数在统计中也很重要,可以更好地帮我们去描述和理解数据。在方向数据中,中位数要满足两个条件:1半数的数据点应该在[,+π]中;2大部分的数据点应该更接近而不是+π。
根据以上两个条件,我们可以得到例1——[43°,45°,52°,61°,75°,88°,88°,297°,357°]——的中位数是52°,这个值接近他的均值。
方向数据方差
上面说到方向平均值的长度是:
因为都是单位向量,因此:
这个值是很有意义的,它能反映数据的离散性。具体是:若都紧紧聚集在一起,那么就接近于1;若相互间都分散地很开,那么就接近于0。我们可以这么说吧——的变化与方向数据的离散性成反比——越小,数据越分散,反之亦反。
的取值范围是[0, 1],当=1时,说明所有点集中分布于同一点上,但是当=0时不能说明点在圆上均匀分布——下图就是两个反例。
关于能反映离散性这一点,参考书目没有细说,直接给出了这么个结论,我想稍稍持有点谨慎的人都会生发出怀疑——真的能反映离散性吗?这里给出稍解释一下,希望能帮助大家理解(之前做了一次展示,但是大家都没怎么明白。。)。
请看下图:
从图7中,可以看到:当点越来越分散的时候,平均值长度变得越来越小,这样也就证明了我们前面所说的结论。当然,你可能会说这不是一个严谨的证明,只不过是几个例子罢了。这样说当然没错,但这里我给不出严谨的数学证明,参考书上也没讲,举这个例子只是为了让大家明白确实有这样的趋势。
值得注意的是当点均匀分布在单位圆上时,计算得到=(0,0),而前面我们知道,是这样求得:
因此这时就不能得到平均角度值了,实际上这个时候也没有相应地平均值,或者说我们不好定义在这种情况下平均值应该是什么。
所以我们可以直接拿这个值来反映数据的离散性,当然,我们可以也可以另V=1-来作为数据的方差,这样离散性与方差就是正相关的了。
此外,Batschelet(1981)把“circular variance”(循环数据方差)定义为
方向数据的标准差
用上面介绍的V和来衡量离散性当然可以,至少能够定性的说明一些问题,比如比较两组数据离散性的大小,或者一组数据变化之后,相应离散性是如何变化的。
下面要介绍的是Mardia书中介绍的一个方向数据的标准差,他提出的这个标准差是为了得到一个类似于线性数据那样的标准差,即是说这个标准差求得的结果对应于角度值,而之前的V和对离散性而言可以认为是一个没有实际意义的量纲。先说结论,在directional statistics(Mardia&Jupp, 2000)一书中,给出的方向数据的标准差是这样的:
他又说对于比较小的,该式可变为(这里Mardia也未说明原因,不知道为什么,难道他以为读者很厉害?这些变换都不言自明,一看就明白了?):
在CircStat: A MATLAB Toolbox for Circular Statistics(Philipp Berens)一文中,作者说上面两个式子中后者更常用,因为是有界的[0, ]。
这个标准差得来的原因是假设方向数据在圆上服从正态分布的,该式得来在directional statistics一书中是这样介绍的:
1关于Wrapped Distribution(下文简称WD,wrapped:包裹、包装、覆盖)
定义:给定线性分布(distribution on the line),我们可以把它包裹(wrap)在单位圆上。即是说,如果x是线上的一个随机变量,那么相应地WD的随机变量是:
若用一组具有单位模长的复数来表示圆,那么就是:
若x有分布函数F,那么的分布函数是:
特别的,若x有概率密度函数f,那么相应地的概率密度函数为:
有了以上关于Wrapped Distribution的介绍我们来看看Wrapped Normal Distribution:
2Wrapped Normal Distribution(姑且称之为:包裹正态分布)
Mardia在他的书中原话是这样的:
其中3.5.57即上述(2)式。书里里讲到这里就完了,我在书中并未看到关于截图中3.5.63是怎么来的。我是这样想的:
包裹正态分布就是把正态分布包裹(wraping )在圆上得到的,其中:
这个式子怎么来的呢?我以为是这样的来的——因为对应线性数据的方差,于是根据上面“包裹”的变换法有:
由(4)进一步得到:
但是(5)相比于(3)等是右边少了一个2倍的关系,这样讲说不通。
基于此我对Mardia提出的这个标准差有以下看法:
1.Mardia在书中也仅讲了摆出来上面那一点,完全没说清楚,就发明了这么个标准差出来,因此我认为这个数没多大参考意义。
2.用到这个数,首先你的数据还得满足正态分布,因此其适用范围有限。
3.既然并没有多大意义,我认为在衡量离散性时用平均值长度就行了: