标量(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)
'''