Numpy: 面向数组编程

前言
使用numpy数组可以使我们利用简单的数组表达式完成多种数据操作任务,而无须写些大量循环(避免循环是核心思想)。这种利用数组表达式来替代显示循环的方法,称为向量化。通常,向量化的数组操作会比纯Python快上一到两个数量级(甚至更多),这对所有种类的数值计算产生了最大影响。

PS: 这篇文章是我写的numpy知识总结的一部分,完整知识总结入口在这篇文章,在这篇文章里我搭建了numpy的基础知识框架,非常适合入门。

1.数组编程威力的证明

以一个列子说明数组编程的威力:

计算函数 f = sqrt(x^2 + y^2),其中x和y均为一维数组。

Python编程思路:通过写双重for循环计算,时间复杂度 O ( n 2 ) O(n^2) O(n2)
面向数组编程思路:将两个数组生成一个二维矩阵,然后利用矩阵的逐元素计算避免循环。

【例1】两种思路的代码运行时间比较

#数组规模为个位数时
In [406]: x
Out[406]: [0, 1, 2, 3, 4]

In [407]: y
Out[407]: [0, 1, 2, 3, 4, 5, 6]

In [408]: %load test.py #魔法函数,加载写好的代码

In [409]: # %load test.py
     ...: import math
     ...:
     ...: def f(x,y):
     ...:     temp = []
     ...:     for i in x:
     ...:         for j in y:
     ...:             temp.append(math.sqrt(math.pow(i,2) + math.pow(j,2)))
     ...:     return temp
 

In [410]: %time f(x,y)
CPU times: user 73 µs, sys: 59 µs, total: 132 µs
Wall time: 136 µs

In [411]: xs,ys = np.meshgrid(x,y)
In [413]: %time np.sqrt(xs ** 2 + ys ** 2)
CPU times: user 71 µs, sys: 33 µs, total: 104 µs
Wall time: 109 µs
#数组规模为千位数时
In [415]: x = list(_ for _ in range(999))
In [416]: y = list(_ for _ in range(1001))

In [417]: %time f(x,y)
CPU times: user 384 ms, sys: 11 ms, total: 395 ms
Wall time: 394 ms

In [418]: xs, ys = np.meshgrid(x,y)
In [419]: %time np.sqrt(xs ** 2 + ys ** 2)
CPU times: user 10.1 ms, sys: 11.5 ms, total: 21.5 ms
Wall time: 22.6 ms

首先需要说明,两个方法计算出的结果是相同的,因为太长,这里没有贴。以下是时间对比:

思路数据规模total time
Python个位132 µs
数组个位104 µs
Python千位395 ms
数组千位21.5 ms

随着数据规模的增大,数组方法的执行效率优势非常明显,我们做数据处理时,十万和百万级的数据非常多,在这种情况下,面向数组编程的优势显而易见。

对meshgrid函数的解释:在这个例子中,显然函数是一个二元函数,那么将这个函数是在三维空间里的,x,y构成一个平面,meshgrid函数的作用就是将这个平面上x,y对应的点用一个二维矩阵表示出来,xs和ys在相同位置的元素对应了平面上的一个点对,所以对其进行的计算结果与for循环计算的结果相同。得到的xs, ys就如下面例子所示,xs是原数组为行的简单复制,ys是原数组变为列后的简单复制。xs的第一列和ys的第一列就对应了平面上的点(0,0),(0,1),(0,2),(0,3),(0,4),(0,5),依此类推整个矩阵也就是数组x和y一一对应计算的所有数值对,对这些数值对的计算也就等同于双重for循环的计算。
【例2】解释meshgrid函数

In [42]: x = np.arange(5)

In [43]: y = np.arange(6)

In [44]: xs, ys = np.meshgrid(x,y)

In [45]: xs
Out[45]:
array([[0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4],
       [0, 1, 2, 3, 4]])

In [46]: ys
Out[46]:
array([[0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1],
       [2, 2, 2, 2, 2],
       [3, 3, 3, 3, 3],
       [4, 4, 4, 4, 4],
       [5, 5, 5, 5, 5]])

注:这里没有加入meshgrid的测试时间,其时间消耗很少,可忽略不计。如果x,y本身是ndarray的话,运算效率可提升约40%。

就像meshgrid函数一样,numpy还提供了很多的函数用以支持面向数组的编程,大致可以分为以下几类:统计方法、排序、布尔值数组处理、唯一值及集合运算、数组版三元表达式where。

2. 统计方法(sum、mean等)
方法描述方法描述
sum沿着轴向计算所有元素的累积和,0长度的数组,累积和为0mean数学平均,0长度的数组平均值为NaN
min, max最小值和最大值argmin, argmax最小值和最大值的位置
cumsum从0开始到当前元素的元素累cumprod从1开始到当前元素的元素累
std, var标准差和方差,可以选择自由度调整(默认分母是n)

注:表格中的方法都是数组的方法,可以通过数组直接调用。
【例】部分统计方法示例

In [450]: arr = np.arange(12).reshape(4,3)

In [451]: arr
Out[451]:
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
#计算总和       
In [453]: arr.sum()
Out[453]: 36
#计算0轴向
In [454]: arr.sum(0)
Out[454]: array([18, 22, 26])
#计算1轴向
In [455]: arr.sum(1)
Out[455]: array([ 3, 12, 21, 30])

