Python中的第三⽅库——Numpy
这⾥快速学习⼀下Python 中⼀个⾮常重要的第三⽅库 NumPy。
  它不仅是 Python 中使⽤最多的第三⽅库,⽽且还是 SciPy、Pandas 等数据科学的基础库。它所提供的数据结构⽐ Python ⾃⾝的“更⾼级、更⾼效”,可以这么说,NumPy 所提供的数据结构是 Python 数据分析的基础。
  在Python 数组结构中的列表 list,它实际上相当于⼀个数组的结构。⽽ NumPy 中⼀个关键数据类型就是关于数组的,那为什么还存在这样⼀个第三⽅的数组结构呢?
  实际上,标准的 Python 中,⽤列表 list 保存数组的数值。由于列表中的元素可以是任意的对象,所以列表中 list 保存的是对象的指针。
  虽然在 Python 编程中隐去了指针的概念,但是数组有指针,Python 的列表 list 其实就是数组。这样如果我要保存⼀个简单的数
组 [0,1,2],就需要有 3 个指针和 3 个整数的对象,这样对于 Python 来说是⾮常不经济的,浪费了内存和计算时间。
  使⽤第三⽅库 NumPy让我们的的 Python 科学计算更⾼效!
为什么要⽤ NumPy 数组结构⽽不是 Python 本⾝的列表 list?
  这是因为列表 list 的元素在系统内存中是分散存储的,⽽ NumPy 数组存储在⼀个均匀连续的内存块中。这样数组计算遍历所有的元素,不像列表 list 还需要对内存地址进⾏查,从⽽节省了计算资源。另外在内存访问模式中,缓存会直接把字节块从 RAM 加载到 CPU 寄存器中。因为数据连续的存储在内存中,NumPy 直接利⽤现代 CPU 的⽮量化指令计算,加载寄存器中的多个连续浮点数。另外 NumPy 中的矩阵计算可以采⽤多线程的⽅式,充分利⽤多核 CPU 计算资源,⼤⼤提升了计算效率。
  当然除了使⽤ NumPy 外,我们还需要⼀些技巧来提升内存和提⾼计算资源的利⽤率。⼀个重要的规则就是:避免采⽤隐式拷贝,⽽是采⽤就地操作的⽅式。举个例⼦,如果我想让⼀个数值 x 是原来的两倍,可以直接写成 x*=2,⽽不要写成 y=x*2。这样速度能快到 2 倍甚⾄更多。
  既然 NumPy 这么厉害,我们快速学习从哪⼉⼊⼿学习呢?在 NumPy ⾥有两个重要的对象:ndarray(N-dimensional array object)解决了多维数组问题,⽽ ufunc(universal function object)则是解决对数组进⾏处理的函数。下⾯,我们来⼀⼀学习。
ndarray 对象
  ndarray 实际上是多维数组的含义。在 NumPy 数组中,维数称为秩(rank),⼀维数组的秩为 1,⼆维数组的秩为 2,以此类推。
在 NumPy 中,每⼀个线性的数组称为⼀个轴(axes),其实秩就是描述轴的数量。
ndarray 对象是如何创建数组的,⼜是如何处理结构数组的呢?
import numpy as np
a = np.array([1,2,3])
b = np.array([[1,2,3],[4,5,6]])
b[1,1] = 10
print(a.shape,b.shape)
print(a.dtype)
print(b)
结果如下:
创建数组前,你需要引⽤ NumPy 库,可以直接通过 array 函数创建数组
如果是多重数组,⽐如⽰例⾥的 b,那么该怎么做呢?你可以先把⼀个数组作为⼀个元素,然后嵌套起来。
⽐如⽰例 b 中的 [1,2,3] 就是⼀个元素,然后 [4,5,6][7,8,9] 也是作为元素,然后把三个元素再放到 [] 数组⾥,赋值给变量 b。
当然数组也是有属性的,⽐如你可以通过函数 shape 属性获得数组的⼤⼩,通过 dtype 获得元素的属性。
如果你想对数组⾥的数值进⾏修改的话,直接赋值即可,注意下标是从 0 开始计的,所以如果你想对 b 数组,九宫格⾥的中间元素进⾏修改的话,下标应该是 [1,1]。
结构数组
  如果你想统计⼀个班级⾥⾯学⽣的姓名、年龄,以及语⽂、英语、数学成绩该怎么办?当然你可以⽤数组的下标来代表不同的字段。⽐如下标为 0 的是姓名、⼩标为 1 的是年龄等,但是这样不显性。
