小蛇学python(16)numpy高阶用法
如果只是从事简单的数据分析,其实numpy的用处并不是很大。简单了解一下numpy,学好pandas已经够用,尤其是对于结构化或表格化数据。但是精通面向数组的编程和思维方式是成为python科学计算牛人的关键一步。
而且使用numpy的代码往往比普通数组要快,因为数组运算一般都比纯python循环要快得多。大量使用列表,将无可避免的使用循环。
当大家对numpy足够熟悉的时候,我建议大家这样做:
- 将python循环和条件逻辑转换为数组运算和布尔数组运算。
- 尽量使用广播。
- 避免复制数据,尽量使用数组视图,即切片。
- 利用ufunc及其它各种方法。
线性代数
import numpy as np
x = np.array([[1, 2, 3], [4, 5, 6]])
y = np.array([[6, 23], [-1, 7], [8, 9]])
print(x)
print(y)
print(x.dot(y))
image.png
这是最基础的矩阵计算。比较常用的矩阵计算函数如下。
函数 | 说明 |
---|---|
diag | 以一位数组形式返回对角线元素 |
dot | 矩阵乘法 |
trace | 矩阵迹 |
det | 行列式值 |
eig | 本征值与本征向量 |
inv | 求逆 |
pinv | Moore-Penrose伪逆 |
qr | QR分解 |
svd | 奇异值分解 |
solve | 解线性方程组Ax=b |
lstsq | 计算Ax=b的最小二乘解 |
高级数据操作
ndarray数组视图不复制任何数据的原因是因为,ndarray不只是一块内存和一个dtype,更准确的说它还有跨度信息,这使得数组能以各种步幅在内存中移动。(其实移动的是指针)也因此,ndarray数组有很多我们意想不到的功能。
import numpy as np
arr = np.arange(8)
print(arr)
new_arr = arr.reshape((4, 2))
print(arr)
print(new_arr)
image.png
同样,既然可以重塑,那也可以扁平化,即展开。
import numpy as np
arr = np.arange(8)
new_arr = arr.reshape((4, 2))
print(new_arr)
new_new_arr = new_arr.ravel()
print(new_new_arr)
image.png
在这里要提及一点。与其他科学计算环境相反(R或matlab),numpy允许更为灵活地控制数据在内存中的布局。具体来说,比如展开数组时是按列优先还是按行优先。
pandas的操作对象主要是结构化数据,numpy的操作对象主要是ndarray数组。这两者之间有很多功能函数是一一对应的,比如,pandas有对表格的拼接,ndarray也有对数组的拼接。
import numpy as np
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[7, 8, 9], [10 ,11, 12]])
print(np.concatenate([arr1, arr2], axis=0))
print(np.concatenate([arr1, arr2], axis=1))
image.png
有拼接就有拆分,split函数用于将一个数组沿指定轴拆分为多个数组。
import numpy as np
from numpy.random import randn
arr = randn(8, 2)
first, second, third = np.split(arr, [2, 3])
print(first)
print(second)
print(third)
image.png
还有很多功能不一一介绍,其实非常简单,在这里只是引起大家注意,知道numpy功能的强大。
还需要注意一点的是,这些函数都是建立在ndarray数组之上的,列表,元组等并无此功能。
广播机制
所谓广播是说不同形状的数组之间的算术运算的执行方式。
将标量值和数组进行组合时就会发生最简单的广播。
import numpy as np
arr = np.arange(5)
print(arr)
print(arr-1)
image.png
如图所示,当数组和数字之间运算时,并没有报错,而是每个数组元素和该数字做了运算。这在很多科研数据处理的时候,会方便很多。
ufunc高级应用
ufunc除了一些通用的施行特定矢量化运算的特殊方法外,还可以自定义函数对数组进行运算。
import numpy as np
def add_elements(x, y):
return x + y
add_them = np.frompyfunc(add_elements, 2, 1)
print(add_them(np.arange(8), np.arange(8)))
image.png
当然,不幸的是,这种创造ufunc的手段虽然很灵活,却非常慢。因为它们在计算的时候都要执行一次python函数调用,这自然会比numpy自带的基于C编写的ufunc慢很多。
为此,python科学计算社区正在开发一些项目,力求使自定义ufunc的性能接近内置的那些。