关于numpy的使用和一些问题解答(Python)
Table of Contents
1
numpy
= Numerical Python 数值计算用的Python2 多维数组
ndarray
3
ndarray
上手4
ndarray
中标量 scalar 的数据类型对象dtype
5 数组的维度与轴 Axis
5.1 改变
ndarray
的形状5.2 加入新的轴
5.3 问题
6 数组的基本索引
6.1 返回一个标量
6.2 返回一个
ndarray
6.3 多维索引
6.4 更新
ndarray
7 进阶索引
7.1 整数数组索引
7.2 与基本索引混合
7.3 条件索引 Boolean Masking
7.4 问题
numpy
= Numerical Python 数值计算用的Python
什么是 NumPy?
NumPy是一个功能强大的Python库,主要用于对多维数组执行计算。NumPy这个词来源于两个单词-- Numerical和Python。NumPy提供了大量的库函数和操作,可以帮助程序员轻松地进行数值计算。这类数值计算广泛用于以下任务:
机器学习模型:在编写机器学习算法时,需要对矩阵进行各种数值计算。例如矩阵乘法、换位、加法等。NumPy提供了一个非常好的库,用于简单(在编写代码方面)和快速(在速度方面)计算。NumPy数组用于存储训练数据和机器学习模型的参数。
图像处理和计算机图形学:计算机中的图像表示为多维数字数组。NumPy成为同样情况下最自然的选择。实际上,NumPy提供了一些优秀的库函数来快速处理图像。例如,镜像图像、按特定角度旋转图像等。
数学任务:NumPy对于执行各种数学任务非常有用,如数值积分、微分、内插、外推等。因此,当涉及到数学任务时,它形成了一种基于Python的MATLAB的快速替代。
多维数组ndarray
NumPy主要由 多维数组ndarray
一个用来储存同一种数据类型的有序容器, 和用来操纵ndarry
的函数组成. ndarray
是NumPy核心概念.
ndarray
中的元素的可以是整数, 浮点数, 固定长度的字符串, 等等, 还可以是任何Python对象的reference(并不储存对象本身). 这些元素称为标量 scalar.ndarrays
是同质的 :即其所有元素在内存中占据了同样的大小. 每一个元素的数据类型对象dtype
是一样的.ndarray
中的元素可以用整数(位置)来索引. 这和list
很像, 但ndarray
可以是多维的 (有多个轴axis
)
一个ndarray
在计算机内存中由两个部分组成
组成了
ndarray
的标量们 (scalars)ndarray
的头文件, 定义了数值中标量们唯一的数据类型
ndarray
上手
使用 import
来调用 pacakage.
调用 numpy
. 将 numpy 简称为 np.
1import numpy as np2a = np.array([[1,2,3,4,5],[2,3,4,5,6]],dtype=int)1a[27]:array([[1, 2, 3, 4, 5], , [2, 3, 4, 5, 6]])1a.shape2a.ndim3a.dtype[28]:dtype('int64')
调用numpy中的一个常数(constant)来检查 numpy的版本
1np.__version__[5]:'1.19.4'
使用np.array()
或者 np.asarray()
来建立 ndarray
1a = np.array([1, 2, 3]) # Create a 1d array2a[6]:array([1, 2, 3])1a = np.asarray([1, 2, 3])2a[7]:array([1, 2, 3])1print(type(a)) # Prints "<class 'numpy.ndarray'>"2print(a.shape) # Prints "(3,)"3print(a.ndim)4print(a.dtype)5print(a[0], a[1], a[2]) # Prints "1 2 3"6a[0] = 5 # Change an element of the array7print(a) # Prints "[5, 2, 3]"<class 'numpy.ndarray'> (3,) 1 int64 1 2 3 [5 2 3]
调用 ndarray
的 属性(attribute)shape
来查看它的形状
ndarray
中标量 scalar 的数据类型对象 dtype
ndarray
中的元素的可以是整数, 浮点数, 字符串, 等等, 还可以是任何Python对象的reference(并不储存对象本身). 与数组对应, 这些元素统称为标量 scalar.
以下是这些标量的数据类型对象之间的继承关系:
对象类型 np.object_
object_
任何Python对象的reference, 而非对象本身. 但是在使用时会返回这个对象本身
数字类型 np.number
NumPy比Python有更丰富的数字类型
bool_
布尔型数据类型(True 或者 False)int_
默认的整数类型(类似于 C 语言中的 long,int32 或 int64)intc
与 C 的 int 类型一样,一般是 int32 或 int 64intp
用于索引的整数类型(类似于 C 的 ssize_t,一般情况下仍然是 int32 或 int64)int8
字节(-128 to 127)int16
整数(-32768 to 32767)int32
整数(-2147483648 to 2147483647)int64
整数(-9223372036854775808 to 9223372036854775807)uint8
无符号整数(0 to 255)uint16
无符号整数(0 to 65535)uint32
无符号整数(0 to 4294967295)uint64
无符号整数(0 to 18446744073709551615)float_
float64 类型的简写float16
半精度浮点数,包括:1 个符号位,5 个指数位,10 个尾数位float32
单精度浮点数,包括:1 个符号位,8 个指数位,23 个尾数位float64
双精度浮点数,包括:1 个符号位,11 个指数位,52 个尾数位
(指数位决定范围,尾数位决定精度)complex_
complex128 类型的简写,即 128 位复数complex64
复数,表示双 32 位浮点数(实数部分和虚数部分)complex128
复数,表示双 64 位浮点数(实数部分和虚数部分)
额外阅读: https://www.numpy.org.cn/user_guide/numpy_basics/data_types.html
字符类型 np.character
str_
字符串 unicode_
unicode字符串, 默认是UTF8, 支持中文
它们的方法和属性与Python原生的str
一致
额外阅读: https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.char.html
NumPy标量与Python的基本数据类型
有些标量的数据类型对象dtype
与Python中的基本数据类型对象int, float, str, bool
概念上是一样的, 两者也可以很容易地互相转换和比较数值大小, 但是他们是不同的对象.
比如Python的int
42
存入ndarray
以后就变成了numpy.int32
对象. 由于它是一个Numpy标量, 它有一个dtype
属性, 其值为int32
.
尽管它是Numpy标量, 但依然可以和int
比较和做运算
Q. s1[0]
, s2[0]
和s2[1]
的类型是什么?
s1 = np.array(['a', 'abc']) s2 = np.array([u'a', 'abc'])
1s1 = np.array(['a', 'abc'])2type(s1[0])[25]:numpy.str_1s1.dtype[27]:dtype('<U3')1s1[0].dtype[26]:dtype('<U1')1s2 = np.array([u'a', 'abc'])2s2.dtype, s2[0].dtype, s2[1].dtype[28]:(dtype('<U3'), dtype('<U1'), dtype('<U3'))
数据类型的转换
1c = np.array([1, 2, 3], dtype=float)2c.dtype[29]:dtype('float64')1c[30]:array([1., 2., 3.])1c.astype(np.int8).dtype[16]:dtype('int8')1c.astype(np.int8)[31]:array([1, 2, 3], dtype=int8)
overflow
默认数据类型为np.float_
1a = np.ones((3, 3))2a.dtype, a[35]:(dtype('float64'), array([[1., 1., 1.], , [1., 1., 1.], , [1., 1., 1.]]))
数组的维度与轴 Axis
ndarray
的维度ndim
即指明了其轴的数量
0轴定义为"最里面的"那个一维数组
1import numpy as np23a = np.array([[1,2],[3,4],[5,6]])4a[42]:array([[1, 2], , [3, 4], , [5, 6]])1from PIL import Image2import numpy as np3im_row = Image.open('1.jpg')4im = Image.open('1.jpg').convert('L')5im_row = np.asarray(im_row)6im = np.array(im)7im.shape8im.ndim9im.dtype10im_row.shape[73]:(300, 390, 3)1im[260,289] = 255 1import matplotlib.pyplot as plt2plt.imshow(im,cmap='gray')3# plt.imshow(im_row)[75]:<matplotlib.image.AxesImage at 0x7f2c2834fb70>
0轴上有3个长度为2的一维数组, 1轴上有2个长度为3的一维数组
1a.shape[43]:(3, 2)
这是3个长度为2的一维数组之和, 即0轴上有3个长度为2的一维数组
1np.sum(a, axis=0)[26]:array([ 9, 12])
这是2个长度为3的一维数组之和, 即1轴上有2个长度为3的一维数组
1np.sum(a, axis=1)[27]:array([ 3, 7, 11])
等间距的数组
1# np.arange(10) # 0 .. n-1 (!)2a = np.arange(0,255,1)3# start,end,4b = a.reshape(5,51)5b[1,34] = 2556plt.imshow(b)1np.arange(1, 9, 2) # start, end (exclusive), step[10]:array([1, 3, 5, 7])
指定元素个数的数组
1np.linspace(0, 1, 6) # start, end, num-points[11]:array([0. , 0.2, 0.4, 0.6, 0.8, 1. ])1np.linspace(0, 1, 5, endpoint=False)[12]:array([0. , 0.2, 0.4, 0.6, 0.8])
改变ndarray
的形状
一维数据结构
1np.arange(12).shape[28]:(12,)
通过reshape, 将一维数据结构转成了3××4的矩阵
1M = np.arange(12).reshape(3,4)2M[62]:array([[ 0, 1, 2, 3], , [ 4, 5, 6, 7], , [ 8, 9, 10, 11]])1M[2][1] = 82M[2,1][92]:81M[1, 1] = -92M[70]:array([[ 0, 1, 2, 3], , [ 4, -9, 6, 7], , [ 8, 8, 10, 11]])
数组的基本索引
基本索引返回的的原数组的另外一个视角(view)而非一个新的数组
view: https://docs.scipy.org/doc/numpy-1.13.0/glossary.html#term-view
额外阅读: https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html#basic-slicing-and-indexing
返回一个标量
ndarray
支持所有list
的索引方式, -1
-3
返回最后一个和倒数第三个
1a=[1,2,3]2a[-1][67]:31import numpy as np2a = np.arange(10)3a, a[0], a[2], a[-1], a[-3][38]:(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 0, 2, 9, 7)
对角矩阵
1a = np.diag(np.arange(3))2a[39]:array([[0, 0, 0], , [0, 1, 0], , [0, 0, 2]])
一维索引, 在0轴上 (axis=0). ndarray
中, 0轴定义为最里面的那个一维数组
1a[1][40]:array([0, 1, 0])
多维索引
1M[1, 1][65]:5
更新索引位置对应的值
1a[2, 1] = 10 # third line, second column2a[42]:array([[ 0, 0, 0], , [ 0, 1, 0], , [ 0, 10, 2]])
返回一个ndarray
切割会返回一个ndarray
, 而不是一个标量
生成一个ndarray
, 注意如果写了指代位置的数字, 其定义的区间是前闭后开的
1np.arange(0, 10, 1)[68]:array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])1a = np.arange(10)2a[69]:array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
使用range
, 从index为2的元素开始(包含), 至最后一个, 注意如果写了指代位置的数字, 其定义的区间是前闭后开的
1a[2:][70]:array([2, 3, 4, 5, 6, 7, 8, 9])
从第一个, 至index为9的位置(不包含), 注意如果写了指代位置的数字, 其定义的区间是前闭后开的
1a[:9][46]:array([0, 1, 2, 3, 4, 5, 6, 7, 8])
Q. a[2:9:2], a[1:3], a[::2], a[3:]
返回的结果分别是什么?
hint [start=0: stop=len(a): step=1]
和 range
一样
1a[2:9:2], a[1:3], a[::2], a[3:][75]:(array([2, 4, 6, 8]), , array([1, 2]), , array([0, 2, 4, 6, 8]), , array([3, 4, 5, 6, 7, 8, 9]))
更新ndarray
要更新一个ndarray
中的值, 我们只需对该ndarray
做索引, 并对结果"赋值". 可以使用一个标量更新, 也可以使用另一个ndarray
更新, 但需要注意形状shape
和dtype
是否匹配.
1a = np.arange(10)2a[5:] = 103a[78]:array([ 0, 1, 2, 3, 4, 10, 10, 10, 10, 10])1b = np.arange(5)2a[5:] = b[::-1]3a[93]:array([0, 1, 2, 3, 4, 4, 3, 2, 1, 0])1M = np.arange(6) + np.arange(0, 51, 10)[:, np.newaxis]2M[80]:array([[ 0, 1, 2, 3, 4, 5], , [10, 11, 12, 13, 14, 15], , [20, 21, 22, 23, 24, 25], , [30, 31, 32, 33, 34, 35], , [40, 41, 42, 43, 44, 45], , [50, 51, 52, 53, 54, 55]])1M[2::2, :] = 02# 开始,结束,间隔3M[84]:array([[ 0, 1, 2, 3, 4, 5], , [10, 11, 12, 13, 14, 15], , [ 0, 0, 0, 0, 0, 0], , [30, 31, 32, 33, 34, 35], , [ 0, 0, 0, 0, 0, 0], , [50, 51, 52, 53, 54, 55]])
注意形状 shape
1M[2::2, :] = np.arange(12).reshape(2,6)2M[95]:array([[ 0, 1, 2, 3, 4, 5], , [10, 11, 12, 13, 14, 15], , [ 0, 1, 2, 3, 4, 5], , [30, 31, 32, 33, 34, 35], , [ 6, 7, 8, 9, 10, 11], , [50, 51, 52, 53, 54, 55]])
Q. 为什么在更新后的M
中, 对应的位置仍是整数? 而N
则是浮点数
1np.linspace(0,10,12)[58]:array([ 0. , 0.90909091, 1.81818182, 2.72727273, 3.63636364, , 4.54545455, 5.45454545, 6.36363636, 7.27272727, 8.18181818, , 9.09090909, 10. ])1M[2::2, :] = np.linspace(0,10,12).reshape(2,6)2M[59]:array([[ 0, 1, 2, 3, 4, 5], , [10, 11, 12, 13, 14, 15], , [ 0, 0, 1, 2, 3, 4], , [30, 31, 32, 33, 34, 35], , [ 5, 6, 7, 8, 9, 10], , [50, 51, 52, 53, 54, 55]])
复制
1M[85]:array([[ 0, 1, 2, 3, 4, 5], , [10, 11, 12, 13, 14, 15], , [ 0, 0, 0, 0, 0, 0], , [30, 31, 32, 33, 34, 35], , [ 0, 0, 0, 0, 0, 0], , [50, 51, 52, 53, 54, 55]])1N = M.copy()2X = M.view()3N[2,1] = 234N5# N = N.astype(np.float_)6# N[2::2, :] = np.linspace(0,10,12).reshape(2,6)7# N[97]:array([[ 0, 1, 2, 3, 4, 5], , [10, 11, 12, 13, 14, 23], , [ 0, 23, 0, 23, 0, 0], , [30, 31, 32, 33, 34, 35], , [ 0, 0, 0, 0, 0, 0], , [50, 51, 52, 53, 54, 55]])
进阶索引
与基本索引不同, 进阶索引总是会返回一个新的数组, 而非原数组的视图, 但更新操作与基本索引无异
额外阅读: https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html#advanced-indexing
条件索引 Boolean Masking
1np.random.seed(42)2a = np.random.randint(0, 21, 15)3a[98]:array([ 6, 19, 14, 10, 7, 20, 6, 18, 10, 10, 20, 3, 7, 2, 20])1c = a % 3 == 02c.dtype3print(c)4a[c][ True False False False False False True True False False False True False False False] [102]:array([ 6, 6, 18, 3])1mask = (a % 3 == 0)2a[mask] # or, a[a%3==0], extract a sub-array with the mask[73]:array([ 6, 6, 18, 3])