3.1.3 数组运算
前面提到,NumPy的核心是多维数组ndarray。我们可以对数组ndarray进行多种变换和科学计算,并且可以在Python中通过将其他形式的数据转换成数组以达到快速计算的目的,因为NumPy数组具有很多非常适合科学运算的特性,能大大提升计算的效率。
1.创建数组
创建数组最简单的方法是使用array函数。它接收一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组。下面以一个列表的转换为例:
In [54]: data1 = [5.5,6.0,7,2,4.5] In [55]: array1 = np.array(data1) In [56]: array1 Out[56]: array([5.5, 6. , 7. , 2. , 4.5])
如果列表中又含有两个或多个等长列表,也就是嵌套序列,那么转换成数组后就会得到一个二维或多维数组。
In [57]: data2 =[[1,2,3],[4,5,6]] In [58]: array2 = np.array(data2) In [59]: array2 Out[59]: array([[1, 2, 3], [4, 5, 6]]) In [62]: data3 = [[[1,2,3],[4,5,6],[7,8,9]]] In [63]: array3 = np.array(data3) In [64]: array3 Out[64]: array([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]])
可以通过数组本身的属性ndim(维度)和shape(形状)两种方法来判断数组的维度。
In [65]: array2.ndim Out[65]: 2 In [66]: array2.shape Out[66]: (2, 3) In [67]: array3.ndim Out[67]: 3 In [68]: array3.shape Out[68]: (1, 3, 3)
由此可以看出,数组的维度是由构成它的列表来决定的,也就是说NumPy数组array2和array3的维度shape分别由data2和data3决定。常用的数组维度是1维(1行n列)、2维(n行n列)、3维(n块n行n列)。
除前面介绍的ndim和shape属性外,我们常用的还有dtype属性,用于判断数组数据的类型。
In [69]: array1.dtype Out[69]: dtype('float64') In [70]: array2.dtype Out[70]: dtype('int32')
可以看到,array1是float64(浮点数),array2是int32(整数)。如果没有特别指定,数据类型基本都是float64(浮点数)。
前面我们是通过np.array来创建数组,除np.array之外,还有一些函数可以新建数组。比如,zeros和ones可以分别创建指定长度(形状)的全0和全1数组,empty可以创建一个没有具体含义值的数组。在使用这些方法创建多维数组时,只需传入一个表示形状的元组即可:
In [71]: np.zeros(5) Out[71]: array([0., 0., 0., 0., 0.]) In [72]: np.ones((3,6)) Out[72]: array([[1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.]]) In [73]: np.empty((3,5,3)) Out[73]: array([[[ 4.59257496e-316, 3.68273449e-316, 3.70849852e-306], [-1.48085204e+108, 8.24866594e+141, 3.21820533e-085], [ 2.21719207e-301, -4.16984184e+122, 3.88557708e-301], [ 2.43359151e-301, 2.29323831e-301, 1.57162294e-181], [ 3.88739986e-301, 2.43541431e-301, 2.29414970e-301]], [[ 3.88928413e-008, 3.88922264e-301, 2.43678142e-301], [ 3.71962406e-306, -1.71736520e+185, 3.89195681e-301], [ 2.43951563e-301, 2.29642818e-301, 2.66613464e-056], [-1.39297646e+214, 8.26652871e+141, 4.92090464e-037], [ 3.89560237e-301, 2.44270555e-301, 2.29825096e-301]], [[ 3.22677015e-032, 3.89742515e-301, 2.44407265e-301], [ 2.29916235e-301, 2.11587939e-027, 3.89924793e-301], [ 2.44543976e-301, 2.30007374e-301, 1.38743820e-022], [ 3.90107071e-301, 2.44680687e-301, 2.30098513e-301], [ 2.79137107e-109, 3.96177991e-297, 4.10074486e-322]]])
np.empty很多情况下返回的都是一些未初始化的值。所以,我们一般不建议使用np.empty函数来创建数组。
2.常用运算
NumPy可以对数组对象ndarray进行多种数学运算,其语法跟标量元素之间的运算一样。
In [74]: data = np.array([[0.9,0.35,0.14,0.46],[0.53,0.78,0.12,0.86]]) In [75]: data Out[75]: array([[0.9 , 0.35, 0.14, 0.46], [0.53, 0.78, 0.12, 0.86]])
数组的一个很重要的特点是,不需要编写大量的循环语句就可以对数据执行批量运算,并且在对大小相等的数组之间进行算术运算时都会将运算应用到元素级上,极大地提升了批量数据运算的效率。需要注意的是,ndarray是一个通用的同构数据多维容器,也就是说,数组中的所有元素必须是相同数据类型的。
In [76]: data + data Out[76]: array([[1.8 , 0.7 , 0.28, 0.92], [1.06, 1.56, 0.24, 1.72]]) In [77]: data - data Out[77]: array([[0., 0., 0., 0.], [0., 0., 0., 0.]]) In [78]: data * data Out[78]: array([[0.81 , 0.1225, 0.0196, 0.2116], [0.2809, 0.6084, 0.0144, 0.7396]]) In [79]: data / data Out[79]: array([[1., 1., 1., 1.], [1., 1., 1., 1.]])
此外,数组与标量之间的算术运算也会将标量值传播到各个元素上。
In [80]: data * 2 Out[80]: array([[1.8 , 0.7 , 0.28, 0.92], [1.06, 1.56, 0.24, 1.72]]) In [81]: data + 3*data Out[81]: array([[3.6 , 1.4 , 0.56, 1.84], [2.12, 3.12, 0.48, 3.44]]) In [82]: 3*data - data/2 Out[82]: array([[2.25 , 0.875, 0.35 , 1.15 ], [1.325, 1.95 , 0.3 , 2.15 ]])
以上是对数组的基础运算的介绍,下面我们来看一下如何利用数组中的函数进行一些算术运算,其中常用的函数如表3-1所示。
表3-1 常用函数及相关说明
假设现在我们有一个新的数组data1,对data1分别使用以上几种常用函数来计算结果,代码如下。
In [83]: data1 = np.array([[-0.9,0.35,0.14,-0.46],[0.53,-0.78,-0.12,-0.86]]) In [84]: data1 Out[84]: array([[-0.9 , 0.35, 0.14, -0.46], [ 0.53, -0.78, -0.12, -0.86]]) In [85]: np.abs(data1) Out[85]: array([[0.9 , 0.35, 0.14, 0.46], [0.53, 0.78, 0.12, 0.86]]) In [86]: np.sqrt(data1) Out[86]: array([[ nan, 0.59160798, 0.37416574, nan], [0.72801099, nan, nan, nan]]) In [87]: np.square(data1) Out[87]: array([[0.81 , 0.1225, 0.0196, 0.2116], [0.2809, 0.6084, 0.0144, 0.7396]]) In [88]: np.exp(data1) Out[88]: array([[0.40656966, 1.41906755, 1.1502738 , 0.63128365], [1.69893231, 0.45840601, 0.88692044, 0.42316208]]) In [89]: np.log2(data1) Out[89]: array([[ nan, -1.51457317, -2.83650127, nan], [-0.91593574, nan, nan, nan]]) In [90]: np.log10(data1) Out[90]: array([[ nan, -0.45593196, -0.85387196, nan], [-0.27572413, nan, nan, nan]])
nan表示空值,表明数组元素在函数定义域内无意义。为了返回结果的完整性,Python会默认返回一个空值nan。