实际上在 C 语⾔⾥,可以定义结构数组,也就是通过 struct 定义结构类型,结构中的字段占据连续的内存空间,每个结构体占⽤的内存⼤⼩都相同,那在 NumPy 中是怎样操作的呢?
import numpy as np
persontype = np.dtype({'names':['name', 'age', 'chinese', 'math', 'english'],'formats':['S32','i', 'i', 'i', 'f']})
peoples = np.array([("ZhangFei",32,75,100, 90),("GuanYu",24,85,96,88.5),("ZhaoYun",28,85,92,96.5),("HuangZhong",29,65,85,100)],dtype=persontype)
ages = peoples[:]['age']
chineses = peoples[:]['chinese']
maths = peoples[:]['math']
englishs = peoples[:]['english']
print (np.mean(ages))
print (np.mean(chineses))
print (np.mean(maths))
print (np.mean(englishs))
  这个例⼦,⾸先在 NumPy 中是⽤ dtype 定义的结构类型,然后在定义数组的时候,⽤ array 中指定了结构数组的类
型 dtype=persontype,这样你就可以⾃由地使⽤⾃定义的 persontype 了。⽐如想知道每个⼈的语⽂成绩,就可以⽤ chineses = peoples[:] [‘chinese’],当然 NumPy 中还有⼀些⾃带的数学运算,⽐如计算平均值使⽤ np.mean。
ufunc 运算
  ufunc 是 universal function 的缩写,确如其名,它能对数组中每个元素进⾏函数操作。NumPy 中很多 ufunc 函数计算速度⾮常快,因为都是采⽤ C 语⾔实现的。
连续数组的创建
NumPy 可以很⽅便地创建连续数组,⽐如我使⽤ arange(注意这⾥最新版的numpy已经没有这个函数(被range取代了))或 linspace 函数进⾏创建
import numpy as np
x1 = range(1,11,2)
x2 = np.linspace(1,9,5)
print(x1,x2) #[1. 3. 5. 7. 9.]
range 和 np.linspace 起到的作⽤是⼀样的,都是创建等差数组。这两个数组的结果 x1,x2 都是 [1 3 5 7 9]。结果相同,但是你能看出来创建的⽅式是不同的。
内置函数 range(),通过指定初始值、终值、步长来创建等差数列的⼀维数组,默认是不包括终值的。
linspace 是 linear space 的缩写,代表线性等分向量的含义。linspace() 通过指定初始值、终值、元素个数来创建等差数列的⼀维数组,默认是包括终值的。
算数运算
通过 NumPy 可以⾃由地创建等差数组,同时也可以进⾏加、减、乘、除、求 n 次⽅和取余数。
import numpy as np
x1 = range(1,11,2)
x2 = np.linspace(1,9,5)
print(np.add(x1,x2))
print(np.subtract(x1,x2))
print(np.multiply(x1,x2))
print(np.divide(x1,x2))
print(np.power(x1,x2))
ainder(x1,x2))
以 x1, x2 数组为例,求这两个数组之间的加、减、乘、除、求 n 次⽅和取余数。在 n 次⽅中,x2 数组中的元素实际上是次⽅的次数,x1 数组的元素为基数
在取余函数⾥,你既可以⽤ np.remainder(x1, x2),也可以⽤ np.mod(x1, x2),结果是⼀样的。
统计函数
  当我们想要对⼀堆数据有更清晰的认识,就需要对这些数据进⾏描述性的统计分析,⽐如了解这些数据中的最⼤值、最⼩值、平均值,是否符合正态分布,⽅差、标准差多少等等。它们可以让你更清楚
地对这组数据有认知。在 NumPy 中如何使⽤这些统计函数?
  计数组 / 矩阵中的最⼤值函数 amax(),最⼩值函数 amin()(注意是延着哪个轴)
