一边学习一边分享,好记性不如烂笔头
目录
一边学习一边分享,好记性不如烂笔头
NumPy问题思考:
numpy是什么?
为什么要学习numpy?
numpy是怎么组成的?特点是什么?
numpy的应用场景有哪些?
NumPy介绍:
Tensor概念:
1、ndarray数组
1.1、为什么引入ndarray数组
1.2、创建ndarray数组
1.3、查看ndarray数组的属性
1.4、改变ndarray数组的数据类型和形状
1.5、ndarray数组的基本运算
1.6、ndarray数组的索引和切片
1.6.1、一维ndarray数组的索引和切片
2、随机数np.random
2.1、创建随机ndarray数组
2.2、随机打乱ndarray数组顺序
2.3、随机选取元素
3、NumPy保存和导入文件
3.1、文件读写
3.2、文件保存
4、应用举例
4.1、图像翻转和裁剪
4.1.1、库安装:
4.1.2、简单读入:
4.1.3、在pychram中查看原始图片【注意要弄一个窗口,控制台无法直接显示】:
4.1.4、图像翻转【效果用代码跑一遍】:
4.1.5、水平翻转【效果用代码跑一遍】:
4.1.6、水平和垂直都翻转【效果用代码跑一遍】:
4.1.7、保存图片【效果用代码跑一遍】:
4.1.8、裁剪图片【效果用代码跑一遍】:
4.1.9、调整亮度
1、NumPy是Python科学计算中非常重要的基础库之一,能更好地应用于数据处理、科学计算和机器学习等领域;
2、NumPy是一个提供了多维数组对象和各种数组操作函数的库
3、NumPy 将python提供的模块中列表和元组对对象进行了封装,并且加入了很多便于使用数组的方法
4、学习完NumPy后建议学习pandas,pandas是进一步对NumPy的封装
1、大大提升了python中数据运算的速度
2、更重要的是NumPy将问题向量化、并行化的思路,在SIMD、CUDA、Shader编程、深度学习的时候都会用到
3、应用场景多都会用到该库
4、是科学计算中非常重要的一个工具,适用于处理多维数组和矩阵,尤其在机器学习和数据科学领域广泛应用
5、在科学计算和机器学习领域,NumPy是一个必不可少的工具,它可以用于处理数据、训练模型和测试模型等方面,并且支持多种常用算法和工具,例如线性回归、逻辑回归、聚类、降维等。
1、底层是C语言编写的多维数组库,所以运行速度更快,提高了数据科学家和研究人员的工作效率。
2、提供了高效地读取、创建、预处理和计算多维数组的方法。
1、数据处理和分析:轻松实现数据的读取、处理、转换和整理
2、科学计算:成为统计分析、概率论、线性代数、图像处理和信号处理等领域的重要工具
3、机器学习和人工智能:可以实现各种机器学习算法,比如回归,分类和聚类等。
4、计算机视觉:如图像重构,特征提取,图像分割等。
NumPy(Numerical Python的简称)是高性能科学计算和数据分析的基础包;
构建神经网络模型时,通常会使用NumPy实现数据预处理和一些模型指标的计算,Tensor数据可以很方便的和ndarray数据进行转换;
NumPy具有如下功能:
ndarray数组:一个具有矢量算术运算符和复杂广播能力的多维数组,具有快速且节省空间的特点;
对整组数据进行快速运算的标准数学函数(无需编写循环);
线性代数、随机数生成以及傅里叶变换功能;
读写磁盘数据、操作内存映射文件;
很多学习框架都是使用Tensor来表示数据,在神经网络中传递的数据均为Tensor。
可以理解成多维数组,可以具有任意多的维度,不同Tensor可以有不同的数据类型(dtype)和形状(shape)。同一Tensor的所有元素的dtype都相同;
如果你对NumPy熟悉,Tensor是类似于NumPy array的概念,接下来就来学学NumPy,案例;
a = np.asarray([1, 2, 3, 4])
b = np.asarray([22, 3, 44, 66])
c = a + b
print(c)
ndarray数组是NumPy的基础数据结构,可以灵活、高效的处理多个元素的操作,主要从五部分展开介绍:
为什么引入ndarray数组
如何创建ndarray数组
ndarray数组的基本运算
ndarray数组的切片和索引
ndarray数组的统计运算
Python中的list列表也可以非常灵活的处理多个元素的操作,但是效率低!与之相比,ndarray数组具有以下特点:
ndarray数组中的所有元素的数据类型相同,数据地址连续,批量操作数组元素时速度更快。
而list列表元素数据类型可以不同,需要通过寻址方式才能找到下一个元素;
ndarray数组支持广播机制,矩阵运算时不需要写for循环;
NumPy底层使用c语言编写,内置并行计算功能,运算速度高于python代码。
举几个例子来证明:
———————案例1———————
# python原生的list操作
a = [1, 2, 3, 4, 5]
b = [2, 3, 4, 5, 6]
# 对a列表的元素+1
# a = a + 1 # 这样会报错
for i in range(5):
a[i] = a[i] + 1
print(a)
# numpy的 a+1 操作方式
b = np.array(b)
b = b + 1
print(b)
———————案例2———————
a = [1, 2, 3, 4, 5]
b = [2, 3, 4, 5, 6]
# 实现c=a+b
# ①传统python的list操作
c = []
for i in range(5):
c.append(a[i] + b[i])
print(c)
# ②numpy的操作方式
a = np.array(a)
b = np.array(b)
c1 = a + b
print(c1)
总结:通过上面的两个案例可以看出,在不写for循环的情况下,ndarray数组就可以非常方便的完成数学计算,在编写矢量或者矩阵的程序时,可以像编写普通数值一样,使得代码非常简洁;
另外,ndarray数组还提供了广播机制,它会按一定规则自动对数组的维度进行扩展以完成计算,下面的例子:一维数组和二维数组进行相加的操作,ndarray就会自动扩展一维数组的维度,然后再对每个位置的元素分别相加;
案例:
———————案例1———————
# 自动广播机制,1维数组和2维数组相加
a = [[1, 2, 3, 4], [5, 6, 7, 8]]
a = np.array(a)
b = [11, 22, 33, 44]
print(a+b)
———————效果———————
[[12 24 36 48]
[16 28 40 52]]
注意事项:
广播机制仅适用于形状相似的数组,或者可以通过扩展数组形状使它们变得相似。
两个数组在任一维度上的大小要么相等,要么其中一个数组的大小为 1;
广播机制不会修改原数组的值,而是返回一个新数组。
创建ndarray数组最简单的方式就是使用 array()函数,他接受一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组。下面通过实例体会下array、arange、zeros、ones四个主要函数的用法;
array():创建嵌套序列(比如由一组等长列表组成的列表),并转换为一个多维数组。
———————案例1———————
# array创建数组
a = [1, 2, 3, 4]
b = np.array(a)
print(b)
c = np.array([5, 6, 7])
print(c)
———————效果———————
[1 2 3 4]
[5 6 7]
arange(start,stop,step):创建从start开始到stop结束的数组,左闭右开,步长为step
———————案例1———————
# arange 创建ndarray数组 左闭右开 从1开始,步长为2,一直到10
a = np.arange(1, 10, 2)
print(a)
———————效果———————
[1 3 5 7 9]
zeros([]):创建指定长度或者形状的全0数组,括号里面是“,”隔开,数字位数可无限写
———————案例1———————
# zeros([]): 默认是浮点型,所有会有".",
# 括号如果是一个值,就是一维数组,
# 2个值[10,4]:二维,n列,
# 3个值[10,3,4]:创建10组3行4列的三维数组
a = np.zeros([10,3,4])
print(a)
———————效果———————
[[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
......
ones([]):效果和zeros([])一样,只是数值为1,这两个可以用来重置,比如棋盘,初始化;
ndarray的属性包括shape、dtype、size和ndim等,通过如下代码可以查看数组的属性。
shape:数组的形状ndarray.shape,1维(N,),2维(M,N),3维(M,N,K);
dtype:数组的数据类型;
size:数组中包含的元素个数ndarray.size,其大小等于各个维度的长度的乘积;
ndim:数组的维度大小,ndarray.ndim,其大小等于ndarray.shape所包含元素的个数;
———————案例1———————
# 查看ndarray的各个属性
a = np.ones([5])
print(a)
print("a的形状:", a.shape)
print("a的数据类型:", a.dtype)
print("a的元素个数:", a.size)
print("a的维度大小:", a.ndim,"维")
———————效果———————
[1. 1. 1. 1. 1.]
a的形状: (5,)
a的数据类型: float64
a的元素个数: 5
a的维度大小: 1 维
生成随机数
———————案例1———————
# 生成随机数 [0,1)之间生成2个
b = np.random.rand(2)
print(np.random.random(5))
print(b)
print("维度:",b.ndim)
———————效果———————
[0.72329108 0.29206742 0.16020119 0.0181518 0.15668087]
[0.28995899 0.99508755]
维度: 1
创建ndarray之后,可以对其数据类型(astype)或形状(reshape)进行修改,代码如下:
———————案例1———————
# 改变数组的类型和形状
a = np.random.rand(2, 3)
print(a, a.dtype, a.shape)
# 转化数据类型astype 这里是0是因为把小数转没了
b = a.astype(np.int64)
print(b)
# 改变形状 reshape
c = b.reshape([1, 6])
print("c的形状:",c)
———————效果———————
[[0.5554663 0.93000141 0.70539356]
[0.21511709 0.00952697 0.83371317]] float64 (2, 3)
[[0 0 0]
[0 0 0]]
c的形状: [[0 0 0 0 0 0]]
改形状注意事项:
个数要对应上,比如在这里不能写成1,5
如果后面参数是-1,前面规定行数,后面能分配成多少列,系统自动分配,可以减少报错,但是前面分配出错导致后面分配不够,那也会报错;比如总数是6个,第一个参数不能写成4;
ndarray数组可以像普通的数值型变量一样进行加减乘除操作,主要包含如下两种运算:
标量(可以理解成正常的数值,常量)和ndarray数组之间的运算
两个ndarray数组之间的运算
———————案例1———————
# 标量加上数组,用标量加上数组的每一个元素
a = np.array([[1, 2, 3], [4, 5, 6]])
print(2.0 + a)
# 标量减法
print(2.0 - a)
# 标量乘法
print(2.0 * a)
# 标量除法 取模等
print(2.0 / a)
print(2.0 % a)
# 数组与数组 1组和3组可以,2组和3组就没法,要有一个基础值
a = np.array([[1, 2, 3]])
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(a + b)
print(a - b)
print(a * b)
print(a / b)
在编写模型过程中,通常需要访问和修改ndarray数组某个位置的元素,所以需要索引;有些情况下可能需要访问或者修改一些区域的元素,则需要切片;
ndarray数组的索引和切片的使用方式与python中的list类似,通过[-n,n-1]的下标进行索引,通过内置的slice函数,设置其start,stop和step参数进行切片,从原数组中切割出一个新数组;
ndarray数组的索引是一个内容丰富的主题,因为选取数据子集或单个元素的方式有很多,下面从一维数组和多维数组两个维度介绍索引和切片的方法。
从表面上看,一维数组跟Python列表的功能类似,他们重要区别在于:数组切片产生的新数组,还是指向原来的内存区域,数据不会被复制,视图上的任何修改都会直接反应到原数组上,将一个标量赋值给一个切片时,该值会自动传播到整个选区;
python的列表切片是单独复制一份,重新创建一块内存区域
numpy是指向原地址
———————案例1———————
# numpy一位数组的索引和切片使用
a = np.array([1, 2, 3, 4])
a[1] = 10
print("a数组的样子:", a)
b = a[0:2]
b[0] = 10
print("更改后a数组的样子:", a)
print("b数组的样子:", b)
———————效果———————
a数组的样子: [ 1 10 3 4]
更改后a数组的样子: [10 10 3 4]
b数组的样子: [10 10]
———————案例1———————
# numpy二维数组的索引和切片使用
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("单个索引:", a[0])
print("两个索引-写法1:", a[0][1])
print("两个索引-写法2:", a[0, 1])
print("————————————切片用法")
b = a[0:2]
b[0, 1] = 10
print(b)
print(a)
———————效果———————
单个索引: [1 2 3]
两个索引-写法1: 2
两个索引-写法2: 2
—————切片用法
[[ 1 10 3]
[ 4 5 6]]
[[ 1 10 3]
[ 4 5 6]
[ 7 8 9]]
综上,我们会发现切片的数据更改后,原数据都改了,这就是因为地址引用,都是指向的原来的数据,那么我怎么在numpy里面解决这种问题呢?
解决办法:通过copy给新数组创建不同的内存空间
———————案例1———————
# numpy二维数组的索引和切片使用
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("单个索引:", a[0])
print("两个索引-写法1:", a[0][1])
print("两个索引-写法2:", a[0, 1])
print("————————————切片用法")
b = a[0:2]
b=np.copy(b)
b[0, 1] = 10
print(b)
print(a)
———————效果———————
单个索引: [1 2 3]
两个索引-写法1: 2
两个索引-写法2: 2
————————————切片用法
[[ 1 10 3]
[ 4 5 6]]
[[1 2 3]
[4 5 6]
[7 8 9]]
主要介绍创建ndarray随机数组以及随机打乱顺序,随机选取元素等相关操作方法;
创建随机ndarray数组主要包含设置随机种子、均匀分布和正态分布三部分内容,具体代码如下:
设置随机种子
———————案例1———————
# 设置随机种子的随机数
# 相同的种子值时,多次生成的随机数序列将完全相同,所以一直设置为1出来的随机数都是相同的
np.random.seed(1)
a = np.random.rand(3, 3)
print(a)
———————效果———————
[[4.17022005e-01 7.20324493e-01 1.14374817e-04]
[3.02332573e-01 1.46755891e-01 9.23385948e-02]
[1.86260211e-01 3.45560727e-01 3.96767474e-01]]
均匀分布,uniform可以指定范围
———————案例1———————
# 生成均匀分布随机数,随机数取值范围在[0,1)之间,每次不一样
a=np.random.rand(3,3)
print(a)
# 生成均匀分布随机数,指定随机数取值范围和数据形状,uniform方法 low=最小, high=最大
a = np.random.uniform(-1.0, 2.0, size=(2, 2))
print(a)
———————效果———————
[[0.29134465 0.22175308 0.86045294]
[0.35354551 0.48047924 0.64565728]
[0.00449983 0.64313651 0.37337913]]
正态分布
# 正态分布 生成标准正态分布(均值为0,方差为1)随机数,random 2就是正态分布模式
np.random.seed(2)
# a = np.random.randn(3, 3)
aa = np.random.normal(1.0, 2.0, size=(3, 3))
print(aa)
随机打乱一维ndarray数组顺序,发现所有元素位置都被打乱了,代码如下:
———————案例1———————
# 打乱一维数组,先生成
a = np.arange(30)
print("打乱前数组为:", a)
np.random.shuffle(a)
print("打乱后:", a)
———————效果———————
打乱前数组为: [ 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]
打乱后: [21 16 17 20 19 2 14 12 15 26 3 8 22 7 10 5 25 18 1 29 27 4 28 6
9 24 11 0 23 13]
二维数组打乱[只有行乱,列不乱],代码如下:
———————案例1———————
# 打乱二维数组
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [11, 12, 13], [14, 15, 16], [17, 18, 19]])
print("打乱前数组为:", a)
np.random.shuffle(a)
print("打乱后:", a)
———————效果———————
打乱前数组为: [[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[11 12 13]
[14 15 16]
[17 18 19]]
打乱后: [[ 7 8 9]
[11 12 13]
[ 4 5 6]
[17 18 19]
[ 1 2 3]
[14 15 16]]
Process finished with exit code 0
在一个已有的数组中随机选取指定个数的元素组成新数组,上代码:
———————案例1———————
# 随机选取元素
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
b = np.random.choice(a, 6)
print(b)
———————效果———————
[ 9 1 2 2 10 8]
fromfile函数可以从文件读取数据文档,比如.txt或者.data
1、在项目中创建test.txt文件
2、复制一些数据进去,这个可以用a = np.random.randn(20)生成正态分布的数据
3、读取的代码如下:
b = np.fromfile('test.txt', sep=' ')
print(b)
———————效果———————
[ 0.71487971 -1.2266247 0.21812245 2.02429348 -0.61132262 1.21231402
0.57125962 -0.01415773 1.57852762 -0.3208648 -2.25181538 -0.35990666
1.66586577 -0.88487449 -1.07157014 -0.6817527 0.11183847 0.28539159
-0.90093036 0.5402392 ]
NumPy提供了save和load接口,直接将数组保存成文件(.npy格式),或者从.npy格式文件读取数组;
———————案例1———————
# 文件保存
# 创建一个二维数组
a = np.random.rand(3, 3)
print("生成的数据为:", a)
np.save('a.npy', a)
b = np.load('./a.npy')
print("从磁盘读取的数据为:", b)
———————效果———————
生成的数据为: [[0.98094485 0.99442615 0.86263148]
[0.75966441 0.95502318 0.33871343]
[0.27829987 0.56337243 0.38992223]]
从磁盘读取的数据为: [[0.98094485 0.99442615 0.86263148]
[0.75966441 0.95502318 0.33871343]
[0.27829987 0.56337243 0.38992223]]
NumPy既然是可以操作数组,那么最典型的就是处理计算机视觉相关的图片领域了,图片是由一个一个点组成的;
图像是由像素点构成的矩阵,其数值可以用ndarray来表示。将上述介绍的操作用在图像数据对应的ndarray上,就能很轻松的实现图片的翻转、裁剪和亮度调整。
在这里,图片文件是可以提供的,但是还需要一个新库matplotlib;
Matplotlib 是一种用于数据可视化的 Python 库,可以用于绘制各种类型的图形
//pip安装
pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
//导入库
import matplotlib.pyplot as plt
from PIL import Image
———————案例1———————
# 项目展示
# 图片导入
image = Image.open('./img.jpg')
image = np.array(image)
# 查看图片的形状, (249, 252, 3) H是高,W是宽,3是通道,jpg有3个,png有个a透明通道
print("图片的形状", image.shape)
———————效果———————
图片的形状 (249, 252, 3)
# 指定要显示的图片
plt.imshow(image)
# 建立一个窗口,让图片显示出来
plt.show()
# 垂直方向翻转,思维步骤:
# 1、使用数组切片的方式完成
# 2、相当于将图片最后一行挪到第一行...一直切换
# 3、对于行来说,使用::-1来表示,从开始到结尾,然后反过来
# 4、对于列来说和RGB通道,使用:表示该维度不改变即可
image = Image.open('./img.jpg')
image = np.array(image)
image2 = image[::-1, :, :]
# 输出更改后的图片
plt.imshow(image2)
plt.show()
# 水平方向翻转,思维步骤
# 改列的顺序
image = Image.open('./img.jpg')
image = np.array(image)
image3 = image[:, ::-1, :]
# 输出
plt.imshow(image3)
plt.show()
# 都反转
image = Image.open('./img.jpg')
image = np.array(image)
image3 = image[::-1, ::-1, :]
plt.imshow(image3)
plt.show()
# 都反转
image = Image.open('./img.jpg')
image = np.array(image)
image3 = image[:, :, ::-1]
plt.imshow(image3)
plt.show()
# 保存图片
img=Image.fromarray(image3)
img.save("./11.jpg")
# 高度、宽度裁剪,注意要找到起点和终点
# 获取到高和宽
image = Image.open('./img.jpg')
image = np.array(image)
print(image.shape)
H = image.shape[0]
W = image.shape[1]
# 高度裁剪,1/2,这里需要两个斜杠才是除法,单根不算
H1 = H // 2
W1=W//2
image4 = image[H1:H,W1:W, :]
plt.imshow(image4)
plt.show()
# 调整亮度
image = Image.open('./img.jpg')
image = np.array(image)
image = image * 1.5
# 要把类型转换一下,不然数值处理不好
plt.imshow(image.astype('uint8'))
plt.show()