利用Python进行数据分析(二)
NumPy基础:数组和矢量计算(基础 重中之重)
对于大部分数据分析应用而言,最关注的功能主要集中在:
·用于数据整理和清理、子集构造和过滤、转换等快速的矢量化数组运算。
·常用的数组算法,如排序、唯一化、集合运算等。
·高效的描述统计和数据聚合/摘要运算。
·用于异构数据集的合并/连接运算的数据对齐和关系型数据运算。
·将条件逻辑表述为数组表达式(而不是带有if-elif-else分支的循环)。
·数据的分组运算(聚合、转换、函数应用等)。第5章将对此进行详细讲解。
ps.看不懂没关系,后面有更详细的描述。最后再来看这 总结。
(作者建议使用import numpy as np 取代from numpy import * 严谨 规范)
NumPy的ndarry
ndarry字面上的意思即N维数组对象,元素必须是相同类型,这个数组可以对整块的数据执行一些数学运算 比如 data*10,data+data。元素的类型通过data.dtype可以查看。data.shape可以查看元组的大小如(2,3)代表2行三列。
创建一个ndarry
使用array函数接受一切序列型的对象(列表 嵌套序列),生成一个新的NumPy数组。比如:
import numpy as np
data1 = [5,7.5,8,0,1]
arr1 = np.array(data1)
arr1
#out:array([ 5. , 7.5, 8. , 0. , 1. ])
其他构建方法及描述如下图:
需要注意的有两点:
np.zeros((3,4))创建多维时输入的是元组,有两个括号。
另外就是empty的数据并不是理想中的0,而是未初始化的值。
ndarray的数据类型
创建ndarray时也可以指明数据类型即dtype。可能会精度丢失,如下:
arr1 = np.array([1,2.5,3],dtype=np.int32)
arr1
#out:array([1,2,3])
这是一种很特殊的做法,直接指明ndarray的数据类型,这使得读写二进制数据流,集成低级语言代码(估计是直接读这个值即可知道数据类型,无需判断什么的)变得更简单。dtype的命名方式为类型+表示位长的数字。比如float64为双精度浮点占用8字节(64位)。支持的类型很多,常见的有int8/16/32/64 float16/32/64/128 bool complex64/128/256(复数) 甚至object 对象等!
既然支持这么多类型 那如果想转换类型怎么操作?当然是支持的啦,显示转换使用astype方法即可。如:
arr=np.array([1,2,3,4,5])
arr.dtype
#out:dtype('int64')
float_arr = arr.astype(np.float64)
float_arr.dtype
dtype('float64')
你也可以使用类型代码如下图所示,你甚至可以使用另一个ndarray的dtype来转换(float_arr = arr.astype(arr2.dtype)),当然转换失败会引发一个TypeError。
注意:调用astype无论如何都会创建出一个新的数组(原始数据的一份拷贝),即使新dtype跟老dtype相同也是如此。
警告:注意,浮点数(比如float64和float32)只能表示近似的分数值。在复杂计算中,由于可能会积累一些浮点错误,因此比较操作只能在一定小数位以内有效。
数组与标量之间的运算
数组很重要,因为它使你不用编写循环即可对数据执行批量运算。这通常就叫做矢量化(vectorization)。(就是不用那种for循环什么的一个一个的取数据了)大小相等的数组之间的任何算术运算都会将运算应用到元素级:
arr = np.array([[1.,2.,3.],[4.,5.,6.]])
arr
out:array([[1.,2.,3.],[4.,5.,6.]])
arr*arr
out:array([[1.,4.,9.],[16.,25.,36.]])
什么1/arr arr**0.5(平方根) 就不列举了。
基本的索引和切片
arr = np.arange(10)
arr
out:array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr[5:8]
out:array([5, 6, 7])
arr[5:8]=12
arr
out:array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
如上所示,当你将一个标量值赋值给一个切片时(如arr[5:8]=12),该值会自动传播(也就说后面将会讲到的“广播”)到整个选区。跟列表最重要的区别在于,数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到源数组上
作者说你可能会感到很诧异,确实很诧异。。。(原因是NumPy是用来处理大数据,所以它尽可能的避免去复制耗费性能和内存)
如果你真的要复制的话使用arr[5:8].copy()
高纬度的如何索引?
切片索引
arr2d
out:array([[1,2,3],[4,5,6],[7,8,9]])
arr2d[:2]
#左闭右开 [:2]为0,1,不包含2
out:array([[1,2,3],[4,5,6]])
arr2d([:2,1:])
#与前面的多个索引一样,对第一维取[:2],第二维度取[1:]
out:array([2,3],[5,6])
arr2d[1,:2]
#取一维的第1个 再去取该元素的第[0,2)元素
out:array([4,5])
arr2d[:,:1]
out:array([[1],[4],[7]])
#对其赋值也会扩散到整个选取
布尔型索引
names = np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])
names == 'Bob'
out:array([True,False,False,True,False,False],dtype=bool)
data[names == 'Bob']
out:array([[-0.048,0.5433,-0.2349,1.2792],[2.1452,0.8799,-0.0523,0.0672]])
#把ture的选出来
当然这种索引还可以与切片整数等等混合来使用。
data[names == 'Bob',2:]
Out:array([[-0.2349,1.2792],[-0.0523,0.0672]])
我的理解是第一维用names == 'Bob'来索引,第二维度用2:切片来索引。这样可能更好理解和记忆一点。
当然 你甚至还可以这样用:data[-(names == 'Bob')]
使用&,|布尔运算符来这样用:mask = (names == 'Bob') | (names == 'Will')
为了使data中所有负值设为0,可以用如此简单的方式:
data[data < 0 ] = 0
花式索引
arr = np.empty((8,4))
for i in range(8):
arr[i] = i
arr
out:array([[0.,0.,0.,0.],
[1.,1.,1.,1.],
[2.,2.,2.,2.],
[3.,3.,3.,3.],
....#偷懒
[7.,7.,7.,7.]])
当你要以特定顺序选取时:
arr[[4,3,-1]]
out:array([[4.,4.,4.,4.],
[3.,3.,3.,3.],
[7.,7.,7.,7.]])
#负数从末尾开始算
花式索引比较特殊的地方:
可以用下面方法 得到这个结果:
另外:花式索引与切片不同,它总是复制数据到新的数组中。
总结:
学习了NumPy的ndarry从创建到各种取数据,你能察觉到它为了方便处理大数据,无论是赋值,修改,还是取数据都做了很多的操作,极其的方便。