什么是NumPy?
NumPy是Python中科学计算的基础包。它是一个Python库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于数组快速操作的各种例程,包括数学,逻辑,形状操作,排序,选择,I / O离散傅立叶变换,基本线性代数,基本统计运算,随机模拟等等。
NumPy包的核心是ndarray对象。这封装了同构数据类型的n维数组,许多操作在编译代码中执行以提高性能。NumPy数组和标准Python序列之间有几个重要的区别:
NumPy数组在创建时具有固定大小,与Python列表(可以动态增长)不同。更改ndarray的大小将创建一个新数组并删除原始数组。
NumPy数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。例外:可以有(Python,包括NumPy)对象的数组,从而允许不同大小的元素的数组。
NumPy数组有助于对大量数据进行高级数学和其他类型的操作。通常,与使用Python的内置序列相比,这些操作的执行效率更高,代码更少。
越来越多的基于Python的科学和数学软件包正在使用NumPy数组; 虽然这些通常支持Python序列输入,但它们在处理之前将这些输入转换为NumPy数组,并且它们通常输出NumPy数组。换句话说,为了有效地使用当今大量(甚至大多数)基于Python的科学/数学软件,只知道如何使用Python的内置序列类型是不够的 - 还需要知道如何使用NumPy数组。
关于序列大小和速度的要点在科学计算中尤为重要。作为一个简单的例子,考虑将1-D序列中的每个元素与相同长度的另一个序列中的相应元素相乘的情况。如果数据被存储在两个Python列表,a并且b,我们可以遍历每个元素:
c = []
for i in range(len(a)):
c.append(a[i]*b[i])
这产生了正确的答案,但如果a且b每个包含数百万个数字,我们将为Python中循环的低效率付出代价。我们可以通过写入在C中更快地完成相同的任务(为了清楚起见,我们忽略了变量声明和初始化,内存分配等)
for (i = 0 ; i < rows ; i ++ ): {
c [ i ] = a [ i ] * b [ i ];
}
这节省了解释Python代码和操作Python对象所涉及的所有开销,但代价是从Python编码中获得的好处。此外,所需的编码工作随着我们数据的维度而增加。例如,在二维数组的情况下,C代码(如前所述)扩展为
for (i = 0; i < rows; i++): {
for (j = 0; j < columns; j++): {
c[i][j] = a[i][j]*b[i][j];
}
}
NumPy为我们提供了两全其美:当涉及到ndarray时,逐个元素的操作是“默认模式” ,但逐个元素的操作由预编译的C代码快速执行。在NumPy
c = a * b
以近C速度执行前面的示例所做的事情,但是我们期望基于Python的代码具有简单性。的确,NumPy成语更简单!最后一个例子说明了NumPy的两个特征,它们是它的大部分功能的基础:矢量化和广播。
Vectorization描述了代码中没有任何显式循环,索引等 - 这些事情当然只是在优化的,预编译的C代码中“幕后”。矢量化代码有许多优点,其中包括:
矢量化代码更简洁,更易于阅读
更少的代码行通常意味着更少的错误
代码更接近于标准的数学符号(通常,更容易,正确编码数学结构)
矢量化导致更多“Pythonic”代码。如果没有矢量化,我们的代码就会被低效且难以阅读的for循环所困扰。
广播是用于描述操作的隐式逐元素行为的术语; 一般来说,在NumPy中,所有操作,不仅仅是算术运算,而是逻辑,位,功能等,都以这种隐式的逐元素方式表现,即它们进行广播。此外,在上面的例子中,a并且b可以是相同形状的多维阵列,或者标量和阵列,或者甚至是具有不同形状的两个阵列,条件是较小的阵列可以“扩展”到更大的形状。结果广播明确无误的方式。有关广播的详细“规则”,请参阅numpy.doc.broadcasting。
NumPy完全支持面向对象的方法,再次使用ndarray开始。例如,ndarray是一个类,拥有许多方法和属性。它的许多方法都在最外层的NumPy命名空间中镜像函数,使程序员可以完全自由地编写她喜欢的范例和/或最适合手头任务的范例。
ndarray数组的切片和索引
基础索引
一维数组切片和索引
和Python的List一样
ndarray 数组可以基于0 - n 的下标进行索引,并设置start, stop 及step 参数进行,从原数组中切割出一个新数组。
注意:切片的修改会修改原来的数组
原因:Numpy经常要处理大数组,避免每次都复制
import numpy as np
a = np.arange(8)
print(a[0]) # 输出 0
print(a[-1]) # 输出 7
print(a[1:6:2]) # 输出 [1 3 5]
print(a[:]) # 输出 [0 1 2 3 4 5 6 7]
print(a[::-1]) # 输出 [7 6 5 4 3 2 1 0]
二维数组切片和索引
# 二维向量,一般用大写字母
X = np.arange(20).reshape(4,5)
# 分别用行坐标、列坐标,实现行列筛选
# X[0][0]
X[0, 0]
X[-1, 2]
# 可以省略后续索引值,返回的数据是降低一个维度的数组
# 这里的2,其实是要筛选第2行
X[2]
# 筛选-1对应的行
X[-1]
# 筛选多行
X[:-1]
# 筛选多行,然后筛选多列
X[:2, 2:4]
import numpy as np
a = np.arange(1,10).reshape(3,3) # [[1 2 3] [4 5 6] [7 8 9]]
print(a[2]) # 获取第三行 输出:[ 7 8 9]
print(a[1][2]) # 获取第二行 第三列 输出:6
print(a[1,2]) # 获取第二行 第三列 输出:6
print(a[:,1]) # 获取所有行 第2列 输出:[2 5 8]
print(a[::2,:]) # 获取奇数行,所有列 输出: [[1 2 3] [7 8 9]]
print(a[::-1,::-1]) # 行列倒序 输出: [[9 8 7] [6 5 4] [3 2 1]]
布尔索引
一维数组的布尔索引
import numpy as np
a = np.arange(1,10)
b = a > 5
print(b) # 输出: [False False False False False True True True True]
print(a[b]) # 输出: [6 7 8 9]
a[a<=5] = 0
a[a>5] += 100
print(a) # 输出: [0 0 0 0 0 106 107 108 109]
二维数组的布尔索引
把特征满足某些条件的数据(行)筛选出来
# 举例:怎样把第3列大于5的行筛选出来
X[:, 3]
# 这里是按照行进行的筛选
X[X[:, 3]>5]
import numpy as np
a = np.arange(1,7).reshape(2,3)
# a[:,1]筛选出所有行第二列
a[:,1][a[:,1]>3] +=100 # 所有行,第二列>3的值+100
print(a) # 输出: [[ 1 2 3] [4 105 6]]
神奇索引
使用整数数组进行数据索引。
与切片不同,花式索引返回的是对象的一个复制而不是引用
一维数组的神奇索引
import numpy as np
a = np.arange(7)
print(a[[1,3,5]]) # 输出:[1 3 5]
二维数组的神奇索引
import numpy as np
a = np.arange(1,7).reshape(2,3)
print(a) # 输出:[[1 2 3] [4 5 6]]
print(a[[0,1],[0,2]]) # 0行0列和1行2列对应的元素 输出:[1,6]
print(a[:,[0,2]]) # 输出:[[1 3] [4 6]]
ndarray对象的运算
ndarray对象的算术运算
加减乘除,代码如下:
import numpy as np
a = np.arange(1,10).reshape(3,3) # [[1 2 3] [4 5 6] [7 8 9]]
b = np.array([1,1,1])
c = a + b # 广播
d = np.add(a,b) # add(),subtract(),multiply(),divide()
print(c) # 输出[[ 2 3 4] [ 5 6 7] [ 8 9 10]]
print(d) # 输出[[ 2 3 4] [ 5 6 7] [ 8 9 10]]
ndarray对象的数学函数
数学运算函数 | 描述 |
---|---|
np.abs /fabs | 绝对值 |
np.add(x1,x2 ) | 按元素添加参数,等效于 x1 + x2 |
np.subtract(x1,x2) | 按元素方式减去参数,等效于x1 - x2 |
np.multiply(x1,x2) | 逐元素乘法参数,等效于x1 * x2 |
np.divide(x1,x2) | 逐元素除以参数,等效于x1 / x2 |
np.exp(x) | 计算e的x次方。 |
np.exp2(x) | 计算2的x次方。 |
np.power(x1,x2) | 计算x1的x2次幂。 |
np.mod(x) | 返回输入数组中相应元素的除法余数. |
np.log(x) | 自然对数,逐元素。 |
np.log2(x) | x的基础2对数。 |
np.log10(x) | 以元素为单位返回输入数组的基数10的对数。 |
np.expm1(x) | 对数组中的所有元素计算exp(x) - 1 |
np.log1p(x) | 返回一个加自然对数的输入数组。 |
np.sqrt(x) | 按元素方式返回数组的正平方根。 |
np.square(x) | 返回输入的元素平方。 |
np.sin(x) | 三角正弦。 |
np.cos(x) | 元素余弦。 |
np.tan(x) | 逐元素计算切线。 |
np.round(x) | 四舍五入 |
np.floor(x) | 向下取整 |
np.ceil(x) | 向上取整 |
import numpy as np
a = np.arange(1,6)
print(a) # 输出: [1 2 3 4 5]
print(np.abs(a)) # 输出: [1 2 3 4 5]
print(np.sqrt(a)) # 输出: [1. 1.41421356 1.73205081 2. 2.23606798]
print(np.power(a, 3)) # 输出: [ 1 8 27 64 125]
print(np.exp(a)) # 输出: [ 2.71828183 7.3890561 20.08553692 54.59815003 148.4131591 ]
print(np.log(a)) # 以e为底 输出: [0. 0.69314718 1.09861229 1.38629436 1.60943791]
ndarray对象的取整函数
np.random.seed(123)
arr = np.random.randn(8)
# np.round 与 np.around 相同
arr_a = np.around(arr, 2) # 四舍五入
arr_f = np.floor(arr) # 向下取整
arr_c = np.ceil(arr) # 向上取整
arr_a
arr_f
arr_c
ndarray对象的统计函数
函数名称 | NaN安全版本 | 描述 |
---|---|---|
函数名称 | NaN安全版本 | 描述 |
np.sum() | np.nansum() | 计算元素的和 |
np.min() | np.nanmin() | 找出最小值 |
np.max() | np.nanmax() | 找出最大值 |
np.prod() | np.nanprod() | 计算元素的积 |
np.ptp() | N/A 计算元素的极差 | (最大值 - 最小值) |
np.mean() | np.nanmean() | 计算元素的算术平均值 |
np.std() | np.nanstd() | 计算标准差 |
np.var() | np.nanvar() | 计算方差 |
np.percentile() | np.nanpercentile() | 计算百分位数 |
np.median() | np.nanmedian() | 计算中位数 |
np.average() | N/A | 返回数组的加权平均值 |
np.any() | N/A | 验证任何一个元素是否为真 |
np.all() | N/A | 验证所有元素是否为真 |
关于NaN安全版本的说明:数组出现NaN空值将影响最终结果输出,如果不希望空值带入计算,则可使用NaN安全版本 | ||
方法 | 说明 | |
-------- | ----- | |
np.argmin( ) | 最小值的索引 | |
np.argmax( ) | 最大值的索引 | |
np.average( ) | 加权平均 | |
代码如下: |
a = np.array([[3,7,5],[8,4,3],[2,4,9]])
np.min(a) # 所有元素中最小的元素
np.min(a, axis = 0) # 每一列中最小的元素
np.max(a, axis = 1) # 每一行中最大的元素
import numpy as np
a = np.arange(1,6)
print(a) # 输出: [1 2 3 4 5]
print(np.sum(a)) # 输出: 15
print(np.mean(a)) # 输出: 3.0
print(np.std(a)) # 输出: 1.4142135623730951
print(np.var(a)) # 输出: 2.0
print(np.median(a)) # 输出: 3.0
print(np.min(a)) # 输出: 1
print(np.max(a)) # 输出: 5
print(np.average(a,weights=np.ones(5))) # 输出: 3.0
arr = np.arange(12).reshape(3,4)
np.percentile(arr, [25, 50, 75])
np.quantile(arr, [0.25, 0.5, 0.75])
ndarray对象的排序和索引函数
np.sort
np.random.seed(123)
arr = np.random.randint(1,30,9)
arr_a = np.sort(arr) # 也可在不同维度上进行索引
np.argsort
argsort返回从小到大的排列在数组中的索引位置
#电影名称
mv_name=['肖申克的救赎','控方证人','美丽人生','阿甘正传','霸王别姬','泰坦尼克号','辛德勒的名单','这个杀手不太冷','疯狂动物成','海豚湾']
#评分人数
mv_num=np.array([692795,42995,327855,580897,478523,157074,306904,662552,284652,159302])
order=np.argsort(mv_num)
mv_name[order[0]] # 获取评分人数最少的电影名称
np.nonzero
arr = np.array([0,2,3,0,7,0])
np.nonzero(arr) # 返回的是非零元素在原数组中的位置
arr[np.nonzero(arr)]
np.where
where函数会返回所有非零元素(满足条件)的索引
np.random.seed(123)
arr = np.random.randint(1,30,9)
arr2 = np.random.randint(6,20,9)
arr
arr2
np.where(arr>5) # 返回的是位置信息
arr[np.where(arr>5)]
arr[arr>5] # 与 arr[np.where(arr>5)] 等价
np.where(arr>arr2,arr,arr2)
ndarray对象的唯一化和集合运算
np.random.seed(123)
arr = np.random.randint(1,30,9)
np.unique(arr)
arr = np.random.randint(1,30,9)
arr2 = np.random.randint(6,20,9)
np.in1d(arr, arr2) # 判断元素是否在数组中
np.intersect1d(arr, arr2) # 求交集
np.union1d(arr, arr2) # 求并集
np.setdiff1d(arr, arr2) # 求差集
ndarray对象的矢量化(向量化)运算
数组表达式替换显式循环的做法通常称为向量化。
矢量化代码有很多优点,其中包括:
1、矢量化代码更简洁易读
2、更少的代码行通常意味着更少的错误
3、执行效率高,运行速度快
arr1 = np.array([1,2,3])
arr2 = np.array([4,5,6])
print(arr1+arr2) # 输出: [5,7,9]
Numpy的广播功能
1、如果数组不具有相同的秩,则将较低等级数组的形状维度添加1,直到两个形状具有相同的大小。即如果运算双方都是数组,而且数组的形状不一致,那么,两个数组各自向一个广播,广播后的两个数组形状相同,则广播可行。
2、其中一个数组的维度大小和元素个数为1,相当于对单个标量进行广播。即如果运算的双方一方是数组,一方是标量(单个的数值),不受广播方向约束,标量可以向两个方向广播。
3、在一个数组的大小为1且另一个数组的大小大于1的任何维度中,第一个数组的行为就像沿着该维度复制一样。即如果运算的双方都是数组,而且数组的形状不一致,那么,一个数组向一个方向广播之后可以得到和另一个数组一样的形状,则广播可行。