第4章 Numpy计算科学模块(三):数组拼接与分裂

第4章 Numpy计算科学模块(三):数组拼接与分裂

4.1 numpy数组的拼接

4.1.1 concatenate()数组拼接方法

类似于python列表,numpy也实现了多个数组的拼接

  1. 拼接方法之一:numpy.concatenate((a1,a2,……),axis=0)
    • 描述:将多个数组沿某个坐标轴axis按a1,a2,……顺序进行拼接
    • 参数:(a1,a2,…)或[a1,a2,…]以元组或列表的形式将需要拼接的数组进行打包。a1,a2,…是需要拼接的多个数组
    • 参数:axis指定拼接数组所沿的某个维度,默认为0
    • 返回:拼接后的数组(副本)
    • 拼接条件:a1,a2,…需要在非拼接的坐标轴上拥有相同的shape,否则会出错
#两个一维数组的拼接
import numpy as np
x = np.array([1,2,3])
y = np.array([3,2,1])
np.concatenate([x,y])
array([1, 2, 3, 3, 2, 1])

4.1.2 (重要)axis参数的解析

  • axis为多维数组的某个维度或坐标轴。为简便,后面所述的轴等价于坐标轴
  • 类似于数组元素的索引是用于访问数组的某个元素。在numpy中,axis用于访问数组的某个轴,从0开始,最大到ndim-1,共有ndim个轴。
  • N维数组可以理解N维空间,N维空间拥有N个坐标轴。numpy可通过axis来索引某个具体的坐标轴。
  • 比如二维数组,它有2个维度/轴。第1维度对应axis=0轴,第2维度对应axis=1轴。
  • 多维数组的切片操作,本质上是沿着数组的每个轴分别做一维的切片操作,然后再将操作结果进行组合

4.1.3(重要)拼接条件和拼接规则

# 多个一维数组的拼接
x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
z = [99, 99, 99]
print(np.concatenate([x, y, z]))
[ 1  2  3  3  2  1 99 99 99]
# 2个二维数组的拼接
x1 = np.random.randint(10, size=(2, 4))
x2 = np.random.randint(10, size=(3, 4))

#输出2个数组的shape
print(x1.shape, x2.shape) 

# 沿着axis=0轴拼接 (符合拼接条件)
# 根据拼接规则,将axis=0轴的shape进行相加,其他不变
x3 = np.concatenate((x1, x2), axis=0)
print('axis=0:', x3.shape)

# 沿着axis=1轴拼接 (不符合拼接条件,程序出错)
x4 = np.concatenate((x1, x2), axis=1)
print('axis=1:', x4.shape)
(2, 4) (3, 4)
axis=0: (5, 4)
ValueError: all the input array dimensions except for the concatenation axis must match exactly
习题

三维数组有3个axisaxis决定了到底沿哪个方向上拼接。例如,两个三维数组(2,2,3)(2,1,3),分别沿axis=0axis=1axis=2轴进行拼接,理论上分析,非拼接轴的shape为?是否满足拼接条件?如果拼接成功,那么拼接后数组shape?并用程序对结果进行验证。

import numpy as np
np.random.seed(0)
x1 = np.random.randint(10,size=(2,2,3))
x2 = np.random.randint(10,size=(2,1,3))
#输出2个数组的shape
print(x1,'\n',x1.shape, '\n',x2,'\n',x2.shape) 

[[[5 0 3]
  [3 7 9]]

 [[3 5 2]
  [4 7 6]]] 
 (2, 2, 3) 
 [[[8 8 1]]

 [[6 7 7]]] 
 (2, 1, 3)
# 沿着axis=0轴拼接 (符合拼接条件)
# 根据拼接规则,将axis=0轴的shape进行相加,其他不变
x3 = np.concatenate((x1, x2), axis=1)
print('axis=0:', x3.shape)
print(x3)

axis=0: (2, 3, 3)
[[[5 0 3]
  [3 7 9]
  [8 8 1]]

 [[3 5 2]
  [4 7 6]
  [6 7 7]]]

非拼接轴为axis=0,axis=2

4.1.4vstack()和hstack()快捷数组拼接方法

numpy.vstack((a1, a2, ...))
  • 描述:沿着第0轴(垂直方向)拼接数组。等价于axis=0concatenate()v代表vertically。
  • 参数:(a1, a2, ...)以元组的形式将需要拼接的数组进行打包。a1, a2, ...是需要拼接的多个数组。
  • 返回:拼接后的数组(副本)。
  • 理解:垂直方向对于超过二维的数组很难想象,因此,将这方法理解为沿着第0轴拼接数组。
  • 拼接条件:非拼接的shape必须要一致。
x1 = np.array([1, 2, 3])
x2 = np.array([2, 3, 4])
print(x1.shape, x2.shape)

x3 = np.vstack((x1,x2))
x4 = np.concatenate((x1,x2),axis=0)
print('vstack:', x3.shape)
print(x3)
print(x4)
(3,) (3,)
vstack: (2, 3)
[[1 2 3]
 [2 3 4]]
