最近在看张若愚老师的《Python科学计算》,也算是对Python的基本功进行一次锻炼,看着就记下笔记,这里开个系列来分享一下个人笔记,文章内容都是markdown直接编译过来的,所以排版也没多花心思了。
NumPy提供了两种基本的对象: ndarray和ufunc
- ndarray(下文统一称之为数组)是存储单一数据类型的多维数组
- ufunc则是能够对数组进行处理的函数
import numpy as np
利用命名行直接查看说明文档和用法示例,输入函数名并添加一个“?”符号即可
np.array?
2.1 ndarray对象
2.1.1 创建数组
常用的主要有arange()、linspace()、logspace()
- arange():类似于内置函数range(),通过指定开始值、终值和步长创建表示等差数列的一维数组,注意得到的结果数组不包含终值。例如下面的程序创建开始值为0、终值为1、步长为0.1的等差数组,注意终值1不在数组中:
np.arange(0,1,0.1)
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
- linspace():通过指定开始值、终值和元素个数创建表示等差数列的一维数组,可以通过endpoint参数指定是否包含终值,默认值为True,即包含终值。下面两个例子分别演示了endpoint为True和False时的结果,注意endpoint的值会改变数组的等差步长:
np.linspace(0,1,10) # 步长为1/9
array([0. , 0.11111111, 0.22222222, 0.33333333, 0.44444444,
0.55555556, 0.66666667, 0.77777778, 0.88888889, 1. ])
np.linspace(0,1,10,endpoint=False) # 步长为1/10
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
- logspace():和linspace()类似,不过它所创建的数组是等比数列。下面的例子产生从1到100、有5个元素的等比数列,注意起始值0表示1,而终值2表示100(以10为底的指数):
np.logspace(0,2,5)
array([ 1. , 3.16227766, 10. , 31.6227766 ,
100. ])
还有很多,比如:
- zeros()、ones()、empty()可以创建指定形状和类型的数组;
- zeroslike()、oneslike()、empty_like()等函数可创建与参数数组的形状及类型相同的数组;
- frombuffer()、fromstring()、fromfile()等函数可以从字节序列或文件创建数组。
2.1.2 存取元素
直接对创建好的ndarray对象进行赋值储存,和列表一样都可以进行切片,和列表不同的是,通过切片获取的新数组是原始数组的一个视图,它与原始数组共享同一块数据存储空间,意味着只要修改了视图的内容,原数组也是会跟着变化的:
a = np.arange(10)
print(a)
b = a[3:7] # 数组a 和数组b 共同一块数据存储空间
print(b)
b[0]= 999 # 将b的第1个元素修改为999
print(b)
print(a) # a的元素也被修改了
[0 1 2 3 4 5 6 7 8 9]
[3 4 5 6]
[999 4 5 6]
[ 0 1 2 999 4 5 6 7 8 9]
对应的,除了用切片来截取元素,还可以用整数列表、整数数组和布尔数组等几种高级下标存取方法。
- 使用整数列表对数组元素进行存取时,将使用列表中的每个元素作为下标。使用列表作为下标得到的数组不和原始数组共享数据
- 使用整数数组作为数组下标时,将得到一个形状和下标数组相同的新数组,新数组的每个元素都是用下标数组中对应位置的值作为下标从原数组获得的值
- 使用布尔数组b作为下标存取数组x中的元素时,将收集数组x中所有在数组b中对应下标为True的元素
x = np.arange(5,1,-1)
print(x)
# 整数列表存取
a1 = x[[1,2,3]]
print(a1)
# 整数数组存取
a2 = x[np.array([[3,3,1,0],[3,3,-3,0]])]
print(a2)
# 布尔数组存取
a3 = x[np.array([True,False,False,False])]
print(a3)
[5 4 3 2]
[4 3 2]
[[2 2 4 5]
[2 2 4 5]]
[5]
2.1.3 多维数组
多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值表示。NumPy采用元组(tuple)作为数组的下标,元组中的每个元素和数组的每个轴对应。图2-1显示了一个形状为(6,6)的数组a,图中用不同颜色和线型标识出各个下标对应的选择区域。
大家可能对于第4个会比较困惑,这个其实是slice对象方法,其中最后那个‘2’是间隔步长。
2.2 ufunc运算
ufunc是universal function的缩写,它是一种能对数组中每个元素进行操作的函数。NumPy内置的许多ufunc函数都是在C语言级别实现的,因此它们的计算速度非常快。让我们先看一个例子:
x = np.linspace(0,2*np.pi,10)
print(x)
print('---------------------------------')
y = np.sin(x)
print(y)
[0. 0.6981317 1.3962634 2.0943951 2.7925268 3.4906585
4.1887902 4.88692191 5.58505361 6.28318531]
---------------------------------
[ 0.00000000e+00 6.42787610e-01 9.84807753e-01 8.66025404e-01
3.42020143e-01 -3.42020143e-01 -8.66025404e-01 -9.84807753e-01
-6.42787610e-01 -2.44929360e-16]
先用linspace()产生一个从0到2π的等差数组,然后将其传递给np.sin()函数计算每个元素的正弦值。由于np.sin()是一个ufunc函数,因此在其内部对数组x的每个元素进行循环,分别计算它们的正弦值,并返回一个保存各个计算结果的数组。
import time
import math
import numpy as np
x = [i*0.001 for i in range(10000000)]
start = time.clock()
for i,t in enumerate(x): # 枚举函数
x[i]=math.sin(t)
print ('math.sin:',time.clock()-start)
x = [i*0.001 for i in range(10000000)]
x = np.array(x)
start = time.clock()
np.sin(x,x)
print('numpy.sin',time.clock()-start)
math.sin: 3.394713154884201
numpy.sin 0.01636017985525484
2.2.1 四则运算
如果没有指定out参数,那么将创建一个新的数组来保存计算结果。如果指定了第三个参数out,就不产生新的数组,而是直接将结果保存到指定的数组中。
2.2.2 比较和布尔运算
使用“==”、“>”等比较运算符对两个数组进行比较,将返回一个布尔数组,它的每个元素值都是两个数组对应元素的比较结果。
由于Python中的布尔运算使用and、or和not等关键字,它们无法被重载,因此数组的布尔运算只能通过相应的ufunc函数进行。这些函数名都以“logical_”开头,在IPython中使用自动补全功能可以很容易地找到它们: