1.3 NumPy高级语法

本文讨论NumPy中常用的一些高级方法。大家需要对各个函数与其输出的结果进行关联,总结相应的规律,这对大家的数学和逻辑思维有一定的要求。

本章节主要内容包括:

一、堆叠

几个阵列可以沿不同的轴堆叠在一起。

示例代码如下:

import numpy as np

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

b = np.array([[5, 6],
              [7, 8]])

print("垂直堆叠:\n", np.vstack((a, b)))
print("\n水平堆叠:\n", np.hstack((a, b)))
print("\n连接到第2轴:\n", np.concatenate((a, b), 1))

c = [5, 6]
print("\n列堆叠:\n", np.column_stack((a, c)))

输出:

垂直堆叠:
 [[1 2]
 [3 4]
 [5 6]
 [7 8]]

水平堆叠:
 [[1 2 5 6]
 [3 4 7 8]]

连接到第2轴:
 [[1 2 5 6]
 [3 4 7 8]]

列堆叠:
 [[1 2 5]
 [3 4 6]]

二、拆分

对于拆分,常见的有以下功能:

示例代码如下:

import numpy as np

a = np.array([[1, 3, 5, 7, 9, 11],
              [2, 4, 6, 8, 10, 12]])

# 沿水平轴拆分
print("沿水平轴分为三部分:\n", np.hsplit(a, 3))

# 沿垂直轴拆分
print("\n沿垂直轴分为两部分:\n", np.vsplit(a, 2))

输出:

沿水平轴分为两部分:
 [array([[1,3,5],
       [2,4,6]]),数组([[7,9,11],
       [8,10,12]])]

沿垂直轴分为2部分:
 [array([[1,3,5,7,9,11]]),array([[2,4,6,8,10,12]])]

三、广播

NumPy的广播用于在算术运算期间处理具有不同形状的数组。受某些约束的影响,较小的阵列在较大的阵列上“广播”,以便它们具有兼容的形状。

广播提供了一种矢量化数组操作的方法,以便在C(而不是Python)中进行循环,它可以在不制作不必要的数据副本的情况下实现这一点,并且实现高效。另外,广播也有一个缺点,因为它会导致内存使用效率低下,从而减慢计算速度。

NumPy操作通常是逐个元素完成的,这需要两个数组具有完全相同的形状。当阵列的形状满足某些约束时,Numpy的广播规则放宽了这种约束。

广播规则:为了进行广播,操作中两个数组的尾轴大小必须相同,或者其中一个必须为1

让我们看一些例子:

A(2-D array): 4 x 3
B(1-D array):     3
Result      : 4 x 3
A(4-D array): 7 x 1 x 6 x 1
B(3-D array):     3 x 1 x 5
Result      : 7 x 3 x 6 x 5

但这将是一个不匹配:

A: 4 x 3
B:     4

当在操作中组合数组和标量值时,会发生最简单的广播示例。 请看下面给出的例子:

import numpy as np

a = np.array([1.0, 2.0, 3.0])

# Example 1
b = 2.0
print(a * b)

# Example 2
c = [2.0, 2.0, 2.0]
print(a * c)

输出:

[2. 4. 6.]
[2. 4. 6.]

我们可以将在算术运算期间被拉伸的标量b想象成具有与a相同形状的数组。b中的新元素,如上图所示,只是原始标量的副本。 Numpy足够聪明,可以使用原始标量值,而无需实际制作副本,以便广播操作尽可能保持内存和计算效率。因为Example 1在乘法期间移动较少的内存(b是标量,而不是数组),所以使用numpy带有一百万个元素的阵列,比Example 2快大约10%!下图使概念更加清晰:

img

在上面的例子中,标量b被拉伸成具有与a相同形状的阵列,因此形状与逐个元素的乘法兼容。

现在,让我们看一个两个数组都被拉伸的例子。

import numpy as np

a = np.array([0.0, 10.0, 20.0, 30.0])
b = np.array([0.0, 1.0, 2.0])
print("a:", a)
print("a[:, np.newaxis]\n", a[:, np.newaxis])
print("a[:, np.newaxis] + b:\n", a[:, np.newaxis] + b)

输出:

[[0. 1. 2.]
 [10. 11. 12.]
 [20. 21. 22.]
 [30. 31. 32.]]

1 在某些情况下,广播会拉伸两个阵列以形成大于任一初始阵列的输出阵列。

四、使用datetime

Numpy具有支持本机日期时间功能的数据类型,数据类型为“datetime64”,因为“datetime”已被Python中的日期时间库所占用。