[1 2 3 2 3 4]
# 2个3维数组的拼接
x1 = np.random.randint(10, size=(2, 3, 4))
x2 = np.random.randint(10, size=(3, 3, 4))

#输出2个数组的shape
print(x1.shape, x2.shape) 
print(x1,'\n',x2)
# 沿着axis=0轴拼接 (符合拼接条件)
x3 = np.vstack((x1, x2))
print('vstack:', x3.shape)
print(x3)
(2, 3, 4) (3, 3, 4)
[[[8 1 5 9]
  [8 9 4 3]
  [0 3 5 0]]

 [[2 3 8 1]
  [3 3 3 7]
  [0 1 9 9]]] 
 [[[0 4 7 3]
  [2 7 2 0]
  [0 4 5 5]]

 [[6 8 4 1]
  [4 9 8 1]
  [1 7 9 9]]

 [[3 6 7 2]
  [0 3 5 9]
  [4 4 6 4]]]
vstack: (5, 3, 4)
[[[8 1 5 9]
  [8 9 4 3]
  [0 3 5 0]]

 [[2 3 8 1]
  [3 3 3 7]
  [0 1 9 9]]

 [[0 4 7 3]
  [2 7 2 0]
  [0 4 5 5]]

 [[6 8 4 1]
  [4 9 8 1]
  [1 7 9 9]]

 [[3 6 7 2]
  [0 3 5 9]
  [4 4 6 4]]]

numpy.hstack((a1, a2, ...))
  • 描述:沿着第1轴(水平方向)拼接数组。等价于axis=1concatenate()h代表horizontally。
  • 参数:(a1, a2, ...)以元组的形式将需要拼接的数组进行打包。a1, a2, ...是需要拼接的多个数组。
  • 返回:拼接后的数组(副本)。
  • 理解:水平方向对于超过二维的数组很难想象,因此,将这方法理解为沿着第1轴拼接数组。
  • 拼接条件:非拼接的shape必须要一致。

与之类似,np.dstack() 将沿着第2轴拼接数组,方法一样,不再叙述。

x1 = np.array([[9, 8, 7], [6, 5, 4]])
x2 = np.array([[99], [99]])

#输出2个数组的shape
print(x1.shape, x2.shape)
print(x1,'\n',x2)
# 沿着axis=1轴拼接 (符合拼接条件)
x3 = np.hstack((x1, x2))
print('hstack:', x3.shape)
print(x3)
(2, 3) (2, 1)
[[9 8 7]
 [6 5 4]] 
 [[99]
 [99]]
hstack: (2, 4)
[[ 9  8  7 99]
 [ 6  5  4 99]]
习题

随机生成3个shape自定义的三维数组。理论上分析,vstack()hstack()方法是否满足拼接条件?如果拼接成功,那么拼接后数组shape?并用程序对结果进行验证。

import numpy as np
np.random.seed(0)
#生成数组x1和x2,验证vstack
x1 = np.random.randint(10,size=(2,2,3))
x2 = np.random.randint(10,size=(1,2,3))
#打印x1和x2以及shape
print(x1,x1.shape,'\n',x2,x2.shape)
#沿axis=0
x3 = np.vstack((x1,x2))
print(x3)

#生成数组x4和x5,验证hstack
x4 = np.random.randint(10,size=(2,1,3))
x5 = np.random.randint(10,size=(2,2,3))
#打印x4和x5以及shape
print(x4,x4.shape,'\n',x5,x5.shape)
x6 = np.hstack((x4,x5))
print(x6)
[[[5 0 3]
  [3 7 9]]

 [[3 5 2]
  [4 7 6]]] (2, 2, 3) 
 [[[8 8 1]
  [6 7 7]]] (1, 2, 3)
[[[5 0 3]
  [3 7 9]]

 [[3 5 2]
  [4 7 6]]

 [[8 8 1]
  [6 7 7]]]
[[[8 1 5]]

 [[9 8 9]]] (2, 1, 3) 
 [[[4 3 0]
  [3 5 0]]

 [[2 3 8]
  [1 3 3]]] (2, 2, 3)
[[[8 1 5]
  [4 3 0]
  [3 5 0]]

 [[9 8 9]
  [2 3 8]
  [1 3 3]]]

4.2 numpy数组的分裂

4.2.1 split()数组分裂方法

知识点:numpy.split(ary, indices_or_sections, axis=0)

  • 描述:将一个数组分裂为多个子数组。

  • 参数:ary需要分裂的numpy数组。

  • 参数:indices_or_sections定义分裂规则。

    • 等分:如果输入是整数N,那么指明原数组被等分为N。另外,整数N必需要能整除axis轴方向的分裂数组shape否则会出错
    • 索引点:如果输入是列表,则列表元素代表被分裂的索引位置
  • 参数:axis指明沿哪个轴进行分裂,默认为0。

  • 返回:一组按规则分裂得到的N个子数组集合。进一步,可以对返回的子数组集合进行解包操作,解包所需的子数组数量必须刚好等于N否则会出错

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

