Numpy 学习

Python模块中的numpy,这是一个处理数组的强大模块,而该模块也是其他数据分析模块(如pandas和scipy)的核心。下面将从这5个方面来介绍numpu模块的内容: 1. 数组的创建 2. 有关数组的属性和函数 3. 数组元素的获取--普通索引、切片、布尔索引和花式索引 4. 统计函数与线性代数运算 5. 随机数的生成

数组的创建

一维数组的创建

可以使用numpy中的arange()函数创建一维有序数组,它是内置函数range的扩展版。

1
2
3
4
5
6
7
8
9
10
11
12
In [1]: import numpy as np
In [2]: ls1 = range(10)
In [3]: list(ls1)
Out[3]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [4]: type(ls1)
Out[4]: range

In [5]: ls2 = np.arange(10)
In [6]: list(ls2)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [7]: type(ls2)
Out[7]: numpy.ndarray

通过arange生成的序列就不是简简单单的列表类型了,而是一个一维数组。

如果一维数组不是一个规律的有序元素,而是人为的输入,就需要array()函数创建了。numpy中使用array()函数创建数组,array的首个参数一定是一个序列,可以是元组也可以是列表

1
2
3
4
5
In [8]: arr1 = np.array((1,20,13,28,22))
In [9]: arr1
Out[9]: array([ 1, 20, 13, 28, 22])
In [10]: type(arr1)
Out[10]: numpy.ndarray

上面是由元组序列构成的一维数组。

1
2
3
4
5
In [11]: arr2 = np.array([1,1,2,3,5,8,13,21])
In [12]: arr2
Out[12]: array([ 1, 1, 2, 3, 5, 8, 13, 21])
In [13]: type(arr2)
Out[13]: numpy.ndarray

上面是由列表序列构成的一维数组。

二维数组的创建

二维数组的创建,其实在就是列表套列表或元组套元组

1
2
3
4
5
6
In [14]: arr3 = np.array(((1,1,2,3),(5,8,13,21),(34,55,89,144)))
In [15]: arr3
Out[15]:
array([[ 1, 1, 2, 3],
[ 5, 8, 13, 21],
[ 34, 55, 89, 144]])

上面是使用元组套元组的方式。

1
2
3
4
5
6
In [16]: arr4 = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
In [17]: arr4
Out[17]:
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])

上面是使用列表套列表的方式。

对于高维数组在将来的数据分析中用的比较少,这里关于高维数组的创建就不赘述了,构建方法仍然是使用嵌套的方式。

上面所介绍的都是人为设定的一维、二维或高维数组,numpy中也提供了几种特殊的数组,它们是:

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
In [18]: np.ones(3)  #返回一维元素全为1的数组
Out[18]: array([ 1., 1., 1.])

In [19]: np.ones([3,4]) #返回元素全为1的3×4二维数组
Out[19]:
array([[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.]])

In [20]: np.zeros(3) #返回一维元素全为0的数组
Out[20]: array([ 0., 0., 0.])

