专栏首页Python机器学习算法说书人NumPy高效使用逻辑,11个角度理顺它!

NumPy高效使用逻辑,11个角度理顺它!

1 Numpy更高效

使用Python的地方,就能看到Numpy,尤其是需要数值计算的地方,Numpy的高性能更是体现的淋漓尽致。

它基于Python,提供远高于Python的高性能向量、矩阵和更高维度的数据结构。之所以性能高是由于它在密集型计算任务中,向量化操作是用C和Fortran代码实现。

2 导入Numpy

只需要一行代码就能导入:

from numpy import *

在numpy包中,描述向量,矩阵和更高维度的数据集使用的术语是array.

3 生成numpy数组

有许多方法能初始化一个新的numpy数组,例如:arange, linspace等,从文件中读入数据,从python的lists等都能生成新的向量和矩阵数组。例:

In [1]: from numpy import *

In [2]: v = array([1,2,3,4])

In [3]: v
Out[3]: array([1, 2, 3, 4])

In [4]: m = array([[1,2],[3,4]])

In [5]: m
Out[5]:
array([[1, 2],
       [3, 4]])

v和m的类型都是ndarray,这是numpy中最主要的数据结构之一

In [6]: type(v),type(m)
Out[6]: (numpy.ndarray, numpy.ndarray)

v和m的不同仅仅是它们的形状(shape), 我们能通过ndarray.shape属性发现它们的形状信息,shape属性很有用,尤其在深度学习模型调试中:

In [7]: shape(v),shape(m)
Out[7]: ((4,), (2, 2))

numpy中获取元素个数通过size:

In [8]: size(v),size(m)
Out[8]: (4, 4)

4 为什么要用numpy?

到此,numpy.ndarray看起来非常像Python的list, 那我们为什么不用Python的list计算,干嘛非要创造一个新的数组(array)类型呢?

有多个原因:

  • Python的list是一个通用结构。Python的list能包括任意类型的对象,并且是动态类型, 它们不支持一些数学函数,比如矩阵的点乘,实现如此的函数对于Python的list而言,不会高效,因为它是动态类型
  • Numpy的array是静态类型和同质的,当array被创建时,元素的类型就确定
  • Numpy的array更节省内存
  • 由于是静态类型,一些数学函数实现起来会更快,例如array间的加减乘除能够用C和Fortran实现

使用ndarray.dtype, 我们能看到一个数组内元素的类型:

In [9]: m.dtype
Out[9]: dtype('int32')

如果我们尝试用str类型赋值给m,会报错:

In [10]: m[0,0]='hello'
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-10-8d5580112ac6> in <module>
----> 1 m[0,0]='hello'

ValueError: invalid literal for int() with base 10: 'hello'

创建数组时,能指定类型,通过为dtype赋值:

In [11]: mc = array([[1,2,],[3,4]],dtype=complex)

In [12]: mc
Out[12]:
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])

dtype更多取值:int, float, complex, bool, object, 我们还可以显示的定义数据位数的类型,如:int64, int16, float128, complex128

5 通过函数生成数组

对于更大的数组,手动初始化数据是不现实的,比如使用python的list. 我们得用numpy提供的函数才能生成不同形式的数组。比如更通用的:

arange 函数:起始点,终点,步长;不包括终点

In [2]: x = arange(0,10,1)
In [3]: x
Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [4]: x = arange(-1, 1, 0.1)
In [5]: x
Out[5]:
array([-1.00000000e+00, -9.00000000e-01, -8.00000000e-01, -7.00000000e-01,
       -6.00000000e-01, -5.00000000e-01, -4.00000000e-01, -3.00000000e-01,
       -2.00000000e-01, -1.00000000e-01, -2.22044605e-16,  1.00000000e-01,
        2.00000000e-01,  3.00000000e-01,  4.00000000e-01,  5.00000000e-01,
        6.00000000e-01,  7.00000000e-01,  8.00000000e-01,  9.00000000e-01])

linspace 函数:起始点,终点,分割份数;包括终点

In [5]: linspace(0,10,5)
Out[5]: array([ 0. ,  2.5,  5. ,  7.5, 10. ])

logspace 函数:如下例子,各项分别为 e^1,e^2, e^3,…e^10

In [17]: logspace(1, 10, 10, base=e)
Out[17]:
array([2.71828183e+00, 7.38905610e+00, 2.00855369e+01, 5.45981500e+01,
       1.48413159e+02, 4.03428793e+02, 1.09663316e+03, 2.98095799e+03,
       8.10308393e+03, 2.20264658e+04])

mgrid 函数,实际工作中也很有用,在这里我列举一个

In [18]: x,y = mgrid[0:5,0:5]

