Day 29:NumPy 通过这五大功能顺利入门 + 10 道练习题
发布日期:2022年3月12日 18:02 阅读: 242 访问: 243
NumPy 更高效Python 已经提供了很多丰富的内置包,我们为什么还要学习 NumPy 呢?先看一个例子,找寻学习 NumPy 的必要性和重要性。打开 IPython,创建 Python 的列表 a 对象。然后,使用列表生成式,创建一个元素都为原来两倍的新列表 a2,并统计这一行的用时为 95.7 ms。<code clas
NumPy 更高效
Python 已经提供了很多丰富的内置包,我们为什么还要学习 NumPy 呢?
先看一个例子,找寻学习 NumPy 的必要性和重要性。
打开 IPython,创建 Python 的列表 a 对象。然后,使用列表生成式,创建一个元素都为原来两倍的新列表 a2,并统计这一行的用时为 95.7 ms。
In [76]: a = list(range(1000000))
In [77]: %time a2 = [i*2 for i in a]
Wall time: 95.7 ms
使用 NumPy,创建同样大小和取值的数组 na。然后,对每个元素乘以 2,返回一个新数组 na2,用时为 2 ms。
In [78]: import numpy as np
In [79]: na = np.array(range(1000000))
In [80]: %time na2 = na * 2
Wall time: 2 ms
完成同样的都对元素乘以 2 的操作,NumPy 比 Python 快了 45 倍之多。
这就是我们要学好 NumPy 的一个重要理由,它在处理更大数据量时,处理效率明显快于 Python;并且内置的向量化运算和广播机制,使得使用 NumPy 更加简洁,会少写很多嵌套的 for 循环,因此代码的可读性大大增强。
NumPy 计算为什么这么快呢?
有多个原因:
- Python 的 list 是一个通用结构。它能包括任意类型的对象,并且是动态类型。
- NumPy 的 ndarray 是静态、同质的类型,当 ndarray 对象被创建时,元素的类型就确定。由于是静态类型,所以 ndarray 间的加、减、乘、除用 C 和 Fortran 实现才成为可能,所以运行起来就会更快。根据官当介绍,底层代码用 C 语言和 Fortran 语言实现,实现性能无限接近 C 的处理效率。
由此可见,NumPy 就非常适合做大规模的数值计算和数据分析。
今天,我们一起学习 NumPy 的基本使用,借助实际的 iris 数据集,使用例子帮助大家最快掌握 NumPy 那些最高频使用的函数。
创建 NumPy 数组
创建一个 ndarray 数组对象,有很多种方法。array 函数能创建新的数组;arange、linspace 等方法;从文件中读入数据返回一个 ndarray 对象;多个 ndarray 对象又能构造生成一个新的 ndarray 对象。
1. 通过构造函数 array 创建一维 array
import numpy as np
In [2]: v = np.array([1,2,3,4])
In [3]: v
Out[3]: array([1, 2, 3, 4])
2. 创建二维 array
In [4]: m = np.array([[1,2],[3,4]])
In [5]: m
Out[5]:
array([[1, 2],
[3, 4]])
v 和 m 的类型都是 ndarray,NumPy 中最主要的数据结构。
In [6]: type(v),type(m)
Out[6]: (numpy.ndarray, numpy.ndarray)
3. arange 数组
In [94]: ara = np.arange(1,10)
In [95]: ara
Out[95]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])
4. linspace 数组,15 个元素
In [97]: np.linspace(1,10,15)
Out[97]:
array([ 1. , 1.64285714, 2.28571429, 2.92857143, 3.57142857,
4.21428571, 4.85714286, 5.5 , 6.14285714, 6.78571429,
7.42857143, 8.07142857, 8.71428571, 9.35714286, 10. ])
5. 组合 ndarray 对象
如下创建一个 ndarray 对象 a:
In [98]: a = np.arange(10).reshape(2,-1)
In [99]: a
Out[99]:
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
找出 a 中大于 3 的元素索引,使用 where 方法,返回一个元组,带有 2 个 ndarray 对象,分别表示大于 3 的元素第一维、第二维度中的位置:
In [100]: np.where(a>3)
Out[100]:
(array([0, 1, 1, 1, 1, 1], dtype=int64),
array([4, 0, 1, 2, 3, 4], dtype=int64))
where 方法返回值可读性不强,我们把它拼接为一个 ndarray 对象:
In [101]: np.array(np.where(a>3))
Out[101]:
array([[0, 1, 1, 1, 1, 1],
[4, 0, 1, 2, 3, 4]], dtype=int64)
然后,再转置,使用 np.transpose 方法:
tuple_to_array = np.array(np.where(a>3))
np.transpose(tuple_to_array)
结果,这回一看就明白了,[0,4] 表示在原 ndarray 对象 a 上的索引:
Out[102]:
array([[0, 4],
[1, 0],
[1, 1],
[1, 2],
[1, 3],
[1, 4]], dtype=int64)
NumPy 数组属性
1. shape 属性
数组的形状信息,非常重要。在深度学习中,构建网络模型,调试多维数组运算代码时,shape 的作用更加凸显。shape 属性返回数组的形状信息,是一个元组对象。
如下,分别创建一维数组 v,二维数组 m:
In [108]: v = np.zeros(10)
In [109]: m = np.ones((3,4))
打印它们的 shape 信息,(10,) 表示为一维数组,且第一维的长度为 10。当元组只有一个元素时,为什么写成这样,我们在前面的 Python 基础部分、进阶部分都提到过。
In [110]: v.shape
Out[110]: (10,)
In [111]: m.shape
Out[111]: (3, 4)
size 属性获取数组内元素个数:
In [112]: m.size
Out[112]: 12
dtype 属性获取数组内元素的类型:
In [113]: m.dtype
Out[113]: dtype('float64')
如果我们尝试用 str 类型赋值给 m,就会报错:
In [10]: m[0,0]='hello'
ValueError: could not convert string to float: 'hello'
创建数组时,还可以通过为 dtype 赋值,指定元素类型:
In [117]: m = np.array([1,2,3],dtype='float')
In [118]: m
Out[118]: array([1., 2., 3.])
dtype 更多取值:int、complex、bool、object,还可以显示的定义数据位数的类型,如:int64、int16、float128、complex128。
个数组函数
1. arange 函数
起始点,终点,步长;不包括终点。前面用到过,不再介绍。
2. li nspace 函数
起始点,终点,分割份数;包括终点。
In [5]: np.linspace(0,10,5)
Out[5]: array([ 0. , 2.5, 5. , 7.5, 10. ])
3. logspace 函数
创建以 e 为底,指数为 1,2,…,10 的数组:
In [17]: np.logspace(1, 10, 10, base=e)
Out[17]:
array([2.71828183e+00, 7.38905610e+00, 2.00855369e+01, 5.45981500e+01,
1.48413159e+02, 4.03428793e+02, 1.09663316e+03, 2.98095799e+03,
8.10308393e+03, 2.20264658e+04])
4. diag
创建对角数组:
In [22]: np.diag([1,2,3])
Out[22]:
array([[1, 0, 0],
[0, 2, 0],
[0, 0, 3]])
主对角线偏移 1 的数组:
In [23]: np.diag([1,2,3],k=1)
Out[23]:
array([[0, 1, 0, 0],
[0, 0, 2, 0],
[0, 0, 0, 3],
[0, 0, 0, 0]])
5. zeros
创建元素全都为 0 的数组:
In [24]: np.zeros((3,3))
Out[24]:
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
6. ones
创建元素全都为 1 的数组:
In [25]: np.ones((3,3))
Out[25]:
array([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
7. 随机数组
np.random 模块生成随机数组,更加方便。
生成 0~1、shape 为 (3,5) 的随机数数组:
In [2]: np.random.rand(3,5)
Out[2]:
array([[0.25366147, 0.18996607, 0.01599463, 0.08113353, 0.99794258],
[0.38813147, 0.33669704, 0.97040282, 0.0836301 , 0.55533133],
[0.17767781, 0.94982834, 0.52045864, 0.58504198, 0.40904079]])
索引和筛选
NumPy 索引,功能强大,不仅支持切片操作,还支持布尔型按条件筛选操作。
In [2]: m = np.arange(18).reshape(2,3,3)
In [3]: m
Out[3]:
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]]])
:
表示此维度的所有元素全部获取:
In [4]: m[:,1:3,:]
Out[4]:
array([[[ 3, 4, 5],
[ 6, 7, 8]],
[[12, 13, 14],
[15, 16, 17]]])
In [5]: m[:,1:3,:1]
Out[5]:
array([[[ 3],
[ 6]],
[[12],
[15]]])
按照维度赋值:
In [7]: m[:,0,:] = -1
In [8]: m
Out[8]:
array([[[-1, -1, -1],
[ 3, 4, 5],
[ 6, 7, 8]],
[[-1, -1, -1],
[12, 13, 14],
[15, 16, 17]]])
In [11]: m[:,:,0]
Out[11]:
array([[-1, 3, 6],
[-1, 12, 15]])
NumPy 还支持掩码索引,用于元素筛选,非常方便。
判断上面切片 m[:,:,0]
中大于 5 的元素,写法简洁,无需写 for 循环。
In [12]: mt = m[:,:,0]
In [13]: mt[mt>5]
Out[13]: array([ 6, 12, 15])
案例:数据归一化
1. 下载数据
使用 NumPy,下载 iris 数据集。仅提取 iris 数据集的第二列 usecols = [1]
:
import numpy as np
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
wid = np.genfromtxt(url, delimiter=',', dtype='float', usecols=[1])
展示数据:
array([3.5, 3. , 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3. ,
3. , 4. , 4.4, 3.9, 3.5, 3.8, 3.8, 3.4, 3.7, 3.6, 3.3, 3.4, 3. ,
3.4, 3.5, 3.4, 3.2, 3.1, 3.4, 4.1, 4.2, 3.1, 3.2, 3.5, 3.1, 3. ,
3.4, 3.5, 2.3, 3.2, 3.5, 3.8, 3. , 3.8, 3.2, 3.7, 3.3, 3.2, 3.2,
3.1, 2.3, 2.8, 2.8, 3.3, 2.4, 2.9, 2.7, 2. , 3. , 2.2, 2.9, 2.9,
3.1, 3. , 2.7, 2.2, 2.5, 3.2, 2.8, 2.5, 2.8, 2.9, 3. , 2.8, 3. ,
2.9, 2.6, 2.4, 2.4, 2.7, 2.7, 3. , 3.4, 3.1, 2.3, 3. , 2.5, 2.6,
3. , 2.6, 2.3, 2.7, 3. , 2.9, 2.9, 2.5, 2.8, 3.3, 2.7, 3. , 2.9,
3. , 3. , 2.5, 2.9, 2.5, 3.6, 3.2, 2.7, 3. , 2.5, 2.8, 3.2, 3. ,
3.8, 2.6, 2.2, 3.2, 2.8, 2.8, 2.7, 3.3, 3.2, 2.8, 3. , 2.8, 3. ,
2.8, 3.8, 2.8, 2.8, 2.6, 3. , 3.4, 3.1, 3. , 3.1, 3.1, 3.1, 2.7,
3.2, 3.3, 3. , 2.5, 3. , 3.4, 3. ])
单变量(univariate),长度为 150 的一维 NumPy 数组。
2. 归一化
求出最大值、最小值:
smax = np.max(wid)
smin = np.min(wid)
In [51]: smax,smin
Out[51]: (4.4, 2.0)
归一化公式:
s = (wid - smin) / (smax - smin)
还有一个更简便的方法,使用 ptp 方法,它直接求出最大值与最小值的差:
s = (wid - smin) / wid.ptp()
3. NumPy 的打印设置
只打印小数点后三位的设置方法:
np.set_printoptions(precision=3)
归一化结果:
array([0.625, 0.417, 0.5 , 0.458, 0.667, 0.792, 0.583, 0.583, 0.375,
0.458, 0.708, 0.583, 0.417, 0.417, 0.833, 1. , 0.792, 0.625,
0.75 , 0.75 , 0.583, 0.708, 0.667, 0.542, 0.583, 0.417, 0.583,
0.625, 0.583, 0.5 , 0.458, 0.583, 0.875, 0.917, 0.458, 0.5 ,
0.625, 0.458, 0.417, 0.583, 0.625, 0.125, 0.5 , 0.625, 0.75 ,
0.417, 0.75 , 0.5 , 0.708, 0.542, 0.5 , 0.5 , 0.458, 0.125,
0.333, 0.333, 0.542, 0.167, 0.375, 0.292, 0. , 0.417, 0.083,
0.375, 0.375, 0.458, 0.417, 0.292, 0.083, 0.208, 0.5 , 0.333,
0.208, 0.333, 0.375, 0.417, 0.333, 0.417, 0.375, 0.25 , 0.167,
0.167, 0.292, 0.292, 0.417, 0.583, 0.458, 0.125, 0.417, 0.208,
0.25 , 0.417, 0.25 , 0.125, 0.292, 0.417, 0.375, 0.375, 0.208,
0.333, 0.542, 0.292, 0.417, 0.375, 0.417, 0.417, 0.208, 0.375,
0.208, 0.667, 0.5 , 0.292, 0.417, 0.208, 0.333, 0.5 , 0.417,
0.75 , 0.25 , 0.083, 0.5 , 0.333, 0.333, 0.292, 0.542, 0.5 ,
0.333, 0.417, 0.333, 0.417, 0.333, 0.75 , 0.333, 0.333, 0.25 ,
0.417, 0.583, 0.458, 0.417, 0.458, 0.458, 0.458, 0.292, 0.5 ,
0.542, 0.417, 0.208, 0.417, 0.583, 0.417])
分布可视化:
import seaborn as sns
sns.distplot(s,kde=False,rug=True)
频率分布直方图:
sns.distplot(s,hist=True,kde=True,rug=True)
带高斯密度核函数的直方图:
10 道 NumPy 基础题
1. 创建一个 [3,5] 所有元素为 True 的数组
In [15]: np.ones((3,5),dtype=bool)
Out[15]:
array([[ True, True, True, True, True],
[ True, True, True, True, True],
[ True, True, True, True, True]])
2. 一维数组转二维
In [18]: a = np.linspace(1,5,10)
In [19]: a.reshape(5,2)
Out[19]:
array([[1. , 1.44444444],
[1.88888889, 2.33333333],
[2.77777778, 3.22222222],
[3.66666667, 4.11111111],
[4.55555556, 5. ]])
3. 数组所有奇数替换为 -1
In [14]: m = np.arange(10).reshape(2,5)
In [16]: m[m%2==1] = -1
In [17]: m
Out[17]:
array([[ 0, -1, 2, -1, 4],
[-1, 6, -1, 8, -1]])
4. 提取出数组中所有奇数
In [18]: m = np.arange(10).reshape(2,5)
In [19]: m[m%2==1]
Out[19]: array([1, 3, 5, 7, 9])
复制
5. 2 个 NumPy 数组的交集
In [21]: m ,n = np.arange(10), np.arange(1,15,3)
In [22]: np.intersect1d(m,n)
Out[22]: array([1, 4, 7])
6. 2 个 NumPy 数组的差集
In [21]: m ,n = np.arange(10), np.arange(1,15,3)
In [23]: np.setdiff1d(m,n)
Out[23]: array([0, 2, 3, 5, 6, 8, 9])
7. 筛选出指定区间内的所有元素
注意:(m >2),必须要添加一对括号
In [21]: m = np.arange(10).reshape(2,5)
In [34]: m[(m > 2) & (m < 7)]
Out[34]: array([3, 4, 5, 6])
8. 二维数组交换 2 列
In [21]: m = np.arange(10).reshape(2,5)
In [37]: m
Out[37]:
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
In [36]: m[:,[1,0,2,3,4]]
Out[36]:
array([[1, 0, 2, 3, 4],
[6, 5, 7, 8, 9]])
可以一次交换多列:
In [38]: m[:,[1,0,2,4,3]]
Out[38]:
array([[1, 0, 2, 4, 3],
[6, 5, 7, 9, 8]])
9. 二维数组,反转行
In [39]: m = np.arange(10).reshape(2,5)
In [40]: m
Out[40]:
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
In [41]: m[::-1]
Out[41]:
array([[5, 6, 7, 8, 9],
[0, 1, 2, 3, 4]])
10. 生成数值 5~10、shape 为 (3,5) 的随机浮点数
In [9]: np.random.seed(100)
In [42]: np.random.randint(5,10,(3,5)) + np.random.rand(3,5)
Out[42]:
array([[9.31623868, 5.68431289, 9.5974916 , 5.85600452, 9.3478736 ],
[5.66356114, 7.78257215, 7.81974462, 6.60320117, 7.17326763],
[7.77318114, 6.81505713, 9.21447171, 5.08486345, 8.47547692]])
小结
今天与大家一起学习了数值计算很有用的 NumPy 包,包括:
- 数组创建
- 数组的常用属性
- 7 个生成数组的函数
- 强大的索引,掩码索引,数据筛选
- 一个使用 NumPy 完成数据归一化的案例
- 10 道 NumPy 基础题目
下一讲,深入探讨 NumPy 包其他常用的方法,及其对应的 NumPy 练习题等。