In [21]: np.zeros([3,4]) #返回元素全为0的3×4二维数组
Out[21]:
array([[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])

In [22]: np.empty(3) #返回一维空数组
Out[22]: array([ 1.13224202e+277, 6.00769955e-307, 2.55195390e-303])

In [23]: np.empty([3,4]) #返回3×4二维空数组
Out[23]:
array([[ 1.09494983e-311, 8.19904309e-314, 1.09678092e-311, 1.09494983e-311],
[ 2.09290548e-313, 1.09678288e-311, 1.09494983e-311, 3.36629926e-313],
[ 1.09678538e-311, 4.03179200e-313, 1.09678287e-311, 1.09494983e-311]])

原文中对于np.empty的举例不够严谨,这个函数返回的是指定规模未经初始化的数组,速度要比 oneszeros 快,但里面的值都是随机的,要谨慎使用。

有关数组的属性和函数

当一个数组构建好后,我们看看关于数组本身的操作又有哪些属性和函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [24]: arr3
Out[24]:
array([[ 1, 1, 2, 3],
[ 5, 8, 13, 21],
[ 34, 55, 89, 144]])

In [25]: arr3.shape #shape方法返回数组的行数和列数
Out[25]: (3, 4)

In [26]: arr3.dtype #dtype方法返回数组的数据类型
Out[26]: dtype('int32')

In [27]: a = arr3.ravel() #通过ravel的方法将数组拉直(多维数组降为一维数组)
In [28]: a
Out[28]: array([ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144])

In [29]: b = arr3.flatten() #通过flatten的方法将数组拉直
In [30]: b
Out[30]: array([ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144])

两者的区别在于ravel方法生成的是原数组的视图,无需占有内存空间,但视图的改变会影响到原数组的变化。而flatten方法返回的是真实值,其值的改变并不会影响原数组的更改

通过下面的例子也许就能明白了:

1
2
3
4
5
6
In [31]: b[:3] = 0
In [32]: arr3
Out[32]:
array([[ 1, 1, 2, 3],
[ 5, 8, 13, 21],
[ 34, 55, 89, 144]])

通过更改b的值,原数组没有变化。

1
2
3
4
5
6
In [33]: a[:3] = 0
In [34]: arr3
Out[34]:
array([[ 0, 0, 0, 3],
[ 5, 8, 13, 21],
[ 34, 55, 89, 144]])

a的值变化后,会导致原数组跟着变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
In [35]: arr4
Out[35]:
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])

In [36]: arr4.ndim #返回数组的维数(嵌套的层数)
Out[36]: 2

In [37]: arr4.size #返回数组元素的个数
Out[37]: 12

In [38]: arr4.T #返回数组的转置结果
Out[38]:
array([[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11],
[ 4, 8, 12]])

如果数组的数据类型为复数的话,real方法可以返回复数的实部,imag方法返回复数的虚部。

介绍完数组的一些方法后,接下来我们看看数组自身有哪些函数可操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
In [39]: len(arr4) #返回数组有多少行
Out[39]: 3

In [40]: arr3
Out[40]:
array([[ 0, 0, 0, 3],
[ 5, 8, 13, 21],
[ 34, 55, 89, 144]])

In [41]: arr4
Out[41]:
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])

In [42]: np.hstack((arr3,arr4))
Out[42]:
array([[ 0, 0, 0, 3, 1, 2, 3, 4],
[ 5, 8, 13, 21, 5, 6, 7, 8],
[ 34, 55, 89, 144, 9, 10, 11, 12]])

横向拼接arr3和arr4两个数组,但必须满足两个数组的行数相同

1
2
3
4
5
6
7
8
In [43]: np.vstack((arr3,arr4))
Out[43]:
array([[ 0, 0, 0, 3],
[ 5, 8, 13, 21],
[ 34, 55, 89, 144],
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])

纵向拼接arr3和arr4两个数组,但必须满足两个数组的列数相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [44]: np.column_stack((arr3,arr4))    #与hstack函数具有一样的效果
Out[44]:
array([[ 0, 0, 0, 3, 1, 2, 3, 4],
[ 5, 8, 13, 21, 5, 6, 7, 8],
[ 34, 55, 89, 144, 9, 10, 11, 12]])

In [45]: np.row_stack((arr3,arr4)) #与vstack函数具有一样的效果
Out[45]:
array([[ 0, 0, 0, 3],
[ 5, 8, 13, 21],
[ 34, 55, 89, 144],
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])

reshape()函数和resize()函数可以重新设置数组的行数和列数:

1
2
3
4
5
6
7
8
9
10
11
12
In [46]: arr5 = np.arange(24)
In [47]: arr5 #此为一维数组
Out[47]:
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 [48]: a = arr5.reshape(4,6)
In [49]: a
Out[49]:
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]])

通过reshape函数将一维数组设置为二维数组,且为4行6列的数组。reshape函数不会改变原数组

1
2
3
4
5
6
7
8
9
In [50]: a.resize(6,4)
In [51]: a
Out[51]:
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]])

通过resize函数会直接改变原数组的形状

