• NumPy操作数组的方法


    1. 索引和切片

    索引是定位一维或多维数组中的单个或多个元素的行为模式。切片是返回一维或多维数组中的单个或多个相邻元素的视图,目的是引用或赋值。NumPy数组对象的内容可以通过索引或切片来访问和修改。

    import numpy as np
    
    a = np.arange(9)
    # 最后一个元素
    print(a[-1])
    # 返回第2到第5个元素
    print(a[2:5])
    # 返回第0到底7个元素,步长为3
    print(a[:7:3])
    # 返回逆序的数组
    print(a[::-1])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    对于多维数组操作,NumPy数组比Python的列表更加灵活、强大。假设有一栋楼,共2层,每层的房间都是3行4列,那我们可以用一个三维数组来保存每个房间的居住人数(也可以是房间面积等其他数值信息)

    import numpy as np
    
    # 2层3行4列
    a = np.arange(24).reshape(2, 3, 4)
    print(a)
    # 虽然可以这样索引
    print(a[1][2][3])
    # 但这样才是规范的用法
    print(a[1, 2, 3])
    # 1层的所有房间,等价于a[0]或a[0,...]
    print(a[0,:,:])
    # 所有楼层所有排的第1到第3列
    print(a[:, :,1:3])
    # 2层每一行的最后一个房间
    print(a[1:,:, -1])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    从上面的代码中可以看出,对多维数组索引或切片得到的结果的维度不是确定的。另外还有一点需要特别提醒:切片返回的数组不是原始数组的副本,而是指向与原始数组相同的内存区域。数组切片不会复制内部数组数据,只是产生了原始数据的一个新视图

    import numpy as np
    
    
    a = np.arange(12).reshape(3, 4)
    print(a)
    # 数组b是数组a的切片
    b = a[1:, 2:]
    print(b)
    # 改变数组b的值,也会同时影响数组a
    b[:, :] = 99
    print(b)
    print(a)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    上面的代码中,数组b是数组a的切片,当改变数b的元素时,数组a也同时发生了改变。这就证明了切片返回的数组不是一个独立数组,而是指向原始数组相同的内存区域

    2. 改变数组结构

    NumPy之所以拥有极高的运算速度,除了并行、广播和矢量化等技术因素外,其数组存储顺序和数组视图相互独立也是一个很重要的原因。改变数组结构的操作通常不会改变所操作的数组本身的存储顺序,只是生成了一个新的视图。np.resize()函数是个例外,它不返回新的视图,而是真正改变了数组的存储顺序

    函数功能
    ndarray.reshape()按照指定的结构(形状)返回数组的新视图,不改变原数组
    ndarray.ravel()返回多维数组一维化的视图,不改变原数组
    ndarray.transpose()返回行变列的视图,不改变原数组
    ndarray.resize()按照指定的结构(形状)改变原数组,无返回值
    numpy.roolaxis()翻滚轴,返回新的视图,不改变原数组
    import numpy as np
    
    a = np.arange(12)
    # reshape()函数返回数组a的一个新视图,但不会改变数组a
    b = a.reshape((3, 4))
    print(a.shape)
    print(b is a)
    print(b.base is a)
    # resize()函数没有返回值,但真正改变了数组a的结构
    a.resize([4, 3])
    # 返回多维数组一维化的视图,但不会改变原数组
    print(a.ravel())
    # 返回行变列的视图,但不会改变原始图
    print(a.transpose())
    # 返回行变列的视图,等价于transpose()函数
    print(a.T)
    # 翻滚轴,1轴变0轴
    print(np.rollaxis(a, 1, 0))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    翻滚轴函数有一个很容易理解的应用,就是用它来实现图像的通道分离。下面的代码生成了一个宽为800像素、高为600像素的彩色随机噪声图,使用翻滚轴函数可以将其分离成RGB三个颜色通道。

    from PIL import Image
    import numpy as np
    
    img = np.random.randint(0, 256, (600, 800, 3), dtype=np.uint8)
    print(img.shape)
    # 将图像数据分离成RGB三个颜色通道
    r, g, b = np.rollaxis(img, 2, 0)
    print(r.shape, g.shape, b.shape)
    Image.fromarray(img).show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3. 合并

    NumPy数组一旦创建不能再改变其元素数量。如果要动态改变数组元素数量,只能通过合并或拆分的方法生成新的数组。NumPy仍然保留了append()函数,只不过这个函数不再是数组的函数,而是升级到最外层的NumPy命名空间了,并且该函数的功能不再是追加元素,而是合并数组

    import numpy as np
    
    print(np.append([[1, 2, 3]], [[4, 5, 6]]))
    print(np.append([[1, 2, 3]], [[4, 5, 6]], axis=0))
    print(np.append([[1, 2, 3]], [[4, 5, 6]], axis=1))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    hstack()水平合并函数,vstack()垂直合并函数,dstack()深度合并函数

    import numpy as np
    
    a = np.arange(4).reshape(2, 2)
    b = np.arange(4, 8).reshape(2, 2)
    # 水平合并
    print(np.hstack((a, b)))
    # 垂直合并
    print(np.vstack((a, b)))
    # 深度合并
    print(np.dstack((a, b)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    stack()函数使用axis轴参数指定合并的规则

    import numpy as np
    
    a = np.arange(60).reshape(3, 4, 5)
    b = np.arange(60).reshape(3, 4, 5)
    print(a.shape, b.shape)
    print(np.stack((a, b), axis=0).shape)
    print(np.stack((a, b), axis=1).shape)
    print(np.stack((a, b), axis=2).shape)
    print(np.stack((a, b), axis=3).shape)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4. 拆分

    import numpy as np
    
    a = np.arange(16).reshape(2, 4, 2)
    # 水平方向拆分成2部分
    print(np.hsplit(a, 2))
    # 垂直方向拆分成2部分
    print(np.vsplit(a, 2))
    # 深度方向拆分成2部分
    print(np.dsplit(a, 2))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5. 复制

    改变数组结构返回的是原数组的一个新视图,而不是原数组的副本。浅复制(view)和深复制(copy)则是创建原数组的副本,但二者之间也有细微差别:浅复制(view)是共享内存,深复制(copy)是独享内存

    import numpy as np
    
    a = np.arange(6).reshape((2, 3))
    b = a.view()
    print(b is a)
    print(b.base is a)
    print(b.flags.owndata)
    c = a.copy()
    print(c is a)
    print(c.base is a)
    print(c.flags.owndata)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6. 排序

    NumPy数组有两个排序函数,一个是sort(),另一个是argsort()sort()函数返回输入数组的排序副本,argsort()函数返回的是数组值从小到大的索引号,这两个函数的参数完全一致

    numpy.sort(arr, axis=-1, kind='quicksort', order=None)
    numpy.argsort(arr, axis=-1, kind='quicksort', order=None)
    # arr: 要排序的数组
    # axis: 轴,指定排序的轴,默认值-1,表示没有指定排序轴,返回结果将沿着最后的轴排序
    # kind: 排序方法,默认为"quickstart"(快速排序),其他选项还有"mergesort"(归并排序)和"heapsort"(堆排序)
    # order: 指定用于排序的字段,前提是数组包含该字段
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    import numpy as np
    
    a = np.random.random((2, 3))
    print(a)
    # 返回行内从小到大排序的索引号(列排序),相当于axis=1(最后的轴)
    print(np.argsort(a))
    # 返回行内从小到大排序的一个新数组(列排序)
    print(np.sort(a))
    # 返回列内从小到大排序的一个新数组(行排序)
    print(np.sort(a, axis=0))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    7. 查找

    这里约定查找是返回数组中符合条件的元素的索引号,或返回和数组具有相同结构的布尔型数组,元素符合条件在布尔型数组中对应True,否则对应False。

    • 最大值和最小值查找(下面的代码演示了返回数组中最大值和最小值的索引号,如果是多维数组,这个索引号是数组转成一维之后的索引号)

      import numpy as np
      
      a = np.random.random((2, 3))
      print(a)
      # 最大值的索引号
      print(np.argmax(a))
      # 最小值的索引号
      print(np.argmin(a))
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 非零元素查找(下面代码演示了返回数组中非零元素的索引号)

      import numpy as np
      
      a = np.random.randint(0, 2, (2, 3))
      print(a)
      print(np.nonzero(a))
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 使用逻辑表达式查找(下面的代码演示了使用逻辑表达式查找符合条件的元素,返回结果是一个和原数组结构相同的布尔型数组,元素符合条件在布尔型数组中对应True,否则对应False)

      import numpy as np
      
      a = np.arange(10).reshape((2, 5))
      print(a)
      print((a>3)&(a<8))
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 使用where条件查找(np.where()函数返回数组中满足给定条件的元素和索引号,其结构为元组,元组的第k个元素对应符合条件的元素在数组k轴上的索引号。这句话可以简单理解为,一维数组返回一个元素的元组,二维数组返回两个元素的元组,以此类推。np.where()函数还可以用于替换符合条件的元素

      import numpy as np
      
      a = np.arange(10)
      print(a)
      print(np.where(a<5))
      a = a.reshape((2, -1))
      print(a)
      print(np.where(a<5))
      # 满足条件的元素不变,其他元素乘10
      print(np.where(a<5, a, 10*a))
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    8. 筛选

    筛选是返回符合条件的元素。筛选条件有三种表示方式,一是使用np.where()函数返回的Python元组,二是使用逻辑表达式返回的布尔型数组,三是使用整形数组

    import numpy as np
    
    a = np.random.random((3, 4))
    print(a)
    # 返回大于0.5的元素(使用np.where()函数返回的Python元组)
    print(a[np.where(a>0.5)])
    # 返回大于0.3且小于0.7的元素(使用逻辑表达式返回的布尔型数组)
    print(a[(a>0.3)&(a<0.7)])
    # 返回整形数组指定的项(使用整型数组)
    print(a[np.array([2, 1])])
    a = a.ravel()
    # 返回整型数组指定的项(使用整型数组)
    print(a[np.array([3, 5, 7, 11])])
    # 返回整型数组指定的项(使用整型数组)
    print(a[np.array([[3, 5], [7, 11]])])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    一般而言,灰度图像每个像素的值域范围是[0, 255]。假如用于表现不同灰度的字符集是['','.','-','+','=','*','#','@'],从'''@'表示从白到黑的8个灰度等级。我们需要将每个像素的灰度值分段转换成相应的字符。例如,灰度值小于32的像素用@表示,大于或等于32且小于64的像素用#表示,依次类推直至大于或等于224的像素用''表示

    import numpy as np
    
    img = np.random.randint(0, 256, (5, 10), dtype=np.uint8)
    print(img)
    # 将256级灰度值转为8级灰度值
    img = (img/32).astype(np.uint8)
    print(img)
    # 灰度级字符集
    chs = np.array([' ', '.', '-', '+', '+', '*', '#', '@'])
    # 用整形数组筛选数组元素
    print(chs[img])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    9. 数组I/O

    数据文件格式存储数据格式读写函数
    CSV通用格式loadtxt()savetxt()
    NPY/NPZ单个数组/多个数组load()save()savez()
    import numpy as np
    
    a = np.random.random((15, 5))
    # 将数组a保存成CSV格式的数据文件
    np.savetxt('demo.csv', a, delimiter=',')
    # 打开CSV格式的数据文件
    data = np.loadtxt('demo.csv', delimiter=',')
    print(data.shape, data.dtype)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    import numpy as np
    
    # 存储单个数组文件名
    single_arr_fn = 'single_arr.npy'
    # 存储多个数组文件名
    multi_arr_fn = 'multi_arr.npz'
    lon = np.linspace(10, 90, 9)
    lat = np.linspace(20, 60, 5)
    # 用save()函数把经度数组保存成.npy文件
    np.save(single_arr_fn, lon)
    # 接着用load()函数读出来
    lon = np.load(single_arr_fn)
    # 保存两个数组到一个文件
    np.savez(multi_arr_fn, longitude=lon, latitude=lat)
    # 用load()函数把这个.npz文件读成一个结构
    data = np.load(multi_arr_fn)
    # 查看所有的数组名
    print(data.files)
    # 使用data[数组名]就可以取得想要的数据
    print(data['longitude'])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

  • 相关阅读:
    SpringBoot运维实用篇
    java.awt.FONT
    Linux中安装jdk、tomcat、mysql
    json字符串和对象之间的转换
    python舔狗学习日记第三期【帮女神自动化生成日报】
    Hibernate/JPA 中的id概述
    【数据测试】之迁移(三)
    45:第四章:开发文件服务:6:第三方云存储解决方案【阿里云OSS】;(购买OSS服务;开通服务;创建一个Bucket;)
    MyBatis 篇
    SSO(single sign on)模式 --单点登录三种登录方式
  • 原文地址:https://blog.csdn.net/feizuiku0116/article/details/126705841