python之numpy学习笔记01

微信公众号: 点击蓝色字体小白图像与视觉进行关注

关于技术、关注yysilence00。有问题或建议,请公众号留言


  • 整理知识,学习笔记
  • 发布日记,杂文,所见所想

一:numpy介绍

NumPy是一个功能强大的Python库,主要用于对多维数组执行计算。NumPy这个词来源于两个单词-- Numerical和Python。NumPy提供了大量的库函数和操作,可以帮助程序员轻松地进行数值计算

#引进numpy库
import numpy

这样你就可以用 numpy 里面所有的内置方法 (build-in methods) 了,比如求和与均值。
numpy.max()
numpy.min()

import numpy as npmy_arr = np.arange(1000000)
my_list = list(range(1000000))
# 看下面「numpy 数组」和「列表」之间的计算效率对比:两个大小都是 1000000,把每个元素翻倍,运行 10 次用 %time 记时。
%time for _ in range(10): my_arr2 = my_arr * 2%time for _ in range(10): my_list2 = [x * 2 for x in my_list]
Wall time: 49 ms
Wall time: 1.68 s

1.1 数组array的创建

#np.array()方法创建
arr = [1, 4, 3.2, 5]
np.array(arr)
array([1. , 4. , 3.2, 5. ])
注意,numpy 数组的输出都带有 array() 的字样,里面的元素用「中括号 []」框住。
#np.arange() 和 np.linspace()方法创建
#定隔的 arange:固定元素大小间隔
#定点的 linspace:固定元素个数
arr_ = np.arange(10)
arr_
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(arr_)
[0 1 2 3 4 5 6 7 8 9]
注:用函数 print 打印 numpy 数组就没有 array() 的字样了,只用其内容,而且元素之间的「逗号」也没有了。
print( np.arange(10) )
print( np.arange(2,10) )
print( np.arange(2,11,2))
[0 1 2 3 4 5 6 7 8 9]
[2 3 4 5 6 7 8 9]
[ 2  4  6  8 10]

函数原型:

arange(start , stop , step)其中 stop 必须要有,start 和 step 没有的话默认为 1
arr2 = np.linspace(2,8,3)
arr2
array([2., 5., 8.])
print(arr2)
[2. 5. 8.]
注:用函数 print 打印 numpy 数组就没有 array() 的字样了,只用其内容,而且元素之间的「逗号」也没有了。

函数原型:

alinspace (start , stop , num))其中 start,stop 必须要有和 num 没有的话默认为 50之间步长计算方法为(stop - start) / (num - 1)

NumPy 创建数组的其他特性

  • 用 zeros() 创建全是 0 的 n 维数组
  • 用 ones() 创建全是 1 的 n 维数组
  • 用 random() 创建随机 n 维数组
  • 用 eye() 创建对角矩阵 (二维数组)

对于前三种,由于输出是 n 为数组,它们的参数是一个「标量」或「元组类型的形状」,下面三个例子一看就懂了:

print( np.zeros(6) ) # 标量5代表形状(5,)
print( np.ones((2,5)) )
print( np.random.random((2,3,4)) )
[0. 0. 0. 0. 0. 0.]
[[1. 1. 1. 1. 1.][1. 1. 1. 1. 1.]]
[[[0.96070029 0.13700133 0.86750795 0.60046375][0.51925163 0.25111592 0.92045058 0.92544552][0.31744394 0.46865624 0.6622886  0.77742483]][[0.94793251 0.27383846 0.99977759 0.45765543][0.70677173 0.07758811 0.78253172 0.46340084][0.25709134 0.32701264 0.29887367 0.11625852]]]
#对于函数 eye(),它的参数就是一个标量,控制矩阵的行数或列数:
np.eye(4)
array([[1., 0., 0., 0.],[0., 1., 0., 0.],[0., 0., 1., 0.],[0., 0., 0., 1.]])

此外还可以设定 eye() 里面的参数 k

  • 默认设置 k = 0 代表 1 落在对角线上
  • k = 1 代表 1 落在对角线右上方
  • k = -1 代表 1 落在对角线左下方
np.eye(4, k=1)
array([[0., 1., 0., 0.],[0., 0., 1., 0.],[0., 0., 0., 1.],[0., 0., 0., 0.]])

1.2 数组array特性

我们来看看数组有什么属性 (attributes) 和方法 (methods)。

一维数组