数组转换:tolist将数组转换为列表,astype()强制转换数组的数据类型,下面是两个函数的例子:

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
In [53]: b = a.tolist()
In [54]: b
Out[54]:
[[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 [55]: type(b)
Out[55]: list

In [56]: c = a.astype(float)
In [57]: c
Out[57]:
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 [58]: a.dtype
Out[58]: dtype('int32')
In [59]: c.dtype
Out[59]: dtype('float64')

数组元素的获取

通过索引和切片的方式获取数组元素,一维数组元素的获取与列表、元组的获取方式一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In [60]: arr7 = np.arange(10)
In [61]: arr7
Out[61]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [62]: arr7[3] #获取第4个元素
Out[62]: 3

In [63]: arr7[:3] #获取前3个元素
Out[63]: array([0, 1, 2])

In [64]: arr7[3:] #获取第4个元素即之后的所有元素
Out[64]: array([3, 4, 5, 6, 7, 8, 9])

In [65]: arr7[-2:] #获取末尾的2个元素
Out[65]: array([8, 9])

In [66]: arr7[::2] #从第1个元素开始,获取步长为2的所有元素
Out[66]: array([0, 2, 4, 6, 8])

补充一下,如果要获取多个元素,且它们的索引没有固定步长这样的规则的话,可以传入一个list作为索引

1
2
3
4
5
>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a[[0,3,4]] #返回数组的第1,4,5个元素
array([0, 3, 4])

二维数组元素的获取:

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
In [67]: arr8 = np.arange(12).reshape(3,4)
In [68]: arr8
Out[68]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])

In [69]: arr8[1] #返回数组的第2行
Out[69]: array([4, 5, 6, 7])

In [70]: arr8[:2] #返回数组的前2行
Out[70]:
array([[0, 1, 2, 3],
[4, 5, 6, 7]])

In [71]: arr8[[0,2]] #返回指定的第1行和第3行
Out[71]:
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11]])

In [72]: arr8[:,0] #返回数组的第1列
Out[72]: array([0, 4, 8])

In [73]: arr8[:,-2:] #返回数组的后2列
Out[73]:
array([[ 2, 3],
[ 6, 7],
[10, 11]])

In [74]: arr8[:,[0,2]] #返回数组的第1列和第3列
Out[74]:
array([[ 0, 2],
[ 4, 6],
[ 8, 10]])

In [75]: arr8[1,2] #返回数组中第2行第3列对应的元素
Out[75]: 6

布尔索引,即索引值为True和False,需要注意的是布尔索引必须是数组对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In [76]: log = np.array([True,False,False,True,True,False])
In [77]: arr9 = np.arange(24).reshape(6,4)
In [78]: arr9
Out[78]:
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 [79]: arr9[log] #返回所有为True的对应行
Out[79]:
array([[ 0, 1, 2, 3],
[12, 13, 14, 15],
[16, 17, 18, 19]])

In [80]: arr9[-log] #通过负号筛选出所有为False的对应行
Out[80]:
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[20, 21, 22, 23]])

举一个场景,一维数组表示区域,二维数组表示观测值,如何选取目标区域的观测?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In [81]: area = np.array(['A','B','A','C','A','B','D'])
In [82]: area
Out[82]:
array(['A', 'B', 'A', 'C', 'A', 'B', 'D'],
dtype='<U1')

In [83]: observes = np.arange(21).reshape(7,3)
In [84]: observes
Out[84]:
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17],
[18, 19, 20]])

In [85]: observes[area == 'A'] #这里[]内的"area == 'A'"语句返回的就是一个全是布尔值的数组对象
Out[85]:
array([[ 0, 1, 2],
[ 6, 7, 8],
[12, 13, 14]])

返回所有A区域的观测。

1
2
3
4
5
6
In [86]: observes[(area == 'A') | (area == 'D')] #条件值需要在&(and),|(or)两端用圆括号括起来
Out[86]:
array([[ 0, 1, 2],
[ 6, 7, 8],
[12, 13, 14],
[18, 19, 20]])

返回所有A区域和D区域的观测。

当然,布尔索引也可以与普通索引或切片混合使用:

1
2
3
4
5
In [87]: observes[area == 'A'][:,[0,2]]
Out[87]:
array([[ 0, 2],
[ 6, 8],
[12, 14]])

返回A区域的所有行,且只获取第1列与第3列数据。

花式索引:实际上就是将数组作为索引将原数组的元素提取出来

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
In [88]: arr10 = np.arange(1,29).reshape(7,4)
In [89]: arr10
Out[89]:
array([[ 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]])

