统计学part3---描述统计的编程实现

192 阅读3分钟

描述统计概念部分:统计学part2---描述统计 - 掘金 (juejin.cn)

python3.10

pycharm

文件目录介绍

项目文件结构如上图所示,Statistics为总项目文件,包含了pq_stats包和main_descriptive_stats.py文件,pq_stats包用于编写实现功能的函数,main_descriptive_stats.py文件用于测试和使用包里面的函数

基础计算

我们会使用到python的collections库完成下面的内容

具体介绍请移步至:docs.python.org/zh-cn/3/lib…

频数计算

from collections import Counter

if __name__ == '__main__':
    # 频数计算
    data = [2, 2, 2, 2, 1, 1, 1, 3, 3]
    counter = Counter(data)
    print(counter.most_common())

可以看见程序输出了三个元组,其中每个元组的第一个元素代表的是我们的数据,第二个元素代表的是对应的数据出现的次数(例:2出现了4次)

频率

下面我们在pq_stats下创建一个名为descriptive_stats.py的文件,并编写一个函数用于计算频率

from collections import Counter

def frequency(data):
    """频率计算"""
    counter = Counter(data)
    ret = []  # 创建一个空列表储存函数返回值
    for point in counter.most_common():
        ret.append((point[0], point[1]/len(data)))
    return ret

然后在main_descriptive_stats.py中对函数frequency进行测试

from collections import Counter
from pq_stats.descriptive_stats import frequency

if __name__ == '__main__':
    # 频率测试
    data = [2, 2, 2, 2, 1, 1, 1, 3, 3]
    print(frequency(data))

集中趋势指标实现

众数

我们知道,众数的出现可能有三种情况:

  1. 只有一个众数
  2. 有多个众数
  3. 没有众数

所以,在寻找众数前,我们需要对数据出现的次数进行判断,然后根据次数判断的结果找众数

依然是在descriptive_stats.py中进行函数定义

def mode(data):
    """寻找众数"""
    counter = Counter(data)
    # 对数据出现次数进行判断
    if counter.most_common()[0][1] == 1:
        return None,None
    count = counter.most_common()[0][1]  # 获取众数出现的次数
    ret = []
    for point in counter.most_common():
        """判断每个数据点出现的次数是否与众数出现的次数一致"""
        if point[1] == count:
            ret.append(point[0])
        else:
            break
    return ret, count
            

解析:由于我们是按照数据出现次数的从大到小对全部数据进行遍历的,因此,只有遇见point[1]countpoint[1] \not= count 的情况,即可说明众数以全部确定,直接退出循环,并将众数列表ret返回,减少函数运行消耗的时间

if counter.most_common()[0][1] == 1:
    return None,None

解析:因为Counter.most_common()是按照数据出现的次数从高到低排序的,因此,如果第一个数据的出现次数是1,也就意味着元素最大出现次数是1,说明这组数据中不存在众数,返回None值(第一个None是众数,第二个None是众数出现的次数)

在main_descriptive_stats.py中对函数mode进行测试

from collections import Counter
from pq_stats.descriptive_stats import frequency, mode

if __name__ == '__main__':
    data = [2, 2, 2, 2, 1, 1, 1, 3, 3]
    # 众数测试
    mode_value, mode_count = mode(data)
    print(mode_value, mode_count)

更加严谨一点,我们应该先判断众数是否存在,众数不存在的情况下,我们需要给出提升信息

if mode_value:
    print(mode_value, mode_count)
else:
    print('Mode does not exist!')

中位数

通过概念我们知道,中位数是通过排序后最中间的一个数,或者是最中间两个数的平均数,因此求中位数前我们需要先对数据进行排序,如果对数据的个数进行奇偶性判断

在descriptive_stats.py中进行函数定义