请看下面的示例:

import numpy as np

# 创建日期
today = np.datetime64('2019-04-18')
print("日期是:", today)
print("年份是:", np.datetime64(today, 'Y'))

# 创建月份日期
dates = np.arange('2019-02', '2019-03', dtype='datetime64[D]')
print("\n2019年2月的日期:\n", dates)
print("今天是二月:", today in dates)

# 计算日期
dur = np.datetime64('2020-01-01') - np.datetime64('2019-01-01')
print("\n天数:", dur)
print("周数:", np.timedelta64(dur, 'W'))

# 日期排序
a = np.array(['2020-02-12', '2019-01-13', '2019-05-22'], dtype='datetime64')
print("\n按日期排序的日期:", np.sort(a))

输出:

日期是: 2019-04-18
年份是: 2019

2019年2月的日期:
 ['2019-02-01' '2019-02-02' '2019-02-03' '2019-02-04' '2019-02-05'
 '2019-02-06' '2019-02-07' '2019-02-08' '2019-02-09' '2019-02-10'
 '2019-02-11' '2019-02-12' '2019-02-13' '2019-02-14' '2019-02-15'
 '2019-02-16' '2019-02-17' '2019-02-18' '2019-02-19' '2019-02-20'
 '2019-02-21' '2019-02-22' '2019-02-23' '2019-02-24' '2019-02-25'
 '2019-02-26' '2019-02-27' '2019-02-28']
今天是二月: False

天数: 365 days
周数: 52 weeks

按日期排序的日期: ['2019-01-13' '2019-05-22' '2020-02-12']

五、线性代数

1. 线性代数的常见函数

NumPy的线性代数模块提供了在任何numpy数组上应用线性代数的各种方法。

你可以找到:

请看下面的示例,演示了我们如何使用NumPy来执行一些矩阵运算。

import numpy as np

A = np.array([[6, 1, 1],
              [4, -2, 5],
              [2, 8, 7]])

print("矩阵A的矩阵等级:", np.linalg.matrix_rank(A))
print("\n矩阵A对角线的总和:", np.trace(A))
print("\n矩阵A的行列式:", np.linalg.det(A))
print("\n矩阵A的逆:\n", np.linalg.inv(A))
print("\n矩阵A提高到幂2:\n", np.linalg.matrix_power(A, 2))

输出:

矩阵A的矩阵等级: 3

矩阵A对角线的总和: 11

矩阵A的行列式: -306.0

矩阵A的逆:
 [[ 0.17647059 -0.00326797 -0.02287582]
 [ 0.05882353 -0.13071895  0.08496732]
 [-0.11764706  0.1503268   0.05228758]]

矩阵A提高到幂2:
 [[42 12 18]
 [26 48 29]
 [58 42 91]]

2. 线性方程求解

让我们假设我们想要求解这个线性方程组:

x + 2*y = 8
3*x + 4*y = 18

使用linalg.solve函数来计算上面这个线性方程,如下所示:

import numpy as np

# 系数
a = np.array([[1, 2], [3, 4]])
# 常数
b = np.array([8, 18])

print("线性方程的解:", np.linalg.solve(a, b))

输出:

线性方程的解:[2. 3.]

可见方程式的解正是x=2, y=3

3. 线性回归

最后,我们来看一个示例,如何使用最小二乘法执行线性回归。

线性回归它是最小化从每个数据点到线的距离的平方和的线。因此,给定n对数据(xi,yi),我们要查找的参数是w1和w2,它们最小化了错误:

Numpy线性回归

下面的例子会使用到matplotlib库,未安装的先安装再使用。

pip install matplotlib

示例如下

import numpy as np
import matplotlib.pyplot as plt

# x坐标
x = np.arange(0, 9)
A = np.array([x, np.ones(9)])

# 线性生成序列
y = [19, 20, 20.5, 21.5, 22, 23, 23, 25.5, 24]
# 求回归线参数
w = np.linalg.lstsq(A.T, y)[0]

# 绘制线条
line = w[0]*x + w[1]  # 回归线
plt.plot(x, line, 'r-')
plt.plot(x, y, 'o')
plt.show()

输出:

对于绘制的代码我们将有专门介绍Matplotlib的章节再作解释。

六、结论

NumPy是一个广泛使用的通用库,它是许多其他计算库的核心,如scipy、scikit-learn、tensorflow、matplotlib、opencv等。对NumPy有基本的了解有助于有效地处理其他更高级别的库。

若有疑问,欢迎联系作者(微信:lixu1770105)。