Python for Kids
  • 0 前言
  • 1 编程环境准备
  • 2 运算符和表达式
  • 3 掌握变量
  • 4 字符串
  • 5 获取用户的输入
  • 6 条件判断
  • 7 条件判断实操
  • 8 FOR循环
  • 9 循环和列表
  • 10 WHILE循环
  • 11 WHILE循环实操
  • 12 WHILE循环再实操
  • 13 多重循环
  • 14 再谈列表
  • 15 初见函数
  • 16 函数实操
  • 17 选择排序
  • 18 冒泡排序
  • 19 递归算法之一
  • 20 递归算法实操
  • 21 快速排序
  • 22 汉诺塔游戏
  • 23 递推算法
  • 24 分治算法
  • 25 集合与组合
  • 26 贪心算法
  • 27 字典和键值对
  • 28 广度优先搜索算法
  • 29 数组和向量化计算
  • 30 随机和模拟
  • 31 数据可视化
  • 32 文件读取和分析
Powered by GitBook
On this page
  • 大纲
  • 一维数组
  • 二维数组
  • 数组操作
  • 向量化计算
  • 练习

Was this helpful?

29 数组和向量化计算

数组能算的更快

大纲

  • 一维数组

  • 二维数组

  • 数组操作

  • 向量化计算

  • 练习

一维数组

前面几课我们学习到了很多数据类型,例如列表、字典、集合等,它们都可以保存一系列的数据信息。不过对于大规模的数据计算,它们并不都适合,而现代很多人工智能处理的数据类型都是数组(array),让我们看一下数组的特点,如何操作,它和之前学到数据类型的联系和区别。

import numpy as np

加载导入numpy模块,将这个模块重命名为np,主要是为了后面代码写起来简单。

x_list = [1,2,3,4]
type(x_list)
list

先建立一个列表,再建立一个数组,打印二者的类型,可以看到类型不同。

x_array = np.array([1,2,3,4])
type(x_array)
numpy.ndarray

数组的特点在于,它是有维度的,ndim可以告诉我们维度有几维,因为x_array只是一个向量,它只有一维,同时shape可以告诉我们有多少个元素。

x_array.ndim
1
x_array.shape
(4,)
x_array.size
4

dtype可以告诉我们数组内部存放的数据是什么格式的,这里的具体格式是32位整数型。

x_array.dtype
dtype('int32')

数组的重要特点在于支持向量化计算,如果我们要计算这些数值的平方再求和,如果是用列表的数据类型,根据我们之前学到的知识,需要写一个循环,或是使用列表解析。

x_sum = 0
for x in x_list:
    x_sum = x_sum+x**2
print(x_sum)
30
sum([x**2 for x in x_list])
30

如果是数组类型,则更加简单,它可以直接对每个元素做平方操作。这个例子可以看到数组的优点,在于向量化运算非常方便快速。

x_array**2
array([ 1,  4,  9, 16], dtype=int32)
sum(x_array**2)
30

前面是手动输入一些数值创建数组,可以无需手动输入,创建一些有规律的数组,例如全为0或全为1的数组。

np.ones(4)
array([1., 1., 1., 1.])
np.zeros(4)
array([0., 0., 0., 0.])

也可以定义一些有规律的序列,例如利用arange来定义从0到1的序列数组,不同元素间的步长间隔是0.2,这样就产生了0、0.2、0.4、0.6、0.8这样的序列。

np.arange(start=0,stop=1,step=0.2)
array([0. , 0.2, 0.4, 0.6, 0.8])

linspace是类似的函数,不过参数num是指最终产生多少个元素,程序会自动判断步长间隔。

np.linspace(start=0, stop=1, num=5)
array([0.  , 0.25, 0.5 , 0.75, 1.  ])

小结:数组和列表很相似,它们都是一组元素构成,例如平面上的坐标就是有两个元素的向量,数组的优点在于向量化计算,快速而方便。

二维数组

前面建立的是向量,是一维的数组,数组可以是高维的,我们先来建立一个二维的数组,其实可以看作是一个矩阵。矩阵从直观上理解就是排成一个矩形的数组。下面来定义一个简单的二维数组。

x = np.array([[1,2],[3,4]])
print(x)
[[1 2]
 [3 4]]

上例用一个嵌套列表放入array函数,定义了一个二维数组,二维数组中有四个元素,以2乘2的形式排列。

x.ndim
2
x.shape
(2, 2)
x.size
4

此时用ndim函数可以告诉我们这个数组有两个维度,排列的形式是2行2列,总共有4个元素。和一维数组一样,也可以用zeros来定义全0的二维数组。

x = np.zeros([3,3])
print(x)
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]
x.shape
(3, 3)

二维数组可以直接定义,也可以通过对一维数组的修改来定义,例如下面是先定义一个一维数组,然后使用reshape,将其修改成一个2行5列的二维数组。

x = np.arange(0,1,0.1)
x.shape
(10,)
x = x.reshape(2,5)
print(x)
[[0.  0.1 0.2 0.3 0.4]
 [0.5 0.6 0.7 0.8 0.9]]
x.shape
(2, 5)

