你可以用 NumPy 做很多有趣的事情。
NumPy 是一个运行速度非常快的数学库,主要用于数组计算。它可以让你在 Python 中使用向量和数学矩阵,以及许多用 C 语言实现的底层函数,你还可以体验到从未在原生 Python 上体验过的运行速度。
NumPy 是 Python 在科学计算领域取得成功的关键之一,如果你想通过 Python 学习数据科学或者机器学习,就必须学习 NumPy。我认为 NumPy 的功能很强大,而且入门也不难。
数组基础
创建数组
NumPy 的核心是数组(arrays)。具体来说是多维数组(ndarrays),但是我们不用管这些。通过这些数组,我们能以闪电般的速度使用像向量和数学矩阵之类的功能。赶紧捡起你的线性代数吧!(只是开玩笑,其实并不需要很多复杂的数学知识)
# 1D Array
a = np.array([0, 1, 2, 3, 4])
b = np.array((0, 1, 2, 3, 4))
c = np.arange(5)
d = np.linspace(0, 2*np.pi, 5)
print(a) # >>>[0 1 2 3 4]
print(b) # >>>[0 1 2 3 4]
print(c) # >>>[0 1 2 3 4]
print(d) # >>>[ 0. 1.57079633 3.14159265 4.71238898 6.28318531]
print(a[3]) # >>>3
上边的代码展示了创建数组的四种不同方式。最基本的方式是传递一个序列给 NumPy 的 array() 函数;你可以传给它任意的序列,不仅仅是我们常见的列表之类的。
注意,当输出的数组中的数值长度不一样的时候,它会自动对齐。这在查看矩阵的时候很有用。数组的索引和 Python 中的列表或其他序列很像。你也可以对它们使用切片,这里我不再演示一维数组的切片,如果你想知道更多关于切片的信息,查看这篇文章。
# MD Array,
a = np.array([[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25],
[26, 27, 28 ,29, 30],
[31, 32, 33, 34, 35]])
print(a[2,4]) # >>>25
通过给 array() 函数传递一个列表的列表(或者是一个序列的序列),可以创建二维数组。如果我们想要一个三维数组,那我们就传递一个列表的列表的列表,四维数组就是列表的列表的列表的列表,以此类推。
注意二维数组是如何成行成列排布的(在我们的朋友--空格的帮助下)。如果要索引一个二维数组,只需要引用相应的行数和列数即可。
多维数组切片
多维数组切片比一维数组要复杂一点,同时它也是你在用 NumPy 的时候经常会用到的。
# MD slicing print(a[0, 1:4]) # >>>[12 13 14] print(a[1:4, 0]) # >>>[16 21 26] print(a[::2,::2]) # >>>[[11 13 15] # [21 23 25] # [31 33 35]] print(a[:, 1]) # >>>[12 17 22 27 32]
就像你看到的一样,多维数组切片就是要分别在每个维度上切片,并用逗号隔开。在二维数组中,第一个切片的含义是对行切片,第二个切片的含义是对列切片。
值得注意的是,你通过输入数字来指定行和列。上边第一个例子是从数组中选择第 0 行。
下边的这幅图阐明了上边切片的例子的含义。
数组属性
在使用 NumPy 时,你会想知道数组的某些信息。很幸运,在这个包里边包含了很多便捷的方法,可以给你想要的信息。
# Array properties
a = np.array([[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25],
[26, 27, 28 ,29, 30],
[31, 32, 33, 34, 35]])
print(type(a)) # >>><class 'numpy.ndarray'>
print(a.dtype) # >>>int64
print(a.size) # >>>25
print(a.shape) # >>>(5, 5)
print(a.itemsize) # >>>8
print(a.ndim) # >>>2
print(a.nbytes) # >>>200
数组的形状(shape)是指它有多少行和列,上边的数组有五行五列,所以他的形状是(5,5)。
'itemsize' 属性是每一个条目所占的字节。这个数组的数据类型是 int64,一个 int64 的大小是 64 比特,8 比特为 1 字节,64 除以 8 就得到了它的字节数,8 字节。
'ndim' 属性是指数组有多少维。这个数组有二维。但是,比如说向量,只有一维。
'nbytes' 属性表示这个数组中所有元素占用的字节数。你应该注意,这个数值并没有把额外的空间计算进去,因此实际上这个数组占用的空间会比这个值大点。
使用数组
基本操作符
仅仅会赋值、取值和得到一些属性是不能满足你的需求的,有时候你还需要做一些数学运算。你可以利用基本的操作符实现这些,比如 +, -, /,等等。
# Basic Operators
a = np.arange(25)
a = a.reshape((5, 5))
b = np.array([10, 62, 1, 14, 2, 56, 79, 2, 1, 45,
4, 92, 5, 55, 63, 43, 35, 6, 53, 24,
56, 3, 56, 44, 78])
b = b.reshape((5,5))
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a ** 2)
print(a < b) print(a > b)
print(a.dot(b))
除了 dot() 之外,这些操作符都是对数组进行逐元素运算。比如 (a, b, c) + (d, e, f) 的结果就是 (a+d, b+e, c+f)。它将分别对每一个元素进行配对,然后对它们进行运算。它返回的结果是一个数组。注意,当使用逻辑运算符比如 “<” 和 “>” 的时候,返回的将是一个布尔型数组,这点有一个很好的用处,后边我们会提到。
dot() 函数计算两个数组的点积。它返回的是一个标量(只有大小没有方向的一个值)而不是数组。
背后的数学知识
dot() 函数有时候也称为点积。理解这个函数的最好方法就是看下边它的计算过程
数组的特定操作符
NumPy 还提供了一些其他很有用的操作符,用于处理数组。
# dot, sum, min, max, cumsum
a = np.arange(10)
print(a.sum()) # >>>45
print(a.min()) # >>>0
print(a.max()) # >>>9
print(a.cumsum()) # >>>[ 0 1 3 6 10 15 21 28 36 45]
很明显就能看出 sum()、min() 和 max() 函数的功能:将所有元素加起来,找到最小值和最大值。
高级索引
花俏的索引
“花俏的索引”是获取数组中我们想要的特定元素的有效方法。
# Fancy indexing
a = np.arange(0, 100, 10)
indices = [1, 5, -1]
b = a[indices]
print(a) # >>>[ 0 10 20 30 40 50 60 70 80 90]
print(b) # >>>[10 50 90]
"""
实际上,对于切片也好,np.arange( 0,100,10)也好 第三个参数都是步长,而不是产生元素的总数。但目前位置,遇见了一个例外,那就是 np.linspace( )这个函数第三个参数代表的是产生的元素的个数,并且包括给定范围的左值和右值
举例:
a = np.linspace(0,100,10)
array([ 0. , 11.11111111, 22.22222222, 33.33333333,
44.44444444, 55.55555556, 66.66666667, 77.77777778,
88.88888889, 100. ])
你会发现,或许和你预先想象的并不一样。既包括0,也包括100.
"""
通过上面的例子,如你所见,我们用想要获取的索引的序列作为索引。它返回了我们索引的元素。
布尔屏蔽(boolean masking)
布尔屏蔽是一个奇妙的特性,它允许我们根据指定条件获取数组中的元素。
# Boolean masking
import matplotlib.pyplot as plt
a = np.linspace(0, 2 * np.pi, 50)
b = np.sin(a)
plt.plot(a,b)
mask = b >= 0
plt.plot(a[mask], b[mask], 'bo')
mask = (b >= 0) & (a <= np.pi / 2)
plt.plot(a[mask], b[mask], 'go')
plt.show()
上边的代码展示了实现布尔屏蔽。你需要做的就是传递给数组一个与它有关的条件式,然后它就会返回给定条件下为真的值。
当然你需要的知道的便是,对于numpy中的数组来说,对数组运用 条件运算符 的时候,便会返回一个bool值组成的同样大小的数组。而且看到 a[mask] 你也会发现数组也吃bool值这一套。 列表就不行,会报错
#list a = [12,12,34,45,5,6] a[True,True,False,False,True,False] TypeError: list indices must be integers or slices, not tuple #array a = np.array(a) b=np.array([True,True,False,False,True,False]) a[b]
array([12, 12, 5])
上边的例子将会生成下边这幅图:
我们用条件式选择了图中不同的点。蓝色的点(也包含图中的绿点,只是绿点覆盖了蓝点),显示的是值大于零的点。绿点显示的是值大于 0 小于 Pi / 2 的点。
缺省索引
缺省索引是从多维数组的第一维获取索引和切片便捷方法。例如,你有一个数组 a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]],那么 a[3] 将会返回数组第一维中索引值为 3 的元素,这里的结果是 4。
# Incomplete Indexing a = np.arange(0, 100, 10) b = a[:5] c = a[a >= 50] print(b) # >>>[ 0 10 20 30 40] print(c) # >>>[50 60 70 80 90]
Where 函数
where() 函数是另外一个根据条件返回数组中的值的有效方法。只需要把条件传递给它,它就会返回一个使得条件为真的元素的列表。
# Where a = np.arange(0, 100, 10) b = np.where(a < 50) c = np.where(a >= 50)[0] print(b) # >>>(array([0, 1, 2, 3, 4]),) print(c) # >>>[5 6 7 8 9]
通过对比 b = np.where( a < 50) 和 b = a[ a < 50] 的结果来看a = np.arange(0,100,10) c = a[a<50] d = np.where(a < 50) c
darray([ 0, 10, 20, 30, 40])
(array([0, 1, 2, 3, 4]),)
where函数返回的是一个元组,所以后面才能够使用索引 [0]