Numpy基础包

标量(scalar)

在python中只存在int、float两种数据类型,在numpy中像int就出现了int8、int16、int64等多种数据类型 更多与C数据类型兼容,要想转换python版本 可以使用对应的int()方法,以下是numpy中层次结构的数据类型:

import numpy as np s = np.float64('2') #定义一个64位float数据类型 print(isinstance(s,np.generic)) #判断是否属于generic print(dir(s)) print(s[()]) #当作0维数组进行索引 print(s[...]) # 返回一个ndarry对象
  • 标量作为对象,也有自己的类型、方法 部分同ndarray对象相同,不过前者属性是不可变。
  • 可以作为0维数组进行index

常量

  • 不同于标量,可理解系统标量,numpy包中已经设定了固定value的,一般有无穷大inf 以及对应的判断:isinf 需要的时候去查对应文档:Numpy常量

数组基本运算

  • 加减乘除:针对每个元素进行计算
import numpy as np arr1 = np.array([1,2,3,4]) arr2 = np.array([1,2,3,4]) print(arr1+arr2) # [2 4 6 8] 加法 print(arr1*arr2) # [ 1 4 9 16] 乘法 print(arr1/arr2) # [1. 1. 1. 1.] 除法 print(arr1**2) # [ 1 4 9 16] 数组平方
  • @操作符、dot点乘函数(待续) 或者放在后面在写

+= 、*=操作

在python 类似a+=b 中将会创建一个新对象a,存储在其他的内存地址上,而在numpy中则不会出现

import numpy as np arr1 = np.array([1,2,3,4]) print(id(arr1)) # 4514284288 未修改之前内存地址 arr2 = np.array([1,2,3,4]) arr1 +=arr2 print(arr1) # [2 4 6 8] print(id(arr1)) # # 4514284288 修改之后内存地址

广播(broadcast)

出现了什么问题?

  • 一个新概念的产生必然是为了解决某个问题,所以来看一下在numpy中出现了什么状况:两个形状不同的数组之间如何求和?

    import numpy as np arr1 = np.array([1.0,2.0,3.0]) a= 2 print(arr1+2) # [3.0 4.0 5.0],将数组里的每个元素均+2

numpy广播做了什么

  • 将较小数组的维度沿着另外一个对应数组的维度进行缩放:在arr1中为1D数组,2是一个int数据,广播机制会将2复制知道长度与arr1中的长度匹配

多维数组该如何处理:

  • 从被操作的各个数组的形状元组尾端开始,确保他们相同或者其中一个维度是1,下例中:两者shape元组中尾端均是3,arr1维度为1对应的是arr2维度是4,则广播机制可以扩充arr1只有1个元素的轴上到4个元素,即arr2对应轴上元素的个数