arr3 = np.array([1, 2.1, 5, 4.3])
arr3
array([1. , 2.1, 5. , 4.3])
dir(arr3) 
['T','__abs__','__add__','__and__','__array__','__array_finalize__','__array_function__','__array_interface__','__array_prepare__','__array_priority__','__array_struct__','__array_ufunc__','__array_wrap__','__bool__','__class__','__complex__','__contains__','__copy__','__deepcopy__','__delattr__','__delitem__','__dir__','__divmod__','__doc__','__eq__','__float__','__floordiv__','__format__','__ge__','__getattribute__','__getitem__','__gt__','__hash__','__iadd__','__iand__','__ifloordiv__','__ilshift__','__imatmul__','__imod__','__imul__','__index__','__init__','__init_subclass__','__int__','__invert__','__ior__','__ipow__','__irshift__','__isub__','__iter__','__itruediv__','__ixor__','__le__','__len__','__lshift__','__lt__','__matmul__','__mod__','__mul__','__ne__','__neg__','__new__','__or__','__pos__','__pow__','__radd__','__rand__','__rdivmod__','__reduce__','__reduce_ex__','__repr__','__rfloordiv__','__rlshift__','__rmatmul__','__rmod__','__rmul__','__ror__','__rpow__','__rrshift__','__rshift__','__rsub__','__rtruediv__','__rxor__','__setattr__','__setitem__','__setstate__','__sizeof__','__str__','__sub__','__subclasshook__','__truediv__','__xor__','all','any','argmax','argmin','argpartition','argsort','astype','base','byteswap','choose','clip','compress','conj','conjugate','copy','ctypes','cumprod','cumsum','data','diagonal','dot','dtype','dump','dumps','fill','flags','flat','flatten','getfield','imag','item','itemset','itemsize','max','mean','min','nbytes','ndim','newbyteorder','nonzero','partition','prod','ptp','put','ravel','real','repeat','reshape','resize','round','searchsorted','setfield','setflags','shape','size','sort','squeeze','std','strides','sum','swapaxes','take','tobytes','tofile','tolist','tostring','trace','transpose','var','view']

print( 'The type is', type(arr3) )
print( 'The dimension is', arr3.ndim )
print( 'The length of array is', len(arr3) )
print( 'The number of elements is', arr3.size )
print( 'The shape of array is', arr3.shape )
print( 'The strides of array is', arr3.strides )
print( 'The type of elements is', arr3.dtype )
The type is 
The dimension is 1
The length of array is 4
The number of elements is 4
The shape of array is (4,)
The strides of array is (8,)
The type of elements is float64

根据结果我们来看看上面属性到底是啥:

  • type:数组类型,当然是 numpy.ndarray
  • ndim:维度个数是 1
  • len():数组长度为 5 (注意这个说法只对一维数组有意义)
  • size:数组元素个数为 5
  • shape:数组形状,即每个维度的元素个数 (用元组来表示),只有一维,元素个数为 5,写成元组形式是 (5,)
  • strides:跨度,即在某一维度下为了获取到下一个元素需要「跨过」的字节数 (用元组来表示),float64 是 8 个字节数 (bytes),因此跨度为 8
  • dtype:数组元素类型,是双精度浮点 (注意和 type 区分)

函数原型:

numpy.array(object, dtype = None, copy = True, order = None, subok = False, ndmin = 0)

二维数组

