zoukankan      html  css  js  c++  java
  • 03-Numpy库

    Numpy库介绍

    NumPy是一个功能强大的Python库,主要用于对多维数组执行计算。NumPy这个词来源于两个单词-- NumericalPythonNumPy提供了大量的库函数和操作,可以帮助程序员轻松地进行数值计算。在数据分析和机器学习领域被广泛使用。他有以下几个特点:

    1. numpy内置了并行运算功能,当系统有多个核心时,做某种计算时,numpy会自动做并行计算。
    2. Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁),其对数组的操作速度不受Python解释器的限制,效率远高于纯Python代码。
    3. 有一个强大的N维数组对象Array(一种类似于列表的东西)。
    4. 实用的线性代数、傅里叶变换和随机数生成函数。

    总而言之,他是一个非常高效的用于处理数值型运算的包。

    安装:

    通过pip install numpy即可安装。

    教程地址:

    1. 官网:https://docs.scipy.org/doc/numpy/user/quickstart.html
    2. 中文文档:https://www.numpy.org.cn/user_guide/quickstart_tutorial/index.html

    Numpy数组和Python列表性能对比:

    比如我们想要对一个Numpy数组和Python列表中的每个素进行求平方。那么代码如下:

    # Python列表的方式
    t1 = time.time()
    a = []
    for x in range(100000):
        a.append(x**2)
    t2 = time.time()
    t = t2 - t1
    print(t)
    

    花费的时间大约是0.07180左右。而如果使用numpy的数组来做,那速度就要快很多了:

    t3 = time.time()
    b = np.arange(100000)**2
    t4 = time.time()
    print(t4-t3)
    

    NumPy数组基本用法

    1. NumpyPython科学计算库,用于快速处理任意维度的数组。
    2. NumPy提供一个N维数组类型ndarray,它描述了相同类型的“items”的集合。
    3. numpy.ndarray支持向量化运算。
    4. NumPy使用c语言写的,底部解除了GIL,其对数组的操作速度不在受python解释器限制。

    numpy中的数组:

    Numpy中的数组的使用跟Python中的列表非常类似。他们之间的区别如下:

    1. 一个列表中可以存储多种数据类型。比如a = [1,'a']是允许的,而数组只能存储同种数据类型。
    2. 数组可以是多维的,当多维数组中所有的数据都是数值类型的时候,相当于线性代数中的矩阵,是可以进行相互间的运算的。

    创建数组(np.ndarray对象):

    Numpy经常和数组打交道,因此首先第一步是要学会创建数组。在Numpy中的数组的数据类型叫做ndarray。以下是两种创建的方式:

    1. 根据Python中的列表生成:

      import numpy as np
      a1 = np.array([1,2,3,4])
      print(a1)
      print(type(a1))
      
    2. 使用np.arange生成,np.arange的用法类似于Python中的range

      import numpy as np
      a2 = np.arange(2,21,2)
      print(a2)
      
    3. 使用np.random生成随机数的数组:

      a1 = np.random.random((2,2)) # 生成2行2列的随机数的数组
      a2 = np.random.randint(0,10,size=(3,3)) # 元素是从0-10之间随机的3行3列的数组
      
    4. 使用函数生成特殊的数组:

      import numpy as np
      a1 = np.zeros((2,2)) #生成一个所有元素都是0的2行2列的数组
      a2 = np.ones((3,2)) #生成一个所有元素都是1的3行2列的数组
      a3 = np.full((2,2),8) #生成一个所有元素都是8的2行2列的数组
      a4 = np.eye(3) #生成一个在斜方形上元素为1,其他元素都为0的3x3的矩阵
      

    ndarray常用属性:

    ndarray.dtype

    因为数组中只能存储同一种数据类型,因此可以通过dtype获取数组中的元素的数据类型。以下是ndarray.dtype的常用的数据类型:

    我们可以看到,Numpy中关于数值的类型比Python内置的多得多,这是因为Numpy为了能高效处理处理海量数据而设计的。举个例子,比如现在想要存储上百亿的数字,并且这些数字都不超过254(一个字节内),我们就可以将dtype设置为int8,这样就比默认使用int64更能节省内存空间了。类型相关的操作如下:

    1. 默认的数据类型:

      import numpy as np
      a1 = np.array([1,2,3])
      print(a1.dtype) 
      # 如果是windows系统,默认是int32
      # 如果是mac或者linux系统,则根据系统来
      
    2. 指定

      import numpy as np
      a1 = np.array([1,2,3],dtype=np.int64)
      #或者 a1 = np.array([1,2,3],dtype="i8")
      print(a1.dtype)
      
      
    3. 修改

      import numpy as np
      a1 = np.array([1,2,3])
      print(a1.dtype) # window系统下默认是int32
      # 以下修改dtype
      a2 = a1.astype(np.int64) # astype不会修改数组本身,而是会将修改后的结果返回
      print(a2.dtype)
      

    ndarray.size

    获取数组中总的元素的个数。比如有个二维数组:

    import numpy as np
    a1 = np.array([[1,2,3],[4,5,6]])
    print(a1.size) #打印的是6,因为总共有6个元素
    

    ndarray.ndim

    数组的维数。比如:

    a1 = np.array([1,2,3])
    print(a1.ndim) # 维度为1
    a2 = np.array([[1,2,3],[4,5,6]])
    print(a2.ndim) # 维度为2
    a3 = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
    print(a3.ndim) # 维度为3
    

    ndarray.shape

    数组的维度的元组。比如以下代码:

    a1 = np.array([1,2,3])
    print(a1.shape) # 输出(3,),意思是一维数组,有3个数据
    
    a2 = np.array([[1,2,3],[4,5,6]])
    print(a2.shape) # 输出(2,3),意思是二位数组,2行3列
    
    a3 = np.array([
        [
            [1,2,3],
            [4,5,6]
        ],
        [
            [7,8,9],
            [10,11,12]
        ]
    ])
    print(a3.shape) # 输出(2,2,3),意思是三维数组,总共有2个元素,每个元素是2行3列的
    
    a44 = np.array([1,2,3],[4,5])
    print(a4.shape) # 输出(2,),意思是a4是一个一维数组,总共有2列
    print(a4) # 输出[list([1, 2, 3]) list([4, 5])],其中最外面层是数组,里面是Python列表
    

    另外,我们还可以通过ndarray.reshape来重新修改数组的维数。示例代码如下:

    a1 = np.arange(12) #生成一个有12个数据的一维数组
    print(a1) 
    
    a2 = a1.reshape((3,4)) #变成一个2维数组,是3行4列的
    print(a2)
    
    a3 = a1.reshape((2,3,2)) #变成一个3维数组,总共有2块,每一块是2行2列的
    print(a3)
    
    a4 = a2.reshape((12,)) # 将a2的二维数组重新变成一个12列的1维数组
    print(a4)
    
    a5 = a2.flatten() # 不管a2是几维数组,都将他变成一个一维数组
    print(a5)
    

    注意,reshape并不会修改原来数组本身,而是会将修改后的结果返回。如果想要直接修改数组本身,那么可以使用resize来替代reshape

    ndarray.itemsize

    数组中每个元素占的大小,单位是字节。比如以下代码:

    a1 = np.array([1,2,3],dtype=np.int32)
    print(a1.itemsize) # 打印4,因为每个字节是8位,32位/8=4个字节
    

    Numpy数组操作

    索引和切片:

    1. 获取某行的数据:

       # 1. 如果是一维数组
       a1 = np.arange(0,29)
       print(a1[1]) #获取下标为1的元素
      
       a1 = np.arange(0,24).reshape((4,6))
       print(a1[1]) #获取下标为1的行的数据
      
    2. 连续获取某几行的数据:

       # 1. 获取连续的几行的数据
       a1 = np.arange(0,24).reshape((4,6))
       print(a1[0:2]) #获取0行到1行的数据
      
       # 2. 获取不连续的几行的数据
       print(a1[[0,2,3]])
      
       # 3. 也可以使用负数进行索引
       print(a1[[-1,-2]])
      
    3. 获取某行某列的数据:

       a1 = np.arange(0,24).reshape((4,6))
       print(a1[1,1]) #获取1行1列的数据
      
       print(a1[0:2,0:2]) #获取0-1行的0-1列的数据
       print(a1[[1,2],[2,3]]) #获取(1,2)和(2,3)的两个数据,这也叫花式索引
      
    4. 获取某列的数据:

       a1 = np.arange(0,24).reshape((4,6))
       print(a1[:,1]) #获取第1列的数据
      

    布尔索引:

    布尔运算也是矢量的,比如以下代码:

    a1 = np.arange(0,24).reshape((4,6))
    print(a1<10) #会返回一个新的数组,这个数组中的值全部都是bool类型
    > [[ True  True  True  True  True  True]
     [ True  True  True  True False False]
     [False False False False False False]
     [False False False False False False]]
    

    这样看上去没有什么用,假如我现在要实现一个需求,要将a1数组中所有小于10的数据全部都提取出来。那么可以使用以下方式实现:

    a1 = np.arange(0,24).reshape((4,6))
    a2 = a1 < 10
    print(a1[a2]) #这样就会在a1中把a2中为True的元素对应的位置的值提取出来
    

    其中布尔运算可以有!===><>=<=以及&(与)|(或)。示例代码如下:

    a1 = np.arange(0,24).reshape((4,6))
    a2 = a1[(a1 < 5) | (a1 > 10)]
    print(a2)
    

    值的替换:

    利用索引,也可以做一些值的替换。把满足条件的位置的值替换成其他的值。比如以下代码:

    a1 = np.arange(0,24).reshape((4,6))
    a1[3] = 0 #将第三行的所有值都替换成0
    print(a1)
    

    也可以使用条件索引来实现:

    a1 = np.arange(0,24).reshape((4,6))
    a1[a1 < 5] = 0 #将小于5的所有值全部都替换成0
    print(a1)
    

    还可以使用函数来实现:

    ## where函数:
    a1 = np.arange(0,24).reshape((4,6))
    a2 = np.where(a1 < 10,1,0) #把a1中所有小于10的数全部变成1,其余的变成0
    print(a2)
    

    数组形状的操作:

    可以通过一些函数,非常方便的操作数组的形状。

    reshape和resize方法:

    两个方法都是用来修改数组形状的,但是有一些不同。

    1. reshape是将数组转换成指定的形状,然后返回转换后的结果,对于原数组的形状是不会发生改变的。调用方式:

      a1 = np.random.randint(0,10,size=(3,4))
      a2 = a1.reshape((2,6)) #将修改后的结果返回,不会影响原数组本身
      
    2. resize是将数组转换成指定的形状,会直接修改数组本身。并不会返回任何值。调用方式:

      a1 = np.random.randint(0,10,size=(3,4))
      a1.resize((2,6)) #a1本身发生了改变
      

    flatten和ravel方法:

    两个方法都是将多维数组转换为一维数组,但是有以下不同:

    1. flatten是将数组转换为一维数组后,然后将这个拷贝返回回去,所以后续对这个返回值进行修改不会影响之前的数组。
    2. ravel是将数组转换为一维数组后,将这个视图(可以理解为引用)返回回去,所以后续对这个返回值进行修改会影响之前的数组。
      比如以下代码:
    x = np.array([[1, 2], [3, 4]])
    x.flatten()[1] = 100 #此时的x[0]的位置元素还是1
    x.ravel()[1] = 100 #此时x[0]的位置元素是100
    

    数据的拼接:

    如果有多个数组想要组合在一起,也可以通过其中的一些函数来实现。

    1. vstack:将数组按垂直方向进行叠加。数组的列数必须相同才能叠加。示例代码如下:

      a1 = np.random.randint(0,10,size=(3,5))
      a2 = np.random.randint(0,10,size=(1,5))
      a3 = np.vstack([a1,a2])
      
    2. hstack:将数组按水平方向进行叠加。数组的行必须相同才能叠加。示例代码如下:

      a1 = np.random.randint(0,10,size=(3,2))
      a2 = np.random.randint(0,10,size=(3,1))
      a3 = np.hstack([a1,a2])
      
    3. concatenate([],axis):将两个数组进行叠加,但是具体是按水平方向还是按垂直方向。则要看axis的参数,如果axis=0,那么代表的是往垂直方向(行)叠加,如果axis=1,那么代表的是往水平方向(列)上叠加,如果axis=None,那么会将两个数组组合成一个一维数组。需要注意的是,如果往水平方向上叠加,那么行必须相同,如果是往垂直方向叠加,那么列必须相同。示例代码如下:

      a = np.array([[1, 2], [3, 4]])
      b = np.array([[5, 6]])
      np.concatenate((a, b), axis=0)
      # 结果:
      array([[1, 2],
          [3, 4],
          [5, 6]])
      
      np.concatenate((a, b.T), axis=1)
      # 结果:
      array([[1, 2, 5],
          [3, 4, 6]])
      
      np.concatenate((a, b), axis=None)
      # 结果:
      array([1, 2, 3, 4, 5, 6])
      

    数组的切割:

    通过hsplitvsplit以及array_split可以将一个数组进行切割。

    1. hsplit:按照水平方向进行切割。用于指定分割成几列,可以使用数字来代表分成几部分,也可以使用数组来代表分割的地方。示例代码如下:

      a1 = np.arange(16.0).reshape(4, 4)
      np.hsplit(a1,2) #分割成两部分
      >>> array([[ 0.,  1.],
           [ 4.,  5.],
           [ 8.,  9.],
           [12., 13.]]), array([[ 2.,  3.],
           [ 6.,  7.],
           [10., 11.],
           [14., 15.]])]
      
      np.hsplit(a1,[1,2]) #代表在下标为1的地方切一刀,下标为2的地方切一刀,分成三部分
      >>> [array([[ 0.],
           [ 4.],
           [ 8.],
           [12.]]), array([[ 1.],
           [ 5.],
           [ 9.],
           [13.]]), array([[ 2.,  3.],
           [ 6.,  7.],
           [10., 11.],
           [14., 15.]])]
      
    2. vsplit:按照垂直方向进行切割。用于指定分割成几行,可以使用数字来代表分成几部分,也可以使用数组来代表分割的地方。示例代码如下:

      np.vsplit(x,2) #代表按照行总共分成2个数组
      >>> [array([[0., 1., 2., 3.],
           [4., 5., 6., 7.]]), array([[ 8.,  9., 10., 11.],
           [12., 13., 14., 15.]])]
      
      np.vsplit(x,(1,2)) #代表按照行进行划分,在下标为1的地方和下标为2的地方分割
      >>> [array([[0., 1., 2., 3.]]),
          array([[4., 5., 6., 7.]]),
          array([[ 8.,  9., 10., 11.],
                 [12., 13., 14., 15.]])]
      
    3. split/array_split(array,indicate_or_seciont,axis):用于指定切割方式,在切割的时候需要指定是按照行还是按照列,axis=1代表按照列,axis=0代表按照行。示例代码如下:

      np.array_split(x,2,axis=0) #按照垂直方向切割成2部分
      >>> [array([[0., 1., 2., 3.],
           [4., 5., 6., 7.]]), array([[ 8.,  9., 10., 11.],
           [12., 13., 14., 15.]])]
      
      

    数组(矩阵)转置和轴对换:

    numpy中的数组其实就是线性代数中的矩阵。矩阵是可以进行转置的。ndarray有一个T属性,可以返回这个数组的转置的结果。示例代码如下:

    a1 = np.arange(0,24).reshape((4,6))
    a2 = a1.T
    print(a2)
    
    

    另外还有一个方法叫做transpose,这个方法返回的是一个View,也即修改返回值,不会影响到原来数组。示例代码如下:

    a1 = np.arange(0,24).reshape((4,6))
    a2 = a1.transpose()
    
    

    为什么要进行矩阵转置呢,有时候在做一些计算的时候需要用到。比如做矩阵的内积的时候。就必须将矩阵进行转置后再乘以之前的矩阵:

    a1 = np.arange(0,24).reshape((4,6))
    a2 = a1.T
    print(a1.dot(a2))
    
    
  • 相关阅读:
    【转载】为什么CPU有多层缓存
    【转载】二叉树的基本概念和实现
    【转载】如何系统地自学 Python?
    【原文】前端程序员必须知道的高性能Javascript知识
    【转载】重磅!中国人工智能/机器人/无人机创业公司100 | 智能内参
    【转载】分析重装系统也无法清除的鬼影病毒
    【转载】UML类图知识整理
    【转载】.NET程序员走向高端必读书单汇总
    【转载】齐次坐标的理解
    59. Spiral Matrix II
  • 原文地址:https://www.cnblogs.com/mark-wq/p/15044074.html
Copyright © 2011-2022 走看看