Python数学编程 第三章数据的统计学特征 第一二三节

第三章 数据的统计学特征

在本章节中,我们将使用Python来探索统计数据,以便我们研究、描述和更好的理解数据集。在学习了一些基本统计指标(均值、中位数、众数和极差)之后,我们会继续学习一些更高级的指标,例如方差和标准差。然后,我们将学习如何计算相关系数,它能够量化两组数据之间的关系。我们将通过学习散点图来结束本章。这一过程中,我们将进一步了解Python语言和标准库模块。让我们从最常用的统计指标——均值开始进入本章学习吧!

第一节 计算均值

均值(mean)是总结一组数字的一种常见而又直观的方法,我们可以简单地将其理解为日常生活中使用的“平均数”这一概念,不过后续我们学习的,还有其他类型的平均数。现在,先取一组样本来计算均值。

假设有一个学校慈善机构在过去12天内接受了捐款(我们将此称为A时期)。在这一期间,以下12个数字代表了每天收到的捐款总额:100,60,70,900,100,200,500,500,503,600,1000,1200。我们可以通过计算总和,再将总和除以天数得到均值。这里,数字的总和是5733。如果我们将这个数字除以12(天数),得到477.75,这就是每天的平均捐款,这个数字让我们大致了解在这12天中的任何一天平均捐赠了多少钱。

稍后,我们将编写一个程序,用于计算和输出一组数字的均值。正如我们刚刚看到的,为了计算均值,需要用数字列表的总和除以列表中的项数。我们来看两个Python函数sum()和 len(),它们使这两个运算变得非常简单。 当你对数字列表使用sum()函数时,它会将列表中的所有数字相加并返回结果:


 >>> shortlist = [1, 2, 3] >>> sum(shortlist) 6

我们可以使用len()函数输出一个列表的长度:


 >>> len(shortlist) 3

当我们在列表中使用len()函数时,结果会返回3,因为shortlist列表中有三项。现在我们来编写一个程序计算每天的捐款均值。


 ''' Caculating the mean ''' def caculate_mean(numbers):     return sum(numbers) / len(numbers) if __name__ == '__main__':     donations = [100, 60, 70, 900, 100, 200, 500, 500, 503, 600, 1000, 1200]     mean = caculate_mean(donations)     N = len(donations)     print('Mean donation over the last {0} days is {1} '.format(N, mean))

可以看到我们定义另一个函数caculate_mean(),接收的参数为一个数字列表,然后返回列表中数据的均值,使用sum()/len()的方式计算列表均值。在main()程序块里,我们创建一个donations[]列表存储12的捐款数,然后将其传入到caculate_mean()函数中,计算均值,然后进行指定格式输出。

运行程序我们会看到:


 Mean donation over the last 12 days is 477.75 

由于calculate_ mean()函数可以将计算任何列表的总和以及长度,因此我们也可以用它来计算其他数字集合的均值,只需要替换掉donations[]列表即可。 我们计算出每天的平均捐款是477.75。值得注意的是,最初几天的捐款远远低于我们计算的平均捐款,而后面几天的捐款要高得多。这个均值为我们提供了一个总结数据的方法,但还没有给出更全面的信息。然而,与均值相比,还有其他统计量可以告诉我们更多关于数据的信息。

第二节 计算中位数

一组数字的中位数(median) 是另一.种类型的平均数。要计算中位数,我们需要对数字进行升序或者降序排列。如果数字列表的长度是奇数,则列表中间的数字是中位数;如果数字列表的长度是偶数,我们通过取两个中间数的均值来获得中位数。下面我计算之前捐款列表的中位数: 100, 60,70, 900, 100, 200, 500, 500, 503, 600,1000,1200。

对上述数字由小到大排序,排序后的数字列表为60,70, 100, 100, 200, 500, 500,503, 600, 900,1000,1200。 列表项数为偶数(12), 因此要得到中位数,我们 需要取两个中间数字的均值。在这种情况下,中间数字是第六位数字和第七位数字(500 和500),这两个数字的均值是(500 + 500) / 2,也是500。这意味着中位数是500。

假设我们在第13天也接受了捐款,这样列表现在看起来像这样: 100, 60, 70, 900,100, 200, 500, 500, 503, 600, 1000, 1200, 800。 我们仍然必须对列表进行排序,分别为60,70,100, 100, 200, 500, 500, 503, 600, 800, 900,1000,1200。该列表中有13个数字(奇数),所以这个列表的中位数就是中间数字,即第 七个数字,也就是500。

