读书笔记:利用Python进行数据分析【第四章:NumPy基础-array数组】
不要误会,并不是作者不识数,1后面直接接了个4。
主要是这本书的第二章其实是一些范例,而且都是从很高的层次来提起兴趣的,可能后面全学完了回来仔细研读更好一些。而第三章主要讲Ipython,不过这个版本的Enthought Canopy已经有自己的IDE了。
总之,接下来就直接第四章了。
Numpy是高性能科学计算和数据分析的基础包,其部分功能如下:
- ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。
- 用于对整组数据进行快速运算的标准数学函数。
- 用于读写磁盘数据的工具以及用于操作内存映射文件的工具。
- 线性代数、随机数生成以及傅里叶变换。
- 用于集成由C、C++、Fortran等语言编写的代码的工具。
依照标准的NumPy约定,加载Numpy时总是使用import numpy as np。
虽然使用from numpy import *也是可行的,但是书中不建议这么做。
NumPy的ndarray:一种多维数组对象
NumPy最重要的一个特点就是其N维数组对象(即ndarray),该对象是一个快速而灵活的大数据集容器。利用这种数组,可以对整块数据执行一些数学运算,而其语法跟标量元素之间的运算是一样的。需要注意的是,下文中提到的NumPy数组、ndarray、数组等,基本上指的都是ndarray对象。
创建ndarray:
创建数组最简单的办法就是使用array函数,该函数接受一切序列型的对象(包括其他数组),然后产生一个新的含有传入数据的NumPy数组,以一个列表转换为例:
#单列表转换:
import numpy as np
data1 = [6,7.5,8,0,1] #list
arr1 = np.array(data1) #把list转换为ndarray对象
这样就把一个列表转换为数组:
[ 60. 75. 80. 0. 10.]
而嵌套序列将会被转换为一个多维数组:
#嵌套序列:
data2 = [[1,2,3,4],[5,6,7,8]]
arr2 = np.array(data2)
本例中的嵌套序列被转换为一个二维数组:
[[1 2 3 4]
[5 6 7 8]]
除非显式说明,np.array会尝试为新建的这个数据推断出一个较为合适的数据类型,数据类型会保存在一个特殊的dtype对象中,比如说,在上面的两个例子中,打印两个数组的dtype对象
print arr1.dtype
print arr2.dtype
结果如下:
float64
int32
可以看到两个数组分别被归类为float64和int32。
除了np.array之外,还有一些函数也可以新建数组,比如,zeros和ones分别可以创建指定长度或形状的全0或全1数组,而empty可以创建一个没有任何具体值的数组,使用这些方法创建多维数组的时候,需要传入一个表示形状的元组:
print np.zeros(10) ##长度为10的1维全0数组
print np.zeros((3,6)) ##3维的长度为6的全0数组
print np.empty((2,3,2)) ##以此类推,维度可以无限扩大
获得结果为:
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0.]]
[[[ 8.11256107e-316 2.19014637e-316]
[ 8.11250850e-316 8.11253617e-316]
[ 8.11257213e-316 8.11257490e-316]]
[[ 8.11257767e-316 8.13140394e-316]
[ 8.13140671e-316 8.13140947e-316]
[ 8.13141224e-316 7.03232764e-316]]]
必须要注意的是,认为np.empty会返回全0数组的想法是不安全的,很多情况下它返回的是一些没有初始化的垃圾值,上述代码已经呈现出这种情况。
而np.arrange则是Python内置函数range的数组版:
print np.arange(15) ##取包括0-14的数组
结果如下:
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
由于NumPy基本上关注的都是数值计算,因此,如果没有特别指定,数据类型基本都是float64。
书中未详述但是提及了的其他数组创建函数,还有eyes、identity和ones_like、zeros_like、empty_like。
后三个都是以现有数组作为参数,返回与该数组形状相同的全1、全0或空数组:
print np.ones_like(arr1)
print np.zeros_like(arr2)
print np.empty_like(arr2[1])
结果如下:
[ 1. 1. 1. 1. 1.]
[[0 0 0 0]
[0 0 0 0]]
[16843009 16843009 16843009 16843009]
而eyes和identity则是创建一个正方形的N*N单位矩阵:
print np.eye(5)
print np.identity(3)
结果如下:
[[ 1. 0. 0. 0. 0.]
[ 0. 1. 0. 0. 0.]
[ 0. 0. 1. 0. 0.]
[ 0. 0. 0. 1. 0.]
[ 0. 0. 0. 0. 1.]]
[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
关于eye和identity的区别可以看这里:http://blog.youkuaiyun.com/lanchunhui/article/details/52928517
ndarray的数据类型
dtype(数据类型)是数组的一个特殊对象,它含有ndarray将一块内存解释为特定数据类型时所需要的信息。前面提到array会给数组默认分配数据类型,不过也可以自行设定:
arr1 = np.array([1,2,3],dtype=np.float64)
arr2 = np.array([1,2,3],dtype=np.int32)
print arr1,arr1.dtype
print arr2,arr2.dtype
结果则为:
[ 1. 2. 3.] float64
[1 2 3] int32
dtype支持的全部数据类型如下:
不管是我还是书中都不建议强行记住这些玩意,通常来说,知道你要处理的数据大致属于什么类别就可以了。但是,当你需要控制数据在内存和磁盘中的存储方式,就必须要了解如何控制存储类型。
可以通过ndarray的astype方法显式地转换数组的dtype:
arr = np.array([1,2,3,4,5])
float_arr = arr.astype(np.float64) ##把arr的数据类别转换为float64
print arr.dtype,float_arr.dtype
print float_arr
结果如下:
int32 float64
[ 1. 2. 3. 4. 5.]
如果用astype把浮点数转换为整数,那么,小数点后的部分会被截断。
arr=np.array([3.7,-1.2,-2.6,0.5,12.9,10.1])
print arr.astype(np.int64)
结果如下:
[ 3 -1 -2 0 12 10]
对于全部由数字组成的字符串数组,也可以用astype转换为数值形式:
numeric_string = np.array(["1.25","-9.6","42"],dtype=np.string_) #下划线表示长度不固定
print numeric_string.astype(float)
结果如下:
[ 1.25 -9.6 42. ]
astype的后面不仅可以接np.float64等数据类型,也可以接其他数组的dtype。
int_array = np.arange(10) #一个0-9的数组,自动判别类型为int
calibers = np.array([.22,.270,.357,.380,.44,.50],dtype=np.float64)
print int_array.astype(calibers.dtype) ##把该数组的类型转化为与int_array数组相同的类型。
结果如下:
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
需要注意的是,调用astype的话,无论如何都会创造出一个新的数组,哪怕新的dtype和旧的相同也是一样的。另外,浮点数只能表示近似的分数值,在复杂的计算中,因为可能会积累一些浮点错误,因此比较操作只能在一定的小数位以内有效。
数组和标量之间的运算
数组最大的作用之一,就是在不需要编写循环的情况下,对数据执行批量运算,这通常就叫做矢量化(vectorization)。大小相等的数组之间的任何运算,则会把运算应用到元素级。
arr = np.array([[1.,2.,3.],
[4.,5.,6.]])
print arr
print arr*arr ##数组之间的运算,反映到各个对应元素之间
print arr-arr
结果如下:
[[ 1. 2. 3.]
[ 4. 5. 6.]]
[[ 1. 4. 9.]
[ 16. 25.