In [19]: x
Out[19]:
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]])

In [20]: y
Out[20]:
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]])

这是基本用法,完全看不出干啥。如果我有10个点,想要得出这10个点的两两间距离:

x,y = mgrid[0:5,0:5]
In [28]: list(map(lambda xe,ye: [(ex,ey) for ex, ey in zip(xe, ye)], x,y))
Out[28]:
[[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)],
 [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4)],
 [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4)],
 [(3, 0), (3, 1), (3, 2), (3, 3), (3, 4)],
 [(4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]]

特殊点的矩阵,对角阵:

In [22]: diag([1,2,3])
Out[22]:
array([[1, 0, 0],
       [0, 2, 0],
       [0, 0, 3]])

主对角线偏移1的矩阵:

In [23]: diag([1,2,3],k=1)
Out[23]:
array([[0, 1, 0, 0],
       [0, 0, 2, 0],
       [0, 0, 0, 3],
       [0, 0, 0, 0]])

零阵:

In [24]: zeros((3,3))
Out[24]:
array([[0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])

1阵:

In [25]: ones((3,3))
Out[25]:
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])

6 随机函数

Numpy提供的random使用起来更友好,如下:

获得取值(0,1)上的均匀分布,生成shape为(1,2,3)的三维数组:

In [36]: random.rand(1,2,3)
Out[36]:
array([[[0.22120906, 0.80388812, 0.52726328],
        [0.37277153, 0.88388754, 0.76586961]]])

满足均值为0,方差为1的高斯分布得到随机数据,生成shape为(5,)的一维数组:

In [38]: random.randn(5)
Out[38]: array([ 0.51924157,  1.05994257, -0.492707  ,  0.87205736, -0.14581919])

7 主要属性

生成1个三维数组:

In [32]: M = random.rand(2,3,4)
In [33]: M
Out[33]:
array([[[0.92362312, 0.68582456, 0.95927478, 0.40126712],
        [0.13534077, 0.42983011, 0.72604572, 0.64202846],
        [0.96822191, 0.8332197 , 0.64065175, 0.8979606 ]],

       [[0.62970371, 0.22183503, 0.58752818, 0.20008916],
        [0.21533794, 0.20558915, 0.751807  , 0.07743367],
        [0.09854002, 0.55408343, 0.8663579 , 0.150306  ]]])

每个元素的字节数:

In [28]: M.itemsize
Out[28]: 8

M的总字节数:

In [30]: M.nbytes
Out[30]: 192

M的维数:

In [29]: M.ndim
Out[29]: 3

8 索引数组

索引数组的元素,可以使用方括号和下标,M是三维,下标索引的方法:

In [38]: M[1,1,2]
Out[38]: 0.7518069979719579

使用 表示全部此维度的所有元素都要获取:

In [41]: M[0,:,:]
Out[41]:
array([[0.92362312, 0.68582456, 0.95927478, 0.40126712],
       [0.13534077, 0.42983011, 0.72604572, 0.64202846],
       [0.96822191, 0.8332197 , 0.64065175, 0.8979606 ]])

也可方便的切片:

In [47]: M[:,:2,1:3]
Out[47]:
array([[[1.        , 0.95927478],
        [1.        , 0.72604572]],

       [[0.22183503, 0.58752818],
        [0.20558915, 0.751807  ]]])

直接赋值:

In [43]: M[0,:,1]=1.0
In [44]: M
Out[44]:
array([[[0.92362312, 1.        , 0.95927478, 0.40126712],
        [0.13534077, 1.        , 0.72604572, 0.64202846],
        [0.96822191, 1.        , 0.64065175, 0.8979606 ]],

       [[0.62970371, 0.22183503, 0.58752818, 0.20008916],
        [0.21533794, 0.20558915, 0.751807  , 0.07743367],
        [0.09854002, 0.55408343, 0.8663579 , 0.150306  ]]])

更有用的掩码索引,对应一个bool类型的数组:

In [49]: M[0,:,[False,True,True,False]]
Out[49]:
array([[1.        , 1.        , 1.        ],
       [0.95927478, 0.72604572, 0.64065175]])

M的维数:2*3*4,结果维度预期:1*3*2,但是实际结果维度:1*2*3

掩码索引,这一特性对于带条件的选取元素很重要。例如,使用arange生成一维数组x:

In [51]: x
Out[51]:
array([0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5, 6. ,
       6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])

判断x 中哪些元素大于5 呢

In [52]: x>5

结果返回的一堆:True, False:

Out[52]:
array([False, False, False, False, False, False, False, False, False,
       False, False,  True,  True,  True,  True,  True,  True,  True,
        True,  True])