在我们编写程序计算数字列表的中位数之前,让我们考虑一下如何在任何一种情况下自动计算列表的中间元素。如果列表的长度(N)是奇数,则中位数位于(N+ 1) / 2的位置。如果N是偶数,则两个中间元素分别位于N/2和(N/2) +1的位置。对于本节的第一一个例子,N= 12,因此两个中间元素是12/2 (第六)和12/2+1 (第七)个元素。在第二个例子中,N=13,因此(N+1) /2 (第七)个元素是中间元素。 为了编写一个计算中位数的函数,我们还需要对列表进行升序排列。幸运的是,可以使用sort()函数来实现:


 >>> samplelist = [4, 1, 3] >>> samplelist.sort() >>> samplelist [1, 3, 4]

现在我们可以编写一个程序,来计算一个数字列表的中位数:


 ''' Caculating the median '''  def caculate_median(numbers):     N = len(numbers)     numbers.sort()     if N % 2 == 0:         return (numbers[int(N / 2) - 1] + numbers[int(N / 2)]) / 2     else:         return numbers[int((N + 1) / 2) - 1]  if __name__ =='__main__':     donations = [100, 60, 70, 900, 100, 200, 500, 500, 503, 600, 1000, 1200]     median = caculate_median(donations)     N = len(donations)     print('Median donation over the last {0} days is {1} '.format(N, median))

该程序的主体结构与计算均值类似。区别在于函数内部caculate_median(),首先对数据进行排序,其次为了区别数据列表的长度为偶数或是奇数,使用了一个分支结构if...else,如果列表长度为偶数,则返回第(N / 2)个与第(N / 2)+ 1个数据的平均数,如果列表长度为奇数,就返回第(N + 1) / 2 个数据,其中对于下标而言都要进行-1的操作,这样由于手算位置和索引不匹配而进行的,并且还要进行int()操作将计算出的浮点型转为整数,比如列表长度N = 12,计算N / 2时,返回的并不是6,而是6.0


 >>> 12 / 2 6.0

由于不能使用浮点数作为列表索引,因此需要使用int()将结果转为整数。

运行程序,我们会得到:


 Median donation over the last 12 days is 500.0 

可以看到,中位数(500)与均值(477.75)相当接近,中位数略高些。

第三节 计众数并创建频数表

如果不是计算一组数字的均值或中位数,而是想找到出现频率最高的数字怎么办?这个数字称为众数(mode)。例如,考虑20名学生的数学考试成绩(满分10分):7,8,9,2,10,9,9,9,9,4,5,6,1,5,6,7,8,6,1,10。这个列表的众数可以告诉你班级中最常见的分数。你可以从列表中发现分数9出现的频率最高,所以9是这个数字列表的众数。没有用于计算众数的符号公式,你只需计算每个数字出现的次数,并找到出现次数最多的数字。 要编写一个程序来计算众数,我们需要让Python计数每个数字在列表中出现的次数,并输出最频繁出现的数字。来自collections 模块的Counter类是标准库的一部分,可以帮助我们轻松地得到结果。

3.3.1 寻找最常见的元素

查找数据集中最常见的数字,可以被认为是寻找任意数量最常见数字的子问题。例如,如果我们想知道前五个出现频率最高的数字而非出现频率最高的那一个数字应该怎么办?Counter类地most_common()函数能够轻松实现这些问题,我们来看一个例子:


 >>> simplelist = [4, 2, 1, 3, 4] >>> from collections import Counter >>> c = Counter(simplelist) >>> c.most_common() [(4, 2), (2, 1), (1, 1), (3, 1)]

我们创建了一个包含五个数字的简单列表,并从collections模块导入了Counters类。然后我们创建了一个Counter对象,使用c来指代该对象。接着我们调用most_common()函数,该函数将返回一个列表。列表的每个成员都是一个元组。其中每个元组的第一个元素是出现次数最频繁的数字,第二个元素是该数字出现的次数。该结果告诉我们,出现最频繁的数字为4,且出现了2次,其他数字均只出现一次。注意:most_common()函数对于出现次数相同的数字顺序是任意的。

可以看到,most_commmon()函数返回的结果是一个列表,那我们可以想到能不能拿到其中的第1个元素呢。当然是可以的,我们只需要提供一个索引即可,如下,就可以返回列表第一个元素:


 >>> c.most_common().[0] (4, 2)  

还有,我们还可以传入参数i,这个就会返回有i个元素的列表:


 >>> c.most_common(1) [(4, 2)] >>> c.most_common(2) [(4, 2), (2, 1)]

对于出现次数相同的数字,返回的列表是任意的,如前所述。

most_common()函数返回数字及其出现的次数。我们直选哟徐子而不需要他们出现的次数应该怎么办?假设要提取第一个元素的第一个位置(数字)


 >>> mode = c.most_common(1) >>> mode [(4, 2)] >>> mode[0] (4, 2) >>> mode[0][0] 4 >>> mode = c.most_common()[0][0] >>> mode 4

综合运用上述讲到的知识,我们就可以先通过mode[0]提取第一个元组,然后根据mode[0][0]提取第一个元组中的第一个数字(出现频率最高的那个数字),结果返回为4。

