天天看点

[数据分析基础] 1. NumPy库

[数据分析基础] 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对象的属性 说明

.ndim

秩,即轴的数量或维度的数量

.shape

ndarray对象的尺度,对于矩阵,n行m列

.size

ndarray对象元素的个数,相当于.shape中的n*m的值

.dtype

ndarray对象的元素类型

.itemsize

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数组
函数 说明

np.arange(n)

类似range()函数,返回ndarray类型,元素从0到n-1

np.ones(shape)

根据shape生成一个全1数组,shape是元组类型

np.zeros(shape)

根据shape生成一个全0数组,shape是元组类型

np.full(shape,val)

根据shape生成一个数组,每个元素值都是val

np.eye(n)

创建一个正方的n*n单位矩阵,对角线为1,其余为0

np.ones_like(a)

根据数组a的形状生成一个全1的数组

np.zeros_like(a)

根据数组a的形状生成一个全0的数组

np.full_like(a,val)

根据数组a的形状状生成一个数组,每个元素值都是val

np.linspace(start,end,num)

根据起止数据及元素个数等间距地填充数据,形成数组

如果将参数endpoint置为False,end将不作为最后一个元素出现

np.concatenate(a)

将两个或多个数组合并成一个新数组, 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数组的维度变换
方法 说明

.reshape(shape)

不改变数组元素,返回一个shape形状的数组,原数组不变

.resize(shape)

与.reshape()功能一致,但修改原数组

.swapaxes(ax1,ax2)

将数组 n个维度中两个维度进行调换

.flatten()

对数组进行降维,返回折叠后的一维数组,原数组不变
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开始的
  1. 一维数组的索引和切片:起始编号: 终止编号(不含) : 步长
    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])
               
  2. 多维数组的索引:每个维度一个索引值,逗号分割
    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
               
  3. 多维数组的切片:没有选取具体维度,那么可以用

    :

    表示所有维度。
    # 选取一个维度用
    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的随机数函数
基本随机数函数 说明

rand(d0,d1,..,dn)

根据d0‐dn创建随机数数组,浮点数,[0,1),均匀分布

randn(d0,d1,..,dn)

根据d0‐dn创建随机数数组,标准正态分布

randint(low[,high,shape])

根据shape创建随机整数或整数数组,范围是[low, high)

seed(s)

随机数种子,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]])
           
高级随机数函数 说明

shuffle(a)

根据数组a的第1轴进行随机排列,改变数组x

permutation(a)

根据数组a的第1轴产生一个新的乱序数组,不改变数组x

choice(a[,size,replace,p])

从一维数组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]])
           
产生分布的函数 说明

uniform(low,high,size)

产生具有均匀分布的数组,low起始值,high结束值,size形状

normal(loc,scale,size)

产生具有正态分布的数组,loc均值,scale标准差,size形状

poisson(lam,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 是统计函数的标配参数)

sum(a, axis=None)

根据给定轴axis计算数组a相关元素之和,axis整数或元组

mean(a, axis=None)

根据给定轴axis计算数组a相关元素的期望,axis整数或元组

average(a,axis=None,weights=None)

根据给定轴axis计算数组a相关元素的加权平均值

std(a, axis=None)

根据给定轴axis计算数组a相关元素的标准差

var(a, axis=None)

根据给定轴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 是统计函数的标配参数)

min(a) max(a)

计算数组a中元素的最小值、最大值

argmin(a) argmax(a)

计算数组a中元素最小值、最大值的降一维后下标

unravel_index(index, shape)

根据shape将一维下标index转换成多维下标

ptp(a)

计算数组a中元素最大值与最小值的差

median(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的梯度函数
函数 说明

np.gradient(f)

计算数组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. 图像的变换

图像变换的基本流程:

  1. 首先打开图像
  2. 对其中RGB对象做一个运算
  3. 运算后的数组再生成一个图像类型
  4. 保存为文件
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. 图像的手绘效果

手绘效果的几个特征:

  1. 黑白灰色
  2. 边界线条较重
  3. 相同或相近色彩趋于白色
  4. 略有光源效果

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 光源效果

根据灰度变化来模拟人类视觉的远近程度

[数据分析基础] 1. NumPy库
  • 设计一个位于图像斜上方的虚拟光源
  • 光源相对于图像的俯视角为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')
           

继续阅读