• NumPy基本操作快速熟悉


    NumPy 是 Python 数值计算非常重要的一个包。很多科学计算包都是以 NumPy 的数组对象为基础开发的。

    本文用代码快速过了一遍 NumPy 的基本操作,对 NumPy 整体有一个把握。希望能对读者有帮助。

    NumPy ndarray:多维数组对象

    ndarray 是 NumPy 很重要的特性,它是快速而灵活的数据集容器。ndarray 可以在整个数据集上进行类似于标量的运算,既快速又方便。

    在 Jupyter Notebook 上,首先引入 NumPy 包:

    1 import numpy as np

    创建 ndarray,其中包含随机值(np.random.randn):

    1 np.random.seed(666)  # 设置随机种子
    2 data = np.random.randn(2, 3)
    3 data
    array([[ 0.82418808,  0.479966  ,  1.17346801],
           [ 0.90904807, -0.57172145, -0.10949727]])

    进行一些运算(乘法和加法):

    1 data * 10
    2 data + data
    array([[ 8.24188083,  4.79966003, 11.73468012],
           [ 9.09048069, -5.71721452, -1.09497268]])
    array([[ 1.64837617,  0.95993201,  2.34693602],
           [ 1.81809614, -1.1434429 , -0.21899454]])

    ndarray 所有的数据必须是相同类型,其属性 shape 表示形状大小,dtype 表示该数组的数据类型。

    1 data.shape
    2 data.dtype
    (2, 3)
    dtype('float64')

    ndarray 的创建

    最简单的创建方法是使用 array 函数。该函数根据接收的序列对象,生成新的 NumPy 数组。

    1 data1 = [6, 66, 666, 6666, 66666]
    2 arr1 = np.array(data1)
    3 arr1
    1 arr2.shape
    2 arr2.ndim
    array([    6,    66,   666,  6666, 66666])

    嵌套的序列(比如说嵌套列表),可以生成多维的数组。

    1 data2 = [[6, 66, 666], [6666, 66666, 666666]]
    2 arr2 = np.array(data2)
    3 arr2
    array([[     6,     66,    666],
           [  6666,  66666, 666666]])

    前面介绍了 ndarray 的属性 shape 表示形状大小,它还有一个属性 ndim 表示维度大小,我们可以对比两者的区别。

    1 arr2.shape
    2 arr2.ndim
    (2, 3)
    2

    除了 np.array,还有一些函数可以创建新数组:zerosonesempty 等等。(注意到 empty 不一定返回零值,某些情况它可能返回未初始化的不确定值

    1 np.zeros(6)
    2 np.ones([2, 3])
    3 np.empty((2, 3, 2))
    array([0., 0., 0., 0., 0., 0.])
    Out[26]:
    array([[1., 1., 1.],
           [1., 1., 1.]])
    Out[26]:
    array([[[0.00000000e+000, 0.00000000e+000],
            [2.96439388e-323, 0.00000000e+000],
            [2.14321575e-312, 1.58817677e-052]],
    
           [[6.50119216e-091, 1.74483574e-076],
            [1.28392645e+160, 1.35027265e+161],
            [3.99910963e+252, 9.02421558e-309]]])

    np.arange 类似于 Python 的 range 函数,只不过它生成的是 ndarray。

    1 np.arange(6)
    2 np.array(range(6))  # arange 等价于该效果
    array([0, 1, 2, 3, 4, 5])
    array([0, 1, 2, 3, 4, 5])

    ndarray 的数据类型

    前面介绍的 dtype 可以在创建时指定:

    1 arr1 = np.array([6, 66, 666], dtype=np.float64)
    2 arr2 = np.array([6, 66, 666], dtype=np.int32)
    3 arr1.dtype
    4 arr2.dtype
    dtype('float64')
    dtype('int32')

    一些典型的数据类型有:uint8、int32、float64、float128、complex64、bool、object、string_、unicode_ 等。

    使用 astype 可以对数据类型进行显式转换。

    1 arr = np.array([6, 66, 666])
    2 arr.dtype
    3 float_arr = arr.astype(np.float64)
    4 float_arr.dtype
    dtype('int64')
    dtype('float64')

    浮点数数组转为整型数组:

    1 arr = np.array([1.7, 2.7, 3.7])
    2 arr
    3 arr.astype(np.int32)
    array([1.7, 2.7, 3.7])
    array([1, 2, 3], dtype=int32)

    字符串数组转为浮点数数组(float 是 Python 类型,NumPy 自动解析识别):

    1 numeric_strings = np.array(['1.7', '-2.7', '3.7'], dtype=np.string_)
    2 float_arr = numeric_strings.astype(float)
    3 float_arr
    4 float_arr.dtype
    array([ 1.7, -2.7,  3.7])
    dtype('float64')

    使用其他数组的 dtype 属性来进行转换:

    1 int_arr = np.arange(6)
    2 int_arr
    3 float_arr = np.array([6., 66., 666.], dtype=np.float64)
    4 int_arr.astype(float_arr.dtype)
    array([0, 1, 2, 3, 4, 5])
    array([0., 1., 2., 3., 4., 5.])

    ndarray 的算术运算

    ndarray 的算术运算可以避免写 for 循环,这既方便,性能也好。

    数组与相同大小的数组之间的运算:

    1 arr = np.array([[1., 2., 3.], [4., 5., 6.]])
    2 arr
    3 arr * arr
    4 arr - arr
    array([[1., 2., 3.],
           [4., 5., 6.]])
    array([[ 1.,  4.,  9.],
           [16., 25., 36.]])
    array([[0., 0., 0.],
           [0., 0., 0.]])

    数组与标量的运算,会按数组的元素一一进行计算。

    1 1 / arr
    2 arr ** 0.5
    array([[1.        , 0.5       , 0.33333333],
           [0.25      , 0.2       , 0.16666667]])
    array([[1.        , 1.41421356, 1.73205081],
           [2.        , 2.23606798, 2.44948974]])

    相同大小的数组之间的比较运算,会返回布尔值的数组:

    1 arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
    2 arr2
    3 arr
    4 arr2 > arr
    array([[ 0.,  4.,  1.],
           [ 7.,  2., 12.]])
    array([[1., 2., 3.],
           [4., 5., 6.]])
    array([[False,  True, False],
           [ True, False,  True]])

    索引和切片

    一维数组很简单,看起来类似于 Python 列表:

    1 arr = np.arange(10)
    2 arr
    3 arr[5]
    4 arr[5:8]
    5 arr[5:8] = 12
    6 arr
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])-
    5
    array([5, 6, 7])
    array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])

    第 5 行用到了 ndarray 的 broadcast 机制,这是和原生 Python 列表的第一个区别,我们可以看到列表会报错:

    1 li = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    2 li
    3 li[5]
    4 li[5:8]
    5 li[5:8] = 12
    6 li
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    5
    [5, 6, 7]
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-43-b59603238145> in <module>
          3 li[5]
          4 li[5:8]
    ----> 5 li[5:8] = 12
          6 li
    
    TypeError: can only assign an iterable

    ndarray 的切片(或索引)是一个指针(NumPy 称之为视图 view),不会复制底层数据,修改切片会直接影响到数组本身。

    1 arr_slice = arr[5:8]
    2 arr_slice
    3 arr_slice[1] = 666
    4 arr
    array([12, 12, 12])
    array([  0,   1,   2,   3,   4,  12, 666,  12,   8,   9])

    切片 [:] 的赋值会影响到所有元素:

    1 arr_slice[:] = 777
    2 arr
    array([  0,   1,   2,   3,   4, 777, 777, 777,   8,   9])

    由于 NumPy 经常和大型数据集打交道,因此切片是指针并不奇怪。如果希望不影响原数组,应该使用 copy 方法

    1 arr_slice = arr[5:8].copy()
    2 arr_slice[:] = 666
    3 arr
    array([  0,   1,   2,   3,   4, 777, 777, 777,   8,   9])

    在二维数组中,每个索引代表的元素不再是标量。要找到每个元素有两种方法。

    1 arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    2 arr2d[2]
    3 # 法 1
    4 arr2d[0][2]
    5 # 法 2
    6 arr2d[0, 2]
    array([7, 8, 9])
    3
    3

    在这样的二维数组中,axis 0 可以理解为,axis 1 可以理解为

    三维数组的使用示例:

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

    标量和数组都可以赋值给 arr3d[0]:

    1 old_values = arr3d[0].copy()
    2 arr3d[0] = 666
    3 arr3d
    4 arr3d[0] = old_values
    5 arr3d
    array([[[666, 666, 666],
            [666, 666, 666]],
    
           [[  7,   8,   9],
            [ 10,  11,  12]]])
    array([[[ 1,  2,  3],
            [ 4,  5,  6]],
    
           [[ 7,  8,  9],
            [10, 11, 12]]])

    用切片进行索引

    1 arr
    2 arr[1:6]
    3 arr2d
    4 arr2d[:2]
    5 arr2d[:2, 1:]
    array([  0,   1,   2,   3,   4, 777, 777, 777,   8,   9])
    array([  1,   2,   3,   4, 777])
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])
    array([[1, 2, 3],
           [4, 5, 6]])
    array([[2, 3],
           [5, 6]])

    注意,如果使用切片作为索引,得到的总是相同维度的视图,而如果使用索引,则必定得到的是低维的数组

    1 arr2d
    2 # 使用切片
    3 arr2d[1:2, :2]
    4 # 使用了索引
    5 arr2d[1, :2]
    6 arr2d[:2, 2]
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])
    array([[4, 5]])
    array([4, 5])
    array([3, 6])

    布尔索引

    首先生成一些数据:

    1 names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
    2 np.random.seed(666)
    3 data = np.random.randn(7, 4)
    4 names
    5 data
    array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [-0.57172145, -0.10949727,  0.01902826, -0.94376106],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737],
           [-1.08879299, -0.57577075, -1.68290077,  0.22918525],
           [-1.75662522,  0.84463262,  0.27721986,  0.85290153],
           [ 0.1945996 ,  1.31063772,  1.5438436 , -0.52904802]])

    假设每个 name 对应着 data 中的每一行,我们希望选择 Bob 有关的数据。

    1 names == 'Bob'
    2 data[names == 'Bob']
    array([ True, False, False,  True, False, False, False])
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737]])

    可以看到,选择了第 0 行和第 3 行的数据。需要注意的是布尔数组的长度必须和该维度的长度相同,否则会报错。

    1 np.array([ True, False, False,  True, False, False, False])[:-1]
    2 data[np.array([ True, False, False,  True, False, False, False])[:-1]]
    array([ True, False, False,  True, False, False])
    ---------------------------------------------------------------------------
    IndexError                                Traceback (most recent call last)
    <ipython-input-72-07628751d568> in <module>
          1 np.array([ True, False, False,  True, False, False, False])[:-1]
    ----> 2 data[np.array([ True, False, False,  True, False, False, False])[:-1]]
    
    IndexError: boolean index did not match indexed array along dimension 0; dimension is 7 but corresponding boolean dimension is 6

    布尔索引结合切片或索引的情况:

    1 data[names == 'Bob', 2:]
    2 data[names == 'Bob', 3]
    array([[ 1.17346801,  0.90904807],
           [-0.29873262, -0.46058737]])
    array([ 0.90904807, -0.46058737])

    要取反,可以使用 != 或者 ~:

    1 data [names != 'Bob']
    2 data[~(names == 'Bob')]
    array([[-0.57172145, -0.10949727,  0.01902826, -0.94376106],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [-1.08879299, -0.57577075, -1.68290077,  0.22918525],
           [-1.75662522,  0.84463262,  0.27721986,  0.85290153],
           [ 0.1945996 ,  1.31063772,  1.5438436 , -0.52904802]])
    array([[-0.57172145, -0.10949727,  0.01902826, -0.94376106],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [-1.08879299, -0.57577075, -1.68290077,  0.22918525],
           [-1.75662522,  0.84463262,  0.27721986,  0.85290153],
           [ 0.1945996 ,  1.31063772,  1.5438436 , -0.52904802]])

    如果要结合多个布尔条件,使用布尔运算符 &(且) 和 |(或):

    1 names
    2 mask = (names == 'Bob') | (names == 'Will')
    3 mask
    4 data[mask]
    array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')
    array([ True, False,  True,  True,  True, False, False])
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737],
           [-1.08879299, -0.57577075, -1.68290077,  0.22918525]])

    注意:使用布尔进行索引后返回的数组总是是原数组的拷贝,而非指针,也没有 broadcast 机制。这是与索引或者切片所不同的地方。

    1 data
    2 data_copy = data[names == 'Bob']
    3 data_copy
    4 data_copy[:, :] = 666
    5 data_copy
    6 data
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [-0.57172145, -0.10949727,  0.01902826, -0.94376106],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737],
           [-1.08879299, -0.57577075, -1.68290077,  0.22918525],
           [-1.75662522,  0.84463262,  0.27721986,  0.85290153],
           [ 0.1945996 ,  1.31063772,  1.5438436 , -0.52904802]])
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737]])
    array([[666., 666., 666., 666.],
           [666., 666., 666., 666.]])
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [-0.57172145, -0.10949727,  0.01902826, -0.94376106],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737],
           [-1.08879299, -0.57577075, -1.68290077,  0.22918525],
           [-1.75662522,  0.84463262,  0.27721986,  0.85290153],
           [ 0.1945996 ,  1.31063772,  1.5438436 , -0.52904802]])

    但是注意是返回后的数组才是拷贝,可以用布尔索引来直接进行赋值,也可以使用 broadcast 机制。

    1 data[names == 'Bob'] = 666
    2 data
    array([[ 6.66000000e+02,  6.66000000e+02,  6.66000000e+02,
             6.66000000e+02],
           [-5.71721452e-01, -1.09497268e-01,  1.90282649e-02,
            -9.43761065e-01],
           [ 6.40573153e-01, -7.86443172e-01,  6.08869993e-01,
            -9.31011849e-01],
           [ 6.66000000e+02,  6.66000000e+02,  6.66000000e+02,
             6.66000000e+02],
           [-1.08879299e+00, -5.75770746e-01, -1.68290077e+00,
             2.29185248e-01],
           [-1.75662522e+00,  8.44632619e-01,  2.77219863e-01,
             8.52901533e-01],
           [ 1.94599603e-01,  1.31063772e+00,  1.54384360e+00,
            -5.29048024e-01]])

    fancy 索引

    fancy 索引在 NumPy 中指的是使用整型数组来进行索引。

    假设有以下一个 ndarray:

    1 arr = np.empty((8, 4))
    2 for i in range(8):
    3     arr[i] = i
    4 arr
    array([[0., 0., 0., 0.],
           [1., 1., 1., 1.],
           [2., 2., 2., 2.],
           [3., 3., 3., 3.],
           [4., 4., 4., 4.],
           [5., 5., 5., 5.],
           [6., 6., 6., 6.],
           [7., 7., 7., 7.]])

    使用 fancy 索引可以选择行的顺序:

    1 arr[[7, 6, 5, 4, 3, 2, 1, 0]]
    2 arr[[-1, 0]]
    array([[7., 7., 7., 7.],
           [6., 6., 6., 6.],
           [5., 5., 5., 5.],
           [4., 4., 4., 4.],
           [3., 3., 3., 3.],
           [2., 2., 2., 2.],
           [1., 1., 1., 1.],
           [0., 0., 0., 0.]])
    array([[7., 7., 7., 7.],
           [0., 0., 0., 0.]])

    如果传入多个索引数组的话,会针对每个既定顺序的行选择既定顺序的列,最终得到几个元素的数组。

    1 arr = np.arange(32).reshape((8, 4))
    2 arr
    3 arr[[3, 2, 1, 0], [3, 2, 1, 0]]
    array([[ 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]])
    array([15, 10,  5,  0])

    下面示例的效果是变换数组所选行和列的顺序,而不会降维。

    1 arr[[3, 2, 1, 0]][:, [3, 2, 1, 0]]
    array([[15, 14, 13, 12],
           [11, 10,  9,  8],
           [ 7,  6,  5,  4],
           [ 3,  2,  1,  0]])

    注意:fancy 索引同样返回的是数组的拷贝,而非指针。

    数组的转置和轴的交换

    转置(.T)返回的是底层数组的视图,而非数组的拷贝。

    1 arr = np.arange(15). reshape((3, 5))
    2 arr
    3 arr.transpose()  # 转置
    4 arr.T  # 更简便的写法
    array([[ 0,  1,  2,  3,  4],
           [ 5,  6,  7,  8,  9],
           [10, 11, 12, 13, 14]])
    array([[ 0,  5, 10],
           [ 1,  6, 11],
           [ 2,  7, 12],
           [ 3,  8, 13],
           [ 4,  9, 14]])
    array([[ 0,  5, 10],
           [ 1,  6, 11],
           [ 2,  7, 12],
           [ 3,  8, 13],
           [ 4,  9, 14]])

    矩阵的内积计算使用 np.dot

    1 arr1 = np.array([[3, -1], [1, 2]])
    2 arr1
    3 arr2 = np.array([[2, 1]])
    4 arr2
    5 np.dot(arr1, arr2.T)
    array([[ 3, -1],
           [ 1,  2]])
    array([[2, 1]])
    array([[5],
           [4]])

    transpose 可以接收元组参数来交换轴:

     1 arr = np.arange(6).reshape((2, 3))
     2 arr
     3 arr.transpose((0, 1))
     4 arr.transpose((1, 0))
     5 arr = np.arange(16).reshape((2, 2, 4))
     6 arr
     7 # 置换 axis=1 和 axis=0, axis=2 不变
     8 # 由于 axis=2 不变,那么
     9 # 设 a = [0, 1, 2, 3] b = [4, 5, 6, 7] c = [8, 9, 10, 11] d = [12, 13, 14, 15]
    10 # arr = [[a, b], [c, d]]
    11 # arr.transpose(1, 0) = arr.T = [[a, c], [b, d]] = [[[0, 1, 2, 3], [8, 9, 10, 11]], [[4, 5, 6, 7], [12, 13, 14, 15]]]
    12 arr.transpose((1, 0, 2))  
    array([[0, 1, 2],
           [3, 4, 5]])
    array([[0, 1, 2],
           [3, 4, 5]])
    array([[0, 3],
           [1, 4],
           [2, 5]])
    array([[[ 0,  1,  2,  3],
            [ 4,  5,  6,  7]],
    
           [[ 8,  9, 10, 11],
            [12, 13, 14, 15]]])
    array([[[ 0,  1,  2,  3],
            [ 8,  9, 10, 11]],
    
           [[ 4,  5,  6,  7],
            [12, 13, 14, 15]]])

    swapaxes 同样可以交换轴:

    1 arr
    2 arr.swapaxes(1, 2)
    3 arr.transpose(0, 2, 1)  # 类似于 arr.swapaxes(1, 2)
    array([[[ 0,  1,  2,  3],
            [ 4,  5,  6,  7]],
    
           [[ 8,  9, 10, 11],
            [12, 13, 14, 15]]])
    array([[[ 0,  4],
            [ 1,  5],
            [ 2,  6],
            [ 3,  7]],
    
           [[ 8, 12],
            [ 9, 13],
            [10, 14],
            [11, 15]]])
    array([[[ 0,  4],
            [ 1,  5],
            [ 2,  6],
            [ 3,  7]],
    
           [[ 8, 12],
            [ 9, 13],
            [10, 14],
            [11, 15]]])

    注意 swapaxes 同样返回的是数组的视图,而不是数组的拷贝。

    通用函数

    通用函数执行的是按元素的运算。

    sqrtexp

    1 arr = np.arange(10)
    2 arr
    3 np.sqrt(arr)
    4 np.exp(arr)
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    array([0.        , 1.        , 1.41421356, 1.73205081, 2.        ,
           2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.        ])
    array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
           5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
           2.98095799e+03, 8.10308393e+03])

    maximumminimum

    1 np.random.seed(666)
    2 x = np.random.randn(8)
    3 x
    4 y = np.random.randn(8)
    5 y
    6 np.maximum(x, y)
    7 np.minimum(x, y)
    array([ 0.82418808,  0.479966  ,  1.17346801,  0.90904807, -0.57172145,
           -0.10949727,  0.01902826, -0.94376106])
    array([ 0.64057315, -0.78644317,  0.60886999, -0.93101185,  0.97822225,
           -0.73691806, -0.29873262, -0.46058737])
    array([ 0.82418808,  0.479966  ,  1.17346801,  0.90904807,  0.97822225,
           -0.10949727,  0.01902826, -0.46058737])
    array([ 0.64057315, -0.78644317,  0.60886999, -0.93101185, -0.57172145,
           -0.73691806, -0.29873262, -0.94376106])

    典型的一元通用函数有:

    • abs, fabs
    • sqrt
    • square
    • exp
    • log, log10, log2, log1p
    • sign
    • ceil, floor, rint
    • modf
    • isnan
    • isfinite, isinf
    • cos, cosh, sin, sinh, tan, tanh
    • arccos, arccosh, arcsin, arcsinh, arctan, arctanh
    • logical_not

    典型的二元通用函数有:

    • add
    • subtract
    • multiply
    • divide, floor_divide
    • power
    • maximum, fmax
    • minimum, fmin
    • mod
    • copysign
    • greater, greater_equal, less, less_equal, equal, not_equal
    • logical_and, logical_or, logical_xor

    面向数组编程

    直接面向数组编程可以避免使用 for 循环,这又称为向量化(vectorization),一般来说性能会好上一两个量级以上。

    1 points = np.arange(-5, 5, 0.01)
    2 xs, ys = np.meshgrid(points, points)
    3 xs
    4 ys
    array([[-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
           [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
           [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
           ...,
           [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
           [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99],
           [-5.  , -4.99, -4.98, ...,  4.97,  4.98,  4.99]])
    array([[-5.  , -5.  , -5.  , ..., -5.  , -5.  , -5.  ],
           [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
           [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
           ...,
           [ 4.97,  4.97,  4.97, ...,  4.97,  4.97,  4.97],
           [ 4.98,  4.98,  4.98, ...,  4.98,  4.98,  4.98],
           [ 4.99,  4.99,  4.99, ...,  4.99,  4.99,  4.99]])
    1 z = np.sqrt(xs ** 2 + ys ** 2)
    2 z
    array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985,
            7.06400028],
           [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
            7.05692568],
           [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
            7.04985815],
           ...,
           [7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603,
            7.04279774],
           [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
            7.04985815],
           [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
            7.05692568]])

    这里使用 matplotlib 建立这个二维数组的数据可视化。

    1 import matplotlib.pyplot as plt
    2 
    3 plt.imshow(z, cmap=plt.cm.gray)
    4 plt.colorbar()
    5 
    6 plt.title('Image plot of $sqrt{x^2 + y^2}$ for a grid of values')

    带条件逻辑的数组运算

    numpy.where 函数是 x if condition else y 的向量化版本。 

    1 x = np.array([6, 66, 666, 6666, 66666])
    2 y = np.array([7, 77, 777, 7777, 77777])
    3 cond = np.array([True, False, True, True, False])
    4 result = np.where(cond, x, y)
    5 result
    array([    6,    77,   666,  6666, 77777])

    典型的使用场景:

    1 np.random.seed(666)
    2 arr = np.random.randn(4, 4)
    3 arr
    4 arr > 0
    5 np.where(arr > 0, 1, -1)  # 对两种情况都赋值
    6 np.where(arr > 0, arr, 0)  # 只处理小于零的情况
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [-0.57172145, -0.10949727,  0.01902826, -0.94376106],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737]])
    array([[ True,  True,  True,  True],
           [False, False,  True, False],
           [ True, False,  True, False],
           [ True, False, False, False]])
    array([[ 1,  1,  1,  1],
           [-1, -1,  1, -1],
           [ 1, -1,  1, -1],
           [ 1, -1, -1, -1]])
    array([[0.82418808, 0.479966  , 1.17346801, 0.90904807],
           [0.        , 0.        , 0.01902826, 0.        ],
           [0.64057315, 0.        , 0.60886999, 0.        ],
           [0.97822225, 0.        , 0.        , 0.        ]])

    数学和统计运算

    典型的聚合函数有:sum,mean,std。

    1 np.random.seed(666)
    2 arr = np.random.randn(6, 6)
    3 arr
    4 arr.mean()
    5 np.mean(arr)
    6 arr.sum()
    7 arr.std()
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807, -0.57172145,
            -0.10949727],
           [ 0.01902826, -0.94376106,  0.64057315, -0.78644317,  0.60886999,
            -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737, -1.08879299,
            -0.57577075],
           [-1.68290077,  0.22918525, -1.75662522,  0.84463262,  0.27721986,
             0.85290153],
           [ 0.1945996 ,  1.31063772,  1.5438436 , -0.52904802, -0.6564723 ,
            -0.2015057 ],
           [-0.70061583,  0.68713795, -0.02607576, -0.82975832,  0.29655378,
            -0.3126795 ]])
    -0.03691228536732661
    -0.03691228536732661
    -1.328842273223758
    0.81811319613762

    mean 和 sum 有一个 axis 参数用于指定针对哪一行:

    1 arr.mean(axis=1)
    2 arr.sum(axis=0)
    array([ 0.45090857, -0.23212411, -0.36376326, -0.20593112,  0.27700915,
           -0.14757295])
    array([-0.3674784 ,  1.0262478 ,  1.27645117, -0.8521562 , -1.13434309,
           -1.27756353])

    一些典型的统计性方法:

    • sum
    • mean
    • std, var
    • min, max
    • argmin, argmax
    • cumsum
    • cumprod

    布尔数组的方法 

    any 检查某数组是否有一个以上的 True,all 检查某数组是否所有值都为 True。

    1 bools = np.array([False, False, True, False])
    2 bools.any()
    3 bools.all()
    True
    False

    排序

    1 np.random.seed(666)
    2 arr = np.random.randn(6)
    3 arr
    4 arr.sort()
    5 arr
    array([ 0.82418808,  0.479966  ,  1.17346801,  0.90904807, -0.57172145,
           -0.10949727])
    array([-0.57172145, -0.10949727,  0.479966  ,  0.82418808,  0.90904807,
            1.17346801])

    针对某一维度进行排序:

    1 np.random.seed(666)
    2 arr = np.random.randn(5, 3)
    3 arr
    4 arr.sort(1)
    5 arr
    array([[ 0.82418808,  0.479966  ,  1.17346801],
           [ 0.90904807, -0.57172145, -0.10949727],
           [ 0.01902826, -0.94376106,  0.64057315],
           [-0.78644317,  0.60886999, -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262]])
    array([[ 0.479966  ,  0.82418808,  1.17346801],
           [-0.57172145, -0.10949727,  0.90904807],
           [-0.94376106,  0.01902826,  0.64057315],
           [-0.93101185, -0.78644317,  0.60886999],
           [-0.73691806, -0.29873262,  0.97822225]])

    查看四分位数:

    1 np.random.seed(666)
    2 arr = np.random.randn(1000)
    3 arr.sort()
    4 arr[int(0.25 * len(arr))]
    5 arr[int(0.75 * len(arr))]
    -0.6786323778244538
    0.6879940536373379

    唯一值和集合逻辑

    NumPy 有一些一维数组的基本的集合运算。其中 np.unique 是非常常用的。

    1 names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
    2 np.unique(names)
    3 ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
    4 np.unique(ints)
    5 # 纯 Python 实现
    6 sorted(set(names))
    array(['Bob', 'Joe', 'Will'], dtype='<U4')
    array([1, 2, 3, 4])
    ['Bob', 'Joe', 'Will']

    np.in1d 检查两个数组值的关系。

    1 values = np.array([6, 0, 0, 3, 2, 5, 6])
    2 np.in1d(values, [2, 3, 6])
    array([ True, False, False,  True,  True, False,  True])

    数组常用的集合运算有:

    • unique(x)
    • intersect1d(x, y)
    • union1d(x, y)
    • in1d(x, y)
    • setdiff1d(x, y)
    • setxor1d(x, y)

    数组的文件输入和输出

    NumPy 可以用文本或者二进制的方式把数据存储到硬盘,我们一般都选用二进制的方式。

    np.savenp.load 分别是以二进制存储和加载的函数。

    1 arr = np.arange(10)
    2 # 持久化
    3 np.save('arr_data.npy', arr)
    4 # 加载
    5 np.load('arr_data.npy')
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

    还可以以非压缩或者压缩的格式保存多个 NumPy 数据:

    1 # 非压缩
    2 np.savez('arr_archive.npz', a=arr, b=arr)
    3 # 压缩
    4 np.savez_compressed('arr_compressed.npz', a=arr, b=arr)
    5 # 加载
    6 arch = np.load('arr_archive.npz')
    7 arch['b']
    8 cmp = np.load('arr_compressed.npz')
    9 cmp['a']
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

    线性代数

    矩阵运算使用的是 dot 函数。

    1 x = np.array([[1., 2., 3.], [4., 5., 6.]])
    2 y = np.array([[1., 2.], [-1, 0], [0, 1]])
    3 x
    4 y
    5 x.dot(y)
    6 np.dot(x, y)  # 等价于 x.dot(y)
    7 x @ y  # Python3.5,等价于 x.dot(y)
    array([[1., 2., 3.],
           [4., 5., 6.]])
    array([[ 1.,  2.],
           [-1.,  0.],
           [ 0.,  1.]])
    array([[-1.,  5.],
           [-1., 14.]])
    array([[-1.,  5.],
           [-1., 14.]])
    array([[-1.,  5.],
           [-1., 14.]])

    numpy.linalg 有很多关于矩阵分解(如求逆、行列式等)的函数。

    1 from numpy.linalg import inv, qr
    2 
    3 np.random.seed(666)
    4 X = np.random.randn(3, 3)
    5 mat = X.T.dot(X)
    6 inv(mat)
    7 mat.dot(inv(mat))  # 等于单位矩阵
    8 q, r = qr(mat)
    9 r
    array([[ 0.94360937,  0.09940334, -0.46256198],
           [ 0.09940334,  0.70123855, -0.05689685],
           [-0.46256198, -0.05689685,  0.78260277]])
    array([[ 1.00000000e+00,  8.64663156e-18, -4.04784612e-17],
           [ 1.27360445e-17,  1.00000000e+00,  6.98841494e-18],
           [-7.26002573e-17, -9.92956304e-18,  1.00000000e+00]])
    array([[-1.7499547 ,  0.22916446, -1.66008207],
           [ 0.        , -1.43686954, -0.22584526],
           [ 0.        ,  0.        ,  1.09786224]])

    常用的 numpy.linalg 函数:

    • diag
    • dot
    • trace
    • det
    • eig
    • inv
    • pinv
    • qr
    • svd
    • solve
    • lstsq

    生成伪随机数

    numpy.random 有各种生成随机数样本的函数,比原生 Python 随机数生成模块速度快很多。

    生成的随机数称为伪随机数,因为这些随机数实际上依赖于 np.random.seed 的值。

    1 np.random.seed(666)
    2 samples = np.random.normal(size=(4, 4))
    3 samples
    array([[ 0.82418808,  0.479966  ,  1.17346801,  0.90904807],
           [-0.57172145, -0.10949727,  0.01902826, -0.94376106],
           [ 0.64057315, -0.78644317,  0.60886999, -0.93101185],
           [ 0.97822225, -0.73691806, -0.29873262, -0.46058737]])

    可以使用全局随机种子:

    1 rng = np.random.RandomState(666)
    2 rng.randn(10)
    array([ 0.82418808,  0.479966  ,  1.17346801,  0.90904807, -0.57172145,
           -0.10949727,  0.01902826, -0.94376106,  0.64057315, -0.78644317])

    常用的随机函数如下:

    • seed
    • permutation
    • shuffle
    • rand
    • randint
    • randn
    • binomial
    • normal
    • beta
    • chisquare
    • gamma
    • uniform

    使用 NumPy 实现一个随机游走的例子

    1 np.random.seed(666)
    2 nsteps = 1000  # 随机游走的步数
    3 draws = np.random.randint(0, 2, size=nsteps)  # 生成 0 或 1,一共 nsteps 个点
    4 steps = np.where(draws == 0, 1, -1)  # 等于 0 则设为 1,不等于 0 则设为 -1
    5 walk = steps.cumsum()  # 累积求和
    6 walk.min()
    7 walk.max()
    8 (np.abs(walk) >= 10).argmax()  # 第一次绝对值超过 10 的索引
    9 plt.plot(walk[:100])

    同时模拟多个随机游走

    1 np.random.seed(666)
    2 nwalks = 5000
    3 nsteps = 1000
    4 draws = np.random.randint(0, 2, size=(nwalks, nsteps))
    5 steps = np.where(draws > 0, 1, -1)
    6 walks = steps.cumsum(1)  # 1 表示针对 axis=1
    7 walks
    8 walks.shape  # 行表示模拟的不同随机游走趋势,列表示不同的步数
    array([[ -1,  -2,  -1, ..., -22, -21, -22],
           [  1,   2,   1, ...,  20,  19,  20],
           [  1,   0,  -1, ..., -14, -13, -12],
           ...,
           [ -1,   0,   1, ...,  24,  25,  26],
           [ -1,  -2,  -1, ...,  16,  15,  16],
           [ -1,   0,  -1, ..., -24, -23, -22]])
    (5000, 1000)

    总结

    本文介绍了 NumPy 基本操作。

    NumPy 最重要的数据结构是 ndarray。我们熟悉了:

    • 怎么创建 ndarray
    • ndarray 的数据类型
    • ndarray 的算术运算
    • 如何使用索引切片布尔索引fancy 索引
    • 数组的转置轴的变换

    围绕着 ndarray,NumPy 还提供了很多函数和方法:

    • 通用函数:实现快速的按元素的运算
    • 面向数组的编程:条件逻辑、数学和统计方法、布尔数组的方法、排序方法、唯一值及集合逻辑
    • 数组的持久化和加载方法
    • 线性代数方面dotnumpy.linalg
    • 有关随机数生成的 np.random

    最后是一个有关随机游走的简单示例。

    通过本文我们主要知道了 NumPy 的核心数据结构 ndarray,以及有关它的各种函数出现的源头都是:

    向量化的思想——数值计算尽量别用 for 循环,这样代码看起来简单,性能也好得多。

    最后欢迎留言,有不足之处请指正。

    参考

    • 《Python for Data Analysis, 2nd Edition》by Wes McKinney
  • 相关阅读:
    DATASTAGE中ODBC连接的配置
    DataStage 服务启动
    VS 安装部署项目自解压程序解压后按顺序执行多个程序
    DataStage系列教程 (Pivot_Enterprise 行列转换)
    [Leetcode] Binary tree-- 637. Average of Levels in Binary Tree
    [Leetcode] Binary tree -- 501. Find Mode in Binary Search Tree
    [Leetcode] Binary tree-- 606. Construct String from Binary Tree
    [Leetcode] DP-- 516. Longest Palindromic Subsequence
    [Leetcode] DP-- 638. Shopping Offers
    [Leetcode] DP-- 464. Can I Win
  • 原文地址:https://www.cnblogs.com/noluye/p/11335118.html
Copyright © 2020-2023  润新知