知晓most_common()的工作原理后,我们可以使用它来解决下面的两个问题。

3.3.2 计算众数

首先,对于之前的课程分数数据,我们编写一个计算数据的众数:


 ''' Caculating the mode '''  from collections import Counter def caculate_mode(numbers):     c = Counter(numbers)     mode = c.most_common()     return mode[0][0]  if __name__ == '__main__':     scores = [7, 8, 9, 2, 10, 9, 9, 9, 9, 4, 5, 6, 1, 5, 6, 7, 8, 6, 1, 10]     mode = caculate_mode(scores)     print('The mode of the list of numbers is : {0}'.format(mode))

对于与程序结构,不再做过多说明。直接进入到caculate_mode()函数内部,创建一个Counter对象,使用标签c来指代它,然后调用most_common()方法返回列表,返回的列表由mode指代,然后返回第一个元素的第一个位置,即出现频率最高的那个数字。

运行程序,会有如下输出:


 The mode of the list of numbers is : 9

如果有一组数据,其中有若干个数字出现的频次一样多且为最多,该怎么办呢,比如5,5,5,4,4,4,3,2,1种,4和5都出现了三次,显然刚刚的程序并不能满足这个需求,它只能返回一个数字,对上述程序进行修改如下:


 ''' Caculating the mode '''  from collections import Counter def caculate_mode(numbers):     c = Counter(numbers)     fre = c.most_common()     max_count = fre[0][1]      modes = []     for num in fre:         if num[1] == max_count:             modes.append(num[0])     return modes           return mode[0][0]  if __name__ == '__main__':     scores = [5, 5, 5, 4, 4, 4, 3, 2, 1]     modes = caculate_mode(scores)     print('The mode(s) of the list of numbers are :')     for mode in modes:         print(mode)

我们需要检查每个数字出现的次数,而不是只查找出现次数最高的一个元素。此时,先统计好此线次数的最大值(max_count = mode[0][1])。然后对于most_common()返回的列表fre里面的每个元组做一个迭代检查,检查出现次数和max_count相等的值,如果相等就将元组的第一个位置(数字),存储到modes列表里

执行程序,可以看到有以下输出:


 The mode(s) of the list of numbers are : 5 4

如果你想找到每个出自出现的次数,而不仅仅是数字,又该怎么办?你可以使用频数表,顾名思义,就是能够显示每个数字及其出现次数的表。而频数表很容易实现,其实刚刚most_common()已经给我们了制表数据。

3.3.3 创建频数表

我们再次考虑分数列表:[7, 8, 9, 2, 10, 9, 9, 9, 9, 4, 5, 6, 1, 5, 6, 7, 8, 6, 1, 10]。该列表的频数表如下图所示,对于每个分数,我们在第二列中给出它出现的次数。

频数表

请注意,第二列中各频数只和需要等于列表数据的总个数(这个例子中为20)。

我们再次使用most_ common()函 数输出一组给定数字的频数表。回想一下,当我们不带参数调用most common()函 数时,它将返回一一个列表,其中包含所有数字及其出现次数的元组。我们可以简单地从这个列表中输出每个数字及其频数,以显示一个频数表。 这是程序:


 ''' Frequency table for a list of numbers '''  from collections import Counter  def frequency_table(numbers):     c = Counter(numbers)     mode = c.most_common()     print('Number	Frequency')     for number in mode:         print('{0} 	 {1}'.format(number[0], number[1])) if __name__ == '__main__':     scores = [7, 8, 9, 2, 10, 9, 9, 9, 9, 4, 5, 6, 1, 5, 6, 7, 8, 6, 1, 10]     frequency_table(scores)

对于不带参数的most_common()列表,其中的每个元素均为元组,我们对里面的每个元组做处理,只需要输出元组的第一个元素以及第二个元素即可,中间使用制表符( )隔开,运行程序,可以看到已经输出了频数表:


 Number  Frequency 9    5 6    3 7    2 8    2 10   2 5    2 1    2 2    1 4    1

可以看到输出的频数表中分数是从大到小的排列,与most_common()函数返回的列表顺序相同,但是我们想要按照分数从小到大进行输出,我们只需在打印之前加入sort()函数即可:


 ''' Frequency table for a list of numbers '''  from collections import Counter  def frequency_table(numbers):     c = Counter(numbers)     mode = c.most_common()     mode.sort()     print('Number	Frequency')     for number in mode:         print('{0} 	 {1}'.format(number[0], number[1])) if __name__ == '__main__':     scores = [7, 8, 9, 2, 10, 9, 9, 9, 9, 4, 5, 6, 1, 5, 6, 7, 8, 6, 1, 10]     frequency_table(scores)

运行代码:


 Number  Frequency 1    2 2    1 4    1 5    2 6    3 7    2 8    2 9    5 10   2

此时可以看到,频数表中的分数列为从小到大排列,到此,众数指标完毕。



发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章