#等分为4份
x_split = np.split(x, 4) 
print(x_split)
[ 1  2  3 99 99  3  2  1]
[array([1, 2]), array([ 3, 99]), array([99,  3]), array([2, 1])]
x = np.array([1, 2, 3, 99, 99, 3, 2, 1])
print(x)

# 在索引位置为2、4、6处分裂
x_split = np.split(x, [2,3,6]) 
# x_split为分裂子数组集合, 包含4个子数组
print(x_split)
[ 1  2  3 99 99  3  2  1]
[array([1, 2]), array([3]), array([99, 99,  3]), array([2, 1])]
x = np.random.randint(10, size=(3, 8))
print(x)

# 沿axis=1方向,在1、5索引位置处分裂
# 对返回的结果,通过赋值语句,将子数组进行解包操作。
x_split1, x_split2, x_split3 = np.split(x, [1, 5], axis=1)
print(x_split1, '\n\n', x_split2, '\n\n', x_split3)
[[3 7 0 1 9 9 0 4]
 [7 3 2 7 2 0 0 4]
 [5 5 6 8 4 1 4 9]]
[[3]
 [7]
 [5]] 

 [[7 0 1 9]
 [3 2 7 2]
 [5 6 8 4]] 

 [[9 0 4]
 [0 0 4]
 [1 4 9]]

习题
  1. 随机生成一维数组x,进行N等分和按索引点方式进行分裂,并输出结果。
x = np.array([1, 2, 3, 99, 99, 3, 2, 1])
print(x)

#等分为4份
x_split_1 = np.split(x, 4) 
print(x_split_1)
# 在索引位置为2、4、6处分裂
x_split_2 = np.split(x, [2,3,6]) 
# x_split为分裂子数组集合, 包含4个子数组
print(x_split_2)
[ 1  2  3 99 99  3  2  1]
[array([1, 2]), array([ 3, 99]), array([99,  3]), array([2, 1])]
[array([1, 2]), array([3]), array([99, 99,  3]), array([2, 1])]
  1. (思考题) np.split(x, 2)np.split(x, [2])相同吗?请程序验证你的想法。

    不一样

    x = np.array([1, 2, 3, 99, 99, 3, 2, 1])
    print(x)
    
    #等分为4份
    x_split_1 = np.split(x, 2) 
    print(x_split_1)
    # 在索引位置为2处分裂
    x_split_2 = np.split(x, [2]) 
    # x_split为分裂子数组集合, 包含4个子数组
    print(x_split_2)
    
    [ 1  2  3 99 99  3  2  1]
    [array([ 1,  2,  3, 99]), array([99,  3,  2,  1])]
    [array([1, 2]), array([ 3, 99, 99,  3,  2,  1])]
    
  2. 随机生成二维数组x,按第0轴,进行N等分和按索引点方式进行分裂,并输出结果。

    x = np.random.randint(10,size=(2,8))
    print(x)
    
    #等分为4份
    x_split_1 = np.split(x, 2) 
    print(x_split_1)
    # 在索引位置为2、4、6处分裂
    x_split_2 = np.split(x, [2]) 
    # x_split为分裂子数组集合, 包含4个子数组
    print(x_split_2)
    
    [[3 0 5 0 1 2 4 2]
     [0 3 2 0 7 5 9 0]]
    [array([[3, 0, 5, 0, 1, 2, 4, 2]]), array([[0, 3, 2, 0, 7, 5, 9, 0]])]
    [array([[3, 0, 5, 0, 1, 2, 4, 2],
           [0, 3, 2, 0, 7, 5, 9, 0]]), array([], shape=(0, 8), dtype=int32)]
    
  3. 分裂过程中,都会遇到什么错误?又是怎么解决的?

4.2.2 vsplit())和hsplit()数组分裂方法

知识点:numpy.vsplit(ary, indices_or_sections)

  • 描述:沿第0轴方向,对数组进行分裂。
  • 参数:见numpy.split()
  • 返回:一组沿第0轴方向按规则分裂得到的N个子数组集合

知识点:numpy.hsplit(ary, indices_or_sections)

  • 描述:沿第1轴方向,对数组进行分裂。
  • 参数:见numpy.split()
  • 返回:一组沿第1轴方向按规则分裂得到的N个子数组集合
grid = np.arange(16).reshape((4, 4))
print(grid)
print('----------')

upper, lower = np.vsplit(grid, [2])
print(upper)
print(lower)
print('----------')

left, right = np.hsplit(grid, [2])
print(left)
print(right)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
----------
[[0 1 2 3]
 [4 5 6 7]]
[[ 8  9 10 11]
 [12 13 14 15]]
----------
[[ 0  1]
 [ 4  5]
 [ 8  9]
 [12 13]]
[[ 2  3]
 [ 6  7]
 [10 11]
 [14 15]]

