描述性统计分析是关于数据的描述和汇总。它使用两种主要方法:
-
定量方法以数值方式描述和汇总数据。
-
可视化方法通过图表,曲线图,直方图和其他图形来说明数据。
一般在数据分析的过程中,拿到数据不会去直接去建模,而是先做描述性分析来对数据有一个大致的把握,很多后续的建模方向也是通过描述性分析来进一步决定的。那么除了在Excel/R中可以去做描述性分析。
本文将细致讲解如何使用python进行描述性分析的定量分析部分:
-
均值
-
中位数
-
方差
-
标准差
-
偏度
-
百分位数
-
相关性
至于可视化的部分可以参考我之前讲解pyecharts的文章,当然后面还会介绍echarts以及ggplot2的方法。
涉及到的python库
-
Python statistics是用于描述性统计信息的内置Python库。如果您的数据集不是太大,或者您不能依赖于导入其他库,则可以使用它。
-
NumPy是用于数字计算的第三方库,已针对使用一维和多维数组进行了优化。它的主要类型是称为的数组类型
ndarray
。该库包含许多用于统计分析的方法。
-
SciPy是基于NumPy的用于科学计算的第三方库。与NumPy相比,它提供了其他功能,包括
scipy.stats
统计分析。Getting started – SciPy.org
-
Pandas是基于NumPy的用于数值计算的第三方库。它擅长处理带有
Series
对象的带标签的一维(1D)数据和带有对象的二维(2D)数据
DataFrame
。
-
Matplotlib是用于数据可视化的第三方库。通常会与NumPy,SciPy和Pandas结合使用
开始
首先导入所有的包
import math
import statistics
import numpy as np
import scipy.stats
import pandas as pd
创建数据
x
和
x_with_nan
都是list。不同之处在于
x_with_nan
包含一个
nan
值。也就是空值(缺失值),这样的数据在分析中十分常见。那么在python里,创建一个nan值可以有以下方法
float(\'nan\')
math.nan
np.nan
当然这三种方法创建的空值都是等价的
但是真的相等吗,两个nan是不相等的,换句话说,是不可以进行比较的,这后面的故事以后再说。
接着,我们使用numpy和pandas来创建两个一维numpy arrays和pandas series
均值
均值的定义啥的,就不说了,R里面直接mean()就可以,而在python,不导入包,怎么计算:
当然也可以用python的内置统计功能
但是如果数据中包含nan,那么将返回nan
>>> mean_ = statistics.mean(x_with_nan)
>>> mean_
nan
如果使用numpy
>>> mean_ = np.mean(y)
>>> mean_
8.7
在上面的示例中,
mean()
是一个函数,但是您也可以使用相应的方法
>>> mean_ = y.mean()
>>> mean_
8.7
如果包含nan,numpy也会返回nan,那么要是想忽略nan,可以使用np.nanmean()
>>> np.mean(y_with_nan)
nan
>>> np.nanmean(y_with_nan)
8.7
pandas也有对应方法,但是,默认情况下,
.mean()
在Pandas中忽略nan值:
mean_ = z.mean()
mean_
>>> z_with_nan.mean()
8.7
中位数
比较平均值和中位数,这是检测数据中异常值和不对称性的一种方法。平均值还是中位数对您更有用,取决于特定问题的背景。而不使用包的计算方法:
>>> n = len(x)
>>> if n % 2:
... median_ = sorted(x)[round(0.5*(n-1))]
... else:
... x_ord, index = sorted(x), round(0.5 * n)
... median_ = 0.5 * (x_ord[index-1] + x_ord[index])
...
>>> median_
4
其他方法
>>> median_ = np.median(y)
>>> median_
4.0
>>> np.nanmedian(y_with_nan)
4.0
方差
方差的意义也不过多说明,在Excel中直接用stdev函数,但是怎么在python中计算?记得当初研究生复试就被问到用python不导入包怎么计算方差?
>>> n = len(x)
>>> mean_ = sum(x) / n
>>> var_ = sum((item - mean_)**2 for item in x) / (n - 1)
>>> var_
123.19999999999999
当然更简单的方法是直接使用函数,不过有nan还是会返回nan
>>> var_ = statistics.variance(x)
>>> var_
123.2
>>> statistics.variance(x_with_nan)
nan
放在numpy里面就更简单了,可以使用np.var()或者.var()
>>> var_ = np.var(y, ddof=1)
>>> var_
123.19999999999999
>>> var_ = y.var(ddof=1)
>>> var_
123.19999999999999
这里ddof就是自由度要设置为1才是无偏的。也就是分母用n-1替换n。如果有nan怎么办?返回nan,但是可以用np.nanvar()跳过nan,不过ddof依旧要设置为1
>>> np.var(y_with_nan, ddof=1)
nan
>>> y_with_nan.var(ddof=1)
nan
>>> np.nanvar(y_with_nan, ddof=1)
123.19999999999999
标准差
有了方差,标准差就很好计算了
#直接计算
>>> std_ = var_ ** 0.5
>>> std_
11.099549540409285
#使用内置包
>>> std_ = statistics.stdev(x)
>>> std_
11.099549540409287
numpy中也很好计算
>>> np.std(y, ddof=1)
11.099549540409285
>>> y.std(ddof=1)
11.099549540409285
>>> np.std(y_with_nan, ddof=1)
nan
>>> y_with_nan.std(ddof=1)
nan
>>> np.nanstd(y_with_nan, ddof=1) #跳过nan,ddof还是要是1哦
11.099549540409285
偏度(skew)
偏度(skewness)也称为偏态、偏态系数,是统计数据分布偏斜方向和程度的度量,是统计数据分布非对称程度的数字特征。偏度是利用3阶矩定义的,偏度的计算公式为:
我们之前研究的数据都是比较对称的数据,但是上图就给出了不对称的数据集,第一组用绿点表示,第二组用白点表示。通常,负偏度值表示左侧有一个占主导地位的尾巴,可以在第一个集合中看到。正偏度值对应于右侧较长或的尾巴,可以在第二组中看到。如果偏度接近0(例如,介于-0.5和0.5之间),则该数据集被认为是非常对称的。
那么不依赖第三包,怎么计算偏度。可以先计算完数据集的大小n,样本均值mean和标准差std后用公式计算
>>> x = [8.0, 1, 2.5, 4, 28.0]
>>> n = len(x)
>>> mean_ = sum(x) / n
>>> var_ = sum((item - mean_)**2 for item in x) / (n - 1)
>>> std_ = var_ ** 0.5
>>> skew_ = (sum((item - mean_)**3 for item in x)
... * n / ((n - 1) * (n - 2) * std_**3))
>>> skew_
1.9470432273905929
可以看出偏度为正,因此x尾巴在右侧。
也可以用第三方包计算
>>> y, y_with_nan = np.array(x), np.array(x_with_nan)
>>> scipy.stats.skew(y, bias=False)
1.9470432273905927
>>> scipy.stats.skew(y_with_nan, bias=False)
nan
>>> z, z_with_nan = pd.Series(x), pd.Series(x_with_nan)
>>> z.skew()
1.9470432273905924
>>> z_with_nan.skew()
1.9470432273905924
百分位数(Percentiles)
如果将一组数据从小到大排序,并计算相应的累计百分位,则某一百分位所对应数据的值就称为这一百分位的百分位数。可表示为:一组n个观测值按数值大小排列。如,处于p%位置的值称第p百分位数。每个数据集都有三个四分位数,这是将数据集分为四个部分的百分位数:
-
第一四分位数 (Q1),又称“较小四分位数”,等于该样本中所有数值由小到大排列后第25%的数字。
-
第二四分位数 (Q2),又称“中位数”,等于该样本中所有数值由小到大排列后第50%的数字。
-
第三四分位数 (Q3),又称“较大四分位数”,等于该样本中所有数值由小到大排列后第75%的数字。
第三四分位数与第一四分位数的差距又称四分位距(InterQuartile Range,IQR)。
那么在python里面怎么计算分位数呢。可以使用statistics.quantiles()
>>> x = [-5.0, -1.1, 0.1, 2.0, 8.0, 12.8, 21.0, 25.8, 41.0]
>>> statistics.quantiles(x, n=2)
[8.0]
>>> statistics.quantiles(x, n=4, method=\'inclusive\')
[0.1, 8.0, 21.0]
可以看到第一行中,8就是x的中位数,而第二个例子中,0.1和21是样本的25%和75%分位数。也可以使用第三方包numpy计算
>>> np.percentile(y, [25, 50, 75])
array([ 0.1, 8. , 21. ])
>>> np.median(y)
8.0
#跳过nan
>>> y_with_nan = np.insert(y, 2, np.nan)
>>> y_with_nan
array([-5. , -1.1, nan, 0.1, 2. , 8. , 12.8, 21. , 25.8, 41. ])
>>> np.nanpercentile(y_with_nan, [25, 50, 75])
array([ 0.1, 8. , 21. ])
pandas也可以使用
.quantile()
计算,需要提供分位数值作为参数。该值可以是0到1之间的数字或数字序列。
>>> z, z_with_nan = pd.Series(y), pd.Series(y_with_nan)
>>> z.quantile(0.05)
-3.44
>>> z.quantile(0.95)
34.919999999999995
>>> z.quantile([0.25, 0.5, 0.75])
0.25 0.1
0.50 8.0
0.75 21.0
dtype: float64
>>> z_with_nan.quantile([0.25, 0.5, 0.75])
0.25 0.1
0.50 8.0
0.75 21.0
dtype: float64
范围(Ranges)
数据的范围是数据集中最大和最小元素之间的差。可以通过函数np.ptp()获得:
>>> np.ptp(y)
27.0
>>> np.ptp(z)
27.0
>>> np.ptp(y_with_nan)
nan
>>> np.ptp(z_with_nan)
27.0
描述性统计摘要
在SciPy和Pandas提供过单个函数或方法调用快速获取描述性统计信息。
>>> result = scipy.stats.describe(y, ddof=1, bias=False)
>>> result
DescribeResult(nobs=9, minmax=(-5.0, 41.0), mean=11.622222222222222, variance=228.75194444444446, skewness=0.9249043136685094, kurtosis=0.14770623629658886)
describe()
返回包含以下信息:
-
nobs:数据集中的观测值或元素数
-
minmax:数据的最大和最小值
-
mean:数据集的平均值
-
variance:数据集的方差
-
skewness:数据集的偏度
-
kurtosis:数据集的峰度
>>> result.nobs
9
>>> result.minmax[0] # Min
-5.0
>>> result.minmax[1] # Max
41.0
>>> result.mean
11.622222222222222
>>> result.variance
228.75194444444446
>>> result.skewness
0.9249043136685094
>>> result.kurtosis
0.14770623629658886
pandas也有类似的函数.describe():
>>> result = z.describe()
>>> result
count 9.000000 #数据集中的元素数
mean 11.622222 #数据集的平均值
std 15.124548 #数据集的标准差
min -5.000000
25% 0.100000 #数据集的四分位数
50% 8.000000
75% 21.000000
max 41.000000
dtype: float64
相关性
相关行的统计学意义也不在过多说明,但是要注意,相关性只是能从数据上判断是否有关系,不能够说明因果关系!!!
度量相关性主要使用协方差和相关性系数:
那么我们先重新创建数据
>>> x = list(range(-10, 11))
>>> y = [0, 2, 2, 2, 2, 3, 3, 6, 7, 4, 7, 6, 6, 9, 4, 5, 5, 10, 11, 12, 14]
>>> x_, y_ = np.array(x), np.array(y)
>>> x__, y__ = pd.Series(x_), pd.Series(y_)
计算协方差
>>> n = len(x)
>>> mean_x, mean_y = sum(x) / n, sum(y) / n
>>> cov_xy = (sum((x[k] - mean_x) * (y[k] - mean_y) for k in range(n))
... / (n - 1))
>>> cov_xy
19.95
numpyh和pandas都有可以返回协方差矩阵函数cov()
# numpy
>>> cov_matrix = np.cov(x_, y_)
>>> cov_matrix
array([[38.5 , 19.95 ],
[19.95 , 13.91428571]])
# pandas
>>> cov_xy = x__.cov(y__)
>>> cov_xy
19.95
>>> cov_xy = y__.cov(x__)
>>> cov_xy
19.95
计算相关系数
我们这里说的均是pearson相关系数。Pearson相关系数(Pearson CorrelationCoefficient)是用来衡量两个数据集合是否在一条线上面,它用来衡量定距变量间的线性关系。计算公式为