[数据分析基础] 1. NumPy库
文章目录
-
- [数据分析基础] 1. NumPy库
-
-
- 一、NumPy的数组对象:ndarray
-
- 1. ndarray对象的属性
- 2. ndarray数组的元素类型
- 二、ndarray数组的创建和变换
-
- 1. 从列表、元组等类型中创建ndarray数组
- 2. 使用NumPy中函数创建ndarray数组
- 3. ndarray数组的维度变换
- 4. ndarray数组的类型变换
- 三、ndarray数组的操作与运算
-
- 1. 数组的索引和切片
- 2. NumPy一元函数
- 3. NumPy二元函数
- 四、Numpy中文件的存取
-
- 1. 数据的CSV文件存取
- 2. 多维数据的存取
- 3. NumPy的便捷文件存取
- 五、NumPy的函数
-
- 1. NumPy的随机数函数
- 2. NumPy的统计函数
- 3. NumPy的梯度函数
- 六、实例:图像的手绘效果
-
- 1. 图像的数组表示
- 2. 图像的变换
- 3. 图像的手绘效果
-
- 3.1 梯度的重构
- 3.2 光源效果
- 3.3 梯度归一化
- 3.4 图像生成
- 3.5 全代码
-
Numpy是一个开源的python科学计算基础库,包含
- 一个强大的N维数组对象ndarry
- 广播功能函数
- 整合C/C++/Fortran代码的工具
- 线性代数、傅里叶变换、随机数生成等功能
NumPy是SciPy、Pandas等数据处理或科学计算库的基础
一、NumPy的数组对象:ndarray
问题:Python已有列表类型表示数组,为什么需要一个数组对象(类型)?
计算 A 2 + B 3 A^2+B^3 A2+B3,其中,A和B是一维数组
- 普通方法
def pySum(): a=[0,1,2,3,4] b=[9,8,7,6,5] c=[] for i in range(len(a)): c.append(a[i]**2+b[i]**3) return c print(pySum())
- Numpy科学计算
import numpy as np #引入numpy模块 def npSum(): a = np.array([0,1,2,3,4]) #生成一个数组 b = np.array([9,8,7,6,5]) c = a**2 + b**3 # '**' 数组中每个元素的次方 return c print(npSum())
- 使用ndarry的原因
- 数组对象可以去掉元素间运算所需的循环,使一维向量更像单个数据。
- 通过设置专门的数组对象,经过优化,可以提升这类应用的运算速度。
- 数组对象采用相同的数据类型,有助于节省运算和存储空间。
ndarray是一个多维数组对象,由两部分构成:
- 实际的数据
- 描述这些数据的元数据(数据维度、数据类型等)
ndarray数组一般要求所有元素类型相同(同质),数组下标从0开始。
1. ndarray对象的属性
ndarray的两个基本概念
- 轴(axis): 保存数据的维度
- 秩(rank): 轴的数量
ndarray对象的属性 | 说明 |
---|---|
| 秩,即轴的数量或维度的数量 |
| ndarray对象的尺度,对于矩阵,n行m列 |
| ndarray对象元素的个数,相当于.shape中的n*m的值 |
| ndarray对象的元素类型 |
| ndarray对象中每个元素的大小,以字节为单位 |
#np.array()生成一个ndarray数组
#ndarray在程序中的别名是:array
>>> a=np.array([[0,1,2,3,4],[9,8,7,6,5]])
>>> a
array([[0, 1, 2, 3, 4],
[9, 8, 7, 6, 5]])
>>> print(a) #打印时元素由空格分割
[[0 1 2 3 4]
[9 8 7 6 5]]
>>> a.ndim
2
>>> a.shape
(2,5)
>>> a.size
10
>>> a.dtype
dtype('int32')
>>> a.itemsize
4
2. ndarray数组的元素类型
数据类型 | 说明 |
---|---|
bool | 布尔类型,True 或False |
intc | 与 C语言中的int类型一致,一般是int32 或int64 |
intp | 用于索引的整数,与 C语言中ssize_t一致,int32 或int64 |
int8 | 字节长度的整数,取值: [ ‐128, 127] |
int16 | 16位长度的整数,取值: [ ‐32768, 32767] |
int32 | 32位长度的整数,取值: [ ‐ 231 , 231 ‐1] |
int64 | 64位长度的整数,取值: [ ‐ 263 , 263 ‐1] |
uint8 | 8位无符号整数,取值:[0, 255] |
uint16 | 16位无符号整数,取值:[0, 65535] |
uint32 | 32位无符号整数,取值:[0, 232 ‐1] |
uint64 | 32位无符号整数,取值:[0, 264 ‐1] |
float16 | 16位半精度浮点数: 1位符号位, 5位指数,10位尾数 |
float32 | 32位半精度浮点数: 1位符号位, 8位指数,23位尾数 |
float64 | 64位半精度浮点数: 1位符号位,11位指数,52位尾数 |
complex64 | 复数类型,实部和虚部都是32位浮点数 实部(.real) + j虚部(.imag) |
complex128 | 复数类型,实部和虚部都是64位浮点数 实部(.real) + j虚部(.imag) |
二、ndarray数组的创建和变换
1. 从列表、元组等类型中创建ndarray数组
① ndarray数组可由列表类型创建;② ndarray数组可由元组类型创建;③ ndarray数组可由列表和元组的混合类型创建(只要他们元素个数相同)
x = np.array(list/tuple)
、
x = np.array(list/tuple, dtype=np.float32)
当 np.array() 不指定 dtype 时,NumPy 将根据数据情况关联一个 dtype 类型
>>> x=np.array([0,1,2,3])
>>> print(x)
[0 1 2 3]
>>> x=np.array((4,5,6,7))
>>> print(x)
[4 5 6 7]
>>> x=np.array([[1,2],[9,8],(0.1,0.2)])
>>> print(x)
[[1. 2. ]
[9. 8. ]
[0.1 0.2]]
2. 使用NumPy中函数创建ndarray数组
函数 | 说明 |
---|---|
| 类似range()函数,返回ndarray类型,元素从0到n-1 |
| 根据shape生成一个全1数组,shape是元组类型 |
| 根据shape生成一个全0数组,shape是元组类型 |
| 根据shape生成一个数组,每个元素值都是val |
| 创建一个正方的n*n单位矩阵,对角线为1,其余为0 |
| 根据数组a的形状生成一个全1的数组 |
| 根据数组a的形状生成一个全0的数组 |
| 根据数组a的形状状生成一个数组,每个元素值都是val |
| 根据起止数据及元素个数等间距地填充数据,形成数组 如果将参数endpoint置为False,end将不作为最后一个元素出现 |
| 将两个或多个数组合并成一个新数组, a为一个元组 |
除了arrange产生的不是浮点类型,其他都产生浮点类型
In [1]: import numpy as np
In [2]: np.arange(10)
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [3]: np.ones((3,6))
Out[3]:
array([[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]])
In [4]: np.zeros((3,6),dtype=np.int32)
Out[4]:
array([[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]])
In [5]: np.eye(5)
Out[5]:
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.]])
In [6]: x=np.ones((2,3,4))
In [7]: print(x)
[[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]]
In [8]: x.shape
Out[8]: (2, 3, 4)
In [10]: np.full(10,4)
Out[10]: array([4, 4, 4, 4, 4, 4, 4, 4, 4, 4])
In [12]: np.ones_like(x)
Out[12]:
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
In [14]: np.zeros_like(x)
Out[14]:
array([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],
[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
In [15]: np.full_like(x,6)
Out[15]:
array([[[6., 6., 6., 6.],
[6., 6., 6., 6.],
[6., 6., 6., 6.]],
[[6., 6., 6., 6.],
[6., 6., 6., 6.],
[6., 6., 6., 6.]]])
In [16]: a=np.linspace(1,10,4)
In [17]: a
Out[17]: array([ 1., 4., 7., 10.])
In [18]: b=np.linspace(1,10,4,endpoint=False)
In [19]: b
Out[19]: array([1. , 3.25, 5.5 , 7.75])
In [20]: c=np.concatenate((a,b))
In [21]: c
Out[21]: array([ 1. , 4. , 7. , 10. , 1. , 3.25, 5.5 , 7.75])
3. ndarray数组的维度变换
方法 | 说明 |
---|---|
| 不改变数组元素,返回一个shape形状的数组,原数组不变 |
| 与.reshape()功能一致,但修改原数组 |
| 将数组 n个维度中两个维度进行调换 |
| 对数组进行降维,返回折叠后的一维数组,原数组不变 |
In [23]: a=np.ones((2,3,4),dtype=np.int32)
In [24]: a
Out[24]:
array([[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]],
[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]])
In [25]: a.reshape((3,8))
Out[25]:
array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1]])
In [26]: a
Out[26]:
array([[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]],
[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]])
In [27]: a.resize((3,8))
In [28]: a
Out[28]:
array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1]])
In [29]: a.flatten()
Out[29]:
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1])
In [30]: a
Out[30]:
array([[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1]])
In [31]: b=a.flatten()
In [32]: b
Out[32]:
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1])
4. ndarray数组的类型变换
new_a = a.astype(new_type)
:
astype()
方法一定会创建新的数组(原始数据的一个拷贝),即使两个类型一致
ls = a.tolist()
:将ndarray数组转换为列表形式
In [33]: a=np.ones((2,3,4),dtype=np.int)
In [34]: a
Out[34]:
array([[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]],
[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]])
In [35]: b=a.astype(np.float)
In [36]: b
Out[36]:
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
In [37]: a=np.full((2,3,4),25,dtype=np.int32)
In [38]: a
Out[38]:
array([[[25, 25, 25, 25],
[25, 25, 25, 25],
[25, 25, 25, 25]],
[[25, 25, 25, 25],
[25, 25, 25, 25],
[25, 25, 25, 25]]])
In [39]: a.tolist()
Out[39]:
[[[25, 25, 25, 25], [25, 25, 25, 25], [25, 25, 25, 25]],
[[25, 25, 25, 25], [25, 25, 25, 25], [25, 25, 25, 25]]]
三、ndarray数组的操作与运算
1. 数组的索引和切片
注意:索引和切片下标都是从0开始的
- 一维数组的索引和切片:起始编号: 终止编号(不含) : 步长
In [40]: a=np.array([9,8,7,6,5]) In [41]: a[2] Out[41]: 7 In [42]: a[1:4:2] #[1,4) Out[42]: array([8, 6])
- 多维数组的索引:每个维度一个索引值,逗号分割
In [43]: a=np.arange(24).reshape((2,3,4)) In [44]: a Out[44]: array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]], [[12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23]]]) In [45]: a[1,2,3] Out[45]: 23 In [46]: a[0,1,2] Out[46]: 6 In [47]: a[-1,-2,-3] Out[47]: 17
- 多维数组的切片:没有选取具体维度,那么可以用
表示所有维度。:
# 选取一个维度用 In [48]: a[:,1,-3] #限制行数为1,列数为-3 Out[48]: array([ 5, 17]) #每个维度切片方法与一维数组相同 In [49]: a[:,1:3,:] #限制行数为[1,3) Out[49]: array([[[ 4, 5, 6, 7], [ 8, 9, 10, 11]], [[16, 17, 18, 19], [20, 21, 22, 23]]]) #每个维度可以使用步长跳跃切片 In [50]: a[:,:,::2] #限制列数的步长为2 Out[50]: array([[[ 0, 2], [ 4, 6], [ 8, 10]], [[12, 14], [16, 18], [20, 22]]])
2. NumPy一元函数
数组与标量之间的运算作用于数组的每一个元素
函数 | 说明 |
---|---|
np.abs(x) np.fabs(x) | 计算数组各元素的绝对值 |
np.sqrt(x) | 计算数组各元素的平方根 |
np.square(x) | 计算数组各元素的平方 |
np.log(x) np.log10(x) np.log2(x) | 计算数组各元素的自然对数、10底对数和 2底对数 |
np.ceil(x) np.floor(x) | 计算数组各元素的ceiling值 或 floor 值 |
np.rint(x) | 计算数组各元素的四舍五入值 |
np.modf(x) | 将数组各元素的小数和整数部分以两个独立数组形式返回 |
np.cos(x) np.cosh(x) np.sin(x) np.sinh(x) np.tan(x) np.tanh(x) | 计算数组各元素的普通型和双曲型三角函数 |
np.exp(x) | 计算数组各元素的指数值 |
np.sign(x) | 计算数组各元素的符号值,1(+), 0, ‐1( ‐ ) |
注意一元函数都不会修改原数组,我们需要再次将它赋给原变量,如 a = np.square(a)
In [51]: a=np.arange(24).reshape((2,3,4))
In [52]: a
Out[52]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
In [53]: a.mean()
Out[53]: 11.5
In [54]: a=a/a.mean()
In [55]: a
Out[55]:
array([[[0. , 0.08695652, 0.17391304, 0.26086957],
[0.34782609, 0.43478261, 0.52173913, 0.60869565],
[0.69565217, 0.7826087 , 0.86956522, 0.95652174]],
[[1.04347826, 1.13043478, 1.2173913 , 1.30434783],
[1.39130435, 1.47826087, 1.56521739, 1.65217391],
[1.73913043, 1.82608696, 1.91304348, 2. ]]])
In [56]: a=np.arange(24).reshape((2,3,4))
In [57]: np.square(a)
Out[57]:
array([[[ 0, 1, 4, 9],
[ 16, 25, 36, 49],
[ 64, 81, 100, 121]],
[[144, 169, 196, 225],
[256, 289, 324, 361],
[400, 441, 484, 529]]], dtype=int32)
In [58]: a=np.sqrt(a)
In [59]: a
Out[59]:
array([[[0. , 1. , 1.41421356, 1.73205081],
[2. , 2.23606798, 2.44948974, 2.64575131],
[2.82842712, 3. , 3.16227766, 3.31662479]],
[[3.46410162, 3.60555128, 3.74165739, 3.87298335],
[4. , 4.12310563, 4.24264069, 4.35889894],
[4.47213595, 4.58257569, 4.69041576, 4.79583152]]])
In [60]: np.modf(a)
Out[60]:
(array([[[0. , 0. , 0.41421356, 0.73205081],
[0. , 0.23606798, 0.44948974, 0.64575131],
[0.82842712, 0. , 0.16227766, 0.31662479]],
[[0.46410162, 0.60555128, 0.74165739, 0.87298335],
[0. , 0.12310563, 0.24264069, 0.35889894],
[0.47213595, 0.58257569, 0.69041576, 0.79583152]]]),
array([[[0., 1., 1., 1.],
[2., 2., 2., 2.],
[2., 3., 3., 3.]],
[[3., 3., 3., 3.],
[4., 4., 4., 4.],
[4., 4., 4., 4.]]]))
3. NumPy二元函数
函数 | 说明 |
---|---|
| 两个数组各元素进行对应运算 |
np.maximum(x,y) np.fmax() np.minimum(x,y) np.fmin() | 元素级的最大值 /最小值计算 |
np.mod(x,y) | 元素级的模运算 |
np.copysign(x,y) | 将数组 y中各元素值的符号赋值给数组 x对应元素 |
| 算术比较,产生布尔型数组 |
In [61]: a=np.arange(24).reshape((2,3,4))
In [62]: b=np.sqrt(a) #a是int,b是float
In [63]: np.maximum(a,b) #max()函数将a转换为float了
Out[63]:
array([[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]],
[[12., 13., 14., 15.],
[16., 17., 18., 19.],
[20., 21., 22., 23.]]])
In [64]: a>b
Out[64]:
array([[[False, False, True, True],
[ True, True, True, True],
[ True, True, True, True]],
[[ True, True, True, True],
[ True, True, True, True],
[ True, True, True, True]]])
四、Numpy中文件的存取
1. 数据的CSV文件存取
CSV (Comma‐Separated Value, 逗号分隔值) 是一种常见的文件格式,用来存储批量数据。
np.savetxt(frame, array, fmt='%.18e', delimiter=None)
:CSV的储存
- frame : 文件、字符串或产生器,可以是.gz或.bz2的压缩文件
- array : 存入文件的数组
- fmt : 写入文件的格式,例如:
%d
%.2f
(科学计数法)%.18e
- delimiter : 分割字符串,默认是任何空格
In [2]: import numpy as np
In [3]: a=np.arange(100).reshape(5,20)
In [4]: np.savetxt('a.csv',a,fmt='%d',delimiter=',') #整数形式,数据五行20列
In [6]: a=np.arange(100).reshape(5,20)
In [7]: np.savetxt('a.csv',a,fmt='%.1f',delimiter=',') #浮点数形式,五行二十列
np.loadtxt(frame, dtype=np.float, delimiter=None, unpack=False)
:CSV的读取
- frame : 文件、字符串或产生器,可以是.gz或.bz2的压缩文件
- dtype : 数据类型,可选
- delimiter : 分割字符串,默认是任何空格
- unpack : 如果True,读入属性将分别写入不同变量
In [8]: b=np.loadtxt('a.csv',delimiter=',') #不指定内容默认为浮点数
In [9]: b
Out[9]:
array([[ 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., 35., 36., 37., 38., 39.],
[40., 41., 42., 43., 44., 45., 46., 47., 48., 49., 50., 51., 52.,
53., 54., 55., 56., 57., 58., 59.],
[60., 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72.,
73., 74., 75., 76., 77., 78., 79.],
[80., 81., 82., 83., 84., 85., 86., 87., 88., 89., 90., 91., 92.,
93., 94., 95., 96., 97., 98., 99.]])
In [10]: b=np.loadtxt('a.csv',dtype=np.int,delimiter=',')
In [11]: b
Out[11]:
array([[ 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, 35,
36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99]])
CSV文件的局限性:CSV只能有效存储一维和二维数组
np.savetxt()
只能有效存取一维和二维数组
np.loadtxt()
2. 多维数据的存取
a.tofile(frame, sep='', format='%s')
:多维数据的储存
- frame : 文件、字符串
- sep : 数据分割字符串,如果是空串,写入文件为二进制
- format : 写入数据的格式
二进制文件会比文本文件占用更少空间
In [12]: a=np.arange(100).reshape(5,10,2)
In [13]: a.tofile("b.dat",sep=",",format='%d') #生成由逗号分隔的序列文件 1,2,3...
#与CSV文件不同,它没有包含任何维度信息,只是把所有元素列出,然后输出到文件。
#会破坏reshape函数设置的格式。
In [14]: a=np.arange(100).reshape(5,10,2)
In [15]: a.tofile("b.dat",format='%d') # 生成二进制文件
np.fromfile(frame, dtype=float, count=‐1, sep='')
:多维数据的读取
- dtype : 读取的数据类型
- count : 读入元素个数,‐1表示读入整个文件
In [19]: c=np.fromfile("b.dat",dtype=np.int,sep=",")
In [20]: c
Out[20]: #输出是一维数组,与输入的格式不一样
array([ 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,
……
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99])
#让c reshape为 a 的格式
In [21]: c=np.fromfile("b.dat",dtype=np.int,sep=",").reshape(5,10,2)
In [22]: c
Out[22]:
array([[[ 0, 1],
[ 2, 3],
[ 4, 5],
[ 6, 7],
[ 8, 9],
……
……
[96, 97],
[98, 99]]])
In [23]: a=np.arange(100).reshape(5,10,2)
In [24]: a.tofile("b.dat",format='%d') #以二进制形式存入文件
In [25]: c=np.fromfile("b.dat",dtype=np.int).reshape(5,10,2) # 读取二进制文件
In [26]: c
Out[26]:
array([[[ 0, 1],
[ 2, 3],
[ 4, 5],
……
[94, 95],
[96, 97],
[98, 99]]])
该方法需要读取时知道存入文件时数组的维度和元素类型和
a.tofile()
需要配合使用 可以通过元数据文件来存储额外信息
np.fromfile()
3. NumPy的便捷文件存取
np.save(fname, array)
或
np.savez(fname, array)
存储文件,前者非压缩,后者压缩
- fname : 文件名,以.npy为扩展名,压缩扩展名为.npz
- array : 数组变量
np.load(fname)
读取文件
In [27]: a=np.arange(100).reshape(5,10,2)
In [28]: np.save("a.npy",a)
In [29]: b=np.load("a.npy")
In [30]: b
Out[30]:
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]
……
[94, 95],
[96, 97],
[98, 99]]])
五、NumPy的函数
1. NumPy的随机数函数
基本随机数函数 | 说明 |
---|---|
| 根据d0‐dn创建随机数数组,浮点数,[0,1),均匀分布 |
| 根据d0‐dn创建随机数数组,标准正态分布 |
| 根据shape创建随机整数或整数数组,范围是[low, high) |
| 随机数种子,s是给定的种子值 |
In [31]: a=np.random.rand(3,4,5) #生成3,4,5维度的数组
In [32]: a
Out[32]:
array([[[8.83299431e-03, 8.05387769e-01, 3.86462185e-01, 6.46913067e-02,
9.93829691e-01],
[4.53347896e-01, 4.22900051e-01, 3.92576772e-01, 8.58568265e-01,
1.20287066e-01],
……
[5.00080272e-01, 4.62143545e-01, 4.03942908e-01, 4.26192622e-01,
6.77575819e-01]]])
In [33]: sn=np.random.randn(3,4,5) #数据符合正太分布
In [34]: sn
Out[34]:
array([[[-2.57248464e+00, 1.03484560e+00, 8.59184836e-01,
-4.11266639e-01, 8.70097584e-01],
[-9.22467511e-01, 6.15670993e-01, 1.17973012e-02,
-2.00623178e+00, -7.09408579e-01],
……
[-2.85435832e-01, -6.54918725e-01, 5.90400730e-01,
2.68995787e-01, 4.09511541e-04]]])
In [35]: b=np.random.randint(100,200,(3,4))
In [36]: b # 100-200间元素的抽取式均匀分布的
Out[36]:
array([[126, 199, 127, 175],
[163, 136, 106, 192],
[164, 193, 110, 113]])
In [37]: np.random.seed(10) #使用相同的随机数种子,会得到相同的数组
In [38]: np.random.randint(100,200,(3,4))
Out[38]:
array([[109, 115, 164, 128],
[189, 193, 129, 108],
[173, 100, 140, 136]])
高级随机数函数 | 说明 |
---|---|
| 根据数组a的第1轴进行随机排列,改变数组x |
| 根据数组a的第1轴产生一个新的乱序数组,不改变数组x |
| 从一维数组a中以概率p抽取元素,形成size形状新数组 replace表示是否可以重用元素,默认为False |
In [39]: b=np.random.randint(100,200,(3,4))
In [40]: b #116,162,149
Out[40]:
array([[116, 111, 154, 188],
[162, 133, 172, 178],
[149, 151, 154, 177]])
In [43]: np.random.shuffle(b)
In [44]: b # 149,116,162
Out[44]:
array([[149, 151, 154, 177],
[116, 111, 154, 188],
[162, 133, 172, 178]])
In [47]: np.random.shuffle(b)
In [48]: b # 149,162,116
Out[48]:
array([[149, 151, 154, 177],
[162, 133, 172, 178],
[116, 111, 154, 188]])
In [50]: b=np.random.randint(100,200,(8,)) #b有八个元素
In [51]: b
Out[51]: array([130, 189, 112, 165, 131, 157, 136, 127])
In [52]: np.random.choice(b,(3,2))
Out[52]:
array([[112, 157],
[157, 136],
[127, 136]])
In [53]:
In [53]: np.random.choice(b,(3,2),replace=False) #设置无重复抽取
Out[53]:
array([[136, 157],
[189, 127],
[112, 131]])
In [54]: np.random.choice(b,(3,2),p=b/np.sum(b)) #设置每个元素的抽取概率
Out[54]:
array([[112, 165],
[130, 136],
[165, 189]])
产生分布的函数 | 说明 |
---|---|
| 产生具有均匀分布的数组,low起始值,high结束值,size形状 |
| 产生具有正态分布的数组,loc均值,scale标准差,size形状 |
| 产生具有泊松分布的数组,lam随机事件发生率,size形状 |
In [55]: u = np.random.uniform(0,10,(3,4))
In [56]: u
Out[56]:
array([[7.31734625, 1.38782465, 7.66880049, 8.31989768],
[3.09778055, 5.9758229 , 8.7239246 , 9.83020867],
[4.67403279, 8.75744495, 2.96068699, 1.31291053]])
In [57]: u=np.random.normal(10,5,(3,4))
In [58]: u
Out[58]:
array([[12.22662292, 14.79969824, 3.2682308 , 20.09449687],
[15.41188173, 1.07141088, 8.63831508, 8.54815806],
[18.28073583, 3.82715648, 4.71157638, 10.69206992]])
2. NumPy的统计函数
函数 | 说明(axis=None 是统计函数的标配参数) |
---|---|
| 根据给定轴axis计算数组a相关元素之和,axis整数或元组 |
| 根据给定轴axis计算数组a相关元素的期望,axis整数或元组 |
| 根据给定轴axis计算数组a相关元素的加权平均值 |
| 根据给定轴axis计算数组a相关元素的标准差 |
| 根据给定轴axis计算数组a相关元素的方差 |
如果给定轴,就是对轴上的元素进行计算。最外层维度为0
In [59]: a=np.arange(15).reshape(3,5)
In [60]: a
Out[60]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
In [61]: np.sum(a)
Out[61]: 105
In [62]: np.mean(a,axis=1) # 将数组中第二维度的数字进行平均数运算
Out[62]: array([ 2., 7., 12.])
In [63]: np.mean(a,axis=0)
Out[63]: array([5., 6., 7., 8., 9.])
In [65]: np.average(a,axis=0,weights=[10,5,1])
Out[65]: array([2.1875, 3.1875, 4.1875, 5.1875, 6.1875])
In [66]: np.std(a)
Out[66]: 4.320493798938574
In [67]: np.var(a)
Out[67]: 18.666666666666668
函数 | 说明(axis=None 是统计函数的标配参数) |
---|---|
| 计算数组a中元素的最小值、最大值 |
| 计算数组a中元素最小值、最大值的降一维后下标 |
| 根据shape将一维下标index转换成多维下标 |
| 计算数组a中元素最大值与最小值的差 |
| 计算数组a中元素的中位数(中值) |
In [68]: b=np.arange(15,0,-1).reshape(3,5)
In [69]: b
Out[69]:
array([[15, 14, 13, 12, 11],
[10, 9, 8, 7, 6],
[ 5, 4, 3, 2, 1]])
In [70]: np.max(b)
Out[70]: 15
In [71]: np.argmax(b) #得到的是扁平化后的下标
Out[71]: 0
In [72]: np.unravel_index(np.argmax(b),b.shape) #将扁平化下标重塑为多维下标,即实际位置
Out[72]: (0, 0)
In [73]: np.ptp(b)
Out[73]: 14
In [74]: np.median(b)
Out[74]: 8.0
3. NumPy的梯度函数
函数 | 说明 |
---|---|
| 计算数组f中元素的梯度,当 f 为多维时,返回每个维度梯度 |
梯度:连续值之间的变化率,即斜率。 XY坐标轴连续三个X坐标对应的Y轴值:a, b, c,其中,a的梯度是: (b‐a)/1、b的梯度是: (c‐a)/2、c的梯度是: (c-b)/1
In [75]: a=np.random.randint(0,20,(5))
In [76]: a
Out[76]: array([16, 18, 16, 14, 19])
In [77]: np.gradient(a)
Out[77]: array([ 2. , 0. , -2. , 1.5, 5. ])
In [78]: b=np.random.randint(0,20,(5))
In [79]: b
Out[79]: array([15, 19, 17, 18, 14])
In [80]: np.gradient(b)
Out[80]: array([ 4. , 1. , -0.5, -1.5, -4. ])
In [81]: c=np.random.randint(0,50,(3,5))
In [82]: c
Out[82]:
array([[48, 42, 17, 32, 17],
[41, 16, 41, 26, 12],
[30, 17, 17, 16, 0]])
In [84]: np.gradient(c) # 分别计算每个维度的梯度
Out[84]:
[array([[ -7. , -26. , 24. , -6. , -5. ], # 最外层维度的梯度
[ -9. , -12.5, 0. , -8. , -8.5],
[-11. , 1. , -24. , -10. , -12. ]]),
array([[ -6. , -15.5, -5. , 0. , -15. ], # 第二层维度的梯度
[-25. , 0. , 5. , -14.5, -14. ],
[-13. , -6.5, -0.5, -8.5, -16. ]])]
梯度有助于发现图像的边缘
六、实例:图像的手绘效果
1. 图像的数组表示
图像一般采用RGB色彩模式,即每个像素点的颜色由红(R),绿(G),蓝(B)组成。RGB三个颜色通道的变化和叠加得到各种颜色,RGB的取值范围都是0-255 ,RGB形成的颜色包括了人类视力所能感知的所有颜色。
图像是一个由像素组成的二维矩阵,每个像素是一个RGB值。
PIL ( Python Image Library ) 是一个具有强大图像处理能力的第三库。
from PIL import Image
引入图像库
Image是PIL库中代表一个图像的类(对象),一个Image对象就代表一个图像。
2. 图像的变换
图像变换的基本流程:
- 首先打开图像
- 对其中RGB对象做一个运算
- 运算后的数组再生成一个图像类型
- 保存为文件
In [86]: from PIL import Image
In [87]: im=np.array(Image.open("D:/pycodes/beijing.jpg")
#图像是一个三维数组,维度分别是高度、宽度和像素RGB值
In [88]: print(im.shape,im.dtype)
(333, 500, 3) uint8
In [89]: b=[255,255,255]-im #计算图像的RGB通道的补值
In [90]: a=Image.fromarray(b.astype('uint8')) #将数组对象重新生成一个图像对象
In [91]: a.save("D:/pycodes/beijing1.jpg")
## convert('L')将彩色图片转为灰度图片,生成的数组im是二维数组,每个元素对应一个灰度值
In [93]: im=np.array(Image.open("D:/pycodes/beijing.jpg").convert('L'))
#区间变换,灰度较轻
In [94]: c=(100/255)*im+150
In [95]: a=Image.fromarray(c.astype('uint8'))
In [97]: a.save("D:/pycodes/beijing2.jpg")
#像素平方,灰度很深
In [108]: d=255*(im/255)**2
In [109]: a=Image.fromarray(d.astype('uint8'))
In [110]: a.save("D:/pycodes/beijing3.jpg")
3. 图像的手绘效果
手绘效果的几个特征:
- 黑白灰色
- 边界线条较重
- 相同或相近色彩趋于白色
- 略有光源效果
3.1 梯度的重构
利用像素之间的梯度值和虚拟深度值对图像进行重构,根据灰度变化来模拟人类视觉的远近程度。
使用公式 深度值 * 方向梯度值 来添加深度对于梯度的影响因素,然后除以 100 进行归一化
depth=10. ##浮点数,预设深度值为10,取值范围0-100
grad=np.gradient(a) #取图像灰度的梯度值
grad_x,grad_y=grad #提取x和y方向的梯度值
深度值
grad_x=grad_x*depth/100.#根据深度调整x和y方向的梯度值
grad_y=grad_y*depth/100.
3.2 光源效果
根据灰度变化来模拟人类视觉的远近程度

- 设计一个位于图像斜上方的虚拟光源
- 光源相对于图像的俯视角为Elevation, 方位角为Azimuth
- 建立光源对个点梯度值的影响函数
- 计算出各点的新像素值
np.cos(vec_el)
可以看做单位光线在平面上的投影长度,再将它们分别投影到x, y ,z轴上,那么 dx, dy, dz 就可以看做光源在坐标轴中具体位置的表示,这里可以视为光线在 x/y/z 方向上对物体的影响程度
vec_el = np.pi/2.2 #光源的俯视角度,弧度值
vec_az = np.pi/4. #光源的方位角度,弧度值
dx = np.cos(vec_el)*np.cos(vec_az) #光源对x轴的影响
dy = np.cos(vec_el)*np.sin(vec_az) #光源对y轴的影响
dz = np.sin(vec_el) #光源对z轴的影响
3.3 梯度归一化
A=np.sqrt(grad_x**2 + grad_y**2 +1.) #构造x和y轴梯度的三维归一化单位坐标系
#uni_x/y/z 表示图像平面的单位法向量
uni_x = grad_x/A
uni_y = grad_y/A
uni_z = 1./A
#将归一化后的各方向梯度分别乘上影响因子,再投影到0-255的灰度范围,完成梯度还原成灰度的步骤
b = 255*(dx*uni_x + dy*uni_y + dz*uni_z) #梯度与光源相互作用,将梯度转化为灰度
3.4 图像生成
b = b.clip(0,255) #为避免数据越界,将生成的灰度值裁剪至0-255区间
im = Image.fromarray(b.astype('uint8')) #重构图像
im.save('./beijingHD.jpg') #生成图像
3.5 全代码
from PIL import Image
import numpy as np
a = np.asarray(Image.open('./beijing.jpg').convert('L')).astype('float')
depth = 10.
grad = np.gradient(a)
grad_x, grad_y = grad
grad_x = grad_x*depth/100.
grad_y = grad_y*depth/100.
A = np.sqrt(grad_x**2 + grad_y**2 + 1.)
uni_x = grad_x/A
uni_y = grad_y/A
uni_z = 1./A
vec_el = np.pi/2.2
vec_az = np.pi/4.
dx = np.cos(vec_el)*np.cos(vec_az)
dy = np.cos(vec_el)*np.sin(vec_az)
dz = np.sin(vec_el)
b = 255*(dx*uni_x + dy*uni_y + dz*uni_z)
b = b.clip(0,255)
im = Image.fromarray(b.astype('uint8'))
im.save('./beijingHD.jpg')