大纲
一维数组
前面几课我们学习到了很多数据类型,例如列表、字典、集合等,它们都可以保存一系列的数据信息。不过对于大规模的数据计算,它们并不都适合,而现代很多人工智能处理的数据类型都是数组(array),让我们看一下数组的特点,如何操作,它和之前学到数据类型的联系和区别。
加载导入numpy模块,将这个模块重命名为np,主要是为了后面代码写起来简单。
Copy x_list = [1,2,3,4]
type(x_list)
先建立一个列表,再建立一个数组,打印二者的类型,可以看到类型不同。
Copy x_array = np.array([1,2,3,4])
type(x_array)
数组的特点在于,它是有维度的,ndim可以告诉我们维度有几维,因为x_array只是一个向量,它只有一维,同时shape可以告诉我们有多少个元素。
dtype可以告诉我们数组内部存放的数据是什么格式的,这里的具体格式是32位整数型。
数组的重要特点在于支持向量化计算,如果我们要计算这些数值的平方再求和,如果是用列表的数据类型,根据我们之前学到的知识,需要写一个循环,或是使用列表解析。
Copy x_sum = 0
for x in x_list:
x_sum = x_sum+x**2
print(x_sum)
Copy sum([x**2 for x in x_list])
如果是数组类型,则更加简单,它可以直接对每个元素做平方操作。这个例子可以看到数组的优点,在于向量化运算非常方便快速。
Copy array([ 1, 4, 9, 16], dtype=int32)
前面是手动输入一些数值创建数组,可以无需手动输入,创建一些有规律的数组,例如全为0或全为1的数组。
Copy array([1., 1., 1., 1.])
Copy array([0., 0., 0., 0.])
也可以定义一些有规律的序列,例如利用arange来定义从0到1的序列数组,不同元素间的步长间隔是0.2,这样就产生了0、0.2、0.4、0.6、0.8这样的序列。
Copy np.arange(start=0,stop=1,step=0.2)
Copy array([0. , 0.2, 0.4, 0.6, 0.8])
linspace是类似的函数,不过参数num是指最终产生多少个元素,程序会自动判断步长间隔。
Copy np.linspace(start=0, stop=1, num=5)
Copy array([0. , 0.25, 0.5 , 0.75, 1. ])
小结:数组和列表很相似,它们都是一组元素构成,例如平面上的坐标就是有两个元素的向量,数组的优点在于向量化计算,快速而方便。
二维数组
前面建立的是向量,是一维的数组,数组可以是高维的,我们先来建立一个二维的数组,其实可以看作是一个矩阵。矩阵从直观上理解就是排成一个矩形的数组。下面来定义一个简单的二维数组。
Copy x = np.array([[1,2],[3,4]])
print(x)
上例用一个嵌套列表放入array函数,定义了一个二维数组,二维数组中有四个元素,以2乘2的形式排列。
此时用ndim函数可以告诉我们这个数组有两个维度,排列的形式是2行2列,总共有4个元素。和一维数组一样,也可以用zeros来定义全0的二维数组。
Copy x = np.zeros([3,3])
print(x)
Copy [[0. 0. 0.]
[0. 0. 0.]
[0. 0. 0.]]
二维数组可以直接定义,也可以通过对一维数组的修改来定义,例如下面是先定义一个一维数组,然后使用reshape,将其修改成一个2行5列的二维数组。
Copy x = np.arange(0,1,0.1)
Copy [[0. 0.1 0.2 0.3 0.4]
[0.5 0.6 0.7 0.8 0.9]]
小结:一维数组可以理解为在一条线上排列的向量,而二维数组在一个矩形平面上排列的向量,也可以定义更高维的数组。
数组操作
和列表等数据一样,可以做创建、选择、修改、删除、补充等操作,先定义一个简单的一维数组。
Copy x = np.arange(0,1,0.1)
print(x)
Copy [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
数组可以做选取操作,就是选择其中某个或某些元素。这种操作是用方括号来进行的。
Copy array([0. , 0.1, 0.2])
和列表类似,可以在方括号内使用数字编号或结合冒号选择一个或多个元素。对于二维数组,选取操作略有不同。
Copy x = x.reshape(2,5)
print(x)
Copy [[0. 0.1 0.2 0.3 0.4]
[0.5 0.6 0.7 0.8 0.9]]
此时选取操作也是在一个方括号中进行,不过需要有一个逗号,逗号前表示取哪一行,后面表示取哪一列,上例就是取第0行,第1列的数字
Copy array([0.5, 0.6, 0.7, 0.8, 0.9])
也可以结合冒号,表示选取第1行所有元素,或是最后一列的所有元素。数组中的元素不仅可以通过方括号来选取,还可以用来修改。
Copy array([[1. , 0.1, 0.2, 0.3, 0.4],
[0.5, 0.6, 0.7, 0.8, 0.9]])
numpy也提供了一系列的函数来对数组进行添加和删除操作。
Copy array([ 1. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 10. ])
Copy array([10. , 1. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
Copy array([1. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
数组里不仅可以存放数字,还可以存放计算逻辑值,例如来计算x这个数组中大于0.5的有哪些。返回了一个逻辑数组。
Copy [[ True False False False False]
[ True True True True True]]
逻辑数组可以放入方括号,作为选取的标准。
Copy array([1. , 0.5, 0.6, 0.7, 0.8, 0.9])
Copy array([1. , 0.5, 0.6, 0.7, 0.8, 0.9])
Copy array([[1. , 0.1, 0.2, 0.3, 0.4],
[1. , 1. , 1. , 1. , 1. ]])
Copy array([[1. , 0.1, 0.2, 0.3, 0.4],
[1. , 1. , 1. , 1. , 1. ]])
前面我们介绍了数组查询和修改,增加和删除,我们可以将两个数组进行拼接
Copy y = np.arange(10,11,0.1).reshape(2,5)
Copy array([[10. , 10.1, 10.2, 10.3, 10.4],
[10.5, 10.6, 10.7, 10.8, 10.9]])
Copy x = np.concatenate([x,y])
Copy array([[ 1. , 0.1, 0.2, 0.3, 0.4],
[ 1. , 1. , 1. , 1. , 1. ],
[10. , 10.1, 10.2, 10.3, 10.4],
[10.5, 10.6, 10.7, 10.8, 10.9]])
小结:二维数组是一维数组的扩展,它的选取、修改和一维数组是类似的,只不过需要两个数字编号进行操作,二维数组是最常见的人工智能计算载体,需要多加练习。
向量化计算
所谓向量化计算,是指numpy格式的数组,不需要做循环就可以进行很丰富的数学计算。例如加减乘除等基本运算。
Copy x = np.linspace(0,1,5)
print(x)
Copy [0. 0.25 0.5 0.75 1. ]
Copy array([0. , 0.75, 1.5 , 2.25, 3. ])
Copy array([0. , 0.125, 0.25 , 0.375, 0.5 ])
Copy array([-3. , -2.4375, -1.75 , -0.9375, 0. ])
Copy array([0. , 0.24740396, 0.47942554, 0.68163876, 0.84147098])
如果是一个自定义的函数,它并不天然支持这种向量化的表达。
Copy x = np.arange(1,10,dtype=int)
print(x)
Copy def odd_even(x):
if x%2 == 0:
return "偶数"
else:
return "奇数"
Copy ValueError Traceback (most recent call last)
<ipython-input-20-451016d16111> in <module>()
----> 1 odd_even(x)
<ipython-input-19-36bfaa3761ac> in odd_even(x)
1 def odd_even(x):
----> 2 if x%2 == 0:
3 return "偶数"
4 else:
5 return "奇数"
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
此时需要用vectorize来将这个普通函数转成一个向量化函数。
Copy odd_even_v = np.vectorize(odd_even)
Copy ['奇数' '偶数' '奇数' '偶数' '奇数' '偶数' '奇数' '偶数' '奇数']
其实这个函数的功能可以直接用下面的代码来完成,即找到那些可以被2整除的数字,然后选取出来
还有一种非常重要的计算就是矩阵计算。
Copy x = np.array([[1,0],[1,0]])
print(x)
Copy y = np.array([[0.1,0.2],[0.3,0.4]])
print(y)
Copy [[0.1 0.2]
[0.3 0.4]]
Copy array([[0.1, 0.2],
[0.1, 0.2]])
小结:向量化计算是数组的特有功能,不需要写循环,计算速度快。在对数值进行编程时应该尽量转化为numpy数组,以利用向量化计算。如果是自定义函数,则可以使用vectorize进行转换。
练习
用列表和数组两种方法,找到一百万以下所有能被3和7整除的数字之和,并比较两种方法的计算消耗时间。
Copy from time import time
time_start = time()
sum_list = sum([x for x in range(n) if x%3==0 and x%7==0])
time_end = time()
print("运行时间{x:.4f}秒,和等于{y}".format(x=time_end-time_start,y=sum_list))
Copy 运行时间0.0719秒,和等于23809976190
Copy from time import time
time_start = time()
x = np.arange(n,dtype='int64')
sum_array = np.sum(x[(x%3==0)&(x%7==0)])
time_end = time()
print("运行时间{x:.4f}秒,和等于{y}".format(x=time_end-time_start,y=sum_array))
Copy 运行时间0.0409秒,和等于23809976190