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的时候 维度其实是(1,3)--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) 为何不是(2,3,1) 在于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=(4,3,1)
print(x[:,:,0:1,:].shape) #0:1是一个切片,进行切片操作以后在轴3处产生维度1,所以最后shape=(4,3,1,1)
#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] #【100,23】shape=(2,) 【0:2,0,0】对应 shape=(2,1,1)符合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尺寸分别是(1,4)(4,2) 不匹配 

分割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规定了最小维度的时候,索引数组如下:(0,1,2,3,N-n....,N-1)list(range(4))(0,1,2,3)
  2.  [:3rd]+[N-n:] +[3rd:N-n] 指代的是将 N维数组根据索引取出元素 然后进行拼接  
  3.  此时(N-n...,N-1)的位置就是3rd

elif 3rd为负数的时候:                                  3rd
    3rd负数的时候 -1代表最后一个 即 N-1,(0,1,2,3,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)

'''
原文地址:https://www.cnblogs.com/ivan09/p/14257529.html