In [458]: arr.cumsum(0)
Out[458]:
array([[ 0,  1,  2],
       [ 3,  5,  7],
       [ 9, 12, 15],
       [18, 22, 26]])

In [459]: arr.cumsum(1)
Out[459]:
array([[ 0,  1,  3],
       [ 3,  7, 12],
       [ 6, 13, 21],
       [ 9, 19, 30]])
3. 排序

numpy可以使用sort方法按位置排序,对于多维数组,需要传入axis值。sort方法有两种使用方式,一种改变原数组,一种创建副本。

arr.sort()   //改变原数组元素顺序
np.sort(arr) //生成一个排序后的副本,不改变原数组顺序

【例】sort方法使用示例

In [467]: arr
Out[467]: array([0.19008705, 0.46913444, 0.33954087, 0.7044972 , 0.2260952 ])

In [468]: np.sort(arr)
Out[468]: array([0.19008705, 0.2260952 , 0.33954087, 0.46913444, 0.7044972 ])
#使用np.sort之后原数组元素顺序未变
In [469]: arr
Out[469]: array([0.19008705, 0.46913444, 0.33954087, 0.7044972 , 0.2260952 ])

In [470]: arr.sort()
#使用arr.sort之后原数组元素顺序改变
In [471]: arr
Out[471]: array([0.19008705, 0.2260952 , 0.33954087, 0.46913444, 0.7044972 ])
4. 布尔值数组处理

对布尔值数组,numpy也提供了处理函数,常见操作需求及其函数如下。

操作函数
计算True的个数arr.sum(),布尔值会强制为1(True)或0(Flase),所以通过求和即可计算
是否全为Truearr.all()
至少一个为Truearr.any()

【例】布尔值数组操作示例

In [476]: arr = np.random.randint(-10,10,size=(3,3))

In [477]: arr
Out[477]:
array([[-5,  5,  6],
       [ 0, -2, -5],
       [-2,  7,  9]])

In [478]: (arr>0).sum()
Out[478]: 4

In [479]: bools = arr>0

In [480]: bools
Out[480]:
array([[False,  True,  True],
       [False, False, False],
       [False,  True,  True]])

In [481]: bools.any()
Out[481]: True

In [482]: bools.all()
Out[482]: False
5. 唯一值及集合运算(对一维数组的操作)

numpy包含一些针对一维ndarray的基础集合操作。

方法描述
unique(x)计算x的唯一值,并排序
intersect1d(x,y)计算x和y的交集,并排序
union1d(x,y)计算x和y的并集,并排序
in1d(x,y)计算x中的元素是否包含在y中,返回一个布尔值数组
setdiff1d(x,y)差集,在x中但不在y中的x的元素
setxor1d(x,y)异或集,在x或y中,但不属于x、y交集的元素

注:以上方法中的x和y均为一维数组。
【例】一维数组处理示例

In [484]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will'])

In [485]: np.unique(names)
Out[485]: array(['Bob', 'Joe', 'Will'], dtype='<U4')

In [486]: values = np.array([6,0,0,3,2,5,6])

In [487]: np.in1d(values, [2,3,6])
Out[487]: array([ True, False, False,  True,  True, False,  True])
6. 数组版三元表达式where

where函数是对Python三元表达式 x if condition else y 的向量化实现。通过where函数我们可以通过条件选择对数组中的数值进行替换,或者进行其他操作。以下例1说明具体语法,例2简单应用。
【例1】where语法示例

In [489]: xarr = np.array([1.1,1.2,1.3,1.4,1.5])

In [490]: yarr = np.array([2.1,2.2,2.3,2.4,2.5])

In [491]: cond = np.array([True,False,True,True,False])

In [492]: result = np.where(cond, xarr, yarr)

In [493]: result
Out[493]: array([1.1, 2.2, 1.3, 1.4, 2.5])

【例2】where应用示例

In [495]: arr = np.random.randn(4,4)

In [496]: arr
Out[496]:
array([[ 0.49042258, -0.15916412,  0.50222044, -0.28018826],
       [ 1.698109  ,  0.72016023, -0.19839624, -0.36279838],
       [ 0.0680141 ,  0.74892178, -0.97592457,  0.58013547],
       [ 0.23533146,  0.01091129,  0.14336871,  1.35089938]])

In [497]: arr > 0
Out[497]:
array([[ True, False,  True, False],
       [ True,  True, False, False],
       [ True,  True, False,  True],
       [ True,  True,  True,  True]])

In [498]: np.where(arr > 0, 2, -2)
Out[498]:
array([[ 2, -2,  2, -2],
       [ 2,  2, -2, -2],
       [ 2,  2, -2,  2],
       [ 2,  2,  2,  2]])

In [499]: np.where(arr > 0, 2, arr)
Out[499]:
array([[ 2.        , -0.15916412,  2.        , -0.28018826],
       [ 2.        ,  2.        , -0.19839624, -0.36279838],
       [ 2.        ,  2.        , -0.97592457,  2.        ],
       [ 2.        ,  2.        ,  2.        ,  2.        ]])
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值