IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    NumPy 学习笔记

    Hacper\'s Blog发表于 2018-06-26 21:01:59
    love 0

    数组 (ndarray)介绍

    这个数组我感觉更像是矩阵,官方文档说它是一个多维数组对象(ndarray)。

    NumPy provides an N-dimensional array type, the ndarray, which describes a collection of “items” of the same type. The items can be indexed using for example N integers.

    Array 的数据结构:

    ndarray 是一个通用的同构数据多维容器,数组中的所有元素类型都必须是相同的。每个数组都有一个 shape 和一个 dtype,分别表示数组的维度和元素的数据类型。

    创建数组

    直接用 np.array()函数将其他序列转换成 ndarray 数组

    输入数据可以是列表、元组、数组等序列类型

    1
    2
    3
    4
    5
    
    import numpy as np
    
    # 列表
    a = np.array([1,2,3,4])
    a
    

    array([1, 2, 3, 4])

    1
    2
    3
    
    # 元组
    b = np.array((1,2,3,4))
    b
    

    array([1, 2, 3, 4])

    1
    2
    3
    
    print(np.rank(a))
    print(np.ndim(a))
    type(a)
    

    1

    1

    i:\python364\lib\site-packages\ipykernel_launcher.py:1: VisibleDeprecationWarning: rank is deprecated; use the ndim attribute or function instead. To find the rank of a matrix see numpy.linalg.matrix_rank.
    “““Entry point for launching an IPython kernel.

    numpy.ndarray

    • rank 表示数组的维数
    1
    
    a.shape #查看维数,shape 是个元组,表示数组大小
    

    (4,)

    这表示是一维向量,但未指定是行向量还是列向量

    • 可以用 reshape 函数指定
    1
    2
    3
    
    a = a.reshape((1,-1)) # 前面的1表示一行,后面的-1是占位符,代表4
    a.shape
    # 行向量
    

    (1, 4)

    1
    2
    3
    
    a = a.reshape((-1,1))
    a.shape
    # 列向量
    

    (4, 1)

    1
    2
    3
    4
    
    a = a.reshape((2,-1))
    print(a)
    a.shape
    # 两行两列的数组
    

    [[1 2]

    [3 4]]

    (2, 2)

    • NumPy 的数组索引是从 0 开始的
    1
    2
    3
    4
    5
    6
    
    print(a)
    print('--------------')
    print(a[1, 1])
    print('--------------')
    a[1,0] = 88
    print(a)
    

    [[1 2][3 4]]


    4

    [[ 1 2][88 4]]

    还可以用这些函数新建数组

    • ones 创建元素为全 1 的数组
    • zeros 创建元素为全 0 的数组
    • full 还可以用 full 函数实现
    • eye/identity 创建单位矩阵
    • empty 创建新数组,只分配内存,但不填充值
    • ones_like 以另一个数组为参数,根据其形状和数据类型创建元素全 1 的数组,类似的有 zeros_like、empty_like
    • random 创建元素值为(0,1)范围的随机值矩阵
    1
    2
    
    a = np.ones((3,4))
    a
    

    array([[1., 1., 1., 1.],
    [1., 1., 1., 1.],
    [1., 1., 1., 1.]])

    1
    2
    
    a = np.zeros((2,3))
    a
    

    array([[0., 0., 0.],
    [0., 0., 0.]])

    1
    2
    
    a = np.full((3,4),1)
    a
    

    array([[1, 1, 1, 1],
    [1, 1, 1, 1],
    [1, 1, 1, 1]])

    1
    2
    
    a =  np.eye(5)
    a
    

    array([[1., 0., 0., 0., 0.],
    [0., 1., 0., 0., 0.],
    [0., 0., 1., 0., 0.],
    [0., 0., 0., 1., 0.],
    [0., 0., 0., 0., 1.]])

    1
    2
    
    a =  np.empty((2,3)) # 元素的值是不定值,不是0哦
    a
    

    array([[6.17021380e-042, 8.98497795e-067, 8.32380315e-071],
    [1.22082702e+165, 3.97948961e-315, 1.89124308e+219]])

    1
    2
    
    b = np.ones_like(a)
    b
    

    array([[1., 1., 1.],
    [1., 1., 1.]])

    1
    2
    
    b = np.zeros_like(a)
    b
    

    array([[0., 0., 0.],
    [0., 0., 0.]])

    1
    2
    3
    
    a =  np.eye(3)
    b = np.empty_like(a)
    b
    

    array([[5.97915591e-305, 6.01020264e-305, 5.98054658e-305],
    [5.98037274e-305, 5.99198485e-305, 5.98252833e-305],
    [5.96785670e-305, 2.15228723e-280, 5.98586589e-305]])

    1
    2
    
    a =  np.identity(4)
    a
    

    array([[1., 0., 0., 0.],
    [0., 1., 0., 0.],
    [0., 0., 1., 0.],
    [0., 0., 0., 1.]])

    eye identity 都可以创建单位矩阵,除了参数不同之外,二者有何区别?

    1
    2
    
    a = np.random.random((2,3))
    a
    

    array([[0.99217117, 0.26712817, 0.27010174],
    [0.30047328, 0.46375106, 0.37945124]])

    索引 和切片

    数组的索引是从 0 开始的,二维数组的索引如下图:

    数组切片是原始数组的视图,也就是说对切片的所有操作都是会影响原数组,视图数据并不是原始数据的拷贝,如果要得到切片的副本,需要显示地进行复制,如 a[2:3].copy()

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

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

    1
    
    a[1][1]
    

    5

    1
    
    a[1,1]
    

    5

    1
    2
    
    b = a[-2,1:3] #取倒数第二行,从第二列开始的两列
    print(b,b.shape)# 取出的元素的维数也跟着改变了
    

    5 6

    1
    2
    3
    
    b = a[-1:,1:3]
    print(b)
    b.shape
    

    [[8 9]]

    (1, 2)

    1
    2
    3
    
    b = a[-1,1:3]
    print(b)
    b.shape
    

    [8 9]

    (2,)

    结果还是有点不同,索引后的数组不小心的话会出现降维的情况
    在 MATLAB 中叫增量运算符,这里有点类似

    • 再看看花式索引(用整数数组进行索引)

    和切片不同,它是复制数据到新数组

    1
    2
    3
    
    # 还可以对数组元素批量操作
    a = np.arange(32).reshape((8,4))
    print(a)
    

    [[ 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]]

    1
    
    np.arange(3)
    

    array([0, 1, 2])

    1
    
    a[[0,1,2],1]
    

    array([1, 5, 9])

    1
    2
    3
    4
    
    a[[1,5,7,2],[0,1,3,2]]
    
    
    # 这样选取的是行列交叉点的值
    

    array([ 4, 21, 31, 13])

    1
    2
    
    a[[1,5,7]][:,[0,2,3]]
    # 这样选取的是行列矩形区域的值
    

    array([[ 4, 9, 7],
    [20, 22, 23],
    [28, 30, 31]])

    • 还可以使用 np.ix 函数生成索引器
    1
    
    a[np.ix_([1,5,7], [0,2,3])]
    

    array([[ 4, 9, 7],
    [20, 22, 23],
    [28, 30, 31]])

    1
    2
    3
    
    # 对第二列数据加三
    a[np.arange(3),1] += 3
    a
    

    array([[ 0, 16, 2, 6],
    [ 4, 17, 9, 7],
    [ 8, 27, 13, 11],
    [12, 13, 14, 15],
    [16, 17, 18, 19],
    [20, 21, 22, 23],
    [24, 25, 26, 27],
    [28, 29, 30, 31]])

    1
    2
    3
    
    # 还可以这样写
    a[np.arange(3),[1,1,1]] += 3
    a
    

    array([[ 0, 19, 2, 6],
    [ 4, 20, 9, 7],
    [ 8, 30, 13, 11],
    [12, 13, 14, 15],
    [16, 17, 18, 19],
    [20, 21, 22, 23],
    [24, 25, 26, 27],
    [28, 29, 30, 31]])

    1
    2
    3
    
    # 那这样呢?应该看出规律来了
    a[np.arange(3),[1,2,1]] += 3
    a
    

    array([[ 0, 22, 2, 6],
    [ 4, 20, 12, 7],
    [ 8, 33, 13, 11],
    [12, 13, 14, 15],
    [16, 17, 18, 19],
    [20, 21, 22, 23],
    [24, 25, 26, 27],
    [28, 29, 30, 31]])

    1
    2
    3
    
    <br></br># 还能这样子
    a[[0,2,2],[3,2,1]] += 3
    a
    

    array([[ 0, 22, 2, 9],
    [ 4, 20, 12, 7],
    [ 8, 36, 16, 11],
    [12, 13, 14, 15],
    [16, 17, 18, 19],
    [20, 21, 22, 23],
    [24, 25, 26, 27],
    [28, 29, 30, 31]])

    • 再来看看布尔型索引
    1
    2
    3
    
    # 还可以这样筛选
    b = a > 10
    b
    

    array([[False, True, False, False],
    [False, True, True, False],
    [False, True, True, True],
    [ True, True, True, True],
    [ True, True, True, True],
    [ True, True, True, True],
    [ True, True, True, True],
    [ True, True, True, True]])

    1
    2
    
    #把这些数取出来
    a[b]
    

    array([22, 20, 12, 36, 16, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
    23, 24, 25, 26, 27, 28, 29, 30, 31])

    1
    
    a[a>10]
    

    array([22, 20, 12, 36, 16, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
    23, 24, 25, 26, 27, 28, 29, 30, 31])

    ndarray 的数据类型

    可以将整个数组理解成是一整块内存,而 dtype 说明了这块内存的数据类型是什么。

    在考虑需要控制数据在内存或磁盘中的存储方式时,了解 dtype 就有必要了

    1
    2
    
    a = np.array([1,0.2])
    a.dtype
    

    dtype(‘float64’)

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

    dtype(‘int32’)

    1
    2
    
    a = np.array([1.3,0.2], dtype=np.int64)
    a
    

    array([1, 0], dtype=int64)

    1
    2
    3
    
    a = np.array([1,0.2])
    b = np.array(a,np.int64)
    b
    

    array([1, 0], dtype=int64)

    • 还可以用 astype 函数转换数据类型
    1
    2
    3
    
    a = np.array([1,0.2])
    b = a.astype(np.int64)
    b
    

    array([1, 0], dtype=int64)

    运算

    标量运算

    大小相等的数组之间的算术运算,都是元素之间对应相加减乘除,运算单位是数组,批量运算。这点和 MATLAB 也很相似。

    • 加减乘除
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
      a = np.array([
        [1,2,3],
        [4,5,6]
    ])
    
    b = np.array([
        [7,8,9],
        [10,11,0]
    ])
    
    
    a + b
    

    array([[ 8, 10, 12],
    [14, 16, 6]])

    1
    
    np.add(a,b)
    

    array([[ 8, 10, 12],
    [14, 16, 6]])

    1
    
    a-b
    

    array([[-6, -6, -6],
    [-6, -6, 6]])

    1
    
    np.subtract(a,b)
    

    array([[-6, -6, -6],
    [-6, -6, 6]])

    1
    
    a*b
    

    array([[ 7, 16, 27],
    [40, 55, 0]])

    1
    
    np.multiply(a,b)
    

    array([[ 7, 16, 27],
    [40, 55, 0]])

    1
    
    a/b
    

    i:\python364\lib\site-packages\ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in true_divide
    “““Entry point for launching an IPython kernel.

    array([[0.14285714, 0.25 , 0.33333333],
    [0.4 , 0.45454545, inf]])

    1
    
    np.divide(a,b)
    

    i:\python364\lib\site-packages\ipykernel_launcher.py:1: RuntimeWarning: divide by zero encountered in true_divide
    “““Entry point for launching an IPython kernel.

    array([[0.14285714, 0.25 , 0.33333333],
    [0.4 , 0.45454545, inf]])

    1
    
    1/a
    

    array([[1. , 0.5 , 0.33333333],
    [0.25 , 0.2 , 0.16666667]])

    1
    
    a ** 0.5
    

    array([[1. , 1.41421356, 1.73205081],
    [2. , 2.23606798, 2.44948974]])

    除零会有警告

    矩阵运算

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    a = np.array([
        [1,2,3],
        [4,5,6]
    ])
    
    b = np.array([
        [1,3,5],
        [2,4,8],
        [9,8,7]
    ])
    
    
    # 矩阵乘法
    a.dot(b)
    

    array([[ 32, 35, 42],
    [ 68, 80, 102]])

    1
    
    np.dot(a,b)
    

    array([[ 32, 35, 42],
    [ 68, 80, 102]])

    1
    2
    3
    
    # 矩阵转置
    print(a)
    a.T
    

    [[1 2 3][4 5 6]]

    array([[1, 4],
    [2, 5],
    [3, 6]])

    1
    
    np.transpose(a)
    

    array([[1, 4],
    [2, 5],
    [3, 6]])

    1
    2
    
    # 矩阵的內积
    a.dot(a.T)
    

    array([[14, 32],
    [32, 77]])

    常用函数

    • sqrt()
    1
    2
    
    # 求根
    np.sqrt(a)
    

    array([[1. , 1.41421356, 1.73205081],
    [2. , 2.23606798, 2.44948974]])

    • power()
    1
    2
    
    # 求幂
    np.power(a,3)
    

    array([[ 1, 8, 27],
    [ 64, 125, 216]], dtype=int32)

    1
    
    a
    

    array([[1, 2, 3],
    [4, 5, 6]])

    • sum()
    1
    2
    3
    
    # 对所有元素求和
    
    np.sum(a)
    

    21

    1
    
    np.sum(a, axis=0) #对没一列元素求和
    

    array([5, 7, 9])

    1
    
    np.sum(a, axis=1) #对没一行元素求和
    

    array([ 6, 15])

    1
    2
    
    # 类似的还有mean函数
    np.mean(a)
    

    3.5

    • mean()
    1
    
    np.mean(a,axis=0)
    

    array([2.5, 3.5, 4.5])

    1
    
    np.mean(a,axis=1)
    

    array([2., 5.])

    • uniform()
    1
    
    np.random.random() #(0,1)之内
    

    0.7807819763980599

    1
    
    np.random.uniform(50,70) # 指定范围
    

    61.458442560252266

    1
    
    np.random.uniform(50,70,(2,3)) #指定维数
    

    array([[59.88385648, 50.59647127, 60.02660835],
    [55.62225737, 54.49158301, 68.14669151]])

    • tile
    1
    
    a
    

    array([[1, 2, 3],
    [4, 5, 6]])

    1
    
    np.tile(a,(1,3)) #将a作为结构元素,重复3次,形成新数组
    

    array([[1, 2, 3, 1, 2, 3, 1, 2, 3],
    [4, 5, 6, 4, 5, 6, 4, 5, 6]])

    1
    
    np.tile(a,(3,1))
    

    array([[1, 2, 3],
    [4, 5, 6],
    [1, 2, 3],
    [4, 5, 6],
    [1, 2, 3],
    [4, 5, 6]])

    1
    
    np.tile(a,(3,4))
    

    array([[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
    [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6],
    [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
    [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6],
    [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
    [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6]])

    • argsort()
    1
    2
    3
    4
    5
    
    a = np.array([[100, 20, 300],
           [400, 110, 60]])
    
    
    np.argsort(a) # 默认是进行行排序,返回数组索引
    

    array([[1, 0, 2],
    [2, 1, 0]], dtype=int32)

    1
    
    np.argsort(a,axis=0)
    

    array([[0, 0, 1],
    [1, 1, 0]], dtype=int32)

    广播

    不同大小数组之间的运算叫广播

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    <br></br>a = np.array([
        [1,2,3],
        [4,5,7],
        [8,55,88],
        [3,54,99]
    ])
    
    
    b = np.array([
        [8,8,8]
    ])
    

    如果要实现 a 的每一行的元素都加上 b,可以这么做:

    1
    2
    3
    4
    
    # 循环
    for i in range(4):
        a[i:i+1,:] += b #注意维数要一致
    a
    

    array([[ 9, 10, 11], [ 12, 13, 15], [ 16, 63, 96], [ 11, 62, 107]])

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    # 用tile函数
    a += np.tile(b,(4,1))
    a
    ···
    
    array([[ 17,  18,  19],
           [ 20,  21,  23],
           [ 24,  71, 104],
           [ 19,  70, 115]])
    
    ​```python
    # 其实可以直接这么做
    a + b
    

    array([[ 25, 26, 27],
    [ 28, 29, 31],
    [ 32, 79, 112],
    [ 27, 78, 123]])

    不同维数的数组可以直接操作

    这就是 numpy 数组的广播特性



沪ICP备19023445号-2号
友情链接