Github上Numpy 100道练习题,练习网址:numpy-100,练习当中有一些知识点记录下来。
import numpy as np
np.show_config
np.show_config
Z = np.zeros((3,5))
Z.shape # 数组大小,结果:(3, 5)
Z.size # 元素个数,结果:15
Z.itemsize # 元素的比特数,默认int类型,结果:8
很多操作都已可直接用
Z.xxx
来实现,例如,均值、最值、方差等。
np.info
np.info(np.add) # 读取numpy.add函数的文档
np.arange
np.arange
与range
的参数基本一直,都是(start, stop, step)
。要注意和切片的范围区别,切片为[start:stop:step]
。
在切片中-1
表示直接取到最后一位,但是一般直接取最后一位时就不用写-1
了。如果表示取到倒数第2列,如下
Z = np.arange(10)
Z[1:-1] # 从第1列取到倒数第2列
可以在切片的中括号里写判断条件,例如取出大于3小于8的数组
Z = np.arange(10)
Z[(3 < Z) & (Z < 8)] *= -1
np.nonzero
通过np.nonzero
来找出数组中非零元素的坐标,如果是一维数组,返回的就是下标,如果是多维数组,返回的是非零元素的每一维的下标。
Z = [[1, 2, 0, 0, 4, 0],[0, 3, 5, 0, 7, 0]]
np.nonzero(Z)
得到:
(array([0, 0, 0, 1, 1, 1], dtype=int64),
array([0, 1, 4, 1, 2, 4], dtype=int64))
Tips: 竖着看就是非零数组的二维坐标了。
np.pad
通过np.pad
函数可以设置矩阵的边界,常用的参数有np.pad(a, pad_width, mode, constant_value)
,其中
a
:输入矩阵;pad_width
:边界的宽度,这里可以是二维的数组,例如对一维数组填充,pad_width=(2,3)
就是表示前面填充2个,后面填充3个;如果是对二维数组填充,pad_width=((2,3),(1,2))
就是对第一维度(行)前面填充2行,后面填充3行;对第二维度(列)前面填充1列,后面填充2列。mode
:填充的模式,一般有以下模式constant
:填充常数,后面的constant_value
表示需要填充的数字;edge
:采用边界值来填充;linear_rmap
:用边缘递减的方式填充;maximum
:用最大值填充;mean
:用均值填充;minimum
:用最小值填充;reflect
:对称填充,这里的对称表示,在前面填充以数组的第一位为对称轴将第一位右边的元素按照镜面填充,在后面填充则以最后一位作为对称轴;symmetric
:对称填充,这里将这个数组视为整体不可分割的,另选数组外的轴作为对称轴进行镜面填充。其实说白了就是 reflect
把第一位或者最后一位作为轴,此时第一位或最后一位乜有进行填充,而symmetric
是整体都会进行镜面填充,第一位或最后一位也会进行镜面填充。wrap
:用原数组后面的值填充前面,前面的值填充后面。np.pad(np.arange(5), pad_width=3, mode="reflect")
得到:array([3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1])
np.pad(np.arange(5), pad_width=3, mode="symmetric")
得到:array([2, 1, 0, 0, 1, 2, 3, 4, 4, 3, 2])
np.diag
np.diag(a, k)
,如果a
为一维数组,那么会得到一个以该数组为对角线,其余元素为0的对角线矩阵,如果a
为矩阵,则会得到其对角线上的元素形成的一维数组。
而当k=x
且 x ≠ 0 x\neq 0 x=0,则表示操作的是操作的对角线是上 x x x阶或下 x x x阶,例如k=1
表示对角线上面1条的对角线,k=-1
表示对角线下面1条的对角线。
np.diag(np.arange(1,5), k=-1)
得到:
array([[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 4, 0]])
np.unravel_index
Numpy返回数组的下标一般是一维下标,即查询的元素在数组中按某种顺序排成一列的下标,但是有时候需要知道该元素在多维数组中的多维下标,即在哪一行哪一列这样的下标,可以采用np.unravel_index(一维下标,多维数组的形状)
。
np.repeat
和np.tile
Numpy中常用的数组复制有两个函数np.repeat
和np.tile
,这两者的区别在于前者操作的层面是元素,后者操作的层面是轴。
np.repeat(a, repeats, axis)
:
a
:表示操作对象,可以是元素也可以是数组repeats
:表示复制的次数,可以对每一维的复制次数进行分别制定,例如repeats=(2,3)
,但是此时需要制定沿哪个轴进行复制。通常repeats
的值是一个数,此时得到的结果就是一维数组,每个元素都复制一样的次数。axis
:复制的轴向,axis=0
为第一维度(行),axis=1
为第二维度(列)。需要注意的是,如果是三维的话,第一维度不是行,是层,第二维度是行,第三维度是列。对于多维数组的排序,最简单的方式就是数中括号的层数和顺序。
Z = np.array([[1, 2], [3, 4]])
np.repeat(Z, (2, 1), axis=0)
得到:
array([[1, 2],
[1, 2],
[3, 4]])
Z = np.array([[1, 2], [3, 4]])
np.repeat(Z, 2)
得到:
array([1, 1, 2, 2, 3, 3, 4, 4])
Z = np.array([[1, 2], [3, 4]])
np.repeat(Z, 2, axis=1)
得到:
array([[1, 1, 2, 2],
[3, 3, 4, 4]])
np.tile(A, repeats)
:
A
:复制的数组repeats
:复制次数,这个与np.repeat
不同的是,这里的复制不再按照元素来考虑,而是当做整体,如果repeats
是一个数字,就是沿着默认的第一维度(行)复制,如果是(a,b)
就是沿着第一维度(行)复制a
次,沿着第二维度(列)复制b
次。Z = np.array([[1, 2], [3, 4]])
np.tile(Z, 2)
得到:
array([[1, 2, 1, 2],
[3, 4, 3, 4]])
Z = np.array([[1, 2], [3, 4]])
np.tile(Z, (2,3))
得到:
array([[1, 2, 1, 2, 1, 2],
[3, 4, 3, 4, 3, 4],
[1, 2, 1, 2, 1, 2],
[3, 4, 3, 4, 3, 4]])
np.copysign
np.copysign(A, B)
:将B的符号赋值给A。
np.array().astype()
np.array().astype()
:可以通过数组后点出方法astype()
改变数组内元素的类型。
np.datetime64()
np.datetime64()
:用去求取日期,如果计算当天时间,填入参数today
。如果计算时间差
(np.datetime64('today') - np.datetime64('2022-01-01')).astype(int)
得到:
26
datetime64
是一种类型,可以通过np.arange
来做一个日历,例如显示2022年1月的日历
np.arange('2021-1', '2022-2', dtype='datetime64[D]')
得到:
array(['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04',
'2022-01-05', '2022-01-06', '2022-01-07', '2022-01-08',
'2022-01-09', '2022-01-10', '2022-01-11', '2022-01-12',
'2022-01-13', '2022-01-14', '2022-01-15', '2022-01-16',
'2022-01-17', '2022-01-18', '2022-01-19', '2022-01-20',
'2022-01-21', '2022-01-22', '2022-01-23', '2022-01-24',
'2022-01-25', '2022-01-26', '2022-01-27', '2022-01-28',
'2022-01-29', '2022-01-30', '2022-01-31'], dtype='datetime64[D]')
其中datetime64[D]
应该是表示day,同样还可以去月份month,年份year,不过要注意如果前面的日期可以到日,但是是包含这一天的。
out
四则运算np.add
,np.subtract
,np.multiply
,np.divide
,这些运算都有一个参数out
。这个参数表示输入的结果赋值在哪个变量上。例如np.add(A+B, out=A)
就是将A+B的结果放入A的位置上,替换掉原来的A值。
np.ceil(x)
:取上整数,天花板np.floor(x)
:取下整数,地板np.trunc/fix(x)
:取整数部分np.rint(x)
:四舍五入np.around(x, decimals)
:四舍五入到小数位,decimals
表示位数。参考:Numpy中的广播机制
Numpy的广播机制,简单的来说就是一个低维度数组(或者是有一个维度等于1的高维度数组)与另一个高维度数组在某一个维度相似,就可以将低维度数组沿着缺失的维度复制形成一个相似的高维度数组与另一个高维度数组相加。
例如,存在一个二维数组,形状为(4,1)
,另一个二维数组,形状为(4,3)
,就可以将一维数组在列上复制3次再相加。不过需要注意的是,如果是二维数组,一定有一个维度为1。
(dtype)
构建自定义类型的数组像是构建结构体一样,可以用于设置一些简单的类型,比如初始化一个物体包含属性为位置和颜色。采用np.zeros()
来进行初始化。
Z = np.zeros(10,
dtype=[('position', [('x', float, 1), ('y', float, 1)]),
('rgb', [('r', float, 1), ('g', float, 1),
('b', float, 1)])])
其中, 10 10 10表示初始化的个数,dtype=[]
中括号的内容就是自己设定的类型,属性通过圆括号来分别表示,比如dtype=[('属性1'), ('属性2'), ...]
。每个属性内的需要设置参数,比如我们针对位置属性来设置。
dtype=[('position', [('x', float, 1), ('y', float, 1)])]
这里要注意的是括号的顺序,可以理解为每个属性与参数放入一个字典中,比如'position
对应的参数有x和y,每个x和y的类型是float
,个数是 1 1 1。而x和y用中括号括起来表示一个数组。类似的,可以很好理解颜色属性的设置。
这里有两种,采用公式和scipy包。
这里会用到将输入转为x维数组的函数 np.atleast_xd
。这里的x
有三种选择,分别是1,2,3,换句话说,就是可以将输入的数据转化为一维、二维和三维数组。就想题目中的要求一样,在计算点到点的距离时,这个函数还是有用的。常规的,我们想求一个距离矩阵,就是一个点到其他各点的距离,简单点的用repeat
或者tile
来扩展一下,再做矩阵的对位计算,笨一点的就是循环。这里可以直接将X和Y坐标转为二维数组,然后用下面的公式计算
d i s t a n c e _ m a t r i x = ( X − X ′ ) 2 + ( Y − Y ′ ) 2 distance\_matrix=\sqrt{(X-X')^2+(Y-Y')^2} distance_matrix=(X−X′)2+(Y−Y′)2
这样的计算更方便。
scipy.spatial.distance.cdist
这是一种很方便的方法,scipy包中有很多常用的函数(以后还是要好好学习一下scipy)。scipy.spatial.distance.cdist
为计算两个输入集合中每对之间的距离的函数。直接用D=scipy.spatial.distance.cdist(Z,Z)
就可以求出Z坐标数组中每两点之间的距离了。
np.ndarray.view()
在numpy中可以构建视图来对同一块内存内的数据以另一种数据类型来呈现。ndarray.view()
方会创建一个新的数组对象,该方法创建的新数组的维数变化不会改变原始数据的维数,此时两个变量是共享内存的,因此新数据称之为原数组的一个视图。而深拷贝还是用ndarray.copy()
。
事实上,没有任何数据类型是固定的,主要取决于如何看待这片数据的内存区域。在numpy.ndarray.view()
中,提供对内存区域不同的切割方式,来完成数据类型的转换,而无须要对数据进行额外的copy,来节约内存空间。
以上都是查的一些资料对ndarray.view()
的解释,这里放上自己对该用法的理解。首先,ndarray.view()
并不会对内存内的数据进行复制和拷贝,也就是说数据还是那些数据,没有变化。但是,对数据的解释就不一样了。比如,在内存中存入了一个int32
类型的数字 1 1 1,内存中存的是按照数据类型int32
对数字 1 1 1解释得到的 4个byte,32位bits,可能是00000000 00000000 00000000 00000001。然后新开了一个视图,这个视图的类型是float32
,那float32
数据类型对00000000 00000000 00000000 00000001的解释就不是 1 1 1,而是其他的数了(这个数是多少,我也不清楚,可能是个负数)。这里有个例子可以很好的解释这一切。
a = np.arange(10, dtype='int16')
print("a is:\n", a)
# using view() method
v = a.view('int32')
print("\n After using view() with dtype = 'int32', a is:\n", a)
print(f'v={v}')
v +=1
# addition of 1 to each element of v
print("\n After using view() with dtype = 'int32' and adding 1, a is:\n", a)
print(f'v={v}')
----------------------------------- 得到 ------------------------------------
a is:
[0 1 2 3 4 5 6 7 8 9]
After using view() with dtype = 'int32' a is:
[0 1 2 3 4 5 6 7 8 9]
v=[ 65536 196610 327684 458758 589832]
After using view() with dtype = 'int32' and adding 1 a is:
[1 1 3 3 5 5 7 7 9 9]
v=[ 65537 196611 327685 458759 589833]
在这个例子中v+=1
,当我们看v
其实每一位都加上了1,那为什么a
数组只有在奇数位才加1了呢。我想答案就是int32
数据类型和int16
数据类型对整数的解释不同,可能前者加2,后者才加1。
np.may_share_memory()
或 ndarray.base
查看数组的内存是否相同,换句话说,就是两个变量是否是指向同一个内存。有两种方法 ①np.may_share_memory(A, B)
;②ndarray.base
。前者很好理解,后者是一个数组如果其内存来自其他对象,则为该对象的基对,所以共享内存。
np.triu()
和 np.tril()
在调用 MOSEK 求解二次优化问题时,系数矩阵 Q Q Q 的赋值只需要下矩阵,因此这里及时地补充下相关知识。
Numpy 中求上下三角矩阵的函数分别为 np.triu()
和 np.tril()
,即 Upper triangle 和 Lower triangle 两种。