In [90]: arr10[[4,1,3,5]] #按照指定顺序返回指定行
Out[90]:
array([[17, 18, 19, 20],
[ 5, 6, 7, 8],
[13, 14, 15, 16],
[21, 22, 23, 24]])

In [91]: arr10[[4,1,5]][:,[0,2,3]] #返回指定的行与列,可以分解为两条命令来看
Out[91]:
array([[17, 19, 20],
[ 5, 7, 8],
[21, 23, 24]])

In [92]: arr10[[4,1,5],[0,2,3]] # 返回指定位置的数字
Out[92]: array([17, 7, 24])

请注意!这与上面的返回结果是截然不同的,上面的命令返回的是二维数组,而这条命令返回的是一维数组。P.S.原文这里没有解释的很清楚,简单来说,如果在方括号内使用两个甚至多个数组/列表作为索引,中间以逗号分割,那么数组的顺序对应的就是从低维到高维,比如 In [92] 中第一个列表的第一个元素是4,第二个列表的第一个元素是0,这就表示要从arr10这个二维数组中取出的第一个元素在第一维第5个对象和第二维第1个对象交叠的地方,也即二维数组的第5行第1个元素17。最后会将所有取出的元素放入一个一维数组中返回。但如果某一维没有用数组/列表指定,而是使用了冒号表示取所有的话,返回的就是二维对象了。再举一个例子吧:

1
2
3
4
5
6
7
8
9
10
11
>>> a = np.arange(10).reshape(2,5)
>>> a
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])

>>> a[:,[0,4]]
array([[0, 4],
[5, 9]])

>>> a[[0,0,1,1],[0,4,0,4]]
array([0, 4, 5, 9])

如果想使用比较简单的方式返回指定行与列的二维数组的话,可以使用ix_()函数

1
2
3
4
5
In [93]: arr10[np.ix_([4,1,5],[0,2,3])] #    允许我们用指定位置的方式,并且保持返回的数组形状不变
Out[93]:
array([[17, 19, 20],
[ 5, 7, 8],
[21, 23, 24]])

这与 arr10[[4,1,5]][:,[0,2,3]] 返回的结果是一致的。

统计函数与线性代数运算

统计函数

统计运算中常见的聚合函数有:最小值、最大值、中位数、均值、方差、标准差等。首先来看看数组元素级别的计算:

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
In [94]: arr11 = 5-np.arange(1,13).reshape(4,3)
# randint返回指定区间的随机整数,区间左闭右开,size不指定时为None,返回1个随机整数
In [95]: arr12 = np.random.randint(1,10,size = 12).reshape(4,3)
In [96]: arr11
Out[96]:
array([[ 4, 3, 2],
[ 1, 0, -1],
[-2, -3, -4],
[-5, -6, -7]])

In [97]: arr12
Out[97]:
array([[1, 3, 7],
[7, 3, 7],
[3, 7, 4],
[6, 1, 2]])

In [98]: arr11 ** 2 #计算每个元素的平方
Out[98]:
array([[16, 9, 4],
[ 1, 0, 1],
[ 4, 9, 16],
[25, 36, 49]])

In [99]: np.sqrt(arr11) #计算每个元素的平方根
Out[99]:
array([[ 2. , 1.73205081, 1.41421356],
[ 1. , 0. , nan],
[ nan, nan, nan],
[ nan, nan, nan]])

由于负值的平方根没有意义,故返回nan。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
In [100]: np.exp(arr11)   #计算每个元素的指数值,exp(a) 即 e^a
Out[100]:
array([[ 5.45981500e+01, 2.00855369e+01, 7.38905610e+00],
[ 2.71828183e+00, 1.00000000e+00, 3.67879441e-01],
[ 1.35335283e-01, 4.97870684e-02, 1.83156389e-02],
[ 6.73794700e-03, 2.47875218e-03, 9.11881966e-04]])

In [101]: np.log(arr12) #计算每个元素的自然对数值,log(a) 即 log_e(a),还有log2和log10两个函数
Out[101]:
array([[ 0. , 1.09861229, 1.94591015],
[ 1.94591015, 1.09861229, 1.94591015],
[ 1.09861229, 1.94591015, 1.38629436],
[ 1.79175947, 0. , 0.69314718]])

