Numpy Learning
numpy可以理解为"number’s analysis of python"。简而言之,就是python专门用于数学科学处理的一个包,支持矩阵运算。
本文简单介绍和总结numpy的基础使用方法。
- Numpy Learning
- 一.numpy基础
- 1.numpy数组的创建以及array对象常用部分属性
- 2.array数组提取元素
- 3.numpy的数据类型
- 4.array数组的形状改变
- 5.array数组的组合
- 6.array数组的分割
- 7.array数组的属性
- 8.array数组与python列表(list)的之间的转换
一.numpy基础
1.numpy数组的创建以及array对象常用部分属性
要了解numpy的基础,实际上就是要了解“矩阵运算”这一大功能。
矩阵运算包含了很多要素和知识点,比如说:数据类型、矩阵的创建和引用以及维度的提取和改变等等。
但是这一切的操作都是通过array这个数据类型来实现的,也就是我们说的数组
所以我们先从array这个数据对象开始.
# 创array对象--使用array函数可以将
# 指定的类array数据转换为array对象
import numpy as np
arr0=np.array([1,2,3,4])
print(arr0)
print(np.version.version)
print(arr0.dtype)
[1 2 3 4]
1.21.2
int32
可以看到在1.21.2的64位计算机中,如果使用numpy的array函数创建array对象,默认的数据类型为”“int64”。
官方函数文档给的解释是:
The desired data-type for the array.
If not given, then the type will be determined as
the minimum type required to hold the objects in the sequence.
翻译过来就是:
数组所需的数据类型。如果没有给出,那么类型将被确定为对象元素被存储在序列中的最小的类型。
那么如果想要制定array的数据类型呢?
arr0=np.array([1,2,3,4],dtype='float32')
print(arr0)
print(arr0.dtype)
[1. 2. 3. 4.]
float32
现在有一个问题:我们输入的object如果是一个矩阵,那么我如何才能确定这个矩阵成为array后的大小呢?难不成还得自己去记忆?并不是,可以通过array对象的一个属性shape来完成读取
arr0=np.array([1,2,3,4])
arr1=np.array([[1,2],[5,6],[3,6]])
arr2=np.array(1)
print("{}\n{}\n{}".format(arr0.shape,arr1.shape,arr2.shape))
(4,)
(3, 2)
()
这里有一个比较有意思的现象,假如说我输入的不是array_like的object,而是直接一个数字标量(scaler-维度为0),那么我们可以发现这个标量还是可以成为array,但是维度为0。
那么我就可以这样想:
1.维度为0:就是一个点,我们一般称作scaler-标量。
2.维度为1:就是多个点构成的“一条”数据,但是他只有宽度,没有高度,我们一般称作vector-向量。
3.维度为2:就是既有高度又有宽度,那就是一个平面了,我们一般称作matrix-矩阵。
4.维度为3:相当于一个多面体了——多个平面在新的维度上无限重叠,称之为tensor-张量。
这些概念是我们在python数据科学学习和深度学习中常用的概念。
Tips:其实vector可以看作matrix的特殊情况——把:
的矩阵称作向量。
线性代数中矩阵和向量可以看作同一个东西。
2.array数组提取元素
提取元素就是引用,array中的引用元素的方式跟python一样,这里不做过多解释:
1.格式:array[a:b][c]
2.如果是a:,:b,a:b,就表示提取一个范围的元素
3.如果知识单个数字就是提取特定数值
# arr3就是一个典型的rgb图数字化矩阵
arr3=np.random.rand(3,28,28)
# 提取dim0单通道图片的数字矩阵
# print(arr3[0])
print(arr3[0].shape)
# 提取1-2通道数字矩阵
print(arr3[1:3])
print(arr3[1:3].shape)
# 提取dim2通道的(14,14)像素值
print(arr3[2][13][13])
print(type(arr3[2][13][13]))
(28, 28)
[[[0.48833191 0.49227286 0.88447359 ... 0.02841924 0.54437058 0.326423 ]
[0.07196849 0.15764609 0.39646607 ... 0.46262122 0.27459863 0.59244376]
[0.16523175 0.00289342 0.46085036 ... 0.12362236 0.75732681 0.61543985]
...
[0.39153824 0.75012216 0.02920516 ... 0.99311179 0.25745318 0.38009034]
[0.75869929 0.66871767 0.92249438 ... 0.48210471 0.88380983 0.05764281]
[0.26983395 0.83233818 0.0950505 ... 0.16445969 0.60758993 0.3941144 ]]
[[0.4411464 0.26172997 0.0181014 ... 0.23755507 0.85238282 0.52344811]
[0.1808304 0.1239361 0.23753657 ... 0.54842241 0.30538224 0.85262662]
[0.57752354 0.04522684 0.10117772 ... 0.675709 0.99090207 0.33322352]
...
[0.67172909 0.90576698 0.59510418 ... 0.70290994 0.88010551 0.84262616]
[0.66646581 0.99893419 0.98898849 ... 0.18653642 0.06164357 0.54193373]
[0.74116007 0.9354973 0.5482605 ... 0.97091991 0.22159573 0.01893494]]]
(2, 28, 28)
0.6671540194279799
<class 'numpy.float64'>
Tips:就算是scaler数据,arr3[2][13][13]还是numpy数据哦,并不是python的基础数据类型——float
3.numpy的数据类型
# 每一种数据类型都有一个对应的转换函数
arr4=np.random.rand(3,28,28)
print(arr4.dtype)
arr4=np.float32(arr4)
print(arr4.dtype)
float64
float32
Tips:
1.complex(复数)是不能转换成int(整型)的哦
2.complex也不能转换成float类型
3.但是float可以转换成complex,float数值充当complex指定部位数据
4.array数组的形状改变
一般来说我们不会单独使用numpy,而是会与其他一些包——比如说:Image、torch库联合使用从而完成某些特定任务。在torch中遵循的是“channel-first”原则,但是numpy没有特定要求,所以这就需要我们对数组形状进行改变。
arr5=np.random.randint(low=0,high=9,size=(5,5,3))
print(arr5.shape)
# ravel:完成展平操作,但是不会申请内存,只会返回一个视图
arr5.ravel()
print(arr5.ravel())
# 这里的展平看起来就是将数组一行一行拼接起来
print(arr5.shape)
# 这里就说明仅仅是返回view并不是原地修改
(5, 5, 3)
[7 3 3 5 8 8 7 0 0 8 8 8 2 5 0 3 6 2 6 4 1 5 4 4 6 2 4 7 0 2 3 5 4 5 4 4 7
8 2 3 1 4 4 2 7 2 6 7 7 5 7 4 1 5 2 6 3 6 7 4 4 2 4 0 0 4 0 6 7 6 6 6 2 2
2]
(5, 5, 3)
# flatten:功能与ravel一样但是会申请内存存储展平数据
print(arr5)
print(arr5.flatten())
[[[7 3 3]
[5 8 8]
[7 0 0]
[8 8 8]
[2 5 0]]
[[3 6 2]
[6 4 1]
[5 4 4]
[6 2 4]
[7 0 2]]
[[3 5 4]
[5 4 4]
[7 8 2]
[3 1 4]
[4 2 7]]
[[2 6 7]
[7 5 7]
[4 1 5]
[2 6 3]
[6 7 4]]
[[4 2 4]
[0 0 4]
[0 6 7]
[6 6 6]
[2 2 2]]]
[7 3 3 5 8 8 7 0 0 8 8 8 2 5 0 3 6 2 6 4 1 5 4 4 6 2 4 7 0 2 3 5 4 5 4 4 7
8 2 3 1 4 4 2 7 2 6 7 7 5 7 4 1 5 2 6 3 6 7 4 4 2 4 0 0 4 0 6 7 6 6 6 2 2
2]
# reshape:输入一个整型元组表示目标shape来完成数组形状的修改
print(arr5.shape)
arr5.reshape((3,5,5))
print(arr5.shape)
# 我们发现reshape并不是“原地修改”,而是有一个返回值。
arr6=arr5.reshape((3,5,5))
print(arr6.shape)
(5, 5, 3)
(5, 5, 3)
(3, 5, 5)
# 那么如果我想要完成原地修改呢?可以使用resize
print(arr5.shape)
arr5.resize(3,5,5)
print(arr5.shape)
(5, 5, 3)
(3, 5, 5)
# transpose函数:pytorch中也有这个函数,作用就是直接输入维度index来完成形状转换
arr6=np.random.randint(0,9,(5,))
print(arr6)
print(arr6.transpose())
# 看得出来transpose对于向量是没有作用的
[7 5 5 2 3]
[7 5 5 2 3]
arr7=np.random.randint(0,9,(5,5))
print(arr7)
print(arr7.transpose())
# 对于二维矩阵是完成了转置
[[2 3 6 5 4]
[5 4 1 1 7]
[3 4 5 8 3]
[4 2 2 2 4]
[4 2 4 6 5]]
[[2 5 3 4 4]
[3 4 4 2 2]
[6 1 5 2 4]
[5 1 8 2 6]
[4 7 3 4 5]]
arr8=np.random.randint(0,9,(3,4,2))
print(arr8.shape)
print(arr8.transpose().shape)
# 如果对于一个tensor就是交换0,2维度
(3, 4, 2)
(2, 4, 3)
arr8=np.random.randint(0,9,(3,4,2,1))
print(arr8.shape)
print(arr8.transpose().shape)
# 现在我们就可以发现transpose其实完成的就是一个维度对称交换
(3, 4, 2, 1)
(1, 2, 4, 3)
Tips:
值得一提的是如果我们对1.21.1的numpy.ndarray.transpose进行参数输入会发生报错
arr8.transpose(axes=[3,2,1,0])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_19128/3580215624.py in <module>
----> 1 arr8.transpose(axes=[3,2,1,0])
TypeError: transpose() takes no keyword arguments
但是如果使用numpy.transpose(ndarray)就不会发生这种问题:
print(arr8.shape)
print(np.transpose(arr8,[3,2,1,0]).shape)
(3, 4, 2, 1)
(1, 2, 4, 3)
总结一下:
1.如果使用numpy.ndarray.transpose方法:
(1)不能输入参数(version=1.21.1)
(2)完成维度的对称交换
2.如果使用numpy.transpose函数:
(1)能输入参数序列,这个序列就是目标输出的维度下标序列
eg:(输入维度序列:[0,1,2,3]->输出维度序列[3,2,1,0])
(2)可以完成维度的任意交换,实用性更强。
5.array数组的组合
# 三种维度的组合:水平组合(hstack)、垂直组合(vstack)、深度组合(dstack)
a0=np.random.randint(0,9,(3,3))
a1=np.eye(3,dtype='int') # 生产单位矩阵
print("a0:\n{}\n\na1:\n{}\n".format(a0,a1))
print('hstack:\n{}\n'.format(np.hstack([a0,a1])))
print('vstack:\n{}\n'.format(np.vstack([a0,a1])))
print('dstack:\n{}\n'.format(np.dstack([a0,a1])))
a0:
[[6 3 2]
[1 2 4]
[5 3 2]]
a1:
[[1 0 0]
[0 1 0]
[0 0 1]]
hstack:
[[6 3 2 1 0 0]
[1 2 4 0 1 0]
[5 3 2 0 0 1]]
vstack:
[[6 3 2]
[1 2 4]
[5 3 2]
[1 0 0]
[0 1 0]
[0 0 1]]
dstack:
[[[6 1]
[3 0]
[2 0]]
[[1 0]
[2 1]
[4 0]]
[[5 0]
[3 0]
[2 1]]]
这三种维度的组合很好理解就跟拼积木一样,但是需要注意:
a0=np.random.randint(0,9,(3))
a1=np.random.randint(0,9,(3))
print(np.dstack([a0,a1]).shape)
(1, 3, 2)
a0=np.random.randint(0,9,(3,3))
a1=np.random.randint(0,9,(3,3))
print(np.dstack([a0,a1]).shape)
(3, 3, 2)
a0=np.random.randint(0,9,(3,3,1))
a1=np.random.randint(0,9,(3,3,1))
print(np.dstack([a0,a1]).shape)
(3, 3, 2)
a0=np.random.randint(0,9,(1,3,3))
a1=np.random.randint(0,9,(1,3,3))
print(np.dstack([a0,a1]).shape)
(1, 3, 6)
a0=np.random.randint(0,9,(3,3,1,1))
a1=np.random.randint(0,9,(3,3,1,1))
print(np.dstack([a0,a1]).shape)
(3, 3, 2, 1)
a0=np.random.randint(0,9,(3,3,1,1,1))
a1=np.random.randint(0,9,(3,3,1,1,1))
print(np.dstack([a0,a1]).shape)
(3, 3, 2, 1, 1)
看出来没有?所谓的深度组合实际上是指定了dim2——第三维度上的叠加(若没有第三维度,则进行reshape((h,w,1)))
接下来再介绍2种方向组合以及一个“万能组合函数”。
a0=np.random.randint(0,9,(4,5,3))
a1=np.random.randint(0,9,(4,5,3))
# 行组合
print(np.row_stack([a0,a1]).shape) # 顾名思义就是将数组一行一行地叠加起来
# 列组合
print(np.column_stack([a0,a1]).shape) # (同上)
(8, 5, 3)
(4, 10, 3)
我们发现这两个的效果和上面的vstack、hstack有点相似,但是我们得“极端一下”来验证验证:
a0=np.random.randint(0,9,(4,5,7,3,2,3,1))
a1=np.random.randint(0,9,(4,5,7,3,2,3,1))
print(np.row_stack([a0,a1]).shape)
print(np.column_stack([a0,a1]).shape)
(8, 5, 7, 3, 2, 3, 1)
(4, 10, 7, 3, 2, 3, 1)
可以看到,我们的猜想是对的。
实际上你会发现:这些函数设计出来其实就是方便不同应用场合的理解,但实际上它们就是一个函数——concatenate(融合)。
a0=np.random.randint(0,9,(4,5,7,3,2,3,1))
a1=np.random.randint(0,9,(4,5,7,3,2,3,1))
dims=a0.ndim # 读取维度数(a0.ndim=a1.ndim)
for i in range(dims):
print(np.concatenate([a0,a1],axis=i).shape)
(8, 5, 7, 3, 2, 3, 1)
(4, 10, 7, 3, 2, 3, 1)
(4, 5, 14, 3, 2, 3, 1)
(4, 5, 7, 6, 2, 3, 1)
(4, 5, 7, 3, 4, 3, 1)
(4, 5, 7, 3, 2, 6, 1)
(4, 5, 7, 3, 2, 3, 2)
用法很简单,指定要组合的序列,再指定要融合的维度下标即可。
6.array数组的分割
有组合,那就有对应的分割。跟组合一样,其实也是有专门的函数和万能分割函数(split)的。
这里我直接上代码,理解方式同组合。
但是我们首先明确一点:split操作相较于concatenate操作,参数有变化:
1.split输入单个array+切片序列(indices_sequence)+可选的维度下标
2.concatenate输入array_sequence+可选维度下标
a2=np.random.randint(0,9,(8,4,3,5,6,3))
print("a2's shape:\n{}\n".format(a2.shape))
# 水平切割,将其切割成4个数组后形成的list变成array,再输出shape
print(np.array(np.hsplit(a2,4)).shape)
# 垂直切割(2个生成数组)
print(np.array(np.vsplit(a2,2)).shape)
# 深度切割
print(np.array(np.dsplit(a2,3)).shape)
a2's shape:
(8, 4, 3, 5, 6, 3)
(4, 8, 1, 3, 5, 6, 3)
(2, 4, 4, 3, 5, 6, 3)
(3, 8, 4, 1, 5, 6, 3)
现在介绍一下分割操作中的万能函数——split
list_array=np.array(np.split(a2,[2,4,5,6],axis=0))
for arr in list_array:
print(arr.shape)
(2, 4, 3, 5, 6, 3)
(2, 4, 3, 5, 6, 3)
(1, 4, 3, 5, 6, 3)
(1, 4, 3, 5, 6, 3)
(2, 4, 3, 5, 6, 3)
可以看到,有三个参数:
1.输入数组:ary
2.切片序列:[a0,a1,a2,…,an]或者是一个可以被:ary.shape[axis]整除的数字n。
3.可选参数:axis(默认值为0)
如果切片序列是一个可以被:ary.shape[axis]整除的数字n:
那么会在下标为axis的维度上将数组平均分为n个小数组并存储到list中
如果切片序列是[a0,a1,a2,…,an]这样的一个有序的下标序列,那么会将输入数组在axis维度上按[:a0],[a0:a1],[a1:a2]…的规律分为一个个小数组并存储到list中
7.array数组的属性
a3=np.random.randint(0,9,(8,4,3,5,6,3))
1.维度数
print(a3.ndim)
print(len(a3.shape)==a3.ndim)
6
True
2.元素个数
print(a3.size)
count=8*4*3*5*6*3
print(count==a3.size)
8640
True
3.元素所占字节数和整个数组所占字节数
print(a3.itemsize)
print(a3.nbytes)
print(a3.nbytes==a3.size*a3.itemsize) # nbytes:数组所占空间大小(bit)
4
34560
True
4.Transpose
a3_t=a3.T
print(a3.shape)
print(a3_t.shape)
(8, 4, 3, 5, 6, 3)
(3, 6, 5, 3, 4, 8)
5.复数的实数、虚数读取
comp=np.array([1.j+1,2.j+3]) # numpy的复数的虚部用j表示
print(comp)
print(comp.real)
print(comp.imag)
[1.+1.j 3.+2.j]
[1. 3.]
[1. 2.]
6.扁平迭代器(flat_iter)
Tips:这是实例化flatiter对象的唯一方式.flatiter的构造函数无法访问
# 起作用就是让我们可以以遍历一维数组的方式遍历多维数组
a3=np.random.randint(0,9,(4,5))
print(a3)
sum=0
for item in a3.flat:
if sum%5==0:
print("\n")
print(item,end=' ')
sum+=1
[[7 3 1 5 3]
[4 3 2 4 7]
[7 0 7 1 3]
[3 5 6 0 2]]
7 3 1 5 3
4 3 2 4 7
7 0 7 1 3
3 5 6 0 2
# 同时flat属性可以赋值
a3.flat=1
print(a3)
# 这会导致flat全部元素直接被覆盖,从而完成原地修改
[[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]
[1 1 1 1 1]]
总结一下:
8.array数组与python列表(list)的之间的转换
# 使用tolist和astype函数
a3.astype(int)
list3=a3.tolist()
print(list3)
[[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]