通常来说,当我们面对大量数据时,第一步就是计算数据集的概要统计结果。也许最重要的概要统计数据就是平均值和标准差,它们能归纳出数据集典型的数值,但是其他的聚合函数也很用(如求和、乘积、中位值、最小值和最大值、分位数等)。
NumPy内建有非常快速的函数用于计算数组的统计值;本节中我们会讨论其中常用的部分。
import numpy as np
L = np.random.random(100)
sum(L)
47.16471352916828
NumPy的sum
函数的语法也差不多,当然,结果也是一样的。
np.sum(L)
47.16471352916824
然后,因为NumPy的函数是编译执行的,因此它的性能会远远超越Python的內建函数:
big_array = np.random.rand(1000000)
%timeit sum(big_array)
%timeit np.sum(big_array)
85.5 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 355 µs ± 792 ns per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
要注意的是:sum
内建函数和np.sum
并不完全相同,这有时会导致混乱。特别的,两个函数的可选参数有着不同的含义,而且np.sum
函数可以处理多维数组运算,我们将在后续章节看到。
min(big_array), max(big_array)
(1.1593646734864294e-06, 0.9999989268230569)
NumPy对应的函数也有相似的语法,但是执行高效很多:
np.min(big_array), np.max(big_array)
(1.1593646734864294e-06, 0.9999989268230569)
%timeit min(big_array)
%timeit np.min(big_array)
58.2 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 344 µs ± 1.83 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
对于min
,max
,sum
和其他NumPy聚合函数来说,也可以通过ndarray
对象的相应方法进行调用:
print(big_array.min(), big_array.max(), big_array.sum())
1.1593646734864294e-06 0.9999989268230569 500121.7142563756
任何情况下,当你操作NumPy数组时,你都应该使用NumPy的聚合函数来代替Python的內建函数。
M = np.random.random((3, 4))
print(M)
[[0.58825819 0.54628085 0.38710941 0.64627323] [0.79441627 0.10726828 0.10424018 0.13600543] [0.9110518 0.55308535 0.02069638 0.14462064]]
默认情况下,NumPy聚合函数都会返回整个数组的聚合结果标量:
M.sum()
4.939306024160479
聚合函数可以接收一个额外的参数指定一个轴让函数沿着这个方向进行聚合运算。例如,我们可以沿着行的方向计算每列的最小值,通过指定axis=0
参数即可:
M.min(axis=0)
array([0.58825819, 0.10726828, 0.02069638, 0.13600543])
这个函数返回四个值,对应着四列。
类似的,我们也可以计算每一行的最大值:
M.max(axis=1)
array([0.64627323, 0.79441627, 0.9110518 ])
上述指定axis参数的方式可能会让具有其他编程语言基础的用户感到不适应。这里的axis
参数指定的是让数组沿着这个方向进行压缩,而不是指定返回值的方向。因此指定axis=0
意味着第一个维度将被压缩:对于一个二维数组来说,就是数组将沿着列的方向进行聚合运算操作。
其他聚合函数
NumPy提供了许多其他聚合函数,但是我们不会在这里详细讨论它们。
需要说明的是,很多聚合函数都有一个NaN
安全的版本,可以忽略空缺的数据并计算得到正确的结果。
NaN
即为IEEE标准中浮点数非数值的定义(完整的讨论空缺数据的内容请参见处理空缺数据)。
部分NaN
安全的函数版本是在NumPy 1.8之后加入的,因此在老版本的NumPy中可能无法使用。
下表列出了NumPy中有用的聚合函数:
函数名称 | NaN安全版本 | 说明 |
---|---|---|
np.sum |
np.nansum |
计算总和 |
np.prod |
np.nanprod |
计算乘积 |
np.mean |
np.nanmean |
计算平均值 |
np.std |
np.nanstd |
计算标准差 |
np.var |
np.nanvar |
计算方差 |
np.min |
np.nanmin |
计算最小值 |
np.max |
np.nanmax |
计算最大值 |
np.argmin |
np.nanargmin |
寻找最小值的序号 |
np.argmax |
np.nanargmax |
寻找最大值的序号 |
np.median |
np.nanmedian |
计算中位值 |
np.percentile |
np.nanpercentile |
计算百分比分布的对应值 |
np.any |
N/A | 是否含有True值 |
np.all |
N/A | 是否全为True值 |
我们在本书后续内容中会经常看到这些聚合函数。
在NumPy中使用聚合统计来对一个数据集进行概要说明是非常有用的。下面我们使用美国总统的身高作为一个简单的例子来说明。这些数据存储在文件president_heights.csv里,文件格式就是简单的逗号分隔的文本文件:
!head -4 ../data/president_heights.csv
order,name,height(cm) 1,George Washington,189 2,John Adams,170 3,Thomas Jefferson,189
我们会使用Pandas包来读取文件和提取数据(注意身高单位是厘米),Pandas的相关内容我们会在第三章中详细介绍。
import pandas as pd
data = pd.read_csv('../data/president_heights.csv')
heights = np.array(data['height(cm)'])
print(heights)
[189 170 189 163 183 171 185 168 173 183 173 173 175 178 183 193 178 173 174 183 183 168 170 178 182 180 183 178 182 188 175 179 183 193 182 183 177 185 188 188 182 185]
获得了NumPy数组之后,我们就能计算各种的基本统计数据了:
print("Mean height: ", heights.mean()) # 身高平均值
print("Standard deviation:", heights.std()) # 标准差
print("Minimum height: ", heights.min()) # 最小值
print("Maximum height: ", heights.max()) # 最大值
Mean height: 179.73809523809524 Standard deviation: 6.931843442745892 Minimum height: 163 Maximum height: 193
上述结果中,每个聚合函数都将整个数组计算后得到一个标量值,可以让我们初步了解数据的基本分布信息。下面来计算分位值:
print("25th percentile: ", np.percentile(heights, 25)) # 25% 分位值
print("Median: ", np.median(heights)) # 50% 分位值 - 中位值
print("75th percentile: ", np.percentile(heights, 75)) # 75% 分位值
25th percentile: 174.25 Median: 182.0 75th percentile: 183.0
我们看到美国总统身高的中位值是182厘米,也就是6英尺。
当然,有时对数据进行图表展示会更加直观,我们可以通过Matplotlib工具进行(Matplotlib的知识会在第四章详细介绍)。例如,下述代码产生相应的图表:
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn; seaborn.set() # 设置图表的风格为seaborn
plt.hist(heights)
plt.title('Height Distribution of US Presidents')
plt.xlabel('height (cm)')
plt.ylabel('number');
这些聚合数据提供了我们对于数据集最基本的理解,我们会在本书后续章节更加深入的讨论它们。