arr02 = [[1,2,3], [4,5,6]]
arr2d = np.array(arr02)
arr2d
array([[1, 2, 3],[4, 5, 6]])
dir(arr2d)
['T','__abs__','__add__','__and__','__array__','__array_finalize__','__array_function__','__array_interface__','__array_prepare__','__array_priority__','__array_struct__','__array_ufunc__','__array_wrap__','__bool__','__class__','__complex__','__contains__','__copy__','__deepcopy__','__delattr__','__delitem__','__dir__','__divmod__','__doc__','__eq__','__float__','__floordiv__','__format__','__ge__','__getattribute__','__getitem__','__gt__','__hash__','__iadd__','__iand__','__ifloordiv__','__ilshift__','__imatmul__','__imod__','__imul__','__index__','__init__','__init_subclass__','__int__','__invert__','__ior__','__ipow__','__irshift__','__isub__','__iter__','__itruediv__','__ixor__','__le__','__len__','__lshift__','__lt__','__matmul__','__mod__','__mul__','__ne__','__neg__','__new__','__or__','__pos__','__pow__','__radd__','__rand__','__rdivmod__','__reduce__','__reduce_ex__','__repr__','__rfloordiv__','__rlshift__','__rmatmul__','__rmod__','__rmul__','__ror__','__rpow__','__rrshift__','__rshift__','__rsub__','__rtruediv__','__rxor__','__setattr__','__setitem__','__setstate__','__sizeof__','__str__','__sub__','__subclasshook__','__truediv__','__xor__','all','any','argmax','argmin','argpartition','argsort','astype','base','byteswap','choose','clip','compress','conj','conjugate','copy','ctypes','cumprod','cumsum','data','diagonal','dot','dtype','dump','dumps','fill','flags','flat','flatten','getfield','imag','item','itemset','itemsize','max','mean','min','nbytes','ndim','newbyteorder','nonzero','partition','prod','ptp','put','ravel','real','repeat','reshape','resize','round','searchsorted','setfield','setflags','shape','size','sort','squeeze','std','strides','sum','swapaxes','take','tobytes','tofile','tolist','tostring','trace','transpose','var','view']
# 一把梭打印属性attributes出来看看:
print( 'The type is', type(arr2d) )
print( 'The dimension is', arr2d.ndim )
print( 'The length of array is', len(arr2d) )
print( 'The number of elements is', arr2d.size )
print( 'The shape of array is', arr2d.shape )
print( 'The stride of array is', arr2d.strides )
print( 'The type of elements is', arr2d.dtype )
The type is 
The dimension is 2
The length of array is 2
The number of elements is 6
The shape of array is (2, 3)
The stride of array is (12, 4)
The type of elements is int32

同样,我们来分析一下上面属性:

  • type:数组类型 numpy.ndarray
  • ndim:维度个数是 2
  • len():数组长度为 2 (严格定义 len 是数组在「轴 0」的元素个数)
  • size:数组元素个数为 6
  • shape:数组形状,每个维度元素的个数,用元组表示(2, 3)
  • strides:跨度 (12, 4) 看完下图再解释
  • dtype:数组元素类型 int32

在 numpy 数组中,默认的是行主序 (row-major order),意思就是每行的元素在内存块中彼此相邻,而列主序 (column-major order) 就是每列的元素在内存块中彼此相邻。

回顾跨度 (stride) 的定义,即在某一维度下为了获取到下一个元素需要「跨过」的字节数。注:每一个 int32 元素是 4 个字节数。

  • 第一维度 (轴 0):沿着它获取下一个元素需要跨过 3 个元素,即 12 = 3×4 个字节
  • 第二维度 (轴 1):沿着它获取下一个元素需要跨过 1 个元素,即 4 = 1×4 个字节

因此该二维数组的跨度为 (12, 4)

n维数组

用 np.random.random() 来生成一个多维数组:

arr4d = np.random.random( (2,2,2,3) )
arr4d
array([[[[0.50169052, 0.95593211, 0.01650688],[0.66450554, 0.89081254, 0.49317664]],[[0.94852445, 0.35366822, 0.67516144],[0.902386  , 0.40697895, 0.69716439]]],[[[0.31261512, 0.67417529, 0.22901764],[0.61775389, 0.89892861, 0.97588106]],[[0.07474652, 0.35708577, 0.5014601 ],[0.59584862, 0.91910878, 0.40630768]]]])
print( 'The type is', type(arr4d) )
print( 'The dimension is', arr4d.ndim )
print( 'The length of array is', len(arr4d) )
print( 'The number of elements is', arr4d.size )
print( 'The shape of array is', arr4d.shape )
print( 'The stride of array is', arr4d.strides )
print( 'The type of elements is', arr4d.dtype )
The type is 
The dimension is 4
The length of array is 2
The number of elements is 24
The shape of array is (2, 2, 2, 3)
The stride of array is (96, 48, 24, 8)
The type of elements is float64

留一道思考题,strides 和 shape 有什么关系?

strides = (96, 48, 24, 8)

shape = (2, 2, 2, 3)

答:因为 跨度 (stride) 的定义是在某一维度下为了获取到下一个元素需要「跨过」的字节数。每一个 float64 元素是 8 个字节数。

所以第四维度 (轴 3)每个元素是相邻的,所以跨度是8,即stride(3)=8。以此类推:

第三维度 (轴 2)的跨度就是stride(2)=shape(3)stride(3)=38=24。

