Python 汇总和计数进行统计分析

Python 汇总和计数进行统计分析,基于函数sum()len(),可以给出算术平均值的简单定义,如下所示:

def mean(items):
    return sum(items) / len(items)

虽然这个定义很简洁,却不能用于可迭代对象,只能用于支持len()函数的集合,这一点通过添加类型标示很容易发现。定义mean(items: Iterable) -> float不成立,因为Iterable类型不支持len()函数。

诚然,对可迭代对象即使是求简单的平均值或者标准差都是很困难的。在Python中,我们要么实例化这些序列对象,要么转而使用一些更复杂的运算来完成求值。

正确的定义方法如下:

from collections import Sequence
def mean(items: Sequence) -> float:
    return sum(items) / len(items)

定义正确的类型标示以确保sum()len()能正常工作。

下面的定义用简洁的表达式定义了平均值和标准差。

import math
s0 = len(data)  # sum(x ** 0 for x in data)
s1 = sum(data)  # sum(x ** 1 for x in data)
s2 = sum(x ** 2 for x in data)
mean = s1 / s0
stdev = math.sqrt(s2 / s0 - (s1 / s0) ** 2)

s0s1s2是3种求和,实现过程都比较简单,结构也类似。通过它们可以很方便地算出平均值。计算标准差虽然稍复杂一些,但仍然可行。
更复杂的统计函数仍然具备这种良好的对称性,包括相关度和最小方差线性回归。

两个样本之间的相关度可以通过它们的标准值计算出来,如下所示:

def z(x, μ_x: float, σ_x: float) -> float:
    return (x - μ_x) / σ_x

计算过程是对每个样本x减去平均值μ_x,再除以标准差σ_x,返回结果以σ为单位。大约2/3的情况下偏差在±1σ范围内,偏差越大,出现的概率越小,超过±3σ的概率则小于1/100。

如下所示使用该标量函数:

>>> d = [2, 4, 4, 4, 5, 5, 7, 9]
>>> list(z(x, mean(d), stdev(d)) for x in d)
[-1.5, -0.5, -0.5, -0.5, 0.0, 0.0, 1.0, 2.0]

这里首先对变量d中的值进行归一化,把计算结果实例化到一个列表中。使用生成器表达式,将标量函数z()应用于序列对象。

利用上面的函数,可以给出函数mean()stdev()的实现方法。

def mean(samples: Sequence) -> float:
    return s1(samples) / s0(samples)
def stdev(samples: Sequence) -> float:
    N = s0(samples)
    return sqrt((s2(samples) / N) - (s1(samples) / N) ** 2)

用类似的方法,如下改写前面3个求和函数:

def s0(samples: Sequence) -> float:
    return sum(1 for x in samples) # or len(data)
def s1(samples: Sequence) -> float:
    return sum(x for x in samples) # or sum(data)
def s2(samples: Sequence) -> float:
    return sum(x * x for x in samples)

尽管简洁明了,但该实现仍无法应用于可迭代对象。计算平均值时,需要对可迭代对象做一次汇总和一次计数。计算标准差时,需要做两次汇总和一次计数,针对这类统计相关的处理,必须将序列对象实例化,才能多次使用数据。

计算两个样本集相关性的函数实现如下:

def corr(samples1: Sequence, samples2: Sequence) -> float:
    m_1, s_1 = mean(samples1), stdev(samples1)
    m_2, s_2 = mean(samples2), stdev(samples2)
    z_1 = (z( x, m_1, s_1 ) for x in samples1)
    z_2 = (z( x, m_2, s_2 ) for x in samples2)
    r = (sum(zx1 * zx2 for zx1, zx2 in zip(z_1, z_2))
        / len(samples1))
    return r

上面的相关性计算用到了样本集的基本统计特征值:平均值和标准差。基于这些特征值,我们定义了两个生成器函数,计算每个样本集的归一值。接着用zip()函数(见下个示例)把两个归一化序列中的元素组对,计算每对的乘积。这个乘积序列的平均值即表示两个样本集的相关度。

计算两个样本集之间的相关度如下所示:

>>> # Height (m)
>>> xi = [1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65,
...     1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83,]
>>> # Mass (kg)
>>> yi = [52.21,53.12,54.48,55.84,57.20,58.57,59.93,61.29,
...     63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46,]
>>> round(corr( xi, yi ), 5)
0.99458

这里的两个数据点序列xiyi的相关度超过了0.99,表明二者关系紧密。

以上代码示例很好地体现了函数式编程的优点。我们用几个函数构建出了一个简单易用的统计模块,且每个函数只是简单的表达式。作为反例,不妨假设把corr()函数写成一个长而复杂的表达式,函数内部有很多只用了一次的内部变量,用复制粘贴的方法把它们替换成各自代表的表达式。不难看出,虽然这里的corr()函数仅由6行Python代码组成,但仍属于函数式编程。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程