Python·总结常用Python机器学习中的向量化操作(持续更新)
本文最后更新于:2024年6月6日星期四晚上6点30分
Numpy
注:本文中提到的第1维度对应于dim=0,以此类推
初始化与属性
常用属性
- 维度ndim
- 数据类型dtype
- 数据总数量size
- 每一项数据所占字节大小itemsize
- 数据总字节大小nbytes
直接初始化
In
n0 = np.array(0)
n1 = np.array([0,1]) # 自动识别数据类型
n2 = np.array([0,1,2],dtype="int32")
n3 = np.array([0,1,2.0]) # 自动识别数据类型
n4 = np.array([[0,1,2]],dtype="float32")
print_format = "{0},{0}.dtype,{0}.ndim,{0}.size,{0}.itemsize,{0}.nbytes"
rprint(print_format.format("n0"))
rprint(print_format.format("n1"))
rprint(print_format.format("n2"))
rprint(print_format.format("n3"))
rprint(print_format.format("n4"))Out
n0:0 ▍ n0.dtype:int64 ▍ n0.ndim:0 ▍ n0.size:1 ▍ n0.itemsize:8 ▍ n0.nbytes:8 ▍
n1:[0 1] ▍ n1.dtype:int64 ▍ n1.ndim:1 ▍ n1.size:2 ▍ n1.itemsize:8 ▍ n1.nbytes:16 ▍
n2:[0 1 2] ▍ n2.dtype:int32 ▍ n2.ndim:1 ▍ n2.size:3 ▍ n2.itemsize:4 ▍ n2.nbytes:12 ▍
n3:[0. 1. 2.] ▍ n3.dtype:float64 ▍ n3.ndim:1 ▍ n3.size:3 ▍ n3.itemsize:8 ▍ n3.nbytes:24 ▍
n4:[[0. 1. 2.]] ▍ n4.dtype:float32 ▍ n4.ndim:2 ▍ n4.size:3 ▍ n4.itemsize:4 ▍ n4.nbytes:12 ▍填充初始化
- 注意这部分的第一个参数
shape需要在外面加对括号 - 默认的datatype似乎会根据版本而异——但zeros和ones应该都是默认float
In
zeros = np.zeros((3,4))
ones = np.ones((3,4,2))
full = np.full((3,4),6) # 形状3*4 填充值为6
full_like = np.full_like(full,7,dtype="float32") # 形状和"full"相同 填充值为7
rprint("zeros",":\n")
rprint("zeros.dtype")
rprint("ones",":\n")
rprint("ones.dtype")
rprint("full",":\n")
rprint("full.dtype")
rprint("full_like",":\n")
rprint("full_like.dtype")Out
zeros:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]] ▍
zeros.dtype:float64 ▍
ones:
[[[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]]
[[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]]
[[1. 1.]
[1. 1.]
[1. 1.]
[1. 1.]]] ▍
ones.dtype:float64 ▍
full:
[[6 6 6 6]
[6 6 6 6]
[6 6 6 6]] ▍
full.dtype:int64 ▍
full_like:
[[7. 7. 7. 7.]
[7. 7. 7. 7.]
[7. 7. 7. 7.]] ▍
full_like.dtype:float32 ▍随机初始化
- 注意,随机初始化时需要注意指明shape的方式
In
np.random.seed(42) # ====Reset the seed to 42====
rand_mat_1 = np.random.rand(2,2) # np.random.rand不需要加tuple
np.random.seed(42) # ====Reset the seed to 42====
rand_mat_2 = np.random.random_sample((2,2)) # np.random.random_sample则需要加tuple
rand_1 = np.random.randint(3) # np.random.randint给出一个[0,x)的整数
rand_2 = np.random.randint(3)
np.random.seed(42) # ====Reset the seed to 42====
rand_3 = np.random.randint(3)
rand_4 = np.random.randint(3)
np.random.seed(42) # ====Reset the seed to 42====
rand_5 = np.random.randint(0,3) # np.random.randint也可以指明区间范围
rand_6 = np.random.randint(0,3)
np.random.seed(42) # ====Reset the seed to 42====
rand_mat_3 = np.random.randint(3,7,(3,3)) # np.random.randint可以形成矩阵!
np.random.seed(42) # ====Reset the seed to 42====
rand_mat_4 = np.array([np.random.randint(3,7) for _ in range(9)]).reshape(3,3)
print("====Reset the seed to 42====")
rprint("rand_mat_1",":\n")
print("====Reset the seed to 42====")
rprint("rand_mat_2",":\n")
rprint("rand_1,rand_2")
print("====Reset the seed to 42====")
rprint("rand_3,rand_4")
print("====Reset the seed to 42====")
rprint("rand_5,rand_6")
print("====Reset the seed to 42====")
rprint("rand_mat_3",":\n")
print("====Reset the seed to 42====")
rprint("rand_mat_4",":\n")Out
====Reset the seed to 42====
rand_mat_1:
[[0.37454012 0.95071431]
[0.73199394 0.59865848]] ▍
====Reset the seed to 42====
rand_mat_2:
[[0.37454012 0.95071431]
[0.73199394 0.59865848]] ▍
rand_1:2 ▍ rand_2:1 ▍
====Reset the seed to 42====
rand_3:2 ▍ rand_4:0 ▍
====Reset the seed to 42====
rand_5:2 ▍ rand_6:0 ▍
====Reset the seed to 42====
rand_mat_3:
[[5 6 3]
[5 5 6]
[3 3 5]] ▍
====Reset the seed to 42====
rand_mat_4:
[[5 6 3]
[5 5 6]
[3 3 5]] ▍关于seed
从上面的例子也可以看到,在设置完随机种子后,同样的生成随机数据命令序列的结果是一样的(rand_3,rand_4和rand_5,rand_6,以及rand_mat_3和rand_mat_4的生成),但不同的命令序列结果就并非如此(如rand_mat_2,rand_1和rand_3,rand_4)。
- 我的理解是:设置完随机种子后,就有一个固定的随机数序列,每次调用生成随机数据的命令就是从这个序列中按顺序取,但是不同的命令(比如生成整数和生成浮点数)的进一步处理可能不同(#TODO探究)
其他初始化
单位矩阵
In
np.identity(5)Out
array([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.]])复制/赋值
直接赋值 =
copy_directly = origin
In
origin = np.arange(4)
copy_directly = origin # 直接赋值
print("======= Before Modify ======")
rprint("origin")
print("copy_directly = origin")
rprint("copy_directly")
print("=========== Infos ==========")
rprint("origin.flags.owndata") # 检查origin是否是底层数据的拥有者
rprint("copy_directly.flags.owndata") # 检查copy_directly是否是底层数据的拥有者
rprint("id(origin) == id(copy_directly)") # 检查两者id是否相同
# =====================
origin[-1] = 10 # 修改origin
print("====== origin[-1] = 10 =====")
rprint("origin")
rprint("copy_directly") # copy_directly也被修改了
# =====================
copy_directly[0] = 10 # 修改copy_directly
print("==== copy_directly[0] = 10 ====")
rprint("copy_directly")
rprint("origin") # origin也被修改了Out
======= Before Modify ======
origin:[0 1 2 3] ▍
copy_directly = origin
copy_directly:[0 1 2 3] ▍
=========== Infos ==========
origin.flags.owndata:True ▍
copy_directly.flags.owndata:True ▍
id(origin) == id(copy_directly):True ▍
====== origin[-1] = 10 =====
origin:[ 0 1 2 10] ▍
copy_directly:[ 0 1 2 10] ▍
==== copy_directly[0] = 10 ====
copy_directly:[10 1 2 10] ▍
origin:[10 1 2 10] ▍- 可以看到,直接赋值获得的array和原始的array之间有以下关系:
- 都是底层数据的拥有者
- id相同
- 修改其中一个都会影响另外一个
切片 = []
copy_slice = origin[:]
- 注意,numpy中的切片的性质和python对于列表的切片的性质不同
In
origin = np.arange(4)
copy_slice = origin[:] # 切片
print("======= Before Modify ======")
rprint("origin")
print("copy_slice = origin[:]")
rprint("copy_slice")
print("=========== Infos ==========")
rprint("origin.flags.owndata") # 检查origin是否是底层数据的拥有者
rprint("copy_slice.flags.owndata") # 检查copy_directly是否是底层数据的拥有者
rprint("id(origin) == id(copy_slice)") # 检查两者id是否相同
# =====================
origin[-1] = 10 # 修改origin
print("====== origin[-1] = 10 =====")
rprint("origin")
rprint("copy_slice") # copy_slice也被修改了
# =====================
copy_slice[0] = 10 # 修改copy_slice
print("==== copy_slice[0] = 10 ====")
rprint("copy_slice")
rprint("origin") # origin也被修改了Out
======= Before Modify ======
origin:[0 1 2 3] ▍
copy_slice = origin[:]
copy_slice:[0 1 2 3] ▍
=========== Infos ==========
origin.flags.owndata:True ▍
copy_slice.flags.owndata:False ▍
id(origin) == id(copy_slice):False ▍
====== origin[-1] = 10 =====
origin:[ 0 1 2 10] ▍
copy_slice:[ 0 1 2 10] ▍
==== copy_slice[0] = 10 ====
copy_slice:[10 1 2 10] ▍
origin:[10 1 2 10] ▍- 可以看到,通过切片获得的array和原始的array之间有以下关系:
- 只有原始array是底层数据的拥有者
- 二者id不再相同
- 修改其中一个同样会影响另一个
深拷贝 = .copy()
- 为了真正的生成修改互不影响的array,我们需要使用.copy()生成一个新的array
- 注意这里直接使用copy就是深拷贝了,不用加上True之类的参数(注意和torch里的tensor区分)
In
origin = np.arange(4)
copy_deep = origin.copy() # 深拷贝
print("======= Before Modify ======")
rprint("origin")
print("copy_deep = origin.copy()")
rprint("copy_deep")
print("=========== Infos ==========")
rprint("origin.flags.owndata") # 检查origin是否是底层数据的拥有者
rprint("copy_deep.flags.owndata") # 检查copy_deep是否是底层数据的拥有者
rprint("id(origin) == id(copy_deep)") # 检查两者id是否相同
# =====================
origin[-1] = 10 # 修改origin
print("====== origin[-1] = 10 =====")
rprint("origin")
rprint("copy_deep") # copy_deep没有被修改
# =====================
copy_deep[0] = 10 # 修改copy_deep
print("==== copy_deep[0] = 10 =====")
rprint("copy_deep")
rprint("origin") # origin没有被修改Out
======= Before Modify ======
origin:[0 1 2 3] ▍
copy_deep = origin.copy()
copy_deep:[0 1 2 3] ▍
=========== Infos ==========
origin.flags.owndata:True ▍
copy_deep.flags.owndata:True ▍
id(origin) == id(copy_deep):False ▍
====== origin[-1] = 10 =====
origin:[ 0 1 2 10] ▍
copy_deep:[0 1 2 3] ▍
==== copy_deep[0] = 10 =====
copy_deep:[10 1 2 3] ▍
origin:[ 0 1 2 10] ▍- 可以看到:
- 两个array都是对底层数据的拥有者
- 两者id不同
- 对其中一个修改,不会影响另一个
变换
reshape
就不用说了
repeat
In
origin = np.array([[1,2,3]])
repeat_0 = np.repeat(origin,3, axis=0) # np.repeat可以指定axis重复n次
repeat_1 = np.repeat(origin,3, axis=1) # 在目标维度然后按目标方向repeat对应的数据
rprint("origin",":\n")
rprint("repeat_0",":\n")
rprint("repeat_1",":\n")Out
origin:
[[1 2 3]] ▍
repeat_0:
[[1 2 3]
[1 2 3]
[1 2 3]] ▍
repeat_1:
[[1 1 1 2 2 2 3 3 3]] ▍关于axis
关于Numpy中的axis(其实Torch和Pandas中也是类似的),可以这么理解
根据axis的维度,沿着该维度走下去的方向就是目标方向
如
origin: [[1 2 3]]
np.repeat(origin,3, axis=0) 沿着
axis=0只有一个[1 2 3],继续走进行repeat其实就是把[1 2 3]repeat了n次,最后也就是[[1 2 3] [1 2 3] [1 2 3]] # or [[1 2 3] [1 2 3] [1 2 3]]
stack
- numpy中的stack主要有两种,一种是np.vstack&np.hstack,另一种就叫np.stack。下面分这两种展开:
vstack&hstack
In
one_array = np.ones((3,4))
zero_array = np.zeros((3,4))
sprint("one_array")
sprint("zero_array")
print("----------------")
sprint("np.vstack([one_array,zero_array])")
sprint("np.hstack([one_array,zero_array])")Out
one_array
- - - - -
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]] ▍
===============
zero_array
- - - - -
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]] ▍
===============
----------------
np.vstack([one_array,zero_array])
- - - - -
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]] ▍
===============
np.hstack([one_array,zero_array])
- - - - -
[[1. 1. 1. 1. 0. 0. 0. 0.]
[1. 1. 1. 1. 0. 0. 0. 0.]
[1. 1. 1. 1. 0. 0. 0. 0.]] ▍
===============- 可以看到,vstack和hstack的效果十分直观,就是一个竖着拼,一个横着拼,所以拼的时候需要保证size对齐
stack
- np.stack稍微复杂一点,可以传入更多参数
In
one_array = np.ones((3,4))
zero_array = np.zeros((3,4))
sprint("one_array")
sprint("zero_array")
print("----------------")
sprint("np.stack([one_array,zero_array],axis=0)")
sprint("np.stack([one_array,zero_array],axis=1)")
sprint("np.stack([one_array,zero_array],axis=2)")Out
one_array
- - - - -
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]] ▍
===============
zero_array
- - - - -
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]] ▍
===============
----------------
np.stack([one_array,zero_array],axis=0)
- - - - -
[[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]] ▍
===============
np.stack([one_array,zero_array],axis=1)
- - - - -
[[[1. 1. 1. 1.]
[0. 0. 0. 0.]]
[[1. 1. 1. 1.]
[0. 0. 0. 0.]]
[[1. 1. 1. 1.]
[0. 0. 0. 0.]]] ▍
===============
np.stack([one_array,zero_array],axis=2)
- - - - -
[[[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]]
[[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]]
[[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]]] ▍
===============注意,无论是vstack/hstack还是stack,传入的被stack的都是用
[]括起来的,所以参数axis指明的应该理解为加上了[]之后的维度当
axis = 0的时候,我们的[one_array,zero_array]的第一维度其实就是这两个array,所以就是:# 将 [[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]] # 与 [[0. 0. 0. 0.] [0. 0. 0. 0.] [0. 0. 0. 0.]] # 拼接,得到 [[[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]] [[0. 0. 0. 0.] [0. 0. 0. 0.] [0. 0. 0. 0.]]]当
axis = 1的时候,我们的[one_array,zero_array]的第二维度其实就是这两个array中的第一维度,也就是:# one_array [ [1. 1. 1. 1.] # <- [1. 1. 1. 1.] # <- [1. 1. 1. 1.] # <- ]所以结果:
[ [ [1. 1. 1. 1.] # 从[one_array,zero_array]中选第一个的 [0. 0. 0. 0.] # 从[one_array,zero_array]中选第二个的 ] # 选完了,封闭一层 [ [1. 1. 1. 1.] [0. 0. 0. 0.] ] [ [1. 1. 1. 1.] [0. 0. 0. 0.] ] ]axis=2也是一样的道理,直接看结果吧:[[[ 1. # 来源于[one_array,zero_array]中第一个的第2维的数据 0. # 来源于[one_array,zero_array]中第二个的第2维的数据 ] # 选完了,封闭一层 [1. 0.] [1. 0.] [1. 0.]] [[1. 0.] [1. 0.] [1. 0.] [1. 0.]] [[1. 0.] [1. 0.] [1. 0.] [1. 0.]]]
数据索引
这部分的更深入介绍请参考:https://numpy.org/doc/stable/user/basics.indexing.html
假设我们有一个3维数组n3d,我们考虑各种读取/修改数组的方式
In
n3d = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
n3dOut
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])- 我们可以用
[,,]的方式分别指定三维数组的三个维度
原则
- 每对一个维度使用单个数字细化选取,则会降低一维
- 用
:(包括含:的表达式)细化维度,不会降低维度 - 每对一个维度使用一维
[]进行细化选取,则会降低一维;但最后总的会加一维 - []的更高维更为复杂,咱不讨论
用单个数字选取
In
n3d = np.arange(1,28).reshape(3,3,3)
sprint("n3d")
sprint("n3d[0]") # 对第1维用单个数字0进行选取
sprint("n3d[:,:,0]") # 对第3维用单个数字0进行选取
sprint("n3d[...,0]") # 也可以用这种...省略号的方式替代Out
n3d
- - - - -
[[[ 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]]] ▍
===============
n3d[0]
- - - - -
[[1 2 3]
[4 5 6]
[7 8 9]] ▍
===============
n3d[:,:,0]
- - - - -
[[ 1 4 7]
[10 13 16]
[19 22 25]] ▍
===============
n3d[...,0]
- - - - -
[[ 1 4 7]
[10 13 16]
[19 22 25]] ▍
===============关于选取逻辑
我们以
n3d[:,:,0]为例- 首先看下
n3d的模样
n3d - - - - - [[[ 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]]]第一维是
:,即保留所有的第一维数据由于n3d的形状为3*3*3,第一维有三个数据,我们可以做以下拆分
# 第一个数据 [[ 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]]第二维也是
:,故也是保留第二维的所有数据上一点我们说到第一维已经全部保留了,所以我们对于第一维的每个数据都要进行第二维的细化选取(只不过我们这里用
:了,可以看作没有细化选取)。以第一维的第一个数据为例:# 第一个数据 [ [ 1 2 3] # 保留 [ 4 5 6] # 保留 [ 7 8 9] # 保留 ] # ... 第一维的其余数据也是同样的操作现在到了最后一维,这里用的是单个数字
0进行细化选取,也就是对于最后一维只选中第一个数据同样由于前面两维都是保留的全部数据,我们以上一维度的数据为例说明单个数字的细化选取:
# 第一个数据 [ # [ 1 # 选中 2 3 ] [ 4 # 选中 5 6 ] [ 7 # 选中 8 9 ] ] # ... 第一维的其余数据也是同样的操作其实就和一个一维的用单个数字索引一样,会降维,如
np.array([1,2,3])[0] = 1一般于是就变成了:
# 第一个数据 [ 1 4 7 ] # ... 第一维的其余数据也是同样的操作拼上第一维的其他两个数据即为2维array
- 首先看下
用:选取
正如上面提到的那样,如果在某一维度单用一个:,则会保留该维度层面的所有数据,也不会降低维度;除此之外,还有进行切片的方式进行选取,同样不会降低维度
- 进阶就是
start:stop:step
In
n3d = np.arange(1,28).reshape(3,3,3)
sprint("n3d")
sprint("n3d[:,:,0]")
sprint("n3d[:,:,0:1]") # 最后一维使用切片的方式进行选取Out
n3d
- - - - -
[[[ 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]]] ▍
===============
n3d[:,:,0]
- - - - -
[[ 1 4 7]
[10 13 16]
[19 22 25]] ▍
===============
n3d[:,:,0:1]
- - - - -
[[[ 1]
[ 4]
[ 7]]
[[10]
[13]
[16]]
[[19]
[22]
[25]]] ▍
===============- 其实这个也很好理解,可以结合上面的”关于选取逻辑”的介绍来讲
- 在用单个数字下标进行选取的时候,可以看作是对于一个array(或者看成python内置的list)选取其中的某个元素,所以维度会下降1;而用切片进行选择也同样可以这样去考虑——使用切片形成的还是array(或list)!故其实维度是保持不变的。
用[]选取
这一块相对比较复杂
纯Integer array indexing
我们先讨论每个维度用到的都是同样shape的array/[]的情况
- 总的来说,对于每一维都是array/[]索引的方式来说,有以下的等式:
result = x[ind_1, ind_2, ..., ind_N]
# result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M],
# ind_2[i_1, ..., i_M],
# ...,
# ind_N[i_1, ..., i_M]]直接看公式可能有点抽象,我们可以通过下面的例子来直观的感受一下:
x = np.array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]]) rows = np.array([[0, 0], [3, 3]], dtype=np.intp) columns = np.array([[0, 2], [0, 2]], dtype=np.intp) x[rows, columns] # 结果: # array([[ 0, 2], # [ 9, 11]])其实就是得到一个array,其中rows指明了行,columns指明了列,比如rows[0,0]和columns[0,0]就指明了得到的result的(0,0)位置的元素为x数组的(rows[0,0],columns[0,0])位置的数——这里刚好(0,0)对应的也刚好是0;可以看result的(1,0)位置的元素(9)其实就是x数组的(3,0)位置的元素,而(3,0)就是(rows[1,0],columns[1,0])
Broadcasting
- 对于不同维度有不同的shape的array进行index的情况,首先会尝试进行broadcast(广播)
- If the index arrays do not have the same shape, there is an attempt to broadcast them to the same shape.
我们还是用上面那个例子,尝试用不同shape的array进行选取
rows = np.array([0, 3], dtype=np.intp)
columns = np.array([0, 2], dtype=np.intp) # 1*2
# rows[:, np.newaxis]: [[0],[3]] —— 2*1
x[rows[:, np.newaxis], columns] # 进行broadcast成2*2,也就是和是上面的两个一样
# array([[ 0, 2],
# [ 9, 11]])简单例子
In
y = np.arange(35).reshape(5, 7)
sprint("y")
sprint("y[np.array([0, 2, 4]), 1]") # 其实这种就是一种会发生broadcast的情形了
sprint("y[np.array([0, 2, 4]), [1,1,1]]") # 这里就是显式地展示怎么进行indexingOut
y
- - - - -
[[ 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]] ▍
===============
y[np.array([0, 2, 4]), 1]
- - - - -
[ 1 15 29] ▍
===============
y[np.array([0, 2, 4]), [1,1,1]]
- - - - -
[ 1 15 29] ▍
===============复杂例子
In
n3d = np.arange(1,28).reshape(3,3,3)
sprint("n3d")
sprint("n3d[[[0],[2]],:]") # 该结果的维度参考rows矩阵[[0],[2]]的维度
sprint("n3d[[[0],[2]],:,[1,2]]") # 这里发生了broadcast,第一维和第三维的都是
sprint("n3d[[[0,0],[2,2]],:,[[1,2],[1,2]]]")# 这里就是显式地将上面的broadcast等价写出Out
n3d
- - - - -
[[[ 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]]] ▍
===============
n3d[[[0],[2]],:] # 相当于[[n3d的第一维的第0个],[n3d的第一维的第2个]]
- - - - -
[[[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]]]
[[[19 20 21]
[22 23 24]
[25 26 27]]]] ▍
===============
n3d[[[0],[2]],:,[1,2]]
- - - - -
[[[ 2 5 8]
[ 3 6 9]]
[[20 23 26]
[21 24 27]]] ▍
===============
n3d[[[0,0],[2,2]],:,[[1,2],[1,2]]]
- - - - -
[[[ 2 5 8]
[ 3 6 9]]
[[20 23 26]
[21 24 27]]] ▍
===============Non-Broadcast
当只有A single advanced index时,如只使用了一个array index,则可以从效果上替换slice/
:A single advanced index can, for example, replace a slice and the result array will be the same. However, it is a copy and may have a different memory layout. A slice is preferable when it is possible.
(但好像不是deep copy)
注意这里的条件是single,如果有多个,则会尝试进行上面所说的broadcast
In
sprint("n3d[:,:,[1,2]]")
sprint("n3d[:,:,1:3]")
sprint("(n3d[:,:,0:3] == n3d[:,:,[0,1,2]]).all()")Out
n3d[:,:,[1,2]]
- - - - -
[[[ 2 3]
[ 5 6]
[ 8 9]]
[[11 12]
[14 15]
[17 18]]
[[20 21]
[23 24]
[26 27]]] ▍
===============
n3d[:,:,1:3]
- - - - -
[[[ 2 3]
[ 5 6]
[ 8 9]]
[[11 12]
[14 15]
[17 18]]
[[20 21]
[23 24]
[26 27]]] ▍
===============
(n3d[:,:,0:3] == n3d[:,:,[0,1,2]]).all()
- - - - -
True ▍
===============我们可以试下假设有多个array index会发生什么:
sprint("n3d[[[0],[2]],:,[1,2]]") sprint("n3d[[[0],[2]],:,1:3]") sprint("(n3d[[[0],[2]],:,0:3] == n3d[[[0],[2]],:,[0,1,2]]).all()")n3d[[[0],[2]],:,[1,2]] - - - - - [[[ 2 5 8] [ 3 6 9]] [[20 23 26] [21 24 27]]] ▍ =============== n3d[[[0],[2]],:,1:3] - - - - - [[[[ 2 3] [ 5 6] [ 8 9]]] [[[20 21] [23 24] [26 27]]]] ▍ =============== (n3d[[[0],[2]],:,0:3] == n3d[[[0],[2]],:,[0,1,2]]).all() - - - - - False ▍ ===============
参考
- https://www.youtube.com/watch?v=QUT1VHiLmmI
- https://builtin.com/data-science/numpy-random-seed
- https://blog.csdn.net/u010099080/article/details/59111207