第二维度 (轴 1)的跨度就是stride(1)=shape(2)stride(2)=224=48。

第一维度 (轴 0)的跨度就是stride(0)=shape(1)stride(1)=248=96。

1.3 数组array存载

数组的「保存」和「加载」,我知道它们简单,但是很重要。假设你已经训练完一个深度神经网络,该网络就是用无数参数来表示的。比如权重都是 numpy 数组,为了下次不用训练而重复使用,将其保存成 .npy 格式或者 .csv 格式是非常重要的

numpy 自身的 .npy 格式

用 np.save 函数将 numpy 数组保存为 .npy 格式,具体写法如下:

#coding=utf-8
import numpy as np
arr_disk = np.arange(10)
np.save("arr_disk", arr_disk)
arr_disk
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.load("arr_disk.npy")
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

文本 .txt 格式

用 np.savetxt 函数将 numpy 数组保存为 .txt 格式,具体写法如下:

arr_text = np.array([[1., 2., 3.], [4., 5., 6.]])
np.savetxt("arr_from_text.txt", arr_text)
np.loadtxt("arr_from_text.txt")
array([[1., 2., 3.],[4., 5., 6.]])

文本 .csv 格式

假设我们已经在 arr_from_csv 的 csv 文件里写进去了 [[1,2,3], [4,5,6]],每行的元素是由「分号 ;」来分隔的,展示如下:

np.genfromtxt(“arr_from_csv.csv”, delimiter=";")

#coding=utf-8
import csv
#打开一个csv文件,模式为读取
csvfile = open('I://arr_from_csv.csv','r')
#定义一个变量,进行读取
readCSV = csv.reader(csvfile)
print(readCSV)
<_csv.reader object at 0x000000000F26FDB0>

1.3 数组array获取

获取数组是通过索引 (indexing) 和切片 (slicing) 来完成的,

  • 索引是获取一个特定位置的元素

  • 切片是获取一段特定位置的元素

索引和切片的方式和列表一模一样

  • 索引写法是 arr[index]
  • 切片写法是 arr[start : stop : step]

因此,切片的操作是可以用索引操作来实现的 (一个一个总能凑成一段),只是没必要罢了。

索引数组有三种形式,正规索引 (normal indexing)、布尔索引 (boolean indexing) 和花式索引 (fancy indexing)。

正规索引

  • 索引得到的是原数组的一个复制 (copy),修改索引中的内容不会改变原数组

  • 切片得到的是原数组的一个视图 (view) ,修改切片中的内容会改变原数组

索引

# 一维数组索引
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
#用 arr[3] 索引第 4 个元素 (记住 Python 是从 0 开始记录位置的)
arr[3]
3
# 把它赋给变量 a,并重新给 a 赋值 100,但是元数组 arr 第 7 个元素的值还是 6,并没有改成 100。
a = arr[3]
a = 100
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# 用 arr[5:8] 切片第 6 到 8 元素 (记住 Python 切片包头不包尾)
arr[5:8]
array([5, 6, 7])
#把它赋给变量 b,并重新给 b 的第二个元素赋值 18,再看发现元数组 arr 第 7 个元素的值已经变成 18 了。
b = arr[5:8]
b[1] = 18
arr
array([ 0,  1,  2,  3,  4,  5, 18,  7,  8,  9])

这就证实了切片得到原数组的视图 (view),更改切片数据会更改原数组,而索引得到原数组的复制 (copy), 更改索引数据不会更改原数组。