然而有时候,我们想知道x 中哪些位置的元素大于5,此时要借助 where :

In [53]: where(x>5)
Out[53]: (array([11, 12, 13, 14, 15, 16, 17, 18, 19], dtype=int64),)

9 元素级操作

NumPy中两个数组加减乘除等,默认都是对应元素的操作:

In [55]: v1
Out[55]: array([0, 1, 2, 3, 4])

v1+2:

In [57]: v1+2
Out[57]: array([2, 3, 4, 5, 6])

v1 * v1:

In [58]: v1 * v1
Out[58]: array([ 0,  1,  4,  9, 16])

10 矩阵运算

线性代数中,矩阵的相乘操作在NumPy 中怎么实现,两种方法:dot函数包装为matrix对象

dot 操作:

# 数值[1,10)内,生成shape为(5,2)的随机整数数组
v2 = random.randint(1,10,(5,2))
In [77]: v2
Out[77]:
array([[8, 6],
       [9, 4],
       [4, 6],
       [1, 3],
       [2, 1]])
# v1:1行5列,v2:5行2列,结果1行2列
In [78]: dot(v1,v2)
Out[78]: array([28, 29])

另一种方法包装为matrix类

In [81]: matrix(v1)*matrix(v2)
Out[81]: matrix([[28, 29]])

经过包装后,shape取值也会变得更符合线代中的常识:

In [83]: matrix(v1).shape
Out[83]: (1, 5)

而在没有包装前:

In [84]: v1.shape
Out[84]: (5,)

求矩阵的行列式,要求数组的最后两个维度相等,因此重新构造一个矩阵:

In [88]: v3 = random.randint(0,10,(3,3))

In [89]: v3
Out[89]:
array([[6, 5, 8],
       [8, 4, 0],
       [3, 8, 1]])

In [90]: v3m = matrix(v3)

In [91]: v3m
Out[91]:
matrix([[6, 5, 8],
        [8, 4, 0],
        [3, 8, 1]])

In [92]: linalg.det(v3m)
Out[92]: 399.9999999999999

11 统计变量

求平均值:

In [93]: v3
Out[93]:
array([[6, 5, 8],
       [8, 4, 0],
       [3, 8, 1]])

In [94]: v3.mean()
Out[94]: 4.777777777777778
In [95]: v3.mean(axis=1)
Out[95]: array([6.33333333, 4.        , 4.        ])

求标准差:

In [97]: v3.std()
Out[97]: 2.8588178511708016

求方差:

In [98]: v3.var()
Out[98]: 8.17283950617284

求最大值:

In [99]: v3.max(axis=1)
Out[99]: array([8, 8, 8])

求最小值:

In [106]: v3.min(axis=1)
Out[106]: array([5, 0, 1])

求和:

In [107]: v3.sum(axis=1)
Out[107]: array([19, 12, 12])

求累乘:

In [108]: v3.cumprod(axis=1)
Out[108]:
array([[  6,  30, 240],
       [  8,  32,   0],
       [  3,  24,  24]], dtype=int32)

求累和:

In [109]: v3.cumsum(axis=1)
Out[109]:
array([[ 6, 11, 19],
       [ 8, 12, 12],
       [ 3, 11, 12]], dtype=int32)

求迹:

In [111]: v3.trace()
Out[111]: 11

In [112]: diag(v3).sum()
Out[112]: 11

12 改变Shape

NumPy数组的shape 被修改而无需复制原有数据,这使它更为高效。

In [117]: v4 = v3.reshape((1,9))
In [118]: v4
Out[118]: array([[6, 5, 8, 8, 4, 0, 3, 8, 1]])

我们验证下v4 是否真的没有被复制:

# 修改v4的第一行第2列的元素5,为10
In [120]: v4[0,1]=10
In [121]: v4
Out[121]: array([[ 6, 10,  8,  8,  4,  0,  3,  8,  1]])
# 查看v3,发现对应元素也变为10
In [122]: v3
Out[122]:
array([[ 6, 10,  8],
       [ 8,  4,  0],
       [ 3,  8,  1]])

所以验证得出:v4仅仅是v3的视图,未发生复制。

NumPy 提供的flatten 函数也有改变shape 的能力,将高维数组变为向量。但是,值得注意的是它会发生数组复制行为,因此不是高效的

有时,我们需要增加维度,此时可使用newaxis,它会插入1个维度,如下在第三个维度插入,v5的shape变为: [3,3,1]

In [128]: v5 = v3[:,:,newaxis]
In [129]: v5
Out[129]:
array([[[ 6],
        [10],
        [ 8]],

       [[ 8],
        [ 4],
        [ 0]],

       [[ 3],
        [ 8],
        [ 1]]])