In [102]: np.abs(arr11) #计算每个元素的绝对值
Out[102]:
array([[4, 3, 2],
[1, 0, 1],
[2, 3, 4],
[5, 6, 7]])

相同形状数组间元素的操作:

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
In [103]: arr11 + arr12   #加
Out[103]:
array([[ 5, 6, 9],
[ 8, 3, 6],
[ 1, 4, 0],
[ 1, -5, -5]])

In [104]: arr11 - arr12 #减
Out[104]:
array([[ 3, 0, -5],
[ -6, -3, -8],
[ -5, -10, -8],
[-11, -7, -9]])

In [105]: arr11 * arr12 #乘
Out[105]:
array([[ 4, 9, 14],
[ 7, 0, -7],
[ -6, -21, -16],
[-30, -6, -14]])

In [106]: arr11 / arr12 #除
Out[106]:
array([[ 4. , 1. , 0.28571429],
[ 0.14285714, 0. , -0.14285714],
[-0.66666667, -0.42857143, -1. ],
[-0.83333333, -6. , -3.5 ]])

In [107]: arr11 // arr12 #整除
Out[107]:
array([[ 4, 1, 0],
[ 0, 0, -1],
[-1, -1, -1],
[-1, -6, -4]], dtype=int32)

In [108]: arr11 % arr12 #取余
Out[108]:
array([[0, 0, 2],
[1, 0, 6],
[1, 4, 0],
[1, 0, 1]], dtype=int32)

统计运算函数

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
In [109]: np.sum(arr11)   #计算所有元素的和
Out[109]: -18

In [110]: np.sum(arr11,axis = 0) #对每一列求和,注意axis是0
Out[110]: array([ -2, -6, -10])

In [111]: np.sum(arr11, axis = 1) #对每一行求和,注意axis是1
Out[111]: array([ 9, 0, -9, -18])

In [112]: np.cumsum(arr11) #对每一个元素求累积和(从上到下,从左到右的元素顺序),即每移动一次就把当前数字加到和值
Out[112]: array([ 4, 7, 9, 10, 10, 9, 7, 4, 0, -5, -11, -18], dtype=int32)

In [113]: np.cumsum(arr11, axis = 0) #计算每一列的累积和,并返回二维数组
Out[113]:
array([[ 4, 3, 2],
[ 5, 3, 1],
[ 3, 0, -3],
[ -2, -6, -10]], dtype=int32)

In [114]: np.cumprod(arr11, axis = 1) #计算每一行的累计积,并返回二维数组
Out[114]:
array([[ 4, 12, 24],
[ 1, 0, 0],
[ -2, 6, -24],
[ -5, 30, -210]], dtype=int32)

In [115]: np.min(arr11) #计算所有元素的最小值
Out[115]: -7

In [116]: np.max(arr11, axis = 0) #计算每一列的最大值
Out[116]: array([4, 3, 2])

In [117]: np.mean(arr11) #计算所有元素的均值
Out[117]: -1.5

In [118]: np.mean(arr11, axis = 1) #计算每一行的均值
Out[118]: array([ 3., 0., -3., -6.])

In [119]: np.median(arr11) #计算所有元素的中位数
Out[119]: -1.5

In [120]: np.median(arr11, axis = 0) #计算每一列的中位数
Out[120]: array([-0.5, -1.5, -2.5])

In [121]: np.var(arr12) #计算所有元素的方差
Out[121]: 5.354166666666667

In [122]: np.std(arr12, axis = 1) #计算每一行的标准差
Out[122]: array([ 2.49443826, 1.88561808, 1.69967317, 2.1602469 ])

numpy中的统计函数运算是非常灵活的,既可以计算所有元素的统计值,也可以计算指定行或列的统计指标。还有其他常用的函数,如符号函数sign(将正数、负数、零分别映射为1、-1、0),ceil(取>=x的最小整数),floor(取<=x的最大整数),modf(将浮点数的整数部分与小数部分分别存入两个独立的数组),cosarccossinarcsintanarctan等。

让我很兴奋的一个函数是where(),它类似于Excel中的if函数,可以进行灵活的变换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [123]: arr11
Out[123]:
array([[ 4, 3, 2],
[ 1, 0, -1],
[-2, -3, -4],
[-5, -6, -7]])