习题
  • 随机生成1个shape自定义的三维数组

    • vsplit()hsplit()使用自定义n等分和按分裂点列表,对数组进行分裂。
    • 对返回的子数组集合进行解包操作。注意解包子数组的数量。
  • grid = np.arange(64).reshape((4,4,4))
    print(grid)
    print('----------')
    
    upper, lower = np.vsplit(grid, [2])
    print(upper)
    print(lower)
    print('----------')
    
    left, right = np.hsplit(grid, [2])
    print(left)
    print(right)
    
  • [[[ 0  1  2  3]
      [ 4  5  6  7]
      [ 8  9 10 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]
      [36 37 38 39]
      [40 41 42 43]
      [44 45 46 47]]
    
     [[48 49 50 51]
      [52 53 54 55]
      [56 57 58 59]
      [60 61 62 63]]]
    ----------
    [[[ 0  1  2  3]
      [ 4  5  6  7]
      [ 8  9 10 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]
      [36 37 38 39]
      [40 41 42 43]
      [44 45 46 47]]
    
     [[48 49 50 51]
      [52 53 54 55]
      [56 57 58 59]
      [60 61 62 63]]]
    ----------
    [[[ 0  1  2  3]
      [ 4  5  6  7]]
    
     [[16 17 18 19]
      [20 21 22 23]]
    
     [[32 33 34 35]
      [36 37 38 39]]
    
     [[48 49 50 51]
      [52 53 54 55]]]
    [[[ 8  9 10 11]
      [12 13 14 15]]
    
     [[24 25 26 27]
      [28 29 30 31]]
    
     [[40 41 42 43]
      [44 45 46 47]]
    
     [[56 57 58 59]
      [60 61 62 63]]]
    

第5章 Numpy计算科学模块(四):向量化运算

5.1 numpy数组向量化计算

5.1.1 numpy的向量化技术

知识点:numpy的向量化技术。

  • 应用场合:需要对数组的元素进行重复性运算或操作,借助向量化技术,避免直接的循环遍历数组元素,进而极大地提高数组元素的计算效率
  • NumPy提供了一个简单灵活的接口来优化数据数组的计算
  • 使NumPy变快的关键是利用向量化操作,通常在NumPy的通用函数(ufunc)中实现。

5.1.2 python解释性语言导致循环很慢

知识点:测试时间魔法函数:%%time%time%timeit

  • %%time将会给出整个cell代码运行一次所花费的时间。将其放在待测试cell开头
  • %time将会给出当前行代码运行一次所花费的时间。将其放在待测试语句之前。
  • %timeit使用Python的timeit模块,它将会执行当前行代码100000次(默认情况下),然后给出运行最快3次的平均值。将其放在待测试语句之前。

5.1.3 初识通用函数

import numpy as np
ar = np.arange(6).reshape(2,3)
print(ar)
print('-------')
print(ar + 10)#加法
print(ar * 2)#乘法
print(1 / (ar+1))#除法
print(ar ** 2)#幂
print('-------')
#与标量的运算
print(ar.mean())#求平均值
print(ar.max())#求最大值
print(ar.min())#求最小值
print(ar.std())#求标准差
print(ar.var())#求方差
print(ar.sum(),np.sum(ar,axis = 0))#求和
print(np.sort(np.array([1,4,3,2,5,6])))#排序

向量化操作是消除代码中显式for循环语句的艺术

numpy向量技术是通过通用函数实现的。通用函数为numpy数组的元素重复操作提供了更高的执行效率中的值执行更快的重复操作。

  • 可用于标量和数组的运算。
  • 可用于两个数组之间的运算(元素个数要匹配,否则会出错)。
  • 可用于一维数组的运算,也可用于多维数组的运算。

知识点:numpy.arange([start, ]stop, [step, ]dtype=None)

  • 描述:给定区间范围[start stop),生成指定等间隔step的一组数。
  • 参数:[]代表可以缺省,[start, ][step, ]可以缺省。
  • 参数:start指定区间开始。默认值为0.
  • 参数:stop指定区间结束。不能缺省。
  • 参数:dtype指定数据类型。
  • 返回:等间隔元素的数组。
  • 注意:生成的元素不包括stop
# 通用函数:除法运算 用于两个一维数组
import numpy as np
x = np.arange(9) / np.arange(1, 10)
print(x)
print('-------')
x = np.arange(9).reshape((3, 3))
# 通用函数:指数运算 用于二维数组
print(2 ** x) 
[0.         0.5        0.66666667 0.75       0.8        0.83333333
 0.85714286 0.875      0.88888889]
-------
[[  1   2   4]
 [  8  16  32]
 [ 64 128 256]]

对数组使用通用函数的向量化运算,几乎总比用Python循环更加有效,特别是大规模数组。只要你看到Python程序对数组的循环,就应该考虑是否能用向量化技术来消除循环。

习题

随机生成2个自定义size=(100,100)的二维数组,然后使用python循环(需要编写函数)和向量化方式,对两个矩阵运算进行相乘,并统计两种运算的时间。

np.random.seed(0)
x1 = np.random.randint(10,size=(100,100))
x2 = np.random.randint(10,size=(100,100))
%time
print(x1*x2)
print('-'*14)
def Multiply(a,b):
    print(a*b)