13 数组由小变大

NumPy中的函数 repeat, tile, vstack, hstack, 和concatenate 具备变换小数组为大数组的能力。

repeat复制元素

In [132]: a = array([[1,2],[3,4]])

In [137]: repeat(a,2,axis=1)
Out[137]:
array([[1, 1, 2, 2],
       [3, 3, 4, 4]])

In [138]: repeat(a,2,axis=0)
Out[138]:
array([[1, 2],
       [1, 2],
       [3, 4],
       [3, 4]])

tile 复制块:

In [5]: tile(a,2)
Out[5]:
array([[1, 2, 1, 2],
       [3, 4, 3, 4]])

In [6]: tile(a,(2,3))
Out[6]:
array([[1, 2, 1, 2, 1, 2],
       [3, 4, 3, 4, 3, 4],
       [1, 2, 1, 2, 1, 2],
       [3, 4, 3, 4, 3, 4]])

vstack竖直方向合并数组:

In [18]: c = array([[-1,-2]])
Out[20]:
array([[ 1,  2],
       [ 3,  4],
       [-1, -2]])

hstack 水平方向合并数组:

b = array([[40],[12]])
hstack((a,b))
array([[ 1,  2, 40],
       [ 3,  4, 12]])

concatenate指定在哪个轴向上合作数组:

In [26]: concatenate((a,c),axis=0) # 效果等于vstack
Out[26]:
array([[ 1,  2],
       [ 3,  4],
       [-1, -2]])

In [27]: concatenate((a,b),axis=1) # 效果等于hstack
Out[27]:
array([[ 1,  2, 40],
       [ 3,  4, 12]])

以上全部手码,利用两个周的课余时间整理出来,从十几个角度汇总NumPy平时经常使用的函数,希望对大家有用。

本文分享自微信公众号 - Python机器学习算法说书人(Python-ML-Algorithm)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python|try/except捕捉SyntaxError

    大家在编写程序的时候,多多少少都会出现各种问题。有的时候会让你的程序根本无法运行起来,有的时候会让你的程序在运行过程中出毛病。如果是第一种情况,我们把这个问题叫...

    不可言诉的深渊
  • 用 Python 对成绩分类汇总

    这学期我们每个人需要填写三张学年鉴定表,每一张表中都有学业总平均分和考试课平均分两项内容,如果想用手工的方式算出这两项内容不知道要花多久,所以我建议编写一个算法...

    不可言诉的深渊
  • Android App 除了 Java 和 Kotlin 没有第三个选择?怎么可能~!

    最近因为好多同学因为各种原因需要学会做一个安卓应用程序,而学会做一个安卓应用程序需要花费的时间和精力是很大的,同时传统的安卓开发还有很多不足,比如当软件进行更新...

    不可言诉的深渊
  • NumPy高效使用逻辑,11个角度理顺它!

    使用Python的地方,就能看到Numpy,尤其是需要数值计算的地方,Numpy的高性能更是体现的淋漓尽致。

    double
  • 《利用Python进行数据分析·第2版》第4章 NumPy基础:数组和矢量计算4.1 NumPy的ndarray:一种多维数组对象4.2 通用函数:快速的元素级数组函数4.3 利用数组进行数据处理4.

    NumPy(Numerical Python的简称)是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。 NumPy...

    SeanCheney
  • Numpy使用3

    上一篇博客介绍了numpy的几种初始化方式和numpy的数据类型(dtype)和shape的相关知识,这篇介绍numpy矩阵的索引与切片 矢量化 numpy数组...

    GavinZhou
  • Numpy使用2

    上一个博客写到了numpy的特性,如何安装以及功能初探。这一篇就讲讲numpy的常用功能以及与其它python库的结合使用。 假设你已经成功执行下列语句: im...

    GavinZhou
  • Python高级特性之---切片操作

    索引正向从0开始,逆向从-1开始 N:切片开始位置 M:切片结束位置(不包含) P:指定切片步长,为正数表示按照指定步长正向切片,为负数反之 一、列表的切片操作...

    py3study
  • Red Hat安全公告—2016年11月

    在2016年10月份至2016年11月份Red hat CVE漏洞库发布了7个“重要”“严重”等级的安全漏洞,针对出现的安全漏洞,发布了对应的Bugzilla。...

    嘉为科技
  • Red Hat安全公告—2017年1月

    在2016年12月份至2017年1月份Red hat CVE漏洞库发布了4个“重要”“严重”等级的安全漏洞,针对出现的安全漏洞,发布了对应的Bugzilla。安...

    嘉为科技

扫码关注云+社区

领取腾讯云代金券