python-数据分析-Numpy-3、数组的运算
数组的运算
使用 NumPy 最为方便的是当需要对数组元素进行运算时,不用编写循环代码遍历每个元素,所有的运算都会自动的矢量化。简单的说就是,NumPy 中的数学运算和数学函数会自动作用于数组中的每个成员。
# -*- coding: utf-8 -*-
#数组的运算
#使用 NumPy 最为方便的是当需要对数组元素进行运算时,不用编写循环代码遍历每个元素,所有的运算都会自动的矢量化。简单的说就是,NumPy 中的数学运算和数学函数会自动作用于数组中的每个成员。
import numpy
# 1、数组跟标量的运算
#NumPy 的数组可以跟一个数值进行加、减、乘、除、求模、求幂等运算,对应的运算会作用到数组的每一个元素上
array1 = numpy.arange(1, 10)
print(array1) #[1 2 3 4 5 6 7 8 9]
print(array1 + 10) #[11 12 13 14 15 16 17 18 19]
print(array1 * 10) #[10 20 30 40 50 60 70 80 90]
#除了上述的运算,关系运算也是没有问题的,之前讲布尔索引的时候已经遇到过了
print(array1 > 5) #[False False False False False True True True True]
print(array1 % 2 == 0) #[False True False True False True False True False]
print('-----------------------------------------------------')
# 2、数组跟数组的运算
#NumPy 的数组跟数组也可以执行算术运算和关系运算,运算会作用于两个数组对应的元素上,这就要求两个数组的形状(shape属性)要相同
array2 = numpy.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
print(array1 + array2) #[ 2 3 4 6 7 8 10 11 12]
print(array1 * array2) #[ 1 2 3 8 10 12 21 24 27]
print(array1 ** array2) #[ 1 2 3 16 25 36 343 512 729]
print(array1 > array2) #[False True True True True True True True True]
print(array1 % array2 == 0) #[ True True True True False True False False True]
print('-----------------------------------------------------')
# 3、通用一元函数
#NumPy 中通用一元函数的参数是一个数组对象,函数会对数组进行元素级的处理
# 例如:sqrt函数会对数组中的每个元素计算平方根,而log2函数会对数组中的每个元素计算以2为底的对数
print(numpy.sqrt(array1))
print(numpy.log2(array1))
'''
[1. 1.41421356 1.73205081 2. 2.23606798 2.44948974
2.64575131 2.82842712 3. ]
[0. 1. 1.5849625 2. 2.32192809 2.5849625
2.80735492 3. 3.169925 ]
'''
通用一元函数表:
函数 | 说明 |
---|---|
abs / fabs |
求绝对值的函数 |
sqrt |
求平方根的函数,相当于array ** 0.5 |
square |
求平方的函数,相当于array ** 2 |
exp |
计算$e^x$的函数 |
log / log10 / log2 |
对数函数(e 为底 / 10 为底 / 2 为底) |
sign |
符号函数(1 - 正数;0 - 零;-1 - 负数) |
ceil / floor |
上取整 / 下取整 |
isnan |
返回布尔数组,NaN对应True ,非NaN对应False |
isfinite / isinf |
判断数值是否为无穷大的函数 |
cos / cosh / sin |
三角函数 |
sinh / tan / tanh |
三角函数 |
arccos / arccosh / arcsin |
反三角函数 |
arcsinh / arctan / arctanh |
反三角函数 |
rint / round |
四舍五入函数 |
# 4、通用二元函数
#NumPy 中通用二元函数的参数是两个数组对象,函数会对两个数组中的对应元素进行运算
# 例如:maximum函数会对两个数组中对应的元素找最大值,而power函数会对两个数组中对应的元素进行求幂操作
array3 = numpy.array([[4, 5, 6], [7, 8, 9]])
array4 = numpy.array([[1, 2, 3], [3, 2, 1]])
print(numpy.maximum(array3, array4))
print(numpy.power(array3, array4))
'''
[[4 5 6]
[7 8 9]]
[[ 4 25 216]
[343 64 9]]
'''
通用二元函数:表
函数 | 说明 |
---|---|
add(x, y) / substract(x, y) |
加法函数 / 减法函数 |
multiply(x, y) / divide(x, y) |
乘法函数 / 除法函数 |
floor_divide(x, y) / mod(x, y) |
整除函数 / 求模函数 |
allclose(x, y) |
检查数组x 和y 元素是否几乎相等 |
power(x, y) |
数组$x$的元素$x_i$和数组$y$的元素$y_i$,计算$x_i^{y_i}$ |
maximum(x, y) / fmax(x, y) |
两两比较元素获取最大值 / 获取最大值(忽略NaN) |
minimum(x, y) / fmin(x, y) |
两两比较元素获取最小值 / 获取最小值(忽略NaN) |
dot(x, y) |
点积运算(数量积,通常记为$\cdot$,用于欧几里得空间(Euclidean space)) |
inner(x, y) |
内积运算(内积的含义要高于点积,点积相当于是内积在欧几里得空间$\mathbb{R}^n$的特例,而内积可以推广到赋范向量空间,只要它满足平行四边形法则即可) |
cross(x, y) |
叉积运算(向量积,通常记为$\times$,运算结果是一个向量) |
outer(x, y) |
外积运算(张量积,通常记为$\bigotimes$,运算结果通常是一个矩阵) |
intersect1d(x, y) |
计算x 和y 的交集,返回这些元素构成的有序数组 |
union1d(x, y) |
计算x 和y 的并集,返回这些元素构成的有序数组 |
in1d(x, y) |
返回由判断x 的元素是否在y 中得到的布尔值构成的数组 |
setdiff1d(x, y) |
计算x 和y 的差集,返回这些元素构成的数组 |
setxor1d(x, y) |
计算x 和y 的对称差,返回这些元素构成的数组 |
广播机制
上面数组运算的例子中,两个数组的形状(shape属性)是完全相同的,我们再来研究一下,两个形状不同的数组是否可以直接做二元运算或使用通用二元函数进行运算
#广播机制
#广播机制是 NumPy 中最令人困惑的特性,它允许 NumPy 的数组运算在数组形状不同的情况下也能正常进行。
array5 = numpy.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])
array6 = numpy.array([1, 2, 3])
print(array5 + array6)
'''
[[1 2 3]
[2 3 4]
[3 4 5]
[4 5 6]]
'''
array7 = numpy.array([[1], [2], [3], [4]])
print(array5 + array7)
'''
[[1 1 1]
[3 3 3]
[5 5 5]
[7 7 7]]
'''
通过上面的例子,我们发现形状不同的数组仍然有机会进行二元运算,但这不代表任意形状的数组都可以进行二元运算。
简单的说,只有两个数组后缘维度相同或者后缘维度不同但其中一个数组后缘维度为1时,广播机制才会被触发。
通过广播机制,NumPy 将两个原本形状不相同的数组变成形状相同,才能进行二元运算。所谓后缘维度,指的是数组形状(shape属性)从后往前看对应的部分,我们举例说明。
下图中,一个数组的形状是(4, 3),另一个数组的形状是(3, ),从后往前看对应的部分都是3,属于后缘维度相同,可以应用广播机制,第二个数组会沿着缺失元素那个轴的方向去广播自己,最终让两个数组形状达成一致。
下图中,一个数组的形状是(3, 4, 2),另一个数组的形状是(4, 2),从后往前看对应的部分都是(4, 2),属于后缘维度相同,可以应用广播机制,第二个数组会沿着缺失元素那个轴的方向去广播自己,最终让两个数组形状达成一致。
下图中,一个数组的形状是(4, 3),另一个数组的形状是(4, 1),这是后缘维度不相同的情况,但是第二个数组跟第一个数组不同的地方为1,第二个数组可以沿着为1 的那个轴广播自己,最终让两个数组形状达成一致
其它常用函数
除了上面讲到的函数外,NumPy 中还提供了很多用于处理数组的函数,ndarray对象的很多方法也可以通过调用函数来实现,下表给出了一些常用的函数。
表:NumPy其他常用函数
函数 | 说明 |
---|---|
unique |
去除数组重复元素,返回唯一元素构成的有序数组 |
copy |
返回拷贝数组得到的数组 |
sort |
返回数组元素排序后的拷贝 |
split / hsplit / vsplit |
将数组拆成若干个子数组 |
stack / hstack / vstack |
将多个数组堆叠成新数组 |
concatenate |
沿着指定的轴连接多个数组构成新数组 |
append / insert |
向数组末尾追加元素 / 在数组指定位置插入元素 |
argwhere |
找出数组中非0元素的位置 |
extract / select / where |
按照指定的条件从数组中抽取或处理数组元素 |
flip |
沿指定的轴翻转数组中的元素 |
fromregex |
通过读取文件和正则表达式解析获取数据创建数组对象 |
repeat / tile |
通过对元素的重复来创建新数组 |
roll |
沿指定轴对数组元素进行移位 |
resize |
重新调整数组的大小 |
place / put |
将数组中满足条件的元素/指定的元素替换为指定的值 |
partition |
用选定的元素对数组进行一次划分并返回划分后的数组 |
#常用函数
#去重(重复元素值保留一项)
print(numpy.unique(array5)) #[0 1 2 3]
#堆叠和拼接
array8 = numpy.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
array9 = numpy.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])
print(numpy.hstack((array8, array9))) #用于水平(按列)堆叠数组,相当于沿着轴 1 进行数组的拼接。
'''
[[1 1 1 4 4 4]
[2 2 2 5 5 5]
[3 3 3 6 6 6]]
'''
print(numpy.vstack((array8, array9))) #用于垂直(按行)堆叠数组,相当于沿着轴 0 进行数组的拼接。
'''
[[1 1 1]
[2 2 2]
[3 3 3]
[4 4 4]
[5 5 5]
[6 6 6]]
'''
#axis:指定连接的轴。默认值为 0=行,即在第一个轴上进行连接。 1=列
print(numpy.concatenate((array8, array9), axis=1)) #用于沿指定轴连接一系列数组。它可以在任意维度上进行拼接。
'''
[[1 1 1 4 4 4]
[2 2 2 5 5 5]
[3 3 3 6 6 6]]
'''
#numpy.concatenate() 是一个通用的拼接函数,可以指定沿哪个轴拼接数组。
#numpy.vstack() 是专门用于垂直堆叠的函数,相当于沿轴 0 拼接。
#numpy.hstack() 是专门用于水平堆叠的函数,相当于沿轴 1 拼接。
print('-----------------------------------------------------')
#追加和插入元素。
print(numpy.append(array1, [10, 100])) #追加元素 追加 10 和 100 [ 1 2 3 4 5 6 7 8 9 10 100]
print(numpy.insert(array1, 1, [98, 99, 100])) #插入元素 插入 98 99 100 到 1 位置 [ 1 98 99 100 2 3 4 5 6 7 8 9]
print('-----------------------------------------------------')
#抽取和处理元素。
#说明:extract函数的操作相当于我们之前讲的布尔索引。
print(numpy.extract(array1 % 2 !=0, array1)) #抽取元素 抽取出 array1 中所有元素值不为偶数的元素 [1 3 5 7 9]
#处理元素 处理 array1 中所有元素值小于等于 3 的元素,将其乘以 10;处理 array1 中所有元素值大于等于 7 的元素,将其平方。
#select函数的第一个参数设置了两个条件,满足第一个条件的元素执行了乘以10的操作,满足第二个条件的元素执行了求平方的操作,两个条件都不能满足的数组元素会被处理为0。
print(numpy.select([array1 <= 3, array1 >= 7], [array1 * 10, array1 ** 2])) #[10 20 30 0 0 0 49 64 81]
#where函数的第一个参数给出了条件,满足条件的元素执行了乘以10的操作,不能满足条件的元素执行了求平方的操作。
print(numpy.where(array1 <= 5, array1 * 10, array1 ** 2)) #[10 20 30 40 50 36 49 64 81]
print('-------------------------------------------------------------')
#重复数组元素创建新数组
#repeat函数的第一个参数是数组,第二个参数是重复次数。
print(numpy.repeat(array1, 3)) #[1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8 9 9 9]
#tile函数的第一个参数是数组,第二个参数是重复次数。
print(numpy.tile(array1, 2)) #[1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9]
#repeat和tile函数的区别在于,repeat对数组中的每个元素进行重复。,tile函数对整个数组进行重复。
print('-------------------------------------------------------------')
#调整数组的大小
#resize函数的第一个参数是数组,第二个参数是调整后的大小。
#提示:array1原本是一个有9个元素的一维数组,通过resize函数调整成为5行3列共15个元素的二维数组,缺少的元素通过复用原数组中的元素来补充。
print(numpy.resize(array1, (5, 3))) #(5, 3) 表示5行3列
'''
[[1 2 3]
[4 5 6]
[7 8 9]
[1 2 3]
[4 5 6]]
'''
print('-------------------------------------------------------------')
#替换数组元素
#replace函数的第一个参数是数组,第二个参数是待替换的元素的索引位置,第三个参数是替换的新元素。
print(array1)
#put函的第二个参数给出了要被替换的元素的索引,但是用来作为替换值的元素只有100和200,所以这两个值会被循环使用
# 因此索引为0、1、-1、3、5的元素被依次替换成了100、200、100、200、100
numpy.put(array1, [0, 1, -1, 3, 5], [100, 200])
print(array1) #[100 200 3 200 5 100 7 8 100]
#place函数的第一个参数是数组,第二个参数是条件,第三个参数是替换的新元素。
numpy.place(array1, array1 > 5, [1, 2, 3])
print(array1) #[1 2 3 3 5 1 2 3 1]
#注意:put函数和place函数都没有返回新的数组对象,而是在原来的数组上直接进行替换。