# 二维数组索引
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
arr2d
array([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
# 用 arr2d[2] 来索引第三行,更严格的说法是索引「轴 0」上的第三个元素。
arr2d[2]
array([7, 8, 9])
# 用 arr2d[0][2] 来索引第一行第三列
arr2d[0][2]
3

索引二维数组打了两个中括号好麻烦,索引五维数组不是要打了五个中括号?还有一个简易方法,用 arr2d[0, 2] 也可以索引第一行第三列

arr2d[0,2]
3

切片

# 用 arr2d[:2] 切片前两行,更严格的说法是索引「轴 0」上的前两个元素。
arr2d[:2] 
array([[1, 2, 3],[4, 5, 6]])
# 用 arr2d[:, [0,2]] 切片第一列和第三列
arr2d[:,[0,2]] 
array([[1, 3],[4, 6],[7, 9]])
# 用 arr2d[1, :2] 切片第二行的前两个元素
arr2d[1, :2]
array([4, 5])
# 用 arr2d[:2, 2] 切片第三列的前两个元素
arr2d[:2, 2]
array([3, 6])

布尔索引

布尔索引,就是用一个由布尔 (boolean) 类型值组成的数组来选择元素的方法。

假设我们有阿里巴巴 (BABA),脸书 (FB) 和京东 (JD) 的

  • 股票代码 code 数组
  • 股票价格 price 数组:每行记录一天开盘,最高和收盘价格。
code = np.array(['BABA', 'FB', 'JD', 'BABA', 'JD', 'FB'])
price = np.array([[170,177,169],[150,159,153],[24,27,26],[165,170,167],[22,23,20],[155,116,157]])
price
array([[170, 177, 169],[150, 159, 153],[ 24,  27,  26],[165, 170, 167],[ 22,  23,  20],[155, 116, 157]])

假设我们想找出 BABA 对应的股价,首先找到 code 里面是 ‘BABA’ 对应的索引 (布尔索引),即一个值为 True 和 False 的布尔数组。

code == 'BABA'
array([ True, False, False,  True, False, False])
# 用该索引可以获取 BABA 的股价:
price[ code == 'BABA' ]
array([[170, 177, 169],[165, 170, 167]])
# 再试试获取 JD 和 FB 的股价:
price[ (code == 'FB')|(code == 'JD') ]
array([[150, 159, 153],[ 24,  27,  26],[ 22,  23,  20],[155, 116, 157]])
# 虽然下面操作没有实际意义,试试把股价小于 25 的清零。price[ price < 25 ] = 0
price
array([[170, 177, 169],[150, 159, 153],[  0,  27,  26],[165, 170, 167],[  0,   0,   0],[155, 116, 157]])

花式索引

花式索引是获取数组中想要的特定元素的有效方法。考虑下面数组:

arr = np.arange(32).reshape(8,4)
arr
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]])

假设你想按特定顺序来获取第 5, 4 和 7 行时,用 arr[ [4,3,6] ]

arr[ [4,3,6] ]
array([[16, 17, 18, 19],[12, 13, 14, 15],[24, 25, 26, 27]])

假设你想按特定顺序来获取倒数第 4, 3 和 6 行时 (即正数第 4, 5 和 2 行),用 arr[ [-4,-3,-6] ]

arr[ [-4,-3,-6] ]
array([[16, 17, 18, 19],[20, 21, 22, 23],[ 8,  9, 10, 11]])

此外,你还能更灵活的设定「行」和「列」中不同的索引,如下

arr[ [1,5,7,2], [0,3,1,2] ]
array([ 4, 23, 29, 10])

等价的代码:

np.array( [ arr[1,0], arr[5,3], arr[7,1], arr[2,2] ] )
array([ 4, 23, 29, 10])

最后,我们可以把交换列,把原先的 [0,1,2,3] 的列换成 [0,3,1,2]。

arr[:,[0,3,1,2]] 
array([[ 0,  3,  1,  2],[ 4,  7,  5,  6],[ 8, 11,  9, 10],[12, 15, 13, 14],[16, 19, 17, 18],[20, 23, 21, 22],[24, 27, 25, 26],[28, 31, 29, 30]])

小结

本节讨论了 NumPy 的前三节,数组创建、数组存载和数组获取。同样把 numpy 数组当成一个对象,要学习它,无非就是学习怎么

  • 创建它:按步就班法、定隔定点法、一步登天法

  • 存载它:保存成 .npy, .txt 和 .csv 格式,下次加载即用

  • 获取它:一段用切片,一个用索引;有正规法、布尔法、花式法

下节讨论 NumPy 的后两节就教怎么

  • 变形它:重塑和打平,合并和分裂,元素重复和数组重复

  • 计算它:元素层面计算,线性代数计算,广播机制计算

最后一个问题 数组的转置

arr = np.arange(16).reshape((2, 2, 4))
arr
array([[[ 0,  1,  2,  3],[ 4,  5,  6,  7]],[[ 8,  9, 10, 11],[12, 13, 14, 15]]])

将第 1, 2, 3 维度转置到第 2, 1, 3 维度,即将轴 0, 1, 2 转置到轴 1, 0, 2。

数组转置的本质:交换每个轴 (axis) 的形状 (shape) 和跨度 (stride)。

更多请扫码关注


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部