def median(data):
    """中位数"""
    sorted_data = sorted(data)
    n = len(sorted_data)
    if n % 2 == 1:
        return sorted_data[n // 2]
    return (sorted_data[n // 2 - 1] + sorted_data[n // 2]) / 2

在main_descriptive_stats.py中对函数median进行测试

from collections import Counter
from pq_stats.descriptive_stats import frequency, mode, median

if __name__ == '__main__':
    # 中位数测试
    data = [2, 3, 1, 9, 6, 7, 5, 8, 4]
    print(median(data))
    data = [2, 3, 1, 4, 6, 7, 5, 8]
    print(median(data))

均值

在descriptive_stats.py中进行函数定义

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

对函数进行测试

from collections import Counter
from pq_stats.descriptive_stats import frequency, mode, median, mean

if __name__ == '__main__':
    # 均值测试
    data = [2, 3, 1, 9, 6, 7, 5, 8, 4]
    print(mean(data))

离散趋势指标实现

极差

极差是一组数据的最大值和最小值的差,我们在descriptive_stats.py中进行函数定义

def rng(data):
    """极差"""
    return max(data) - min(data)

对函数进行测试

from collections import Counter
from pq_stats.descriptive_stats import rng

if __name__ == '__main__':

    # 极差测试
    data = [2,4,6,8,2,3,9,3,0]
    print(rng(data))

四分位数

四分位数和中位数相似,同样和数据的长度相关,如果数据个数是偶数的话,我们就分别对中位数左边和右边的n2\frac n2 个数求中位数,如果数据个数是奇数,我们需要先把中位数进行剔除,然后再求四分位数,在descriptive_stats.py中进行函数定义

def quartile(data):
    n = len(data)
    q1, q2, q3 = None, None, None
    if n >= 4:
        sorted_data = sorted(data)
        q2 = median(sorted_data)  # q2就是中位数,可直接使用求中位数的函数求得
        if n % 2 == 1:  # 数据长度为奇数
            q1 = median(sorted_data[:n // 2])
            q3 = median(sorted_data[n // 2 + 1:])
        else:
            q1 = median(sorted_data[:n // 2])
            q3 = median(sorted_data[n//2:])
    return q1, q2, q3
if n >= 4:

解析:如果数据的个数小于4,四分位数不存在

对函数进行测试

from collections import Counter
from pq_stats.descriptive_stats import frequency, mode, median, mean,rng, quartile

if __name__ == '__main__':
    # 四分位数测试
    data = [9,3,6,4,8,1,2,7,5]
    print(quartile(data))

方差

方差的计算,我们需要知道数据的增值和数据长度,在descriptive_stats.py中进行函数定义

def variance(data):
    """方差"""
    n = len(data)
    if n <= 1:
        return None
    mean_value = mean(data)   # 方差的计算需要使用均值
    return sum((e - mean_value) ** 2 for e in data) / (n - 1)
if n <= 1:
    return None

解析:如果数据为空或只有一个数据,方差也为空

sum((e - mean_value) ** 2 for e in data) / (n - 1)

解析:在方差的数学公式在,我们的被除数是n,但是这个时候我们需要根据实际情况进行判断,如果我们计算的是全部数据,我们的被除数为n,如果我们计算的是样本数据,我们的被除数应该为n-1,在数据分析过程中,我们获取到的数据大多数情况下都是样本数据,因此这里我们把被除数设置为n-1

对函数进行测试

from collections import Counter
from pq_stats.descriptive_stats import frequency, mode, median, mean,rng, quartile, variance
# 方差测试
data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(variance(data))

标准差

标准差从定义上来讲,就是对方差进行开根,我们需要在math库在调用开根的函数sqrt(),上面已经对方差进行计算了,下面我们在descriptive_stats.py中进行函数定义

def std(data):
    """标准差"""
    return sqrt(variance(data))

对函数进行测试

from collections import Counter
from pq_stats.descriptive_stats import frequency, mode, median, mean,rng, quartile, variance,std

if __name__ == '__main__':
    # 方差测试
    data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    print(variance(data))

    # 标准差测试
    print(std(data))