• 利用python数据分析——Numpy基础:数组和矢量计算


    0 写在前面

    1. ndarray,是一个具有矢量算术运算和复杂广播能力的快速,且节省空间的多维数组;
    2. 搞明白具体的性能差距,考察一个包含一百万整数的数组,和一个等价的Python列表。
    3. ndarray,是一个通用的同构数据多维容器,也就是说,所有的元素必须是相同类型的。每一个数组都有一个shape(表示各维度大小的元组)和一个dtype(一个用于说明数组数据类型的对象)

    import numpy as np
    my_arr = np.arange(1000000)
    my_list = list(range(1000000))
    %time for _ in range(10): my_arr2 = my_arr * 2
    Wall time: 21.7 ms
    %time for _ in range(10): my_list2 = [x * 2 for x in my_list]
    Wall time: 855 ms
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 基于NumPy的算法要比纯Python快10到100倍(甚至更快),并且使用的内存更少。

    1 ndarray:一种多维数组对象

    1.1 numpy.random.randn(d0,d1, …,dn) 函数介绍

    • rand() 函数,会根据指定的维度,生成[0,1) 之间的数据;
    • dn 表示每个维度
    • 返回值为指定维度的 array

    代码:

    import numpy as np
    data = np.random.randn(2, 3)
    print(data)
    
    • 1
    • 2
    • 3

    输出:

    [[-0.98166625 -0.75308133  1.02950877]
     [ 0.92072303 -0.98693389 -0.68642432]]
    
    • 1
    • 2

    1.2 numpy.random.seed() 函数介绍

    • 使得随机数据可预测
    • 当设置相同的seed时,那么每次生成的随机数相同;如果不设置seed,则每次会生成不同的随机数。

    1.3 创建 ndarray

    1.3.1 np.array()

    • 创建数组最简单的办法就是使用array函数。它接受一切序列型的对象(包括其他数组),然后返回一个NumPy数组。
    • 以一个列表的转换为例:
    In [19]: data1 = [6, 7.5, 8, 0, 1]
    In [20]: arr1 = np.array(data1)
    In [21]: arr1
    Out[21]: array([ 6. ,  7.5,  8. ,  0. ,  1. ])
    
    • 1
    • 2
    • 3
    • 4
    • 嵌套序列(比如由一组等长列表组成的列表)将会被转换为一个多维数组:
    In [22]: data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
    In [23]: arr2 = np.array(data2)
    In [24]: arr2
    Out[24]: 
    array([[1, 2, 3, 4],
           [5, 6, 7, 8]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • Numpy数组有两个属性:ndimshape
    In [22]: data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
    In [23]: arr2 = np.array(data2)
    In [24]: arr2
    Out[24]: 
    array([[1, 2, 3, 4],
           [5, 6, 7, 8]])
           
    In [25]: arr2.ndim
    Out[25]: 2
    In [26]: arr2.shape
    Out[26]: (2, 4)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.3.2 np.zeros()和np.ones()

    • 需要传入的是,创建数组的指定长度或形状。
    In [22]: data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
    In [23]: arr2 = np.array(data2)
    In [24]: arr2
    Out[24]: 
    array([[1, 2, 3, 4],
           [5, 6, 7, 8]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.3.3 np.empty()

    • empty可以创建一个没有任何具体值的数组,因此只需要传入一个表示形状的元组即可。
    In [31]: np.empty((2, 3, 2))
    Out[31]: 
    array([[[ 0.,  0.],
            [ 0.,  0.],
            [ 0.,  0.]],
           [[ 0.,  0.],
            [ 0.,  0.],
            [ 0.,  0.]]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:认为np.empty会返回全0数组的想法是不安全的。很多情况下(如前所示),它返回的都是一些未初始化的垃圾值。

    1.3.4 np.arange()

    • arange是Python内置函数range的数组版:
    In [32]: np.arange(15)
    Out[32]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
    
    • 1
    • 2

    1.3.5 函数总结

    由于NumPy关注的是数值计算,因此,如果没有特别指定,数据类型基本都是float64(浮点数)。
    在这里插入图片描述

    1.4 ndarray的数据类型

    前面说了,ndarray默认是 float64

    但是可以用dtype进行指定:

    In [33]: arr1 = np.array([1, 2, 3], dtype=np.float64)
    In [34]: arr2 = np.array([1, 2, 3], dtype=np.int32)
    In [35]: arr1.dtype
    Out[35]: dtype('float64')
    In [36]: arr2.dtype
    Out[36]: dtype('int32')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.4.1 np.astype()

    • 你可以通过ndarray的astype方法明确地将一个数组从一个dtype转换成另一个dtype:
    In [37]: arr = np.array([1, 2, 3, 4, 5])
    In [38]: arr.dtype
    Out[38]: dtype('int64')
    In [39]: float_arr = arr.astype(np.float64)
    In [40]: float_arr.dtype
    Out[40]: dtype('float64')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 如果将浮点数转换成整数,则小数部分将会被截取删除:
    In [41]: arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
    In [42]: arr
    Out[42]: array([  3.7,  -1.2,  -2.6,   0.5,  12.9,  10.1])
    In [43]: arr.astype(np.int32)
    Out[43]: array([ 3, -1, -2,  0, 12, 10], dtype=int32)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2 NumPy数组的运算

    • 数组很重要,因为它使你不用编写循环,也可以对数据执行批量运算

    • 大小相等的数组,之间的任何算术运算,都会应用到元素级。

    In [51]: arr = np.array([[1., 2., 3.], [4., 5., 6.]])
    In [52]: arr
    Out[52]: 
    array([[ 1.,  2.,  3.],
           [ 4.,  5.,  6.]])
    In [53]: arr * arr
    Out[53]: 
    array([[  1.,   4.,   9.],
           [ 16.,  25.,  36.]])
    In [54]: arr - arr
    Out[54]: 
    array([[ 0.,  0.,  0.],
           [ 0.,  0.,  0.]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 数组与标量的算术运算,会将标量值传播到各个元素:
    In [55]: 1 / arr
    Out[55]: 
    array([[ 1.    ,  0.5   ,  0.3333],
           [ 0.25  ,  0.2   ,  0.1667]])
    In [56]: arr ** 0.5
    Out[56]: 
    array([[ 1.    ,  1.4142,  1.7321],
           [ 2.    ,  2.2361,  2.4495]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 大小相同的数组之间的比较会生成布尔值数组:
    In [57]: arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])
    In [58]: arr2
    Out[58]: 
    array([[  0.,   4.,   1.],
           [  7.,   2.,  12.]])
    In [59]: arr2 > arr
    Out[59]:
    array([[False,  True, False],
           [ True, False,  True]], dtype=bool)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.1 基本的索引和切片

    • 简单用法如下:

    可以发现,如果将一个标量赋给一个切片的话,那么这一段的切片内容都会发生改变;
    而且数组切片是原始数组视图,在视图上的任何修改都会反映到原始数组上。

    In [60]: arr = np.arange(10)
    In [61]: arr
    Out[61]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    In [62]: arr[5]
    Out[62]: 5
    In [63]: arr[5:8]
    Out[63]: array([5, 6, 7])
    In [64]: arr[5:8] = 12
    In [65]: arr
    Out[65]: array([ 0,  1,  2,  3,  4, 12, 12, 12,  8,  9])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如果不想对原数组进行操作,那么几一定要明确的进行复制操作。如:arr[5:8].copy()

    • 在一个二维数组中,各索引位置上的元素不再是标量而是一维数组。
    In [72]: arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    In [73]: arr2d[2]
    Out[73]: array([7, 8, 9])
    
    • 1
    • 2
    • 3
    • 可以传入一个以逗号隔开的索引列表来选取单个元素:
    In [74]: arr2d[0][2]
    Out[74]: 3
    In [75]: arr2d[0, 2]
    Out[75]: 3
    
    • 1
    • 2
    • 3
    • 4

    2.2 切片索引

    • ndarry的切片语法,跟Python列表的一维数组差不多:
    • 注意 arr[1:6] 是左闭右开的。
    In [88]: arr
    Out[88]: array([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])
    In [89]: arr[1:6]
    Out[89]: array([ 1,  2,  3,  4, 64])
    
    • 1
    • 2
    • 3
    • 4
    • 对于之前的二维数组arr2d,其切片方式稍显不同:
    • 可以看出,这里是沿着第0轴切片的,也就是说选取arr2d的前两行。
    In [90]: arr2d
    Out[90]: 
    array([[1, 2, 3],
           [4, 5, 6],
           [7, 8, 9]])
    In [91]: arr2d[:2]
    Out[91]: 
    array([[1, 2, 3],
           [4, 5, 6]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 传入多个切片:
    In [92]: arr2d[:2, 1:]
    Out[92]: 
    array([[2, 3],
           [5, 6]])
    
    • 1
    • 2
    • 3
    • 4
    • 将整数索引和切片混合,可以得到低维度的切片:
    In [93]: arr2d[1, :2]
    Out[93]: array([4, 5])
    
    • 1
    • 2
    In [94]: arr2d[:2, 2]
    Out[94]: array([3, 6])
    
    • 1
    • 2
    • 只有冒号 : 表示选取整个轴。
    In [95]: arr2d[:, :1]
    Out[95]: 
    array([[1],
           [4],
           [7]])
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3 布尔型索引

    • 例子:首先定义一个存储姓名的数组(7个名字),然后初始化一个7行的数组,每一行表示一个名字。
    In [98]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
    In [99]: data = np.random.randn(7, 4)
    In [100]: names
    Out[100]: 
    array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'],
          dtype='<U4')
    In [101]: data
    Out[101]: 
    array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
           [ 1.0072, -1.2962,  0.275 ,  0.2289],
           [ 1.3529,  0.8864, -2.0016, -0.3718],
           [ 1.669 , -0.4386, -0.5397,  0.477 ],
           [ 3.2489, -1.0212, -0.5771,  0.1241],
           [ 0.3026,  0.5238,  0.0009,  1.3438],
           [-0.7135, -0.8312, -2.3702, -1.8608]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 现在的需求是,根据Bob对应在数组中的索引,选出对应的随机数据的对应行的数据。
    • 可以看到,names == 'Bob' 会生成一个bool类型的数组。
    In [102]: names == 'Bob'
    Out[102]: array([ True, False, False,  True, False, False, False], dtype=bool)
    
    • 1
    • 2
    • 写出来的代码就如下所示:
    In [103]: data[names == 'Bob']
    Out[103]: 
    array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
           [ 1.669 , -0.4386, -0.5397,  0.477 ]])
    
    • 1
    • 2
    • 3
    • 4
    • 选取了names == 'Bob’的行,并索引了列:
    In [104]: data[names == 'Bob', 2:]
    Out[104]: 
    array([[ 0.769 ,  1.2464],
           [-0.5397,  0.477 ]])
    In [105]: data[names == 'Bob', 3]
    Out[105]: array([ 1.2464,  0.477 ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 选取这三个名字中的两个需要组合应用多个布尔条件,使用&(和)、|(或)之类的布尔算术运算符即可:
    In [110]: mask = (names == 'Bob') | (names == 'Will')
    In [111]: mask
    Out[111]: array([ True, False,  True,  True,  True, False, False], dtype=bool)
    In [112]: data[mask]
    Out[112]: 
    array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
           [ 1.3529,  0.8864, -2.0016, -0.3718],
           [ 1.669 , -0.4386, -0.5397,  0.477 ],
           [ 3.2489, -1.0212, -0.5771,  0.1241]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 通过布尔型数组设置值是一种经常用到的手段。为了将data中的所有负值都设置为0,我们只需:
    In [113]: data[data < 0] = 0
    In [114]: data
    Out[114]: 
    array([[ 0.0929,  0.2817,  0.769 ,  1.2464],
           [ 1.0072,  0.    ,  0.275 ,  0.2289],
           [ 1.3529,  0.8864,  0.    ,  0.    ],
           [ 1.669 ,  0.    ,  0.    ,  0.477 ],
           [ 3.2489,  0.    ,  0.    ,  0.1241],
           [ 0.3026,  0.5238,  0.0009,  1.3438],
           [ 0.    ,  0.    ,  0.    ,  0.    ]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 通过一维布尔数组设置 整行或整列的值也很简单:
    In [115]: data[names != 'Joe'] = 7
    In [116]: data
    Out[116]: 
    array([[ 7.    ,  7.    ,  7.    ,  7.    ],
           [ 1.0072,  0.    ,  0.275 ,  0.2289],
           [ 7.    ,  7.    ,  7.    ,  7.    ],
           [ 7.    ,  7.    ,  7.    ,  7.    ],
           [ 7.    ,  7.    ,  7.    ,  7.    ],
           [ 0.3026,  0.5238,  0.0009,  1.3438],
           [ 0.    ,  0.    ,  0.    ,  0.    ]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4 花式索引

    花式索引,就是利用传入整数数组进行索引。

    In [117]: arr = np.empty((8, 4))
    In [118]: for i in range(8):
       .....:     arr[i] = i
    In [119]: arr
    Out[119]: 
    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.]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 为了以特定顺序选取行子集,只需传入一个用于指定顺序的整数列表或ndarray即可:
    In [120]: arr[[4, 3, 0, 6]]
    Out[120]: 
    array([[ 4.,  4.,  4.,  4.],
           [ 3.,  3.,  3.,  3.],
           [ 0.,  0.,  0.,  0.],
           [ 6.,  6.,  6.,  6.]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 使用负数索引将会从末尾开始选取行:
    In [121]: arr[[-3, -5, -7]]
    Out[121]: 
    array([[ 5.,  5.,  5.,  5.],
           [ 3.,  3.,  3.,  3.],
           [ 1.,  1.,  1.,  1.]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 如果传入多个索引数组,返回的还是一个一维数组。

    • 最终选出的是元素(1,0)、(5,3)、(7,1)和(2,2)。无论数组是多少维的,花式索引总是一维的。

    In [122]: arr = np.arange(32).reshape((8, 4))
    In [123]: arr
    Out[123]: 
    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]])
    In [124]: arr[[1, 5, 7, 2], [0, 3, 1, 2]]
    Out[124]: array([ 4, 23, 29, 10])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5 数组转置和轴对换

    • 转置返回的是源数据的视图。
    • 数组不仅有transpose方法,还有一个特殊的 T 属性。
    In [126]: arr = np.arange(15).reshape((3, 5))
    In [127]: arr
    Out[127]: 
    array([[ 0,  1,  2,  3,  4],
           [ 5,  6,  7,  8,  9],
           [10, 11, 12, 13, 14]])
    In [128]: arr.T
    Out[128]: 
    array([[ 0,  5, 10],
           [ 1,  6, 11],
           [ 2,  7, 12],
           [ 3,  8, 13],
           [ 4,  9, 14]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 在进行矩阵计算时,经常需要这个操作。比如说利用 np.dot 计算矩阵内积。
    In [129]: arr = np.random.randn(6, 3)
    In [130]: arr
    Out[130]: 
    array([[-0.8608,  0.5601, -1.2659],
           [ 0.1198, -1.0635,  0.3329],
           [-2.3594, -0.1995, -1.542 ],
           [-0.9707, -1.307 ,  0.2863],
           [ 0.378 , -0.7539,  0.3313],
           [ 1.3497,  0.0699,  0.2467]])
    In [131]: np.dot(arr.T, arr)
    Out[131]:
    array([[ 9.2291,  0.9394,  4.948 ],
           [ 0.9394,  3.7662, -1.3622],
           [ 4.948 , -1.3622,  4.3437]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 简单的转置可以使用.T,它其实就是进行轴对换而已。
    • ndarray还有一个swapaxes方法,它需要接受一对轴编号。也是返回的源数据的视图。
    In [135]: arr
    Out[135]: 
    array([[[ 0,  1,  2,  3],
            [ 4,  5,  6,  7]],
           [[ 8,  9, 10, 11],
            [12, 13, 14, 15]]])
    In [136]: arr.swapaxes(1, 2)
    Out[136]: 
    array([[[ 0,  4],
            [ 1,  5],
            [ 2,  6],
            [ 3,  7]],
           [[ 8, 12],
            [ 9, 13],
            [10, 14],
            [11, 15]]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    html图片控制处理放大缩小移动多种案例(附源码)
    腾讯云服务器多少钱一年?2023年腾讯云优惠云服务器推荐
    【Java SE】封装的详解
    Java 进阶 01 —— 5 分钟回顾一下 Java 基础知识
    SCM供应链管理的背景及意义
    websocket学习
    ASO优化之为应用创建屏幕截图的技巧(下)
    redis一主一从搭建
    web server apache tomcat11-11-Jasper 2 JSP Engine
    Springboot 集成 RocketMQ(进阶-消息)
  • 原文地址:https://blog.csdn.net/weixin_42521185/article/details/125469873