一 NumPy ndarray: 多维数组对象上一次总结了该书第三章的内容,这次带来第四章,也就是NumPy 的基础部分,有关Numpy的历史发展等相关知识点,读者可直接参考该书的87、88页。从这两页,我们知道Numpy主要是在数组部分,其算法库是采用C编写,数据存储在连续内存块上,这一特性,使得Numpy数组方法在性能上要远优于传统的Python方法。
环境依旧是 Pycharm 2020.2.4 社区版, Anconda3
作为NumPy的核心特征之一,ndarray即N维数组对象,是Python中一个快速、灵活的大型数据集容器。
用下面的随机NumPy数组代码,来感受一下
import numpy as np # 生成随机数组 data = np.random.randn(2, 3) print(data) # 对numpy 数组进行数组 *** 作 print(data * 10) print(data + data) # 描述数组每一维度的数量 print(data.shape) # 描述数组的数据类型————ndarray 所有元素的数据类型相同 print(data.dtype)1. 生成 ndarray
几个示例代码可以参考一下
import numpy as np data1 = [6, 7.5, 8, 0, 1] # 将任意序列类型转换成 ndarray对象, 数据类型自动转换(低变高) arr1 = np.array(data1) print(arr1, arr1.dtype) # 嵌套序列自动转化成 高维数组 data2 = [[1, 2, 3, 4], [5, 6, 7, 8]] arr2 = np.array(data2) print(arr2, arr2.dtype) # 查看数组维度 print(arr2.ndim, arr1.ndim) print(arr2.shape) # 其他生成数组函数 # 生成全0 print(np.zeros((3, 6))) # 生成未初始化数组 print(np.empty((2, 3, 2))) # range 的数组版 print(np.arange(12))
2. ndarray 的数据类型更多的数组生成函数可以参考该书的 表4-1
数据类型,即dtype,是一个特殊对象,它包含了ndarray 需要为某一种类型数据申明的内存块信息(也称为元数据,即表示数据的数据)
import numpy as np arr1 = np.array([1, 2, 3], dtype=np.float64) arr2 = np.array([1, 2, 3], dtype=np.int32) print(arr1.dtype) print(arr2.dtype)
更多数组类型请参考该书的 表4-2
import numpy as np arr1 = np.array([1, 2, 3], dtype=np.float64) # 使用astype方法显示转换数据类型 arr1 = arr1.astype(np.int32) print(arr1.dtype) numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_) numeric_strings = numeric_strings.astype(np.float64) print(numeric_strings) # 可以使用类型代码来传入数据类型 empty_unit32 = np.empty(8, dtype='u4') print(empty_unit32, empty_unit32.dtype)
3. NumPy 数组算术使用 astype时 会生成一个新的数组,即使你传入的dtype 与之前一样。
数组之所以重要,是因为它允许进行批量 *** 作而无须进行任何 for 循环。这一特性 也称为 向量化。
import numpy as np arr = np.array([[1., 2., 3.], [4., 5., 6.]]) print(arr * arr) print(arr - arr) # 带有标量的算术 *** 作也适用 print(1 / arr) print(arr ** 0.5) # 同尺寸数组之间的比较,会产生一个布尔值数组 arr2 = np.array([[0., 4., 1.], [7., 2., 12.]]) print(arr2 > arr)4. 基础索引与切片
NumPy 数组索引 方式有很多种
import numpy as np arr = np.arange(10) print(arr) # 单个索引 print(arr[5]) # 数组切片 print(arr[5:8]) # 对切片段修改 arr[5:8] = 12 # 不会生成新数组,而是对原数组进行修改 print(arr) arr_slice = arr[5:8] # 当arr_slice改变时,变化也会体现在原数组, 这一特性类似于cpp的引用 arr_slice[1] = 12345 print(arr) # 不写 切片值 的 [:] 将会引用数组的所有值 arr_slice[:] = 13 print(arr) # 如果需要复制,则需要显示调用复制 copy = arr[5:8].copy() copy[:] = 0 print(copy) print(arr) # 对于 二维数组 每个索引值对应的元素是一个一维数组 arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(arr2d[0]) # 单个元素可以通过递归方式获得,也可以传递索引的逗号分隔列表去选择单个元素 print(arr2d[0][2], arr2d[0, 2]) # 需要注意的是上诉通过索引返回的元素都是视图,即引用 arr2d[0][2] = 5 print(arr2d)4.1 切片索引
其实这种索引方式在上面有所提及,对于高维数组,我们可以使用切片索引获取低维度的切片,并重新赋值
import numpy as np # 对于 二维数组 每个索引值对应的元素是一个一维数组 arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(arr2d[:2, 2]) arr2d[:2, :1] = 0 print(arr2d)5. 布尔索引
其实就是bool值去索引, 需注意的是,布尔值数组长度应该和数组轴索引长度一致, 且可能是因为用C编写的原因,and和or关键字对布尔值数组没有用,需要使用 & 和 | 替代。
import numpy as np names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe']) data = np.random.randn(7, 4) print(names == 'Bob') print(data[names == 'Bob']) print(data[~(names == 'Bob')]) mask = (names == 'Bob') | (names == 'Will') print(data[mask]) data[data < 0] = 0 print(data)6. 神奇索引
其实就是用整数数组进行索引
import numpy as np arr = np.empty((8, 4)) print(arr[[4, 3, 0, -6]]) arr = np.arange(32).reshape((8, 4)) print(arr) # 得到对应的元素 print(arr[[1, 5, 7, 2], [0, 3, 1, 2]]) # 得到相应的矩形区域 print(arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]])7. 数组转置和换轴
import numpy as np arr = np.arange(15).reshape((3, 5)) print(arr) print(arr.T) # 计算矩阵内积 print(np.dot(arr, arr.T)) arr1 = np.arange(16).reshape((2, 2, 4)) # 使用 transpose 方法对数组的轴重新排序 print(arr1.transpose((1, 0, 2))) print(arr1) # 转置其实就是换轴的一种特殊案例, 使用swapaxes方法对轴进行调整用于重组数据 print(arr1.swapaxes(1, 2))
二 通用函数以上方法,都是返回数据的底层视图,而不生成新的数组,也不对数据进行复制
通用函数,ufunc,是一种在 ndarray 数据中 进行逐元素 *** 作的函数,某些简单函数接收一个或多个标量差值, 并产生 一个或多个 标量结果, 而通用函数就是对这些函数是对这些简单函数的向量化封装
import numpy as np arr = np.arange(10) print(np.sqrt(arr)) print(np.exp(arr)) x = np.random.randn(8) y = np.random.randn(8) print(x, y) # 比较x,y对应元素,返回最大值 print(np.maximum(x, y)) # modf 返回一个浮点数组的小数部分和整数部分 reminder, whole_part = np.modf(x) print(reminder) print(whole_part) arr = np.random.randn(7) * 5 print(arr)
三 使用数组进行面向数组编程更多的通用函数请参考该书的表 4-3,4-4
使用Numpy数组,可以使用简单的数组表达式代替显示循环的方法, 这种方法称为向量化,向量化的数组 *** 作会比纯Python的等价实现在速度上更快
import numpy as np points = np.arange(-5, 5, 0.01) # meshgrid 方法 传入两个一维数组, 并根据两个数组的所有(x,y)对生成一个二维矩阵 xs, ys = np.meshgrid(points, points) print(ys) z = np.sqrt(xs ** 2 + ys ** 2) print(z) import matplotlib.pyplot as plt plt.imshow(z, cmap=plt.cm.gray) plt.colorbar() plt.title("Image plot of $sqrt{x^2 + y^2}$ for a grid of values") plt.show()1. 将条件逻辑作为数组 *** 作
这里主要讲的是where函数, 其实就是三元表达式 x if condition else y 的向量化版本。如果直接使用三元表达式,其需要解释器解释python完成, 速度会很慢,且对高维数组需要修改代码,无法轻易复用。而使用哪np.where就可以非常简单的完成
import numpy as np xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5]) yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5]) cond = np.array([True, False, True, True, False]) result = [(x if c else y) for x, y, c in zip(xarr, yarr, cond)] print(result) result = np.where(cond, xarr, yarr) print(result) arr = np.random.randn(4, 4) print(arr) print(arr > 0) # 标量也可以作为结果,不一定非要使用数组 # 将正值设为2,负值设为-2 print(np.where(arr > 0, 2, -2)) # 只将正值设为2 print(np.where(arr > 0, 2, arr))
2. 数学和统计方法传递给 np.where 的数组既可以是同等大小的数组,也可以是标量
import numpy as np arr = np.random.randn(5, 4) print(arr) print(arr.mean()) # mean 表示平均数 print(np.mean(arr)) # 像mean,sum等函数,可以接受一个可选参数axis,可用于计算给定轴向上的统计值 # mean(1) 计算每一行的平均值 print(arr.mean(1)) # sum(0) 计算每一行的和 print(arr.sum(axis=0)) arr = np.array([0, 1, 2, 3, 4, 5, 6, 7]) print(arr.cumsum()) # 对于高维数组,可以在指定轴向根据较低维度的切片进行部分聚合 arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) print(arr.cumsum(axis=0)) print(arr.cumprod(axis=1))
3. 布尔值数组的方法更多地请参考该书的表4-5,然后提一句,该书在该页有一个问题,axis=0 是表示每一行,axis=1 表示每一列
布尔值会被强制为 1(True)和 0(False)
import numpy as np arr = np.random.randn(100) print((arr > 0).sum()) bools = np.array([False, False, True, True]) # any 检查数组中是否至少有一个True print(bools.any()) # all 检查是否每个值都是 True print(bools.all())
4. 排序这些方法也适用非布尔值数组,所有非0的元素都按 True 处理
import numpy as np arr = np.random.randn(6) print(arr) arr.sort() print(arr) # 对于高维数组可以使用参数指定轴进行排序 arr = np.random.randn(5, 3) print(arr) arr.sort(1) print(arr)
5. 唯一值与其他集合逻辑顶层的 np.sort 方法 返回的是已经排序好的数组拷贝,而不是对原数组继续拷贝
NumPy 包含一些针对一维nadarray 的基础集合 *** 作,一个常用的方法是 np.unique,返回的是数组中唯一值排序后形成的数组
import numpy as np names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe']) print(np.unique(names)) ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4]) print(np.unique(ints))
另一个函数, np.in1d, 可以检查一个数组中的值是否存在另外一个数组中,并返回一个布尔值数组
import numpy as np values = np.array([6, 0, 0, 3, 2, 5, 6]) print(np.in1d(values, [2, 3, 6]))
四 使用数组进行文件输入和输出更多的ndarray数组的集合 *** 作,请参考表 4-6
import numpy as np arr = np.arange(10) np.save('som_array', arr) print(np.load('som_array.npy')) # 数据未压缩存储 np.savez('array_archive.npz', a=arr, b=arr) arch = np.load('array_archive.npz') print(arch['b']) # 数据已经压缩存储 np.savez_compressed('array_compressed.npz', a=arr, b=arr) print(np.load('array_compressed.npz'))五 线性代数
这部分函数大多集合在了numpy的linalg中
import numpy as np x = np.array([[1., 2., 3.], [4., 5., 6.]]) y = np.array([[6., 23.], [-1, 7], [8, 9]]) print(x.dot(y)) # x.dot(y) 等价于 np.dot(x, y) print(np.dot(x, y)) # @ 也作为 中缀 *** 作符, 用于点乘 *** 作 print(x @ y) # 求逆和qr分解 from numpy.linalg import inv, qr X = np.random.randn(5, 5) mat = X.T.dot(X) print(inv(mat)) print(mat.dot(inv(mat))) q, r = qr(mat) print(r)
六 伪随机数生成更多的线性代数 *** 作方法,请参考表4-7
import numpy as np samples = np.random.normal(size=(4, 4)) print(samples) # 改变随机种子 np.random.seed(1234) # 创建一个随机数生成器 rng = np.random.RandomState(1234) print(rng.randn(10))
其他numpy.random 中的部分函数列表参考表 4-8
七 示例:随机漫步一个简单的随机漫步
import matplotlib.pyplot as plt import numpy as np import random # 偏python基础语法的实现形式 position = 0 walk = [position] steps = 1000 for i in range(steps): step = 1 if random.randint(0, 1) else -1 position += step walk.append(position) # 偏NumPy形式 nsteps = 1000 draws = np.random.randint(0, 2, size =nsteps) steps = np.where(draws > 0, 1, -1) walk = steps.cumsum() # 在漫步轨道提取 最大值和最小值 print(walk.min(), walk.max()) # 返回第一次走了 10 步或 -10 步的位置 print((np.abs(walk) >= 10).argmax()) # 随机漫步的前100步 plt.plot(walk[:100]) plt.show()1. 一次性模拟多次随机漫步
import matplotlib.pyplot as plt import numpy as np import random nwalks = 5000 nsteps = 1000 draws = np.random.randint(0, 2, size=(nwalks, nsteps)) # 0 或 1 steps = np.where(draws > 0, 1, -1) walks = steps.cumsum(1) print(walks) # 这些随机步的最大值和最小值 print(walks.max(), walks.min()) # 采用 any 方法检查 最小穿越时间 hits30 = (np.abs(walks) >= 30).any(1) print(hits30) print(hits30.sum()) # 达到30 或 -30 的数字 # 使用布尔值数组来 选出绝对步数超过 30步所在的行 crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1) print(crossing_times) print(crossing_times.mean())小结
本书接下来的部分将会集中在打造 pandas 数据处理技能上,继续在基于数组的风格下处理数据
终于写完了,最近的效率真的低下
_
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)