小结:一维数组可以理解为在一条线上排列的向量,而二维数组在一个矩形平面上排列的向量,也可以定义更高维的数组。

数组操作

和列表等数据一样,可以做创建、选择、修改、删除、补充等操作,先定义一个简单的一维数组。

x = np.arange(0,1,0.1)
print(x)
[0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]

数组可以做选取操作,就是选择其中某个或某些元素。这种操作是用方括号来进行的。

x[0]
0.0
x[-1]
0.9
x[:3]
array([0. , 0.1, 0.2])

和列表类似,可以在方括号内使用数字编号或结合冒号选择一个或多个元素。对于二维数组,选取操作略有不同。

x = x.reshape(2,5)
print(x)
[[0.  0.1 0.2 0.3 0.4]
 [0.5 0.6 0.7 0.8 0.9]]
x[0,1]
0.1

此时选取操作也是在一个方括号中进行,不过需要有一个逗号,逗号前表示取哪一行,后面表示取哪一列,上例就是取第0行,第1列的数字

x[1,:]
array([0.5, 0.6, 0.7, 0.8, 0.9])
x[:,-1]
array([0.4, 0.9])

也可以结合冒号,表示选取第1行所有元素,或是最后一列的所有元素。数组中的元素不仅可以通过方括号来选取,还可以用来修改。

x[0,0] = 1
x
array([[1. , 0.1, 0.2, 0.3, 0.4],
       [0.5, 0.6, 0.7, 0.8, 0.9]])

numpy也提供了一系列的函数来对数组进行添加和删除操作。

np.append(x,10)
array([ 1. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9, 10. ])
np.insert(x,0,10)
array([10. ,  1. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9])
np.delete(x,-1)
array([1. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])

数组里不仅可以存放数字,还可以存放计算逻辑值,例如来计算x这个数组中大于0.5的有哪些。返回了一个逻辑数组。

y = x>=0.5
print(y)
[[ True False False False False]
 [ True  True  True  True  True]]

逻辑数组可以放入方括号,作为选取的标准。

x[y]
array([1. , 0.5, 0.6, 0.7, 0.8, 0.9])
x[x>=0.5]
array([1. , 0.5, 0.6, 0.7, 0.8, 0.9])
x[x>=0.5] = 1
x
array([[1. , 0.1, 0.2, 0.3, 0.4],
       [1. , 1. , 1. , 1. , 1. ]])
x
array([[1. , 0.1, 0.2, 0.3, 0.4],
       [1. , 1. , 1. , 1. , 1. ]])

前面我们介绍了数组查询和修改,增加和删除,我们可以将两个数组进行拼接

y = np.arange(10,11,0.1).reshape(2,5)
y
array([[10. , 10.1, 10.2, 10.3, 10.4],
       [10.5, 10.6, 10.7, 10.8, 10.9]])
x = np.concatenate([x,y])
x
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格式的数组,不需要做循环就可以进行很丰富的数学计算。例如加减乘除等基本运算。

x = np.linspace(0,1,5)
print(x)
[0.   0.25 0.5  0.75 1.  ]
3 * x
array([0.  , 0.75, 1.5 , 2.25, 3.  ])
x/2
array([0.   , 0.125, 0.25 , 0.375, 0.5  ])
x**2 + 2*x - 3
array([-3.    , -2.4375, -1.75  , -0.9375,  0.    ])
np.sin(x)
array([0.        , 0.24740396, 0.47942554, 0.68163876, 0.84147098])

如果是一个自定义的函数,它并不天然支持这种向量化的表达。

x = np.arange(1,10,dtype=int)
print(x)
[1 2 3 4 5 6 7 8 9]
def odd_even(x):
    if x%2 == 0:
        return "偶数"
    else:
        return "奇数"
odd_even(x)
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来将这个普通函数转成一个向量化函数。

odd_even_v = np.vectorize(odd_even)
print(odd_even_v(x))
['奇数' '偶数' '奇数' '偶数' '奇数' '偶数' '奇数' '偶数' '奇数']

其实这个函数的功能可以直接用下面的代码来完成,即找到那些可以被2整除的数字,然后选取出来

x[x%2==0]
array([2, 4, 6, 8])

还有一种非常重要的计算就是矩阵计算。

x = np.array([[1,0],[1,0]])
print(x)
[[1 0]
 [1 0]]
y = np.array([[0.1,0.2],[0.3,0.4]])
print(y)
[[0.1 0.2]
 [0.3 0.4]]
x.dot(y)
array([[0.1, 0.2],
       [0.1, 0.2]])

小结:向量化计算是数组的特有功能,不需要写循环,计算速度快。在对数值进行编程时应该尽量转化为numpy数组,以利用向量化计算。如果是自定义函数,则可以使用vectorize进行转换。

练习

用列表和数组两种方法,找到一百万以下所有能被3和7整除的数字之和,并比较两种方法的计算消耗时间。

n = 1000000
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))
运行时间0.0719秒,和等于23809976190
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))
运行时间0.0409秒,和等于23809976190

Previous28 广度优先搜索算法Next30 随机和模拟

Last updated 4 years ago

Was this helpful?