Numpy 学习
Python模块中的numpy,这是一个处理数组的强大模块,而该模块也是其他数据分析模块(如pandas和scipy)的核心。下面将从这5个方面来介绍numpu模块的内容: 1. 数组的创建 2. 有关数组的属性和函数 3. 数组元素的获取--普通索引、切片、布尔索引和花式索引 4. 统计函数与线性代数运算 5. 随机数的生成
数组的创建
一维数组的创建
可以使用numpy中的arange()函数创建一维有序数组,它是内置函数range的扩展版。
1 | In [1]: import numpy as np |
通过arange生成的序列就不是简简单单的列表类型了,而是一个一维数组。
如果一维数组不是一个规律的有序元素,而是人为的输入,就需要array()函数创建了。numpy中使用array()函数创建数组,array的首个参数一定是一个序列,可以是元组也可以是列表。
1 | In [8]: arr1 = np.array((1,20,13,28,22)) |
上面是由元组序列构成的一维数组。
1 | In [11]: arr2 = np.array([1,1,2,3,5,8,13,21]) |
上面是由列表序列构成的一维数组。
二维数组的创建
二维数组的创建,其实在就是列表套列表或元组套元组 1
2
3
4
5
6In [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 | In [16]: arr4 = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]]) |
上面是使用列表套列表的方式。
对于高维数组在将来的数据分析中用的比较少,这里关于高维数组的创建就不赘述了,构建方法仍然是使用嵌套的方式。
上面所介绍的都是人为设定的一维、二维或高维数组,numpy中也提供了几种特殊的数组,它们是:
1 | In [18]: np.ones(3) #返回一维元素全为1的数组 |
原文中对于np.empty
的举例不够严谨,这个函数返回的是指定规模未经初始化的数组,速度要比 ones
和 zeros
快,但里面的值都是随机的,要谨慎使用。
有关数组的属性和函数
当一个数组构建好后,我们看看关于数组本身的操作又有哪些属性和函数:
1 | In [24]: arr3 |
两者的区别在于ravel方法生成的是原数组的视图,无需占有内存空间,但视图的改变会影响到原数组的变化。而flatten方法返回的是真实值,其值的改变并不会影响原数组的更改。
通过下面的例子也许就能明白了:
1 | In [31]: b[:3] = 0 |
通过更改b的值,原数组没有变化。
1 | In [33]: a[:3] = 0 |
a的值变化后,会导致原数组跟着变化。
1 | In [35]: arr4 |
如果数组的数据类型为复数的话,real方法可以返回复数的实部,imag方法返回复数的虚部。
介绍完数组的一些方法后,接下来我们看看数组自身有哪些函数可操作:
1 | In [39]: len(arr4) #返回数组有多少行 |
横向拼接arr3和arr4两个数组,但必须满足两个数组的行数相同。
1 | In [43]: np.vstack((arr3,arr4)) |
纵向拼接arr3和arr4两个数组,但必须满足两个数组的列数相同。
1 | In [44]: np.column_stack((arr3,arr4)) #与hstack函数具有一样的效果 |
reshape()函数和resize()函数可以重新设置数组的行数和列数:
1 | In [46]: arr5 = np.arange(24) |
通过reshape函数将一维数组设置为二维数组,且为4行6列的数组。reshape函数不会改变原数组。
1 | In [50]: a.resize(6,4) |
通过resize函数会直接改变原数组的形状。
数组转换:tolist将数组转换为列表,astype()强制转换数组的数据类型,下面是两个函数的例子:
1 | In [53]: b = a.tolist() |
数组元素的获取
通过索引和切片的方式获取数组元素,一维数组元素的获取与列表、元组的获取方式一样:
1 | In [60]: arr7 = np.arange(10) |
补充一下,如果要获取多个元素,且它们的索引没有固定步长这样的规则的话,可以传入一个list作为索引:
1 | 5) a = np.arange( |
二维数组元素的获取:
1 | In [67]: arr8 = np.arange(12).reshape(3,4) |
布尔索引,即索引值为True和False,需要注意的是布尔索引必须是数组对象。
1 | In [76]: log = np.array([True,False,False,True,True,False]) |
举一个场景,一维数组表示区域,二维数组表示观测值,如何选取目标区域的观测?
1 | In [81]: area = np.array(['A','B','A','C','A','B','D']) |
返回所有A区域的观测。
1 | In [86]: observes[(area == 'A') | (area == 'D')] #条件值需要在&(and),|(or)两端用圆括号括起来 |
返回所有A区域和D区域的观测。
当然,布尔索引也可以与普通索引或切片混合使用:
1 | In [87]: observes[area == 'A'][:,[0,2]] |
返回A区域的所有行,且只获取第1列与第3列数据。
花式索引:实际上就是将数组作为索引将原数组的元素提取出来
1 | In [88]: arr10 = np.arange(1,29).reshape(7,4) |
请注意!这与上面的返回结果是截然不同的,上面的命令返回的是二维数组,而这条命令返回的是一维数组。P.S.原文这里没有解释的很清楚,简单来说,如果在方括号内使用两个甚至多个数组/列表作为索引,中间以逗号分割,那么数组的顺序对应的就是从低维到高维,比如 In [92]
中第一个列表的第一个元素是4,第二个列表的第一个元素是0,这就表示要从arr10这个二维数组中取出的第一个元素在第一维第5个对象和第二维第1个对象交叠的地方,也即二维数组的第5行第1个元素17。最后会将所有取出的元素放入一个一维数组中返回。但如果某一维没有用数组/列表指定,而是使用了冒号表示取所有的话,返回的就是二维对象了。再举一个例子吧:
1 | 10).reshape(2,5) a = np.arange( |
如果想使用比较简单的方式返回指定行与列的二维数组的话,可以使用ix_()函数
1 | In [93]: arr10[np.ix_([4,1,5],[0,2,3])] # 允许我们用指定位置的方式,并且保持返回的数组形状不变 |
这与 arr10[[4,1,5]][:,[0,2,3]]
返回的结果是一致的。
统计函数与线性代数运算
统计函数
统计运算中常见的聚合函数有:最小值、最大值、中位数、均值、方差、标准差等。首先来看看数组元素级别的计算:
1 | In [94]: arr11 = 5-np.arange(1,13).reshape(4,3) |
由于负值的平方根没有意义,故返回nan。
1 | In [100]: np.exp(arr11) #计算每个元素的指数值,exp(a) 即 e^a |
相同形状数组间元素的操作:
1 | In [103]: arr11 + arr12 #加 |
统计运算函数
1 | In [109]: np.sum(arr11) #计算所有元素的和 |
numpy中的统计函数运算是非常灵活的,既可以计算所有元素的统计值,也可以计算指定行或列的统计指标。还有其他常用的函数,如符号函数sign
(将正数、负数、零分别映射为1、-1、0),ceil
(取>=x的最小整数),floor
(取<=x的最大整数),modf
(将浮点数的整数部分与小数部分分别存入两个独立的数组),cos
,arccos
,sin
,arcsin
,tan
,arctan
等。
让我很兴奋的一个函数是where()
,它类似于Excel中的if函数,可以进行灵活的变换:
1 | In [123]: arr11 |
当然,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 | In [125]: arr13 = np.array([[1,2,3,5],[2,4,1,6],[1,1,4,3],[2,5,4,1]]) |
随机数生成
统计学中经常会讲到数据的分布特征,如正态分布、指数分布、卡方分布、二项分布、泊松分布等,下面就讲讲有关分布的随机数生成。
正态分布直方图
1 | In [137]: import matplotlib #用于绘图的模块 |
以上将直方图的频数和组距存放在counts和bins内。
1 | In [142]: sigma = 1; mu = 0 |
补充一下,这个例子想说的是 random.normal
生成指定数目的随机数(可以进一步指定分布的均值、标准差和中心),然后把这些随机数用直方图画了出来。并且画了一条同一区间内的正态分布曲线来对比。注意种子的设置和生成随机数的大小是无关的,只是说使用不同的种子会生成不同的随机数,详细可以查找相关资料。
使用二项分布进行赌博
同时抛弃9枚硬币,如果正面朝上少于5枚,则输掉8元,否则就赢8元。如果手中有1000元作为赌资,请问赌博10000次后可能会是什么情况呢?
1 | In [146]: np.random.seed(1234) |
使用随机整数实现随机游走
一个醉汉在原始位置上行走10000步后将会在什么地方呢?如果他每走一步是随机的,即下一步可能是1也可能是-1。
1 | In [152]: np.random.seed(1234) #设定随机种子 |
上面的代码还可以写成(结合前面所讲的where函数,cumsum函数):
1 | In [158]: np.random.seed(1234) |
避免for循环,可以达到同样的效果。