• 深入理解numpy


    numpy是一个很大的库,完全了解它是不现实的,只能是了解常用的功能。平时遇见不懂的地方弄清楚,注意积累。

    组元不需要圆括号,虽然我们经常在Python中用圆括号将组元括起来,但是其实组元的语法定义只需要用逗号隔开即可,例如 x,y=y,x 就是用组元交换变量值的一个例子。

    一、为啥需要numpy

    Python虽然说注重优雅简洁,但它终究是需要考虑效率的。
    Python的列表,跟Java一样,其实只是一维列表。一维列表相当于一种类型,这样对于元素的访问效率是很低的。
    Python中一切皆引用,每一个int对象都要用指针指一下再用int存储一下,浪费空间也浪费时间。当读取某个元素的时候需要先读取引用,再根据引用指向的内存地址来读取int值。
    numpy相当于完全采用了C语言那套数组机制。

    二、numpy原则

    • 一切皆一维,多维只是马甲
      多维数组的内部实现就是一维。
    • 定长,一切皆矩形,一切皆长方体。
      比如定义了一个数组a[3],则len(a[0])=len(a[1])=len(a[2]),各个元素不能变长。正是因为定长这个原则,才有可能实现“一切皆一维”这个原则。
    • 数组中元素类型相同,长度相同
      numpy中的数组都是一维数组,并且这个一维数组中每个元素的长度相同,各个元素属于同一种类型。
      numpy中的元素相当于结构体,一个结构体所占字节数是固定的,numpy是允许用户自定义结构体类型的。
    • 数组就是一块空间
      想对它作何解释就作何解释,想给它穿上什么马甲就给它穿上什么马甲。
      对于一个包含24个元素的一维数组,可以把它理解为4×6或者2×12或者3×8的二维数组,也可以把它理解为2×2×6或者3×2×4的三维数组。

    三、numpy概念

    • ndarray.ndim
      n前缀表示个数,dim表示dimension,ndim表示维数
    • ndarray.shape
      数组的维度,这是一个指示数组在每个维度上大小的整数元组,这个元组的长度显然是秩,即维度或者ndim属性
    • ndarray.size
      数组元素的总个数,等于shape属性中元组元素的乘积。
    • ndarray.dtype
      一个用来描述数组中元素类型的对象,可以通过创造或指定dtype使用标准Python类型。另外NumPy提供它自己的数据类型。
    • ndarray.itemsize
      数组中每个元素的字节大小。例如,一个元素类型为float64的数组itemsiz属性值为8(=64/8),又如,一个元素类型为complex32的数组item属性为4(=32/8)。
    • ndarray.data
      包含实际数组元素的缓冲区,通常我们不需要使用这个属性,因为我们总是通过索引来使用数组中的元素。
      这个属性太重要了,因为numpy中的ndarray只是一个马甲,data部分才是真真正正的数据。ndarray中的shape、dtype等属性相当于“数据解释器”,用来描述data中的数据是如何组织的。

    一个例子

    >>> from numpy  import *
    >>> a = arange(15).reshape(3,5)
    >>> a
    array([[ 0,  1,  2,  3,  4],
           [ 5,  6,  7,  8,  9],
           [10, 11, 12, 13, 14]])
    >>> a.shape
    (3, 5)
    >>> a.ndim
    2
    >>> a.dtype.name
    'int32'
    >>> a.itemsize
    4
    >>> a.size
    15
    >>> type(a)
    numpy.ndarray
    >>> b = array([6, 7, 8])
    >>> b
    array([6, 7, 8])
    >>> type(b)
    numpy.ndarray
    

    改变数组维度的两种方式:

    • a.reshape(d1,d2...),不改变a本身
    • a.shape=元组,改变a本身,相当于a=a.reshape(元组)
    a=np.arange(24)
    
    b=a.reshape(4,-1)
    
    a.shape,b.shape
    Out[21]: ((24,), (4, 6))
    
    a.shape=-1,5
    Traceback (most recent call last):
    
      File "<iPython-input-22-0a3b0c92d497>", line 1, in <module>
        a.shape=-1,5
    
    ValueError: total size of new array must be unchanged
    
    
    a.shape=-1,4
    
    a
    Out[24]: 
    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]])
    

    一个比较常用的功能是把多维数组变成一维数组,有如下三种实现方式

    • a.shape=-1
    • b=a.reshape(-1)
    • b=a.ravel() b和a共用同一份data
    • b=a.flatten() b的data是a的data的复制品
    • a.resize((d1,d2...)) 相当于a=a.reshape((d1,d2...)),也相当a.shape=d1,d2... ,它直接更改a的形状

    四、创建ndarray对象

    • np.array([[1,2],[3,4]],dtype=userType)
      使用array对象来封装Python的列表或者元祖或者range对象。

    • 创建一维对象
      np.linspace(start,stop,num,endpoint=True,retStep=false,dtype=None)产生等差数列,指明数组的首元素、末元素和数组长度,生成一个等差数列。
      np.logspace产生等比数列,参数跟linspace完全一致
      np.arange(start=0,end,step=1,dtype=None)产生等差数列

    • 给定维数数组创建ndarray
      np.ones全1数组
      np.zeros全0数组
      np.empty不做处理的数组,只负责开辟空间,比前面两个速度快
      创建随机ndarray,可以指定不同的随机分布,详见下文

    • 从字节序列出发创建ndarray
      np.fromstring,np.frombuffer,np.fromfile
      一切都是小头序

    • 从迭代器出发创建ndarray
      np.fromiter:np。fromiter(range(100),dtype=np.int32,count=10)

    • 从函数出发创建ndarray
      np.fromfunction

    >>> def func2(i, j):
    ...     return (i+1) * (j+1)
    ...
    >>> a = np.fromfunction(func2, (9,9))
    >>> a
    array([[  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.]])
    

    五.创建随机ndarray对象

    随机值都是在区间[0,1)上

    • 均匀分布
      np.random.random(size=None)默认返回一个0~1之间的数字,可以指明维度序列来生成特定维度的随机值。
      np.random.randint(low,high,size)返回一个int型数组,每个元素都在[low,high)之间。Python标注库中的np.random.randint(low,high)返回一个[low,high]之间的值(注意是闭区间)。
      np.random.rand,相当于np.random.random((d1,d2,d3)),只不过这个函数的参数可以是多个而不仅仅是一个元组。
    import numpy as np
    print(np.random.random((1,2,3)))
    print(np.random.rand(1,2,3))
    
    • 正态分布
      np.random.randn

    六、存取元素

    1、共享存储空间的切片操作
    numpy的设计原则就是“高效”。Python中的切片是复制原数组,效率很低。而numpy中的数组切片与原数组共享同一内存,效率极高。

    >>> b = a[3:7] # 通过下标范围产生一个新的数组b,b和a共享同一块数据空间
    >>> b
    array([101,   4,   5,   6])
    >>> b[2] = -10 # 将b的第2个元素修改为-10
    >>> b
    array([101,   4, -10,   6])
    >>> a # a的第5个元素也被修改为10
    array([  0,   1, 100, 101,   4, -10,   6,   7,   8,   9])
    

    2、numpy下标操作完全包括Python中的下标操作

    >>> a = np.arange(10)
    >>> a[5]    # 用整数作为下标可以获取数组中的某个元素
    5
    >>> a[3:5]  # 用范围作为下标获取数组的一个切片,包括a[3]不包括a[5]
    array([3, 4])
    >>> a[:5]   # 省略开始下标,表示从a[0]开始
    array([0, 1, 2, 3, 4])
    >>> a[:-1]  # 下标可以使用负数,表示从数组后往前数
    array([0, 1, 2, 3, 4, 5, 6, 7, 8])
    >>> a[2:4] = 100,101    # 下标还可以用来修改元素的值
    >>> a
    array([  0,   1, 100, 101,   4,   5,   6,   7,   8,   9])
    >>> a[1:-1:2]   # 范围中的第三个参数表示步长,2表示隔一个元素取一个元素
    array([  1, 101,   5,   7])
    >>> a[::-1] # 省略范围的开始下标和结束下标,步长为-1,整个数组头尾颠倒
    array([  9,   8,   7,   6,   5,   4, 101, 100,   1,   0])
    >>> a[5:1:-2] # 步长为负数时,开始下标必须大于结束下标
    array([  5, 101])
    

    3、numpy下标操作更加灵活
    在Python下标操作基础上,numpy的下标操作更加灵活:

    a=np。arange(25)。reshape(5,5)
    
    a
    Out[63]: 
    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]])
    
    a[3,3]
    Out[65]: 18
    
    a[:,2]
    Out[66]: array([ 2,  7, 12, 17, 22])
    

    4、通过下标数组获取元素数组
    numpy提供了两种方式:

    • 使用整数序列
    • 使用bool数组

    使用整数序列获取多个元素,返回的是原数组的副本

    >>> x = np.arange(10,1,-1)
    >>> x
    array([10,  9,  8,  7,  6,  5,  4,  3,  2])
    >>> x[[3, 3, 1, 8]] # 获取x中的下标为3, 3, 1, 8的4个元素,组成一个新的数组
    array([7, 7, 9, 2])
    >>> b = x[np.array([3,3,-3,8])]  #下标可以是负数
    >>> b[2] = 100
    >>> b
    array([7, 7, 100, 2])
    >>> x   # 由于b和x不共享数据空间,因此x中的值并没有改变
    array([10,  9,  8,  7,  6,  5,  4,  3,  2])
    >>> x[[3,5,1]] = -1, -2, -3 # 整数序列下标也可以用来修改元素的值
    >>> x
    array([10, -3,  8, -1,  6, -2,  4,  3,  2])
    

    使用布尔数组获取元素,如果布尔数组不够长,那么不够长的部分默认为false

    >>> x = np.arange(5,0,-1)
    >>> x
    array([5, 4, 3, 2, 1])
    >>> x[np.array([True, False, True, False, False])]
    >>> # 布尔数组中下标为0,2的元素为True,因此获取x中下标为0,2的元素
    array([5, 3])
    >>> x[[True, False, True, False, False]]
    >>> # 如果是布尔列表,则把True当作1, False当作0,按照整数序列方式获取x中的元素
    array([4, 5, 4, 5, 5])
    >>> x[np.array([True, False, True, True])]
    >>> # 布尔数组的长度不够时,不够的部分都当作False
    array([5, 3, 2])
    >>> x[np.array([True, False, True, True])] = -1, -2, -3
    >>> # 布尔数组下标也可以用来修改元素
    >>> x
    array([-1,  4, -2, -3,  1])
    

    5、多维数组
    多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标。

    多维数组的下标也可以使用下标数组和掩码数组,但要注意这时得到的是原数组的副本。

    七、numpy数据类型

    bool_ Boolean (True or False) stored as a byte
    int_ Default integer type (same as C long; normally either int64 or int32)
    intc Identical to C int (normally int32 or int64)
    intp Integer used for indexing (same as C ssize_t; normally either int32 or int64)
    int8 Byte (-128 to 127)
    int16 Integer (-32768 to 32767)
    int32 Integer (-2147483648 to 2147483647)
    int64 Integer (-9223372036854775808 to 9223372036854775807)
    uint8 Unsigned integer (0 to 255)
    uint16 Unsigned integer (0 to 65535)
    uint32 Unsigned integer (0 to 4294967295)
    uint64 Unsigned integer (0 to 18446744073709551615)
    float_ Shorthand for float64。
    float16 Half precision float: sign bit, 5 bits exponent, 10 bits mantissa
    float32 Single precision float: sign bit, 8 bits exponent, 23 bits mantissa
    float64 Double precision float: sign bit, 11 bits exponent, 52 bits mantissa
    complex_ Shorthand for complex128。
    complex64 Complex number, represented by two 32-bit floats (real and imaginary components)
    complex128 Complex number, represented by two 64-bit floats (real and imaginary components)

    要注意,直接更改ndarray的dtype属性,并不改变data部分。还是那句话,ndarray是一个马甲,用来描述一块内存。

    • a.dtype=np.float32:并不改变data部分
    • b=a.astype(np.float32):并不改变a,只是将a的data部分进行数据类型转换后复制到了b的data部分
    a=np.random.random(4)
    
    a.dtype
    Out[21]: dtype('float64')
    
    a.dtype=np.float32
    
    len(a)
    Out[23]: 8
    
    a.dtype=np.float16
    
    len(a)
    Out[25]: 16
    

    八、通用函数

    通用函数的作用对象是数组中的每一个元素。
    通用函数通常有一个out参数,如果带上这个参数就可以避免开辟新的内存空间。

    九、广播broadcast

    两个不同维度的数组相加

    import numpy as np
    
    a = np.arange(5)
    b = np.arange(6).reshape(-1, 1)
    
    
    def rep(a, c):
       for i in range(a.ndim-1, -1, -1):
          if a.shape[i] == c.shape[i]: continue
          if a.shape[i] == 1:
             a = a.repeat(c.shape[i], axis=i)
          else:
             raise Exception("dimention not match exception")
       return a
    
    
    def add(a, b):
       if a.ndim>b.ndim: a, b = b, a
       ashape = [1] * (b.ndim-a.ndim) + list(a.shape)
       a = a.reshape(ashape)
       cshape = [max(a.shape[i], b.shape[i]) for i in range(a.ndim)]
       c = np.empty(cshape)
       a = rep(a, c)
       b = rep(b, c)
       a = a.reshape(-1)
       b = b.reshape(-1)
       cc = c.reshape(-1)
       for i in range(len(cc)):
          cc[i] = a[i] + b[i]
       return c
    
    
    print(add(a, b))
    print(a + b)
    

    九、ndarray的组合与拆分

    np.hstack
    np.vstack
    np.dstack
    np.column_stack
    np.row_stack

    np.hsplit
    np.vsplit
    np.dsplit
    np.split

  • 相关阅读:
    《Orange‘s》 Bochs环境配置
    《CSAPP》读书笔记
    基于SDL2实现俄罗斯方块
    VS 提示对象被多次指定;已忽略多余的指定
    SDL2.0 vs2017环境配置
    初探模板元编程
    从代码理解 cartographer X --- 浅析Cartographer框架之美
    从代码理解 cartographer 1
    Arch Linux 安装总结
    从代码理解 cartographer 3 --- 理解 Range data 数据的流入
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/6186477.html
Copyright © 2020-2023  润新知