%time
print(Multiply(x1,x2))
Wall time: 0 ns
[[45  0 21 ...  2 16 14]
 [ 0 15 16 ... 32 18  0]
 [72 35 45 ...  0  0  0]
 ...
 [ 7  0  4 ... 56  0  8]
 [72 48  8 ...  3 54 30]
 [12 21  5 ...  0  0  0]]
--------------
Wall time: 0 ns
[[45  0 21 ...  2 16 14]
 [ 0 15 16 ... 32 18  0]
 [72 35 45 ...  0  0  0]
 ...
 [ 7  0  4 ... 56  0  8]
 [72 48  8 ...  3 54 30]
 [12 21  5 ...  0  0  0]]
None

5.2 进阶通用函数

知识点:通用函数有两种存在形式。

  • 一元通用函数(unary ufunc)对单个输入操作。
  • 二元通用函数(binary ufunc)对两个输入操作。

5.2.1 算术运算符

numpy通用函数的使用方式非常自然,因为它用到了 Python 原生的算术运算符,标准的加、减、乘、除都可以使用:

x = np.arange(4)
print("x     =", x)
print("x + 5 =", x + 5)
print("x - 5 =", x - 5)
print("x * 2 =", x * 2)
print("x / 2 =", x / 2)
print("x // 2 =", x // 2)  #整除法运算
#逻辑运算、**指数运算符和%模运算符的一元通用函数
print('-'*14)
x = np.arange(4)
print("-x     = ", -x)
print("x ** 2 = ", x ** 2)
print("x % 2  = ", x % 2)
print('-'*14)
#注意运算的优先级
x = np.arange(4)
print(-(0.5*x + 1) ** 2)
x     = [0 1 2 3]
x + 5 = [5 6 7 8]
x - 5 = [-5 -4 -3 -2]
x * 2 = [0 2 4 6]
x / 2 = [0.  0.5 1.  1.5]
x // 2 = [0 0 1 1]
--------------
-x     =  [ 0 -1 -2 -3]
x ** 2 =  [0 1 4 9]
x % 2  =  [0 1 0 1]
--------------
[-1.   -2.25 -4.   -6.25]

知识点:所有算术运算符都是numpy通用函数的简单封装器。

  • 下表给出了所有算术运算符等价的numpy通用函数
  • 可以调用运算符进行向量化运算,也可以调用numpy通用函数。
  • 例如+运算符就是一个add函数的封装器。
运算符对应的通用函数描述
+np.add加法运算(即 1 + 1 = 2
-np.subtract减法运算(即 3 - 2 = 1
-np.negative负数运算( 即 -2
*np.multiply乘法运算(即 2 * 3 = 6
/np.divide除法运算(即 3 / 2 = 1.5
//np.floor_divide地板除法运算(floor division,即 3 // 2 = 1
**np.power指数运算(即 2 ** 3 = 8
%np.mod模 / 余数( 即 9 % 4 = 1
x = np.arange(4)
x1 = np.add(x, 2)
x2 = x**2
print(x1)
print(x2)
[2 3 4 5]
[0 1 4 9]
习题

随机生成2个自定义size=(5,)的一维随机数组,使用上述表格中8个运算符和通用函数,进行向量化运算。比较它们的结果是否是一样的。

np.random.seed(0)
x1 = np.random.randint(10,size=(5,))
x2 = np.random.randint(10,size=(5,))

print(x1+x2)
print(np.add(x1,x2))
print('-'*14)
print(x1-x2)
print(np.subtract(x1,x2))
print('-'*14)
print(-x1,-x2)
print('-'*14)
print(x1*x2)
print(np.multiply(x1,x2))
[14  3  8  5 11]
[14  3  8  5 11]
--------------
[-4 -3 -2  1  3]
[-4 -3 -2  1  3]
--------------
[-5  0 -3 -3 -7] [-9 -3 -5 -2 -4]
--------------
[45  0 15  6 28]
[45  0 15  6 28]

5.2.2 绝对值

知识点:numpy可以直接使用python内置(build-in)函数进行运算。对于numpy数组进行绝对值运算有三种途径:

  • 调用python函数abs(x)
  • 调用numpy函数np.abs(x)
  • 调用numpy函数np.absolute(x)
  • 注意:numpy函数需要通过numpy模块(np.)进行调用,python内置函数直接调用。np.absnp.absolute功能等价。
x = np.array([-2, -1, 0, 1, 2])
print(abs(x))
print(np.abs(x))
print(np.absolute(x))
[2 1 0 1 2]
[2 1 0 1 2]
[2 1 0 1 2]

知识点:当处理复数时,绝对值np.abs(x)返回的是该复数的幅度。

x = np.array([3 - 4j, 4 - 3j, 2 + 0j, 0 + 1j])
print(np.abs(x))
[5. 5. 2. 1.]
习题

随机生成1个size=(1000,)的一维随机数组(包含负数),使用上述3种绝对值函数对其进行操作,并记录相应的运算时间。

x = np.random.randint(-100,100,size=(1000,))
print(abs(x))
print(np.abs(x))
print(np.absolute(x))
[ 13  30  12  40  42  93  61  13  74  12  19  65  75  23  28  91  48  15
  97  21  75  92  18   1  77  71  47  47  42  67  68  93  91  85  27  68
  20   9  68  16  64  76  75  33   3  65  14  70  71  6
  16  44  38  84  52  45  17  85  91  90  21  61  32  44  29  38  77   9
  10  93  78   2  58  20  55  53  14  89  22  70  10  66  46  19  63  29
   9  41  12  65  32  81  87   1  81   7  92   4  97  24   0  29  57  33
  68  50  70  81  96  48   6  26  23  95  40   7  69  60  88  79  60  41
  71   6  65  26  84   1  67  57  35  77]

5.2.3 三角函数

知识点:numpy提供了一系列的三角函数和反三角函数。

  • 三角函数参数:弧度制,不是角度。
  • 反函数参数:需要转换的三角函数值。
  • π在numpy中定义了常量:np.pi
# 声明一个弧度数组
theta = np.linspace(0, np.pi, 3)
# 三角函数运算
print("theta      = ", theta)
print("sin(theta) = ", np.sin(theta))
print("cos(theta) = ", np.cos(theta))
print("tan(theta) = ", np.tan(theta))
# 声明一个三角函数值数组
x = [-1, 0, 1]
# 反三角函数运算
print("x         = ", x)
print("arcsin(x) = ", np.arcsin(x))
print("arccos(x) = ", np.arccos(x))
print("arctan(x) = ", np.arctan(x))

三角函数:numpy提供的三角函数功能。

  • numpy.sin(x):三角正弦。
  • numpy.cos(x):三角余弦。
  • numpy.tan(x):三角正切。
  • numpy.arcsin(x):三角反正弦。
  • numpy.arccos(x):三角反余弦。
  • numpy.arctan(x):三角反正切。
  • numpy.hypot(x1,x2):直角三角形求斜边。
  • numpy.degrees(x):弧度转换为度。
  • numpy.radians(x):度转换为弧度。
  • numpy.deg2rad(x):度转换为弧度。
  • numpy.rad2deg(x):弧度转换为度。

双曲函数:在数学中,双曲函数是一类与常见的三角函数类似的函数。双曲函数经常出现于某些重要的线性微分方程的解中,使用numpy计算它们的方法为:

  • numpy.sinh(x):双曲正弦。
  • numpy.cosh(x):双曲余弦。
  • numpy.tanh(x):双曲正切。
  • numpy.arcsinh(x):反双曲正弦。
  • numpy.arccosh(x):反双曲余弦。
  • numpy.arctanh(x):反双曲正切。

5.2.4 小数位修约

知识点:小数位修约, 是指在进行具体的数字运算前,按照一定的规则舍去某些数字后面多余的小数位。比如,我们常听到的4舍5入就属于小数位修约中的一种。

知识点:numpy.around(x)

  • 舍弃小数位,取离x最近的整数。
  • 如果小数位是.5,那么取离它最近的偶数整数。

知识点:numpy.floor(x)返回小于元素的最大整数。

知识点:numpy.ceil(x)返回大于元素的最小整数。

习题

随机生成一个二维浮点数数组,然后使用np.around(x)np.floor(x)np.ceil(x),结合修约规则分析结果。

x = np.random.rand(10)
xx = x*10
print(xx.astype(float))

print(np.around(xx))
print(np.floor(xx))
print(np.ceil(xx))
[3.80335184 1.47808677 6.84934439 6.56761958 8.62062596 0.97257995
 4.97776908 5.8108193  2.4155704  1.69025406]
[4. 1. 7. 7. 9. 1. 5. 6. 2. 2.]
[3. 1. 6. 6. 8. 0. 4. 5. 2. 1.]
[4. 2. 7. 7. 9. 1. 5. 6. 3. 2.]

5.2.5 指数和对数

知识点:

  • 指数函数:以e为底np.exp(x)、以2为底np.exp2(x)、以自定义n为底np.power(n, x)
  • 对数函数:以为e底np.log(x)、以2为底np.log2(x)、以自定义10为底np.log10(x)。要求参数x>0
  • 特殊组合函数:计算exp(x)-1函数np.expm1(x)、计算log(1+x)函数np.log1p(x)。当x的值很小时,以上函数给出的值比np.log()np.exp()的计算更精确。
习题
  • 随机生成1个size=(10,)的一维随机数组,对其进行指数、对数和特殊组合函数运算,并记录相应的运算时间。
x = np.random.randint(10,size=(10,))
print("x     =", x)
print("e^x   =", np.exp(x))
print("2^x   =", np.exp2(x))
print("3^x   =", np.power(3, x))
print('-'*20)
print("ln(x)    =", np.log(x))
print("log2(x)  =", np.log2(x))
print("log10(x) =", np.log10(x))
print('-'*20)
print("exp(x) - 1 =", np.expm1(x))
print("log(1 + x) =", np.log1p(x))
x     = [1 9 1 6 9 8 6 3 0 7]
e^x   = [2.71828183e+00 8.10308393e+03 2.71828183e+00 4.03428793e+02
 8.10308393e+03 2.98095799e+03 4.03428793e+02 2.00855369e+01
 1.00000000e+00 1.09663316e+03]
2^x   = [  2. 512.   2.  64. 512. 256.  64.   8.   1. 128.]
3^x   = [    3 19683     3   729 19683  6561   729    27     1  2187]
--------------------
ln(x)    = [0.         2.19722458 0.         1.79175947 2.19722458 2.07944154
 1.79175947 1.09861229       -inf 1.94591015]
log2(x)  = [0.         3.169925   0.         2.5849625  3.169925   3.
 2.5849625  1.5849625        -inf 2.80735492]
log10(x) = [0.         0.95424251 0.         0.77815125 0.95424251 0.90308999
 0.77815125 0.47712125       -inf 0.84509804]
--------------------
exp(x) - 1 = [1.71828183e+00 8.10208393e+03 1.71828183e+00 4.02428793e+02
 8.10208393e+03 2.97995799e+03 4.02428793e+02 1.90855369e+01
 0.00000000e+00 1.09563316e+03]
log(1 + x) = [0.69314718 2.30258509 0.69314718 1.94591015 2.30258509 2.19722458
 1.94591015 1.38629436 0.         2.07944154]
  • 当对数函数的参数输入<0时,会发生什么错误?

    x = np.random.randint(10,size=(10,))
    x=-x
    print("x     =", x)
    print("3^x   =", np.power(3, x))
    
    x     = [-4 -5 -7 -4 -4 -2 -2 -1 -3  0]
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-40-26ca17d3ae17> in <module>
          2 x=-x
          3 print("x     =", x)
    ----> 4 print("3^x   =", np.power(3, x))
    
    ValueError: Integers to negative integer powers are not allowed.
    

5.2.6 高阶函数

除了以上介绍到的,numpy还提供了很多通用函数,包括双曲三角函数、比特位运算、比较运算符、弧度转化为角度的运算、取整和求余运算等。此外,专门有一个scipy.special模块提供了用于数学统计的通用函数。如果学统计的同学,可以使用该模块进行数值分析和实验。

5.3 高级的通用函数特性

5.3.1 通用函数的指定输出

知识点:

  • 当被运算的数组规模很大时,之前的通过赋值=方式接受通用函数的返回结果(例如y=np.multiply(x, 10)),容易造成大内存空间分配不足。

  • 为此,NumPy为通用函数提供可以指定结果存放位置的out参数。

    • 首先创建接受结果的空数组y
    • 然后np.multiply(x, 10, out=y)通过配置out=y参数来指定y来接受函数结果。
  • 理解:不同于将返回结果间接赋值给临时数组;NumPy提供了out参数,可以直接将计算结果储存在预先分配的存储位置。

  • 小规模两种方式都可以,大规模数组建议使用out参数

#赋值方式:y用于接收函数的返回值
x = np.arange(20)
y = np.multiply(x, 10)
print(y)
#out参数方式:直接将结果存在y中,而非通过返回值。
x = np.arange(20)
# 预先创建用于接受结果的数组,事先分配好空间
y = np.empty(20)
np.multiply(x, 10, out=y)
print(y)

可以将计算结果写入指定数组的每隔一个元素的位置。

#out参数方式
x = np.arange(20)
y = np.zeros(40)

# 结果存放在y[::2],间隔元素的赋值。
np.power(x, 2, out=y[::2])
print(y)

out数组与函数结果输出不匹配

预分配空间的数组类型必须要和通用函数输出类型匹配,否则会TypeError

#随机一个自定义`size`的二维数组
x =np.random.randint(1,10,size=(2,2))

# np.log2(x)返回的类型为float类型
z = np.log2(x)
print(z.dtype)

# 使用np.full()来预分配空间,此时空间为int类型
y = np.full((2,2),0)
print(y.dtype)

# 出现TypeError:int和float不兼容
np.log2(x,out=y)
 出现TypeError:int和float不兼容
习题
  • 随机一个自定义size的二维数组,预先创建接受结果的数组用于指定out参数,调用通用函数对其进行指数和对数运算。
x =np.random.randint(1,10,size=(2,2))

t =np.exp2(x)
print(t.dtype)
# np.log2(x)返回的类型为float类型
z = np.log2(x)
print(z.dtype)

# 使用np.full()来预分配空间,此时空间为int类型
k = np.full((2,2),0,dtype=float)
y = np.full((2,2),0,dtype=float)
print(y.dtype)
print('-'*20)
# 出现TypeError:int和float不兼容
print(np.exp2(x,out=k))
print(np.log2(x,out=y))

float64
float64
float64
--------------------
[[512. 512.]
 [ 32.  64.]]
[[3.169925   3.169925  ]
 [2.32192809 2.5849625 ]]
  • 随机两个自定义size的一维数组,预先创建接受结果的数组用于指定out参数,调用通用函数对其进行四则运算,使用切片方式将结果返回。
x =np.random.randint(1,10,size=(2,2))
print(x)
# 使用np.full()来预分配空间,此时空间为int类型
k = np.full((2,2),0,dtype=float)
y = np.full((2,2),0,dtype=float)
print(y.dtype)
print('-'*20)
# 出现TypeError:int和float不兼容
print(np.add(x,2,out=k))
print(np.subtract(x,2,out=y))
print(np.multiply(x,2,out=y))
print(np.divide(x,2,out=y))
[[8 2]
 [3 6]]
float64
--------------------
[[10.  4.]
 [ 5.  8.]]
[[6. 0.]
 [1. 4.]]
[[16.  4.]
 [ 6. 12.]]
[[4.  1. ]
 [1.5 3. ]]

5.3.2 reduce()通用函数的聚合操作

知识点:

  • numpy二元通用函数提供了非常有用的聚合reduce()方法,通过聚合可以实现对数组元素的累积操作。
  • reduce()聚合方法:对给定数组元素累积执行二元通用函数操作。即,将上次运算的结果和下个数组元素进行累积运算,直到遍历完所有元素。

例如,对add通用函数调用reduce方法会返回数组中所有元素的和。

x = np.arange(1, 6)
print(np.add.reduce(x))
15

例如,对multiply通用函数调用reduce方法会返回数组中所有元素的乘积。有阶乘的味道

x = np.arange(1, 6)
print(np.multiply.reduce(x))
120

知识点:如果需要存储每次计算的中间结果,可以使用accumulate()方法。


x = np.arange(1, 6)
print(np.add.accumulate(x))
print(np.multiply.accumulate(x))
[ 1  3  6 10 15]
[  1   2   6  24 120]
习题

随机一个自定义size的二维数组,选定4种通用函数,然后使用reduce()accumulate()对数组实现聚合运算,并输出结果。

x = np.arange(1, 10)
print(np.add.reduce(x))
print(np.subtract.reduce(x))
print(np.multiply.reduce(x))
#print(np.divide.reduce(x))
print('-'*20)
print(np.add.accumulate(x))
print(np.subtract.accumulate(x))
print(np.multiply.accumulate(x))
#print(np.divide.accumulate(x))
45
-43
362880
--------------------
[ 1  3  6 10 15 21 28 36 45]
[  1  -1  -4  -8 -13 -19 -26 -34 -43]
[     1      2      6     24    120    720   5040  40320 362880]

5.3.3 outer()通用函数的外积操作

知识点:

  • 任何通用函数都可使用outer外积方法获得两个输入数组所有元素对的函数运算结果
  • 联系线性代数的叉乘外积(展成元素对),
  • outer方法:首先将两个数组展成元素对形式,然后再对这些元素对实现通用函数运算。
习题

使用外积方式,实现99乘法表,并输出结果。

x = np.arange(1,10)
print(np.multiply.outer(x,x))
[[ 1  2  3  4  5  6  7  8  9]
 [ 2  4  6  8 10 12 14 16 18]
 [ 3  6  9 12 15 18 21 24 27]
 [ 4  8 12 16 20 24 28 32 36]
 [ 5 10 15 20 25 30 35 40 45]
 [ 6 12 18 24 30 36 42 48 54]
 [ 7 14 21 28 35 42 49 56 63]
 [ 8 16 24 32 40 48 56 64 72]
 [ 9 18 27 36 45 54 63 72 81]]

accumulate()方法。


x = np.arange(1, 6)
print(np.add.accumulate(x))
print(np.multiply.accumulate(x))
[ 1  3  6 10 15]
[  1   2   6  24 120]
习题

随机一个自定义size的二维数组,选定4种通用函数,然后使用reduce()accumulate()对数组实现聚合运算,并输出结果。

x = np.arange(1, 10)
print(np.add.reduce(x))
print(np.subtract.reduce(x))
print(np.multiply.reduce(x))
#print(np.divide.reduce(x))
print('-'*20)
print(np.add.accumulate(x))
print(np.subtract.accumulate(x))
print(np.multiply.accumulate(x))
#print(np.divide.accumulate(x))
45
-43
362880
--------------------
[ 1  3  6 10 15 21 28 36 45]
[  1  -1  -4  -8 -13 -19 -26 -34 -43]
[     1      2      6     24    120    720   5040  40320 362880]

5.3.3 outer()通用函数的外积操作

知识点:

  • 任何通用函数都可使用outer外积方法获得两个输入数组所有元素对的函数运算结果
  • 联系线性代数的叉乘外积(展成元素对),
  • outer方法:首先将两个数组展成元素对形式,然后再对这些元素对实现通用函数运算。
习题

使用外积方式,实现99乘法表,并输出结果。

x = np.arange(1,10)
print(np.multiply.outer(x,x))
[[ 1  2  3  4  5  6  7  8  9]
 [ 2  4  6  8 10 12 14 16 18]
 [ 3  6  9 12 15 18 21 24 27]
 [ 4  8 12 16 20 24 28 32 36]
 [ 5 10 15 20 25 30 35 40 45]
 [ 6 12 18 24 30 36 42 48 54]
 [ 7 14 21 28 35 42 49 56 63]
 [ 8 16 24 32 40 48 56 64 72]
 [ 9 18 27 36 45 54 63 72 81]]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值