In [124]: np.where(arr11 < 0, 'negtive','positive')
Out[124]:
array([['positive', 'positive', 'positive'],
['positive', 'positive', 'negtive'],
['negtive', 'negtive', 'negtive'],
['negtive', 'negtive', 'negtive']],
dtype='<U8')

当然,np.where 还可以嵌套使用,完成复杂的运算。

其它函数

unique(x): 计算x的唯一元素,并返回有序结果 intersect(x,y): 计算x和y的公共元素,即交集 union1d(x,y): 计算x和y的并集 setdiff1d(x,y): 计算x和y的差集,即元素在x中,不在y中 setxor1d(x,y): 计算集合的对称差,即存在于一个数组中,但不同时存在于两个数组中 in1d(x,y): 判断x的元素是否包含于y中

线性代数运算

同样numpu也跟R语言一样,可以非常方便的进行线性代数方面的计算,如行列式、逆、迹、特征根、特征向量等。但需要注意的是,有关线性代数的函数并不在numpy中,而是numpy的子例linalg中。

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
In [125]: arr13 = np.array([[1,2,3,5],[2,4,1,6],[1,1,4,3],[2,5,4,1]])
In [126]: arr13
Out[126]:
array([[1, 2, 3, 5],
[2, 4, 1, 6],
[1, 1, 4, 3],
[2, 5, 4, 1]])

In [127]: np.linalg.det(arr13) #返回方阵的行列式
Out[127]: 51.000000000000021

In [128]: np.linalg.inv(arr13) #返回方阵的逆
Out[128]:
array([[-2.23529412, 1.05882353, 1.70588235, -0.29411765],
[ 0.68627451, -0.25490196, -0.7254902 , 0.2745098 ],
[ 0.19607843, -0.21568627, 0.07843137, 0.07843137],
[ 0.25490196, 0.01960784, -0.09803922, -0.09803922]])

In [129]: np.trace(arr13) #返回方阵的迹(对角线元素之和),注意迹的求解不在linalg子例程中
Out[129]: 10

In [130]: np.linalg.eig(arr13) #返回由特征根和特征向量组成的元组
Out[130]:
(array([ 11.35035004, -3.99231852, -0.3732631 , 3.01523159]),
array([[-0.4754174 , -0.48095078, -0.95004728, 0.19967185],
[-0.60676806, -0.42159999, 0.28426325, -0.67482638],
[-0.36135292, -0.16859677, 0.08708826, 0.70663129],
[-0.52462832, 0.75000995, 0.09497472, -0.07357122]]))

In [131]: np.linalg.qr(arr13) #返回方阵的QR分解
Out[131]:
(array([[-0.31622777, -0.07254763, -0.35574573, -0.87645982],
[-0.63245553, -0.14509525, 0.75789308, -0.06741999],
[-0.31622777, -0.79802388, -0.38668014, 0.33709993],
[-0.63245553, 0.580381 , -0.38668014, 0.33709993]]),
array([[-3.16227766, -6.64078309, -5.37587202, -6.95701085],
[ 0. , 1.37840488, -1.23330963, -3.04700025],
[ 0. , 0. , -3.40278524, 1.22190924],
[ 0. , 0. , 0. , -3.4384193 ]]))

In [132]:np.linalg.svd(arr13) #返回方阵的奇异值分解
Out[132]:
(array([[-0.50908395, 0.27580803, 0.35260559, -0.73514132],
[-0.59475561, 0.4936665 , -0.53555663, 0.34020325],
[-0.39377551, -0.10084917, 0.70979004, 0.57529852],
[-0.48170545, -0.81856751, -0.29162732, -0.11340459]]),
array([ 11.82715609, 4.35052602, 3.17710166, 0.31197297]),
array([[-0.25836994, -0.52417446, -0.47551003, -0.65755329],
[-0.10914615, -0.38326507, -0.54167613, 0.74012294],
[-0.18632462, -0.68784764, 0.69085326, 0.12194478],
[ 0.94160248, -0.32436807, -0.05655931, -0.07050652]]))