import numpy as np arr2 = np.array([[ 0.0, 0.0, 0.0], # shape = (4,3) 2D [10.0, 10.0, 10.0], [20.0, 20.0, 20.0], [30.0, 30.0, 30.0]]) arr1 = np.array([1.0,2.0,3.0]) # shape = (3,) 1D 强制arr1最小维度ndmin=2的时候 维度其实是(13--2D,知道这一点会对后面有帮助,尤其是stack操作的时候 print(arr2+arr1) 输出: [[ 1. 2. 3.] [11. 12. 13.] [21. 22. 23.] [31. 32. 33.]]

  • 从shape元组尾端开始看元素个数不同,将会报错;下例中 a尾端第一个是4,b对应的是3
import numpy as np a = np.array([0.0, 10.0, 20.0, 30.0]) #shape = (4,) b = np.array([1.0, 2.0, 3.0]) # shape=(3,) print(a+b) # ValueError: operands could not be broadcast together with shapes

广播机制进一步讲解

  • numpy中创建数组有一个方法:array(object, dtype=None, *, copy=True, order='K', subok=False, ndmin=0)其中ndmin表示的是要创建的数组对象最小维度,如果obj提供维度小于ndmin,则会在left_pading 值为:1
a= np.array([1,2,3],ndmin=4) print(a.shape) # (1, 1, 1, 3) ndmin默认为0,要求最小维度是4,则默认shape从(3,)成为(1,1,1,3)进行1的左填充。
  • array中最小维度与广播机制有何关系呢?其实可以看到在上述示意图中存在很多缩放,我们也可以当作维度扩充
a= np.array([1,2,3],ndmin=4) print(a.shape) # (1, 1, 1, 3),ndmin默认为0,此时a.shape = (3,)
  • 广播机制可以用以下一段伪代码解释:
Inputs: array A with m dimensions; array B with n dimensions p = max(m, n) if m < p: left-pad A's shape with 1s until it also has p dimensions else if n < p: left-pad B's shape with 1s until is also has p dimensions result_dims = new list with p elements for i in p-1 ... 0: A_dim_i = A.shape[i] B_dim_i = B.shape[i] if A_dim_i != 1 and B_dim_i != 1 and A_dim_i != B_dim_i: raise ValueError("could not broadcast") else: result_dims[i] = max(A_dim_i, B_dim_i)
  • 举例说明:
import numpy as np a = np.array([0.0, 10.0, 20.0, 30.0]).reshape(4,1) #shape =(4,1) b = np.array([1.0, 2.0, 3.0]) #shape =(3,) # a b 中 最大维度是为a的维度2,b仅有一个,所以对b进行左填充变为(1,3),比较a中尾端维度上的元素1 与b对应的3,满足尾端中有一个轴上为1条件,a中4对应的是b中的1,同样满足前者条件。 case1:------------------- (4, 3) (4, 3) == padding ==> == result ==> (4, 3) #取每个轴上最大的元素个数 (3,) (1, 3) case2:------------------- (3,) (1, 1, 3) == padding ==> == result ==> (5, 4, 3) #取每个轴上最大的元素个数 (5, 4, 3) (5, 4, 3) case3:------------------- (5,) (1, 1, 5) == padding ==> ==> error (5 != 3) #尾端中同一轴上存在不同元素个数,报错 (5, 4, 3) (5, 4, 3) case4:------------------- (5, 4, 3) (1, 5, 4, 3) == padding ==> == result ==> (6, 5, 4, 3) ##取每个轴上最大的元素个数 (6, 5, 4, 3) (6, 5, 4, 3) case5:------------------- (5, 4, 1) == no padding needed ==> result ==> (5, 4, 3) #因两个数组之间维度已相同,无须再进行填充 (5, 1, 3)

索引

基本索引

python中的sequence[index]扩展到了numpy中,python中作为索引的有integer、slice对象 slice(value1,value2,value3)以及冒号[start:stop:step],在numpy中存在以下几种类型:

import numpy as np x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) print(x[-3:6:-2]) # [7] 可以转换为负数索引 [-3:-4:-2] 最后仅剩 index= -3 print(x[-2:10]) # [8 9] 同理 [8:10] index = 8 and index =9
import numpy as np x =np.array([[[1],[2],[3]], [[4],[5],[6]]]) print(x[1,1,0]) #分别代表轴0(2),1(3),2(1) print(x[1]) # 仅列出轴0,未列出其他维度则代表包含剩余所有维度,此情况属于:索引的数量少于轴的数量的时候,索引被视为后面紧跟着 : 的多个实例,用于表示剩余轴。所以x[1]等同于x[1,...]。 print(x[1,:2]) # 列出轴0[1],轴1前两个元素 y =np.array([1,2,3,4]) print(y[1]) # i shape = () 数组标量 print(y[1:2]) #i:i+1 shape=(1,) 1D数组

newaxis

newaxis 等同于None,在newaxis所在索引中的位置添加1个维度

x = np.array([1,2,3,4,5]) print(x[np.newaxis,:].shape) # 由1D数组(5,)变为shape=(1,5) print(x[None,:].shape) #与newaxis等同

Ellipsis

Ellipsis 等同于(...),又名3个点号(...),为了扩展冒号:代表产生完整索引元组所需的冒号

import numpy as np x = np.array([[[1],[2],[3]], [[4],[5],[6]]]) print(x.shape) #(2, 3, 1) print(x[...,0]) # [[1 2 3] # [4 5 6]] print(x[Ellipsis,0].shape) # shape = (2, 3) 为何不是(231) 在于0 与(i 与 i:i+1)类似,维度少了1 案例2: x = np.arange(24).reshape(4,3,2,1) print(x[...,0,:]) #(4, 3, 1) print(x[:,:,0,:].shape) #等同于上面 ,在轴2已经仅包含一个标量,没有维度了,所以shape=(431print(x[:,:,0:1,:].shape) #0:1是一个切片,进行切片操作以后在轴3处产生维度1,所以最后shape=(4311#print(x[:,:,0:1,:]) 等同于print(...,0:1,:) 即x总共有4个轴,其中(0:1)、:分别代表轴2、轴3,Ellipsis代表剩余的两个轴

注意

通过x[index] =value,为了确保赋值正确,需要确保index对应shape与value对应shape符合广播规则

x = np.array([[[1],[2],[3]], [[4],[5],[6]]]) print(x.shape) #(2, 3, 1) x[0:2,0,0] =[100,23] #【10023】shape=(2,) 【0:2,0,0】对应 shape=(211)符合broadcast,如果换成x[0:2] =[100,23] 将会报错 valueerror:can‘t shape (2) into shape (2,3,1) print(x) # [[[100] [ 2] [ 3]] [[ 23] [ 5] [ 6]]]

整数索引

  • 纯整数索引各自所在的位置分别代表各对应的轴,同时还可以与切片进行组合使用,切片获得的是数组的view,高级索引获得的是数据的副本。
import numpy as np arr =np.array([1,2,3,4,5,6]).reshape(3,2,1) print(arr[1,1,0]) #1 代表轴0第2个元素,1代表轴1第2个元素,0代表轴2第1个元素 print(arr[1,:,:]) # 表示所属轴0的第2个位置上所有的元素

高级索引(数组索引、布尔索引)

  • 索引中使用了ndarray对象即数组对象的时候,其中ndarray数据类型是布尔类型、整数类型将会触发高级索引。

数组索引

  • x[obj] 当obj对象是ndarray的时候:
import numpy as np arr = np.arange(35).reshape(5,7) print(arr) row = np.array([1,2,3]) column = np.array([2,3,4]) print(arr[row,column]) #分别取第2行、3列,第3行第4列,第4行第5列 print(arr[[1,2,3],[2,3,2]]) #与上述等同 两个形状相同 均为(3,),符合广播规则 #print(arr[[1,2,3],[2,3]]) # IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (3,) (2,) c= np.array([1,2]).reshape(2,1) d= np.array([1,2,3]) print(arr[d,c]) #d的形状为(3,),c为(2,1) 符合广播规则,并且index没有超出arr的界限
  • 高级索引相邻arr[ind1,ind2,:]:
import numpy as np arr = np.arange(30).reshape(6,5) #高级索引被切片相邻 arr[ind1,ind2,:] row =np.array([1,2,3,4,5]).reshape(5,1) # shape(3,1) print(arr[row,1:3].shape) # 高级索引与切片 :其中轴0 =6这一维度被row shape=(5,1)索引子空间替代,轴1 shape=(5,)经切片操作1:3 仅有2个元素,所以产生的结果形状为(5,1,2) print(arr[row,1:3]) #shape=(5, 1, 2) arr = np.arange(30).reshape(3,5,2) print(arr) r = np.array([0,1]).reshape(2,1) c = np.array([1,2,3]).reshape(1,3) print(arr[r,c].shape) # (2, 3, 2) 其中r shape(2,1)索引子空间替代(3,),(1,3)替代(5,),轴2上的空间未使用。轴2上的维度在索引中未列出,相当于arr[r,c,:]
  • 高级索引被切片相隔arr[ind1,:,ind2]:
import numpy as np arr = np.arange(30).reshape(6,5) arr = np.arange(30).reshape(5,2,3) r = np.array([0,1]).reshape(2,1) c = np.array([1,2,0]).reshape(1,3) print(arr[r,:,c].shape) # shape = (2, 3, 2)索引数组存在 轴0以及轴2处,因为在高级索引中numpy机制是整体广播,r、c广播以后形状为(2,3),广播以后因为存在两个索引数组位置,所以numpy中将其放置在形状元组的开头 #r = np.array([0,1]) c = np.array([1,2,0]) 报错:IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (2,) (3,)
  • 高级索引与ix_
    • numpy.ix_方法是使用N个1D数组形成网格索引,类似于渔网,确定竖条、横条就可以确定每个点的位置,这个方法也是同一个道理,在通用函数中会详细讲解。
import numpy as np arr =np.arange(63).reshape(7,9) ''' [[ 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 32 33 34 35] [36 37 38 39 40 41 42 43 44] [45 46 47 48 49 50 51 52 53] [54 55 56 57 58 59 60 61 62]] ''' #取对角线元素 #@1 row = np.array([[0,0],[6,6]]) column = np.array([[0,8],[0,8]],dtype=np.intp) #print(arr[row,column]) #@2 使用广播 row = np.array([[0,6]],dtype=np.intp).reshape(2,1) column = np.array([0,8],dtype=np.intp) # row 形状(2,1) 与 column形状 (1,2)符合广播 #@3 使用newaxis 可以提升维度 row = np.array([0,6],dtype=np.intp) column = np.array([0,8],dtype=np.intp) print(arr[row[:,np.newaxis],column]) #加入np.newaxis以后 row的形状变为(2,1)成为一个二维数组,剩余处理与上述类似 #@4 使用ix_函数 作用类似于将row变形为(2,1),column形状保持不变,row与column形成交叉网格,确定对角的点的索引 row = np.array([0,6]) column = np.array([0,8]) index =np.ix_(row,column) #np.ix_()的作用同样像上述处理row、column,从以下打印结果可以看出 print(arr[index]) print(index) '''(array([[0], [6]]), array([[0, 8]])) '''

索引的复制与切片的视图

  • 切片使用的是原数组的视图 view,共享同一段内存地址的内容,在对y进行赋值的时候,需确保赋值对象与被赋值对象shape符合广播,否则将会报错。
    + 以下修改y,修改以后的值同样会反映到数组x对象对应位置上去。

  • 形状不符合广播

import numpy as np arr =np.arange(20).reshape(4,5) ''' [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19]] ''' sice = arr[1:3] sice[:]=np.array([100,200]).reshape(2,1) #右边shape=(2,1),而左边切片对象是shape=(2,5) 符合,应用赋值以后将会同步修改arr数组第2、3行的value print(sice) print(arr) ''' [[100 100 100 100 100] [200 200 200 200 200]] [[ 0 1 2 3 4] [100 100 100 100 100] [200 200 200 200 200] [ 15 16 17 18 19]] '''
  • 高级索引使用原数组数据的副本,修改了来自arr高级索引创建而来的arr1,原数组arr数据并未发生改变。

索引额外补充

import numpy as np arr = np.arange(32).reshape(8,4) ''' [[ 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]] ''' print(arr[[1,5,7,2]][:,[0,3,1,2]]) #等同于下面数组k 上述先生成临时数组arr[[1,5,7,2]] 即k,然后在数组k中进行索引 index = [:,[0,3,1,2]] k =arr[[1,5,7,2]] print(k) ''' [[ 4 5 6 7] [20 21 22 23] [28 29 30 31] [ 8 9 10 11]] ''' print(k[:,[0,3,1,2]]) ''' [[ 4 7 5 6] [20 23 21 22] [28 31 29 30] [ 8 11 9 10]] '''

布尔索引

  • 数组中数据类型是Bool_,表达式或True、 False,布尔索引中最需要注意一点:布尔数组与被索引数组对应尺寸一定要相同,否则将会报错:IndexError: boolean index did not match indexed array along dimension
arr = np.arange(32).reshape(4,8) value = arr>15 print(value) #shape=(4,8) print(arr[value]) #布尔数组value 与数组arr维度相同,生成一维数组 # output [16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31] k = np.array([True,False,True,False]).reshape(4,1) print(arr[k]) # ValueError: operands could not be broadcast together with shapes (8,4) (2,16) arr = np.arange(32).reshape(8,4) value = arr>15 k = value.reshape(2,16) #print(arr[k]) # ValueError: operands could not be broadcast together with shapes (8,4) (2,16) m = np.array([True,False,True,False]).reshape(1,4) #在轴0处,一个尺寸是8一个尺寸是1,引发索引错误,不然的话剩余7个该如何处理 没有明确的False、True #print(arr[m]) # IndexError: boolean index did not match indexed array along dimension 0; dimension is 8 but corresponding boolean dimension is 1 arr = np.arange(32).reshape(4,8) m = np.array([True,False,True,False]).reshape(4,) print(arr) print(arr[m]) # 等同于取arr数组中第1、3行
  • 布尔数组维度低于被索引数组维度,类似于整数索引中x[i,...],默认了剩余所有维度
arr = np.arange(32).reshape(4,8) m = np.array([True,False,True,False]).reshape(4,) print(arr) print(arr[m]) # 等同于取arr数组中第1、3行 print(arr[m,...]) #与arr[m] 等同
  • 不同于整数索引数组,布尔数组只显示为True的元素,根据True元素所在布尔数组中的索引位置,对应匹配被索引数组的value
  • 以下布尔数组为(2,3)形状与x数组对应尺寸相同,此时布尔数组b的维度小于x数组,等同于x[b,...],补齐x数组剩余所有维度
  • 布尔数组b中存在4个True,x中未使用维度为5,最后经过布尔数组索引后,形成新的维度为(4,5)
import numpy as np x = np.arange(30).reshape(2,3,5) ''' [[[ 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]]] ''' b = np.array([[True, True, False], [False, True, True]]) #4个True元素对应位置分别为(0,0),(0,1),(1,1),(1,2),映射到x数组即:(0,0,:),(0,1,:),(1,1,:),(1,2,:) print(b.shape) # (2, 3) print(x[b]) ''' [[ 0 1 2 3 4] [ 5 6 7 8 9] [20 21 22 23 24] [25 26 27 28 29]] ''' # 换另外一种方式去理解 则采取将低维的布尔数组提升与被索引数组同维度: b = np.array([[True, True, False], [False, True, True]]).reshape(2,3,1) print(b) # (2, 3) ''' #x[b] 则代表分别取轴0的第一个元素处 、轴1的第 1、2行,以及轴0第2个元素处、轴1的第2,3行 [[[ True] [ True] [False]] [[False] [ True] [ True]]] '''

合并与分割

concatenate

  • concatenate((arr1,arr2,...),axis=)最主要是合并数组、以及轴两个参数,作为通用合并函数
import numpy as np a = np.array([[1, 2], [3, 4]]).reshape(4,1) b = np.array([[5, 6,3,4,4,5,3,3]]).reshape(2,4) arr = np.concatenate((a, b),axis=None)# axis 默认为0,为None的时候将会将数组压扁变为1D数组合并,在flatten基础上在合并 print(arr) #[1 2 3 4 5 6] a = np.array([[1, 2], [3, 4]]).reshape(4,1) b = np.array([[5, 6,3,4,4,5,3,3]]).reshape(2,4) #arr1 = np.concatenate([a,b],axis=0) #沿着轴0方向进行合并,那么势必在轴1上数组a、尺寸要匹配,以下就报错,提示在轴1方向,a的尺寸是4,b的尺寸是2 #print(arr1) #ValueError: all the input array dimensions for the concatenation axis must match exactly, #but along dimension 1, the array at index 0 has size 4 and the array at index 1 has size 2 a = np.array([[1, 2], [3, 4]]).reshape(4,1) b = np.array([[5, 6,3,4,4,5,3,3]]).reshape(8,1) arr1 = np.concatenate([a,b],axis=0) print(arr1) ''' [[1] [2] [3] [4] [5] [6] [3] [4] [4] [5] [3] [3]] '''
  • 多维数组合并:
import numpy as np a = np.array([[1, 2], [3, 4]]).reshape(1,1,4) b = np.array([[5, 6,3,4,4,5,3,3]]).reshape(1,4,2) arr = np.concatenate((a,b),axis=0)#沿着轴0合并,对应轴轴 1以及轴2尺寸需相互匹配 print(arr)# 会提示报错,因为在轴1 以及轴2处,数组a、b尺寸分别是(14)(42) 不匹配

分割split

  • split是将concatenate合并以后的数组进行分割,同合并一样,分为通用,vsplit、hsplit,需注意的是 split将数组拆分为子数组,并将其作为数组的视图
a = np.array([[1, 2], [3, 4]]).reshape(1,4) b = np.array([[5, 6,3,4,4,5,3,3]]).reshape(2,4) arr = np.concatenate((a,b),axis=0) ''' [[1 2 3 4] [5 6 3 4] [4 5 3 3]] ''' # axis默认为轴0 第一根轴,中间[1] 为参数indices_or_sections,整数 N:将数组arr均分 N等份,否则将报错: split does not result in equal division. #为1D数组ndarray [m,n] 相当于要分割数组arr 的三部分:arr[:m] 、arr[m:n]、arr[n:],当然情况是在axis指定轴上,本例中[1] 相当于np.array([1])则将数组分为两部分,arr[:1] 以及arr[1:] arr_split = np.split(arr,[1],axis=0) print(arr_split) # 返回由ndarray数组组成的列表 ''' [array([[1, 2, 3, 4]]), array([[5, 6, 3, 4], [4, 5, 3, 3]])] ''' arr_split[0][0][1] =1000 #第一个【0】取出分割后数组的第一个子数组,然后对该2D子数组进行第1行、第2列进行索引赋值1000 print(1,arr_split[0]) # print(2,arr) # 原数组在子数组被赋值的对应位置也被修改了为1000,分割后的子数组成为arr的view 视图 ''' 1 [[ 1 1000 3 4]] 2 [[ 1 1000 3 4] [ 5 6 3 4] [ 4 5 3 3]] '''

vstack、vsplit

  • Vstack(tuple)中文其实是沿着轴0方向去合并,等同于 np.concatenate((arr1,...),axis=0),前者处理不超过3个dimension的数组效率高,
a = np.array([[1, 2], [3, 4]]).reshape(1,4,1) b = np.array([[5, 6,3,4,4,5,3,3]]).reshape(2,4,1) arr = np.concatenate((a,b),axis=0) print(np.vstack((a,b))) #接受一个包含数组的元组 ''' [[[1] [2] [3] [4]] [[5] [6] [3] [4]] [[4] [5] [3] [3]]] '''
  • vsplit 可以还原由vstack合并的数组,默认为第一根轴的split的另一种方法
a = np.arange(20).reshape(4,5) # vsplit(ary, indices_or_sections) k = np.vsplit(a,np.array([2,7])) #等同于np.split(a,np.array([2,7],axis=0)) ,将数组分割为a[:2],a[2:7],a[7:]三部分,在轴0上仅有4个元素可分割,剩余用空数组进行填充 print(k) ''' [array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]), array([[10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]), array([], shape=(0, 5), dtype=int64)] '''

hstack、hsplit

a = np.arange(20).reshape(5,4) b = np.arange(10).reshape(5,2) # hstack 合并 arr = np.hstack((a,b)) #按列合并 相比较与vstack 按行合并,要求轴0上各数组尺寸需匹配 print(arr) arr2 = np.concatenate((a,b),axis=1) #等同于通用合并方法 沿着第二根轴 #print(arr2) ''' [[ 0 1 2 3 0 1] [ 4 5 6 7 2 3] [ 8 9 10 11 4 5] [12 13 14 15 6 7] [16 17 18 19 8 9]] ''' # 输入数组是1D数组 a = np.arange(20) #shape =(20,) b = np.arange(10) #shape =(10,) print(np.hstack((a,b))) #对两者在对应轴上尺寸无要求 # [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 0 1 2 3 4 5 6 7 8 9] # hsplit 分割 等同于 np.split(arr,axis=1) 按列分割,沿着数组arr第二根轴,可以分割由hstack合并而来的数组 arr_1 = np.arange(20).reshape(4,5) print(np.hsplit(arr_1,[2,3])) # 在轴1方向上 分别取arr[:2],arr[2:3]以及arr[3:] 三个作为arr_1视图的子数组 ''' [array([[ 0, 1], [ 5, 6], [10, 11], [15, 16]]), array([[ 2], [ 7], [12], [17]]), array([[ 3, 4], [ 8, 9], [13, 14], [18, 19]])] '''

dstack、dsplit

  • 因为dstack是处理沿着第三根轴的合并,所以需关注的是dstack对于1D 数组(N,)经过reshape (1,N,1),二维数组(M,N)reshape以后成为(M,N,1)
import numpy as np # 2D数组 a = np.array([[10,100],[8,9]]) #2D数组会被reshape成shape=(m,n,1) b= np.array([[1,2],[2,3]]) #(m,n,1) print(np.dstack((a,b))) ''' [[[10 1] [100 2]] [[8 2] [9 3]]] ''' #1D数组 arr1 =np.array([1,2,3]) # 1D数组会被reshape成shape=(1,n,1) arr2 = np.array([4,5,6]) #(1,n,1) print(np.dstack((arr1, arr2))) # shape =(1,3,2) 其中1,3均为原数组合并之前的维度,2是有两个合并数组新生成维度1之后得来 ''' [[[1 4] [2 5] [3 6]]] ''' #dsplit 等同于split(array,indices_or_sections,axis=2) 沿着第三根轴对array数组进行分割,可以还原由dstack创建而来数组,不过此时生成的数组要经过reshape才可以完全还原 arr = np.dstack((arr1, arr2)) print(np.dsplit(arr,2)) #integer =2 在 ''' [array([[[1], [2], [3]]]), array([[[4], [5], [6]]])] ''' print(np.split(arr,2,axis=2)) #与dsplit等同

stack 堆叠

  • 其实stack也可以看作是合并,不过不同于concatenate,stack是自己创造出一根新轴,然后沿着该轴进行合并,部分源代码如下:
import numpy as np a = np.arange(20).reshape(5,4) b = np.arange(20).reshape(5,4) ''' stack方法部分关键源码:arrays是将需要合并数组都存储在该列表对象里 result_ndim = arrays[0].ndim + 1 确定最后结果数组的维度要比原来数组维度多一个 axis = normalize_axis_index(axis, result_ndim) # 确定指定轴是结果数组中第几根轴,存在负数,所以规范化一下 sl = (slice(None),) * axis + (_nx.newaxis,) # axis =1 (None,) # 相当于[:,newaxis] 或 [:,new.axis,...] 切记newaxis放置在第二个维度后面 expanded_arrays = [arr[sl] for arr in arrays] return _nx.concatenate(expanded_arrays, axis=axis, out=out) # 进行合并 沿着新轴 ''' print(np.stack((a,b),axis=2)) ''' [[[ 0 1 2 3] [ 4 5 6 7] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15] [16 17 18 19]] [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15] [16 17 18 19]]] ''' # stack 等同于先将需要合并的数组进行维度提升,然后在新增维度上进行concatenate a = np.arange(20).reshape(5,4).reshape(5,4,1) #原数组shape = (5,4),因为是需要在第三根轴即 新轴进行合并 b = np.arange(20).reshape(5,4).reshape(5,4,1) #同上 print(np.concatenate((a,b),axis=2)) ''' [[[ 0 1 2 3] [ 0 1 2 3]] [[ 4 5 6 7] [ 4 5 6 7]] [[ 8 9 10 11] [ 8 9 10 11]] [[12 13 14 15] [12 13 14 15]] [[16 17 18 19] [16 17 18 19]]] '''

ix_

  • 利用多个序列构建网格,可以用于索引,numpy.ix_(*args) 接收N个1D数组
arr = np.array([[4,5,6],[6,7,9]]) print(arr) a = np.array([0,1]) b = np.array([1,2]) ''' def ix_(*args): 其中k代表一维数组中各item的index值 0,1,2,3,4,由enumerate(arga)生成 if issubdtype(new.dtype, _nx.bool_): new, = new.nonzero() new = new.reshape((1,)*k + (new.size,) + (1,)*(nd-k-1)) # 每个args在*args中所在索引位置的维度为 本身维度即(new.size,),在这之前或之后均是由1来填充维度 out.append(new) return tuple(out) ''' ix = np.ix_(a,b) print(ix) ''' (array([[1], [2]]), array([[0, 1]])) #其中数组a shape(2,1),数组b shape(1,2) ''' print(arr[ix]) # 使用ix 返回的有数组组成的元组作为索引,其中a、b会进行广播,得到第2、3行与第1、2列交叉形成的网格索引,共4个交点 ''' [[5 6] [7 9]] '''

r_

  • r_不是作为一个函数,使用方法有以下三种形式:
    • r_['r'、r_['c']: 返回矩阵
print(np.r_['c',[1,2,3], [4,5,6],[7,8,9]]) #输入数组均是1维数组,利用concatenate将各个数组默认第一根轴进行合并,然后matrix进行矩阵(可看作特殊2D数组)转化,然后transpose,生成3行一列矩阵 ''' [[1] [2] [3] [4] [5] [6] [7] [8] [9]] ''' print(np.r_['r',[1,2,3], [4,5,6],[7,8,9]]) #输入数组均是1维数组,利用concatenate将各个数组默认第一根轴进行合并,然后matrix进行矩阵(可看作特殊2D数组)转化。 ''' [[1 2 3 4 5 6 7 8 9]] '''
  • r_[str,array1,array2,...arrayn],其中str参数是可包含3个数字的字符串,‘a,b,c’,b代表生成的result最小维度ndmin,c代表各array1,、array2,...的shape元组在result中shape元组中的位置,a代表最后经过处理的各array沿着轴a代表的轴进行concatenate
import numpy as np a = np.r_['0,4,-2',[1,2,3],[2,3,4]] #最后生成有4个维度的数组 print(a) #shape (2,1,3,1) ''' [[[[1] [2] [3]]] [[[2] [3] [4]]]] '''
  • r_代码拆解
    r_带有字符串的时候,功能可以分解成以下几个功能:分别将各数组按照第2个参数扩展维度,然后按照第三个参数设定进行转置,然后在第一个参数指定轴进行合并
    k = np.array([1,2,3], copy=False, subok=True,ndmin=4) #ndmin=4 ----(1,1,1,3)---经过c参数以后--(-2代表数组的shape元组位于result对应的倒数第二位)即(1,1,3,1)
    arr= k.transpose((0,1,3,2)) #注意了 经过c参数以后 相当于将轴3与轴2元素对调,故而得到(01,3,2)然后进行转置
    k1 = np.array([2,3,4], copy=False, subok=True,ndmin=4) # shape--(1,1,1,3)--同理 (1,1,3,1)
    arr1 = k1.transpose((0,1,3,2))
    print(np.concatenate((arr,arr1),axis=0))

  • 图示r_带有字符串各个参数的作用

  • 附上r_部分源代码,方便以后查询:

''' item_ndim =1 ndmin=2 if trans1d != -1 and item_ndim < ndmin: k2 = ndmin - item_ndim # k2 = 3-2= 1 k1 = trans1d # -2 if k1 < 0: k1 += k2 + 1 # k1 =-2+1+1 =0 defaxes = list(range(ndmin)) # [0,1,2] axes = defaxes[:k1] + defaxes[k2:] + defaxes[k1:k2] # [:0]+[1:]+[0:1] (1,2,0) 2rd的作用 newobj = newobj.transpose(axes) # 转置 ---------[1,0] (3,1) 3rd的作用 ''' ''' a = np.r_['0,4,-2',[1,2,3],[2,3,4]] 3个参数 代表的是放置的数组的起始位置 ------- 0 第一个位置 ------- -1 代表最后一个位置 第三个参数 【 第二个参数 -放置数组的维度】 N=4 n=1 if 3rd是正数: N代表放置数组的维度 个数 1. array规定了最小维度的时候,索引数组如下:(0123,N-n....,N-1)list(range(4))(0123 2. [:3rd]+[N-n:] +[3rd:N-n] 指代的是将 N维数组根据索引取出元素 然后进行拼接 3. 此时(N-n...,N-1)的位置就是3rd elif 3rd为负数的时候: 3rd 3rd负数的时候 -1代表最后一个 即 N-1,(0123,N-n....,N-1) 将(N-n....,N-1)当作一个元素,位置是3rd x+ [N-n....,N-1] + y N-n+1+3rd (其中N-n计算除去这n个剩余元素,然后将n个元素当成一个元素得出所有总共元素 然后其+3rd就等于 这n个元素所处的正数索引)
  • r_中包含slice对象,相当于np.arrange(start,stop,step)
k = np.r_[1:4:1,[0]*3, 5,6] print(k) # [1 2 3 0 0 0 5 6] 生成一维数组

c_

  • c_[] 相当于 r_['-1,2,0']
#第2 3 参数如下: ''' '-1,2,0' arr1以及arr2 要转置shape (1,0) (1,0) ''' arr1 = np.array([1,2,3],ndmin=2) arr2 = np.array([4,5,6], ndmin=2) k1 = arr1.transpose((1,0)) print(1,k1) k2 = arr2.transpose((1,0)) print(2,np.concatenate((k1, k2),axis=-1)) # 在第一个参数 指示轴1处进行合并 #print(3,np.c_[arr1,arr2]) print(3,np.c_[np.array([1,2,3]),np.array([4,5,6])]) #与下面等同 print(4,np.r_['-1,2,0',[1,2,3],[4,5,6]])

重复 (repeat)

  • 对数组的元素层面进行操作,repeat((a, repeats, axis=None))
import numpy as np arr = np.array([[1,2,3],[4,5,6]]) #repeats是整数 print(arr.repeat(3)) # [1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6] 此时没有axis设定 repeat将会将arr压扁以后作为输入数组,然后输出压扁以后的数组 print(arr.repeat(3,axis=1)) #沿着轴1方向,数组进行广播shape =3*3 =9 ''' [[1 1 1 2 2 2 3 3 3] #轴0方向仍然与输入数组arr相同shape [4 4 4 5 5 5 6 6 6]] print(np.array([[1, 2, 3, 4, 5, 6]]).repeat(2,-1).shape) # (1, 12),-1代表沿着最后一根轴进行重复,2代表重复次数 ''' #repeats是整数数组: np.repeat(x, [1, 2], axis=0) ''' array([[1, 2], #沿着轴0对元素进行复制,第一行复制1次,第二行复制2次 [3, 4], [3, 4]]) ''' x = np.array([[1,2],[3,4]]) print(np.repeat(x, 2,1)) # 2代表reps,1代表轴1 沿着第二根轴进行复制 ''' [[1 1 2 2] [3 3 4 4]] '''

拼接(tile)

  • 内部实现也是通过重复 repeat,类似于广播机制,可用于创建数组 通过给定次数重复
# np.tile(a,reps) # arr.ndim > d (d为reps长度) arr = np.array([[1,2,3],[4,5,6]]).reshape(1,2,3) #(1,2,3) reps长度仅有2,等同于将reps左填充为【1,2,3】 reps = [2,3,4] #print(np.tile(arr,reps)) #在第1根轴 复制1次,第2根轴复制2次,第三根轴复制3次 ''' [[[1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6] [1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6]]] ''' # arr.dim <d (d为reps长度) arr = np.array([[1,2,3],[4,5,6]]) # 维度为2, reps长度为3 所以会将arr数组shape提升到3D shape=(1,2,3) reps = [2,3,4] print(np.tile(arr,reps)) # #在第1根轴 复制2次,第2根轴复制3次,第三根轴复制4次 ''' [[[1 2 3 1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6 4 5 6] [1 2 3 1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6 4 5 6] [1 2 3 1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6 4 5 6]] [[1 2 3 1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6 4 5 6] [1 2 3 1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6 4 5 6] [1 2 3 1 2 3 1 2 3 1 2 3] [4 5 6 4 5 6 4 5 6 4 5 6]]] '''

排序(sort)

  • sort对数组进行排序 ,沿着轴或者按照指定字段进行排序
arr = np.array([10,4,2,100,45,2]) arr.sort() #print(arr) # [ 2 2 4 10 45 100] 默认按升序排序 #print(arr[::-1]) # [100 45 10 4 2 2] 逆序,此时在[ 2 2 4 10 45 100]基础上从右往左取数,stpe=-1 # numpy.sort(a, axis=- 1, kind=None, order=None) arr1 = np.array([10 ,1, 452, 32, 4,9, 60, 70, 8, 5]).reshape(2,5) # [0 1 2 3 4 5 6 7 8 9] #print(arr1) ''' [[ 10 1 452 32 4] [ 9 60 70 8 5]] ''' # ndarray.sort() arr1[1,:].sort() # 对arr1数组第2行进行排序,然后打印原arr1数组,发现数组发生改变 #print(arr1) ''' [[ 10 1 452 32 4] [ 5 8 9 60 70]] ''' #np.sort() arr2 = np.array([10 ,1, 452, 32, 4,9, 60, 70, 8, 5]).reshape(2,5) #print(arr2) # 同arr1 np.sort(arr2[1,:]) #print(arr2) # np.sort() 排序对原数组不会直接进行改动 ''' [[ 10 1 452 32 4] [ 9 60 70 8 5]] ''' # 对于轴 的设定 arr3 = np.array([10 ,1, 452, 32, 4,9, 60, 70, 8, 5]).reshape(2,5) print(np.sort(arr3)) # np.sort()中 axis 默认对最后一根轴进行排序 即axis=-1,当axis=None的时候,会将数组压扁在尽心排序 ''' [[ 1 4 10 32 452] [ 5 8 9 60 70]] ''' #sort()中第三个参数order,当数组中存在字段的时候,主要针对结构数组,按照指定字段进行排序 dtype = [('name', 'S10'), ('height', float), ('age', int)] values = [('Arthur', 1.8, 41), ('Lancelot', 1.9, 38),('Galahad', 1.7, 38)] a = np.array(values, dtype=dtype) #创建结构数组 np.sort(a, order='height') #先按照字段height进行排序,然后是name字段 ''' array([('Galahad', 1.7, 38), ('Arthur', 1.8, 41), ('Lancelot', 1.8999999999999999, 38)], dtype=[('name', '|S10'), ('height', '<f8'), ('age', '<i4')]) '''

argsort

  • 可用作索引index,返回经过sort排序的数组的索引,换一句话说用各数字在原数组中索引来代替在排序result中本该由该数字本身放置位置的呈现
x = np.array([[100, -2], [4, 2]]) # argsort(arr, axis=- 1, kind=None, order=None) ind = np.argsort(x, axis=0) #argsort 换句话说用各数字在原数组中索引来代替在排序result中本该由该数字本身放置位置的呈现 print(ind) ''' [[1 0] 轴0方向:1代表是数组x中-2的索引,0代表100在原数组x中索引 按照sort应为【-2,100】 现在使用各value对应索引替代 [0 1]] ''' # 使用order位置参数,按照字段排序 x = np.array([(100, 0), (100, 10000)], dtype=[('x', '<i4'), ('y', '<i4')]) a = x.argsort(order=('x', 'y')) print(a) # [0 1] print(x[a]) # [(100, 0) (100, 10000)] # 当 argsort返回的是1D数组 arr = np.array([1,3,5,2]) indices = arr.argsort() # [0,3,1,2] print(arr[indices]) # [1 2 3 5] 返回经过sort以后的数组arr # 当argsort返回的是多维数组的时候,无法像上面处理了,涉及高级索引,indices返回数组会被默认为数组arr第一个维度上的索引 # 仍然需要得到经过排序以后的数组arr,使用take_along_axis(arr,indices,axis)

take_along_axis

  • 根据在对应轴上的索引值返回数组
a = np.array([1,11,10,9,2,4,7,8,6,3,4,5]).reshape(2,3,2) indices = np.argsort(a,axis=1) ''' [[[0 2] [2 1] [1 0]] [[2 1] [1 2] [0 0]]] ''' take = np.take_along_axis(a, indices, axis=1) #argsort 或者sort argpartition可以为该函数提供索引 ''' a [[[ 1 11] [10 9] [ 2 4]] [[ 7 8] [ 6 3] [ 4 5]]] take_along_axis [[[ 1 4] [ 2 9] [10 11]] [[ 4 3] [ 6 5] [ 7 8]]] ''' print(1,a[np.array([0,1]).reshape(2,1,1), indices, np.array([0,1]).reshape(1,1,2)]) #与take_along_axis等同功能 ''' 主要是分为两部分 第一部分该函数沿着axis指示的轴,将其他轴(Ni,...,J,..Nk)分别reshape(Ni,1,...),...,(1,...,Nk) ,在轴axis所在位置的维度由indices形成的索引空间替代 第二部分 利用花式索引 完成对轴axis方向上的元素按照indices索引指示返回该方向上的元素 a[funcy_index] funcy_dex = ((Ni,1,...),...,indices,...(1,...,Nk)) ''' # 自定义indices index = np.array([1,0]).reshape(1,1,2) #indices的维度 需与数组arrshape元组长度相同 即两者维度相同,否则将会Valueerror, ## 源码设计如此,其实只要保证index 与剩余轴维度索引广播一样可以完成花式索引,此处没有深究。 print(2,np.take_along_axis(a, index, axis=1)) #print(a[np.array([0,1]).reshape(2,1,1), np.array([1,0]).reshape(1,2), np.array([0,1]).reshape(1,1,2)]) #轴1上索引仍能与轴0 、轴2进行广播 ''' [[[10 11]] [[ 6 8]]] '''

take

  • 沿着指定轴 获取该轴方向上的元素,np.take(arr,indices,axis=2) 等同于a[funcy_index] 其中funcy_index = (:,:,indices,...)
import numpy as np a = [4, 3, 5, 7, 6, 8] a = np.array(a).reshape(3,2) print(np.take(a, [[0, 1], [1, 2]], axis=0)) ''' [[[4 3] [5 7]] [[5 7] [6 8]]] ''' print(a[ [[0, 1], [1, 2]],:]) #等同于将indices放置在花式索引中 axis的位置, 完成数组的花式索引 #axis 未设置的时候会将数组压扁 变成1D数组处理 print(2,np.take(a,[[0, 1], [1, 5]])) # 可以看见value =5 并不会报错,整个length=6 [4, 3, 5, 7, 6, 8] ''' [[4 3] [3 8]] '''

插入(insert)

  • 对数组应用insert,返回的是输入数组arr的copy版本,对原数组不会有影响;
  • numpy.insert(arr, obj, values, axis=None) insert是根据obj作为输入数组arr的索引,在对应索引之前插入对应values
  • 背后是对索引数组的赋值,arr[...,obj,...] =values 需保证values可以广播到arr该索引结果的形状中去
a = np.array([[1, 1],[2, 2],[3, 3]]) ''' [[1 1] [2 2] [3 3]] ''' # insert(array, obj, values, axis=None ) #1 #obj 为slice对象 或者整数序列,axis默认为None,会将输入数组arr压扁 为1D数组处理 print(np.insert(a, slice(2,5), 5)) #插入到 原数组的索引 [2,3,4] 的前面 [1 1 5 2 5 2 5 3 3] print(a) #数组a仍然保持不变 inset返回的是输入数组的copy版本 print(np.insert(a, (1,2), 5)) # 原数组索引 1 2 处插入5 [1 5 1 5 2 2 3 3] # axis 不为None print(np.insert(a, 1,[[2,3,4]],axis=1 )) a[:,1:2]=np.array([[20,30,40]]).reshape((1,3)) # ValueError: could not broadcast input array from shape (1,3) into shape (3,1) ''' [[1 2 1] [2 3 2] [3 4 3]] ''' # obj not a single value k = np.insert(a, [1,2,1],[20,40,100],axis=1 ) #轴1方向插入3个数,加上轴1:2 总共size=5,分别在插入values之前数组的索引1 索引2 插入values print(k) ''' [[ 1 20 100 1 40] [ 2 20 100 2 40] [ 3 20 100 3 40]] ''' # np.insert(arr,1,values,axis) 与 np.insert(arr,[1],values,axis) 处理方式不同 a = np.array([[1, 1],[2, 2],[3, 3]]) arr = np.insert(a, [1], [20,30],axis=1) # 序列 ''' insert会将【20,30】创建ndarray对象,维度提升到数组a的维度为(1,2)这样就就可以广播到数组a shape(3,1) 需符合赋值: a[:,1:3] = np.array([20,30]).reshape(1,2) [[ 1 20 30 1] [ 2 20 30 2] [ 3 20 30 3]] ''' arr = np.insert(a, 1, [20,30],axis=1) ''' obj是标量 1 ,insert会将【20,30】创建ndarray对象以后 进行 np.moveaxis(values, 0, axis),第一根轴移到axis轴上(1,2),shape改为(2,1) 此时会报错ValueError: could not broadcast input array from shape (2,1) into shape (3,1) '''

__EOF__

本文作者ivan09
本文链接https://www.cnblogs.com/ivan09/p/14257529.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   烦恼1234  阅读(225)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示