目录
10.3、关于什么时间选择concatenate 函数和堆栈函数进行数组连接
1、NumPy简介
NumPy 是用于处理数组的 python 库
它还拥有在线性代数、傅立叶变换和矩阵领域中工作的函数
在 Python 中,我们有满足数组功能的列表,但是处理起来很慢
NumPy 旨在提供一个比传统 Python 列表快 50 倍的数组对象
NumPy 中的数组对象称为 ndarray
,它提供了许多支持函数,使得利用 ndarray
非常容易
数组在数据科学中非常常用,因为速度和资源非常重要
2、利用元组、列表创建多维数组
从下面的程序中可以看出来两个一维数组可以可以构成一个二维数组,也即一个二维数组的子元素是每一个一维数组
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr)
同理,两个二维数组可以构成一个三维数组
import numpy as np
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
print(arr)
3、数组索引
数组索引等同于访问数组元素
可以通过引用其索引号来访问数组元素
NumPy 数组中的索引以 0 开头,这意味着第一个元素的索引为 0,第二个元素的索引为 1,以此类推
import numpy as np
arr = np.array([1, 2, 3, 4])
print(arr[2] + arr[3]) # arr[2] = 3,arr[3] = 4
访问第二维中的第五个元素(访问第二个数组中的第五个元素)
import numpy as np
arr = np.array([[1,2,3,4,5], [6,7,8,9,10]])
print('5th element on 2nd dim: ', arr[1, 4]) # 10
print(f"当然这种方式也可以,类似于其他语言中的数组访问形式:{arr[1][4]}")
访问第一个数组的第二个数组的第三个元素
import numpy as np
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
print(arr[1, 1, 2]) # 6
print(f"当然这种方式也可以,类似于其他语言中的数组访问形式:{arr[1][1][2]}")
使用负索引
import numpy as np
arr = np.array([[1,2,3,4,5], [6,7,8,9,10]])
print('Last element from 2nd dim: ', arr[1, -1]) # 10
4、数组裁切
4.1、一维数组操作
裁切(切片操作)从开头到索引 4(不包括)的元素,返回的是一个新的数组
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7])
print(arr[:4])
从末尾开始的索引 3 到末尾开始的索引 1,对数组进行裁切
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7])
print(arr[-3:-1])
请使用 step 值确定裁切的步长,从索引 1 到索引 5,返回相隔的元素
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7])
print(arr[1:5:2])
返回数组中相隔的元素
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7])
print(arr[::2])
4.2、二维数组操作
arr[x,y],其中x是具体要操作的行数,y是具体要操作的列
从第二个元素开始,对从索引 1 到索引 4(不包括)的元素进行切片
其中 1 指的是 [6, 7, 8, 9, 10]这个数组,也即第二行
import numpy as np
arr = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
print(arr[1, 1:4])
取一个二维数组 `arr` 中前两行(第一行、第二行,因为索引为 0、1,不包括2)的第 3 列(索引为 2)组成的一维数组
import numpy as np
arr = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
print(arr[0:2, 2])
取一个二维数组中第 1 到第 2 行和第 2 到第 4 列所组成的子数组
import numpy as np
arr = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
print(arr[0:2, 1:4])
5、数据类型
NumPy 有一些额外的数据类型,并通过一个字符引用数据类型,例如 i
代表整数,u
代表无符号整数等。
以下是 NumPy 中所有数据类型的列表以及用于表示它们的字符。
i
- 整数b
- 布尔u
- 无符号整数f
- 浮点c
- 复合浮点数m
- timedeltaM
- datetimeO
- 对象S
- 字符串U
- unicode 字符串V
- 固定的其他类型的内存块 ( void )
我们使用 array()
函数来创建数组,该函数可以使用可选参数:dtype
,它允许我们定义数组元素的预期数据类型
用数据类型字符串创建数组
需要注意的是,字符类型的数组在使用时需要使用 `b''` 将字符串进行编码。在上面的例子中,`b'1'` 表示字符 `'1'` 的字节编码
在 numpy 中,`'S'` 数据类型表示字符串类型,`'S1'` 表示长度为 1 的字符串类型,`'S2'` 表示长度为 2 的字符串类型,以此类推。因,,`dtype='S'` 表示创建字符类型长度为 1 的数组
import numpy as np
arr = np.array([1, 2, 3, 4], dtype='S')
print(arr)
print(arr.dtype)
创建数据类型为 4 字节整数的数组
import numpy as np
arr = np.array([1, 2, 3, 4], dtype='i4')
print(arr)
print(arr.dtype)
通过使用 'i'/int
作为参数值,将数据类型从浮点数更改为整数
import numpy as np
arr = np.array([1.1, 2.1, 3.1])
# newarr = arr.astype(int)
newarr = arr.astype('i')
print(newarr)
print(newarr.dtype)
将数据类型从整数更改为布尔值
import numpy as np
arr = np.array([1, 0, 3])
newarr = arr.astype(bool)
print(newarr)
print(newarr.dtype)
6、副本/视图
副本和数组视图之间的主要区别在于副本是一个新数组,而这个视图只是原始数组的视图
副本拥有数据,对副本所做的任何更改都不会影响原始数组,对原始数组所做的任何更改也不会影响副本
视图不拥有数据,对视图所做的任何更改都会影响原始数组,而对原始数组所做的任何更改都会影响视图
说白了和数据库中的视图是一个道理
副本不应受到对原始数组所做更改的影响,也即副本和
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
x = arr.copy()
arr[0] = 61
print(arr)
print(x)
视图应该受到对原始数组所做更改的影响,有点像人和人的影子一样,影子和人本身同时是相同的动作
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
x = arr.view()
x[0] = 31
print(arr)
print(x)
副本拥有数据,而视图不拥有数据
每个 NumPy 数组都有一个属性 base
,如果该数组拥有数据,则这个 base 属性返回 None
。
否则,base
属性将引用原始对象
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
x = arr.copy()
y = arr.view()
print(x) # [1 2 3 4 5]
print(x.base) # None
print(y.base) # [1 2 3 4 5]
7、数组形状
数组的形状是每个维中元素的数量
NumPy 数组有一个名为 shape
的属性,该属性返回一个元组,每个索引具有相应元素的数量、
下面的例子返回 (2, 4)
,这意味着该数组有 2 个维,每个维有 4 个元素
import numpy as np
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(arr.shape)
8、数组重塑
重塑意味着更改数组的形状,但是要切记原数组中元素总个数一定要等于重塑数组的元素总个数
我们可以将 8 元素 1D 数组重塑为 2 行 2D 数组中的 4 个元素,但是我们不能将其重塑为 3 元素 3 行 2D 数组,因为这将需要 3x3 = 9 个元素
数组的形状是每个维中元素的数量
通过重塑,我们可以添加或删除维度或更改每个维度中的元素数量
将以下具有 12 个元素的 1-D 数组转换为 2-D 数组
最外面的维度将有 4 个数组,每个数组包含 3 个元素
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
newarr = arr.reshape(4, 3)
print(newarr)
将一个数组重新变形为 3 维矩阵,其中第一维大小为 2(最外层,也即最高维),第二维大小为 3(最外层的次层),第三维大小为 2(最里面的一层,也就是一维)
最外面的维度将具有 2 个数组,其中包含 3 个数组,每个数组包含 2 个元素
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
newarr = arr.reshape(2, 3, 2)
print(newarr)
对上面的数组进行重新改变形状,变为最外层是2个数组,但是每一个数组中又包含两个数组,这两个数组中每一个又包含3个元素
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
newarr = arr.reshape(2,2 , 3)
print(newarr)
重塑之后的多维数组是一个视图
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
print(arr.reshape(2, 4).base)
可以使用一个“未知”维度。
这意味着我们不必在 reshape 方法中为维度之一指定确切的数字。
传递 -1
作为值,NumPy 将计算该数字
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
newarr = arr.reshape(2, 2, -1)
print(newarr)
展平数组(Flattening the arrays)是指将多维数组转换为 1D 数组
把数组转换为 1D 数组
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
newarr = arr.reshape(-1)
print(newarr)
9、多维数组的迭代
使用 for 循环迭代三维数组,n维数组则必有n个for循环
import numpy as np
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
for x in arr: # 迭代所有的二维数组,即 x 为 [[1, 2, 3], [4, 5, 6]] 、 [[7, 8, 9] [10, 11, 12]]
for y in x: # 迭代二维数组x中的一维数组,即y为 [1, 2, 3]、[4, 5, 6]、[7, 8, 9]、[10, 11, 12]
for z in y: # 迭代一维数组y中的元素,即z为 1.....12
print(z)
对上面的三维数组使用nditer()辅助函数进行迭代
import numpy as np
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
for x in np.nditer(arr):
print(x)
我们可以使用 op_dtypes
参数,并传递期望的数据类型,以在迭代时更改元素的数据类型。
NumPy 不会就地更改元素的数据类型(元素位于数组中),因此它需要一些其他空间来执行此操作,该额外空间称为 buffer,为了在 nditer()
中启用它,我们传参 flags=['buffered']
import numpy as np
arr = np.array([1, 2, 3])
for x in np.nditer(arr, flags=['buffered'], op_dtypes=['S']):
print(x)
每遍历 2D 数组的一个标量元素,跳过 1 个元素
import numpy as np
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
for x in np.nditer(arr[:, ::2]):
print(x)
使用 ndenumerate()
方法进行枚举迭代,换句话说就是一边迭代一边打印带元素对应的下标
枚举是指逐一提及事物的序号
有时,我们在迭代时需要元素的相应索引,对于这些用例,可以使用 ndenumerate()
方法
import numpy as np
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
for idx, x in np.ndenumerate(arr):
print(idx, x)
10、数组连接
需要注意的是,待合并的几个数组的维度必须相同,即一维数组只能和一维数组合并,二维数组只能和二维数组合并。此外,如果合并的是一维数组,数组的形状可以不一样;但如果合并的是多维数组,则数组的形状必须相同,也就是数组的行列数必须一样
10.1、使用concatenate()
函数进行数组连接
行之和,列不变,使用参数 axis = 0(改变行),连接两个二维数组
import numpy as np
arr1 = np.array([[1, 2], [3, 4]]) # 2行2列
arr2 = np.array([[5, 6], [7, 8]]) # 2行2列
arr = np.concatenate((arr1, arr2), axis=0)
print(arr)
print("---------------------------");
print(arr.shape)
行不变,列之和,使用参数 axis = 1(改变列),连接两个二维数组,结果函数二维数组
import numpy as np
arr1 = np.array([[1, 2], [3, 4]]) # 2 行 2 列
arr2 = np.array([[5, 6], [7, 8]]) # 2 行 2 列
arr = np.concatenate((arr1, arr2), axis=1)
print(arr)
print("---------------------------");
print(arr.shape)
深度和,行不变,列不变,使用参数 axis = 0(改变深度),连接两个三维数组
import numpy as np
arr1 = np.ones((2, 3, 4))
arr2 = np.zeros((2, 3, 4))
result = np.concatenate((arr1, arr2), axis=0)
print(arr1)
print("----------------------------------")
print(arr2)
print("----------------------------------")
print(result)
print("----------------------------------")
print(result.shape)
深度不变,行之和,列不变,使用参数 axis = 1(改变行),连接两个三维数组
import numpy as np
arr1 = np.ones((2, 3, 4))
arr2 = np.zeros((2, 3, 4))
result = np.concatenate((arr1, arr2), axis=1)
print(arr1)
print("----------------------------------")
print(arr2)
print("----------------------------------")
print(result)
print("----------------------------------")
print(result.shape)
深度不变,行不变,列之和,使用参数 axis = 2(改变列),连接两个三维数组
import numpy as np
arr1 = np.ones((2, 3, 4))
arr2 = np.zeros((2, 3, 4))
result = np.concatenate((arr1, arr2), axis=2)
print(arr1)
print("----------------------------------")
print(arr2)
print("----------------------------------")
print(result)
print("----------------------------------")
print(result.shape)
10.2、使用堆栈函数连接数组
import numpy as np
arr1 = np.array([1, 2, 3])
print(arr1.shape)
arr2 = np.array([4, 5, 6])
print(arr2.shape)
arr = np.stack((arr1, arr2), axis=0)
print(arr)
print(arr.shape)
import numpy as np
arr1 = np.array([1, 2, 3])
print(arr1.shape)
arr2 = np.array([4, 5, 6])
print(arr2.shape)
arr = np.stack((arr1, arr2), axis=1)
print(arr)
print(arr.shape)
NumPy 提供了一个辅助函数:hstack()
沿行堆叠。
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.hstack((arr1, arr2))
print(arr)
print(arr.shape)
NumPy 提供了一个辅助函数:vstack()
沿列堆叠
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.vstack((arr1, arr2))
print(arr)
print(arr.shape)
NumPy 提供了一个辅助函数:dstack()
沿高度堆叠,该高度与深度相同
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
arr = np.dstack((arr1, arr2))
print(arr)
print(arr.shape)
10.3、关于什么时间选择concatenate 函数和堆栈函数进行数组连接
在 Numpy 中,有两种方法可以用来连接多个数组,即使用堆栈函数(stack、hstack、vstack、dstack等)或者使用 concatenate 函数。两者都可以将多个数组沿着某个维度拼接起来形成一个新的数组
使用堆栈函数连接数组的情况一般是比较简单的,例如: - 如果想要将两个一维数组沿着行方向(即水平方向)拼接起来,可以使用 hstack 函数
- 如果想要将两个一维数组沿着列方向(即垂直方向)拼接起来,可以使用 vstack 函数
- 如果想要将两个二维数组沿着深度方向(即沿着第三个维))拼接起来,可以使用 dstack 函数
使用 concatenate 函数连接数组的情况一般是比较复杂的
例如: - 如果想要将多个数组沿着不同的轴(即不同的维度)拼接起来,可以使用 concatenate 函数,并且需要在函数参数中指定拼接的轴。
- 如果想要将多个不同形状的数组拼接起来,需要先对这些数组进行变形(reshape),使得它们在拼接轴上的大小相同或者为 1,然后再使用 concatenate 函数进行拼接。 因此,一般来说,在拼接时只需要考虑拼接的方向和是否需要考虑变形即可。如果非常简单,比如只需要在水平或者垂直方向上拼接几个数组,那么可以使用堆栈函数;如果比较复杂,需要在不同的轴上进行拼接或者需要进行变形操作,那么可以使用 concatenate 函数
11、拆分数组
拆分是连接的反向操作
连接(Joining)是将多个数组合并为一个,拆分(Spliting)将一个数组拆分为多个
我们使用 array_split()
分割数组,将要分割的数组和分割数传递给它
访问拆分的数组
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
newarr = np.array_split(arr, 3)
print(newarr)
print(newarr[0])
print(newarr[1])
print(newarr[2])
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17, 18]])
newarr = np.array_split(arr, 3, axis=0)
print(newarr)
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17, 18]])
newarr = np.array_split(arr, 3, axis=1)
print(newarr)
使用 hsplit() 方法将 2-D 数组沿着行分成三个 2-D 数组
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17, 18]])
newarr = np.hsplit(arr, 3)
print(newarr)
vsplit()
和 dsplit()
可以使用与 vstack()
和 dstack()
类似的替代方法
12、搜索数组
可以在数组中搜索(检索)某个值,然后返回获得匹配的索引
使用 where()
方法查找值为 4 的索引
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 4, 4])
x = np.where(arr == 4)
print(x) # (array([3, 5, 6], dtype=int64),),也即数字4对应的下标
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8])
x = np.where(arr%2 == 1)
print(x) # 返回元组(array([0, 2, 4, 6], dtype=int64),)
有一个名为 searchsorted()
的方法,该方法在数组中执行二进制搜索,并返回将在其中插入指定值以维持搜索顺序的索引
假定 searchsorted()
方法用于排序数组
应该在索引 1 上插入数字 7,以保持排序顺序
该方法从左侧开始搜索,并返回第一个索引,其中数字 7 不再大于下一个值
import numpy as np
arr = np.array([6, 7, 8, 9])
x = np.searchsorted(arr, 7)
print(x) # 1
从右边开始查找应该插入值 7 的索引
应该在索引 2 上插入数字 7,以保持排序顺序
该方法从右边开始搜索,并返回第一个索引,其中数字 7 不再小于下一个值
import numpy as np
arr = np.array([6, 7, 8, 9])
x = np.searchsorted(arr, 7, side='right')
print(x) # 2
使用拥有指定值的数组,查找应在其中插入值 2、4 和 6 的索引
import numpy as np
arr = np.array([1, 3, 5, 7])
x = np.searchsorted(arr, [2, 4, 6])
print(x) # [1 2 3]
# 2 插入的是原数组 3 的位置,为索引 1
# 4 插入的是原数组 5 的位置,为索引 2
# 6 插入的是原数组 7 的位置,为索引 3
13、数组排序
排序是指将元素按有序顺序排列
有序序列是拥有与元素相对应的顺序的任何序列,例如数字或字母、升序或降序
NumPy ndarray 对象有一个名为 sort()
的函数,该函数将对指定的数组进行排序
此方法返回数组的副本,而原始数组保持不变
import numpy as np
arr = np.array(['banana', 'cherry', 'apple'])
arr1 = np.array([True, False, True])
arr2 = np.array([[3, 2, 4], [5, 0, 1]])
# 对数组以字母顺序进行排序
print(np.sort(arr))
# 对布尔数组进行排序
print(np.sort(arr1))
# 如果在二维数组上使用 sort() 方法,则将对两个数组进行排序
print(np.sort(arr2))
14、数组过滤
从现有数组中取出一些元素并从中创建新数组称为过滤(filtering)
在 NumPy 中,我们使用布尔索引列表来过滤数组
布尔索引列表是与数组中的索引相对应的布尔值列表
如果索引处的值为 True
,则该元素包含在过滤后的数组中;如果索引处的值为 False
,则该元素将从过滤后的数组中排除
# 用索引 0 和 2、4 上的元素创建一个数组
import numpy as np
arr = np.array([61, 62, 63, 64, 65])
x = [True, False, True, False, True] # 61对应true、62对应false、63对应true、64对应false、65对应true,所以保留true对应的数字重新构成数组
newarr = arr[x]
print(newarr) # [61, 63, 65]
根据条件创建过滤器,创建一个过滤器数组,该数组仅返回原始数组中的偶数元素
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7])
# 创建一个空列表
filter_arr = []
# 遍历 arr 中的每个元素
for element in arr:
# 如果元素可以被 2 整除,则将值设置为 True,否则设置为 False
if element % 2 == 0:
filter_arr.append(True)
else:
filter_arr.append(False)
newarr = arr[filter_arr]
print(filter_arr)
print(newarr)
直接从数组创建过滤器
上例是 NumPy 中非常常见的任务,NumPy 提供了解决该问题的好方法
我们可以在条件中直接替换数组而不是 iterable 变量,它会如我们期望地那样工作
创建一个仅返回大于 62 的值的过滤器数组
import numpy as np
arr = np.array([61, 62, 63, 64, 65])
filter_arr = arr > 62
newarr = arr[filter_arr]
print(filter_arr)
print(newarr)
15、随机数
15.1 、系统生成随机数
NumPy 提供了 random 模块来处理随机数
# 生成一个 0 到 100 之间的随机整数
from numpy import random
x = random.randint(100)
print(x)
randint()
方法接受 size
参数,您可以在其中指定数组的形状
# 生成一个 1-D 数组,其中包含 5 个从 0 到 100 之间的随机整数
from numpy import random
x=random.randint(100, size=(5))
print(x)
# 生成有 3 行的 2-D 数组,每行包含 5 个从 0 到 100 之间的随机整数
from numpy import random
x = random.randint(100, size=(3, 5))
print(x)
# 生成有 3 行的 2-D 数组,每行包含 5 个随机数
from numpy import random
x = random.rand(3, 5)
print(x)
random 模块的 rand()
方法返回 0 到 1 之间的随机浮点数
# random 模块的 rand() 方法返回 0 到 1 之间的随机浮点数
from numpy import random
x = random.rand()
print(x)
15.2、从数组中生成随机数
choice()
方法使您可以基于值数组生成随机值
choice()
方法将数组作为参数,并随机返回其中一个值
# 返回数组中的值之一
from numpy import random
x = random.choice([3, 5, 7, 9])
print(x)
choice()
方法还允许返回一个值数组
添加一个 size
参数以指定数组的形状
# 生成由数组参数(3、5、7 和 9)中的值组成的二维数组
from numpy import random
x = random.choice([3, 5, 7, 9], size=(3, 5))
print(x)
16、NumPy ufuncs
ufuncs 指的是“通用函数”(Universal Functions),它们是对 ndarray 对象进行操作的 NumPy 函数
ufunc 用于在 NumPy 中实现矢量化,这比迭代元素要快得多
它们还提供广播和其他方法,例如减少、累加等,它们对计算非常有帮助
ufuncs 还接受其他参数,比如:
where
布尔值数组或条件,用于定义应在何处进行操作
dtype
定义元素的返回类型
out
返回值应被复制到的输出数组
将迭代语句转换为基于向量的操作称为向量化
由于现代 CPU 已针对此类操作进行了优化,因此速度更快
对此,NumPy 有一个 ufunc,名为 add(x, y)
,它会输出相同的结果
通过 ufunc,我们可以使用 add()
函数
import numpy as np
x = [1, 2, 3, 4]
y = [4, 5, 6, 7]
z = np.add(x, y)
print(z)