In [133]: np.dot(arr13,arr13) #方阵的正真乘积运算
Out[133]:
array([[18, 38, 37, 31],
[23, 51, 38, 43],
[13, 25, 32, 26],
[18, 33, 31, 53]])

In [134]:arr14 = np.array([[1,-2,1],[0,2,-8],[-4,5,9]])
In [135]: vector = np.array([0,8,-9])
In [136]: np.linalg.solve(arr14,vector) # 求解线性方程组
Out[136]: array([ 29., 16., 3.]) #1*29 - 2*16 + 1*3 = 0,以此类推

随机数生成

统计学中经常会讲到数据的分布特征,如正态分布、指数分布、卡方分布、二项分布、泊松分布等,下面就讲讲有关分布的随机数生成。

正态分布直方图

1
2
3
4
5
In [137]: import matplotlib #用于绘图的模块
In [138]: np.random.seed(1234) #设置随机种子
In [139]: N = 10000 #随机产生的样本量
In [140]: randnorm = np.random.normal(size = N) #生成正态随机数
In [141]: counts, bins, path = matplotlib.pylab.hist(randnorm, bins = np.sqrt(N), normed = True, color = 'blue') #绘制直方图

以上将直方图的频数和组距存放在counts和bins内。

1
2
3
In [142]: sigma = 1; mu = 0
In [143]: norm_dist = (1/np.sqrt(2*sigma*np.pi))*np.exp(-((bins-mu)**2)/2) #正态分布密度函数
In [144]: matplotlib.pylab.plot(bins,norm_dist,color = 'red') #绘制正态分布密度函数图
graph1

补充一下,这个例子想说的是 random.normal 生成指定数目的随机数(可以进一步指定分布的均值、标准差和中心),然后把这些随机数用直方图画了出来。并且画了一条同一区间内的正态分布曲线来对比。注意种子的设置和生成随机数的大小是无关的,只是说使用不同的种子会生成不同的随机数,详细可以查找相关资料。

使用二项分布进行赌博

同时抛弃9枚硬币,如果正面朝上少于5枚,则输掉8元,否则就赢8元。如果手中有1000元作为赌资,请问赌博10000次后可能会是什么情况呢?

1
2
3
4
5
6
7
8
9
10
11
12
In [146]: np.random.seed(1234)
In [147]: binomial = np.random.binomial(9,0.5,10000) #生成二项分布随机数
In [148]: money = np.zeros(10000) #生成10000次赌资的列表
In [149]: money[0] = 1000 #首次赌资为1000元
In [150]: for i in range(1,10000):
...: if binomial[i] < 5:
...: money[i] = money[i-1] - 8
#如果少于5枚正面,则在上一次赌资的基础上输掉8元
...: else:
...: money[i] = money[i-1] + 8
#如果至少5枚正面,则在上一次赌资的基础上赢取8元
In [151]: matplotlib.pylab.plot(np.arange(10000), money)
graph2

使用随机整数实现随机游走

一个醉汉在原始位置上行走10000步后将会在什么地方呢?如果他每走一步是随机的,即下一步可能是1也可能是-1。

1
2
3
4
5
6
7
8
9
In [152]: np.random.seed(1234)    #设定随机种子
In [153]: position = 0 #设置初始位置
In [154]: walk = [] #创建空列表
In [155]: steps = 10000 #假设接下来行走10000步
In [156]: for i in np.arange(steps):
...: step = 1 if np.random.randint(0,2) else -1 #每一步都是随机的(如果随机到1就走1步,随机到0就走-1步)
...: position = position + step #对每一步进行累计求和
...: walk.append(position) #确定每一步所在的位置
In [157]: matplotlib.pylab.plot(np.arange(10000), walk) #绘制随机游走图
graph3

上面的代码还可以写成(结合前面所讲的where函数,cumsum函数):

1
2
3
4
In [158]: np.random.seed(1234)
In [159]: step = np.where(np.random.randint(0,2,10000)>0,1,-1)
In [160]: position = np.cumsum(step)
In [161]: matplotlib.pylab.plot(np.arange(10000), position)
graph4

避免for循环,可以达到同样的效果

作者

ฅ´ω`ฅ

发布于

2017-06-29

更新于

2021-06-08

许可协议


评论