import numpy as np
a = np.array([[1,2,3], [4,5,6], [7,8,9]])
print(np.amin(a))      # 1
print (np.amin(a,0))  # [1 2 3]
print (np.amin(a,1))  # [1 4 7]
print (np.amax(a))    # 9
print (np.amax(a,0))  # [7 8 9]
print (np.amax(a,1))  # [3 6 9]
amin() ⽤于计算数组中的元素沿指定轴的最⼩值。对于⼀个⼆维数组 a,amin(a) 指的是数组中全部元素的最⼩值
amin(a,0) 是延着 axis=0 轴的最⼩值(理解成横轴),axis=0 轴是把元素看成了 [1,4,7], [2,5,8], [3,6,9] 三个元素,所以最⼩值为 [1,2,3] amin(a,1) 是延着 axis=1 轴的最⼩值(理解成纵轴),axis=1 轴是把元素看成了 [1,2,3], [4,5,6], [7,8,9] 三个元素,所以最⼩值为 [1,4,7]同理 amax() 是计算数组中元素沿指定轴的最⼤值。
统计最⼤值与最⼩值之差 ptp()
import numpy as np
a = np.array([[1,2,3], [4,5,6], [7,8,9]])
print (np.ptp(a))      # 8
print (np.ptp(a,0))  # [6,6,6]
print (np.ptp(a,1))  # [2,2,2]
对于相同的数组 a,np.ptp(a) 可以统计数组中最⼤值与最⼩值的差,即 9-1=8。
同样 ptp(a,0) 统计的是沿着 axis=0 轴的最⼤值与最⼩值之差,即 7-1=6(当然 8-2=6,9-3=6,第三⾏
减去第⼀⾏的 ptp 差均为 6)ptp(a,1) 统计的是沿着 axis=1 轴的最⼤值与最⼩值之差,即 3-1=2(当然 6-4=2, 9-7=2,即第三列与第⼀列的 ptp 差均为 2)。
统计数组的百分位数 percentile()
import numpy as np
a = np.array([[1,2,3], [4,5,6], [7,8,9]])
编程先学c语言还是pythonprint (np.percentile(a, 50))          # 5.0
print (np.percentile(a, 50, axis=0))  # [4,5,6]
print (np.percentile(a, 50, axis=1))  # [2,5,8]
同样,percentile() 代表着第 p 个百分位数,这⾥ p 的取值范围是 0-100,如果 p=0,那么就是求最⼩值,如果 p=50 就是求平均值,如果 p=100 就是求最⼤值。
同样你也可以求得在 axis=0 和 axis=1 两个轴上的 p% 的百分位数。
统计数组中的中位数 median()、平均数 mean()
import numpy as np
a = np.array([[1,2,3], [4,5,6], [7,8,9]])
# 求中位数
print (np.median(a))        # 5.0
print (np.median(a, axis=0)) # [4. 5. 6.]
print (np.median(a, axis=1)) # [2. 5. 8.]
# 求平均数
print (np.mean(a))          # 5.0
print (np.mean(a, axis=0))  # [4. 5. 6.]
print (np.mean(a, axis=1))  # [2. 5. 8.]
⽤ median() 和 mean() 求数组的中位数、平均值,同样也可以求得在 axis=0 和 1 两个轴上的中位数、平均值。
统计数组中的加权平均值 average()
import numpy as np
a = np.array([1,2,3,4])
wts = np.array([1,2,3,4])
print (np.average(a))              # 2.5
print (np.average(a,weights=wts))  # 3.0
average() 函数可以求加权平均,加权平均的意思就是每个元素可以设置个权重,默认情况下每个元素的权重是相同的
所以 np.average(a)=(1+2+3+4)/4=2.5,你也可以指定权重数组 wts=[1,2,3,4],这样加权平均 np.average(a,weights=wts)=
(1*1+2*2+3*3+4*4)/(1+2+3+4)=3.0。
统计数组中的标准差 std()、⽅差 var()
import numpy as np
a = np.array([1,2,3,4])
print(np.std(a),np.var(a)) # 1.118033988749895 1.25
⽅差的计算是指每个数值与平均值之差的平⽅求和的平均值,即 mean((x - x.mean())** 2)。
标准差是⽅差的算术平⽅根。在数学意义上,代表的是⼀组数据离平均值的分散程度。
所以 np.var(a)=1.25, np.std(a)=1.118033988749895。
NumPy 排序
  排序是算法中使⽤频率最⾼的⼀种,也是在数据分析⼯作中常⽤的⽅法,在⼤学期间的算法是(数据结构)课中学习(排序是必修课)。
  那么这些排序算法在 NumPy 中实现起来其实⾮常简单,⼀条语句就可以搞定。这⾥你可以使⽤ sort 函数,sort(a, axis=-
1, kind=‘quicksort’, order=None)
  默认情况下使⽤的是快速排序;在 kind ⾥,可以指定 quicksort、mergesort、heapsort 分别表⽰快速排序、合并排序、堆排序。同
样 axis 默认是 -1,即沿着数组的最后⼀个轴进⾏排序,也可以取不同的 axis 轴,或者 axis=None 代表采⽤扁平化的⽅式作为⼀个向量进⾏排序。另外 order 字段,对于结构化的数组可以指定按照某个字段进⾏排序。
import numpy as np
a = np.array([[4,3,2],[2,4,1]])
print (np.sort(a))
print()
print (np.sort(a, axis=None))
print()
print (np.sort(a, axis=0))
print()
print (np.sort(a, axis=1))
总结
  在 NumPy 学习中,重点要掌握的就是对数组的使⽤,因为这是 NumPy 和标准 Python 最⼤的区别。在 NumPy 中重新对数组进⾏了定义,同时提供了算术和统计运算,你也可以使⽤ NumPy ⾃带的排序功能,⼀句话就搞定各种排序算法。
  当然要理解 NumPy 提供的数据结构为什么⽐ Python ⾃⾝的“更⾼级、更⾼效”,要从对数据指针的引⽤⾓度进⾏理解。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。