第三方库的导入:
import numpy as np
import pandas as pd
from pandas import DataFrame,Series
from numpy.random import randn
一、ndarray对象的内部机理
numpy
的adarray
可以将同质数据块解释为多维数组的方式,数据类型(dtype)
决定了数据的解释方式
ndarray
是所有数组对象都是数据块的一个跨度视图
ndarray
的内容组成:
- 一个指向数组(一个系统内存块)的指针
- 数据类型
dtype
- 一个表示数组形状的元组
np.ones((10,5)).shape
- 一个跨度元组,其中的整数指的是为了前进到当前维度下一个元素需要跨过的字节数
np.ones((3,4,5),dtype=np.float64).strides
跨度信息是构建非复制式数组视图的重要因素,跨度可以是负数,使数组在内存中后向移动
二、Numpy数据类型体系
判断dtype
是否属于某个大类
ints = np.ones(10,dtype=np.uint16)
floats = np.ones(10,dtype=np.float32)
np.issubdtype(ints.dtype,np.integer)
Out:True
np.issubdtype(floats.dtype,np.floating)
Out:True
调用dtype
的mro
方法查看所有大类
np.float64.mro()
Out:
[numpy.float64,
numpy.floating,
numpy.inexact,
numpy.number,
numpy.generic,
float,
object]
三、高级数组操作
1、数组重塑
Shape函数
arr = np.arange(8)
arr.reshape((4,2))
多维数组的重塑
arr.reshape((4,2)).reshape((2,4))
“-1”
可以作为参数的形状,它表示该维度的大小由数据本身推断而来:
arr = np.arange(15)
arr.reshape((5,-1))
Out:
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11],
[12, 13, 14]])
也可以传入shape
属性的元组
other_arr = np.ones((3,5))
arr.reshape(other_arr.shape)
ravel函数
与reshape
相反的多维数组的运算过程,称为扁平化或者散开
arr = np.arange(15).reshape((5,3))
flatten
方法返回的是数据的副本,ravel
函数不会产生源数据的副本
arr.flatten()
四、C和Fortran顺序
Numpy
可以灵活控制在内存中的布局。
Numpy
数组是按行优先顺序创建的,也叫C
顺序,意味着每行中的数据项是被存放在相邻内存位置上的;另一种顺序是列优先顺序,也叫Fortran
顺序,矩阵全都是列优先的
reshape
和ravel
函数可以接受一个表示数组数据存放顺序的order
参数。
arr = np.arange(12).reshape((3,4))
arr.ravel('F')
Out:
array([ 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11])
arr.ravel('C')
Out:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
对于,二维或者更高维度的重塑过程,C
和Fortran
顺序的关键区别就是维度的行进顺序
C
/行优先顺序:先经过更高的维度(轴1会优先于轴0被处理)Fortran
/列优先顺序:后经过更高的维度(轴0会优先轴1被处理)
五、数组的合并和拆分
np.concatenate
可以按指定走将一个由数组组成的序列(如元组、列表)连接到一起
arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([[7,8,9],[10,11,12]])
np.concatenate([arr1,arr2],axis=0)
np.concatenate([arr1,arr2],axis=1)
Numpy
提供了一些较为方便的方法(vstack
、hstack
)
np.vstack((arr1,arr2))
np.hstack((arr1,arr2))
split
用于将数组沿指定轴拆分为多个数组
from numpy.random import randn
arr = randn(5,2)
arr
Out:
array([[ 0.29126855, -0.66748463],
[-0.68677925, 0.360508 ],
[ 0.49896033, -1.62448238],
[-1.32739471, 1.26042767],
[ 0.80410505, 0.29827872]])
first,second,third = np.split(arr,[1,3])
first
Out:
array([[-1.32739471, 1.26042767],
[ 0.80410505, 0.29827872]])
second
Out:
array([[-0.68677925, 0.360508 ],
[ 0.49896033, -1.62448238]])
third
Out:
array([[-1.32739471, 1.26042767],
[ 0.80410505, 0.29827872]])
1、堆叠辅助类r_和c_
它们可以使得数组的堆叠操作更为简洁
arr = np.arange(6)
arr1 = arr.reshape((3,2))
arr2 = randn(3,2)
np.r_[arr1,arr2]
Out:
array([[ 0. , 1. ],
[ 2. , 3. ],
[ 4. , 5. ],
[-1.50478906, -0.98422073],
[-1.26121877, 2.42921896],
[-0.25016918, 0.60142896]])
np.c_[np.r_[arr1,arr2],arr]
Out:
array([[ 0. , 1. , 0. ],
[ 2. , 3. , 1. ],
[ 4. , 5. , 2. ],
[-1.50478906, -0.98422073, 3. ],
[-1.26121877, 2.42921896, 4. ],
[-0.25016918, 0.60142896, 5. ]])
也可以将切片翻译成数组
np.c_[1:6,-10:-5]
Out:
array([[ 1, -10],
[ 2, -9],
[ 3, -8],
[ 4, -7],
[ 5, -6]])
2、元素的重复操作:tile和repeat
对数组进行重复以产生一个数组,repeat
会将数组中的各个元素重复一定次数,从而产生一个更大的数组
arr = np.arange(3)
arr.repeat(3)
arr.repeat([2,3,4])
tile
的功能是沿指定轴向堆叠数组的副本,相当于“铺瓷砖”
arr = randn(2,2)
np.tile(arr,(2,1))
3、花式索引的等价函数:take和put
获取数组自己的一个办法是通过整数数组使用花式索引
arr = np.arange(10)*100
inds = [7,1,2,6]
arr[inds]
ndarray
有两个方法专门用于获取和设置单个轴向上的选区
arr.take(inds)
arr.put(inds,42)
arr.put(inds,[41,42,43,44])
inds = [2,0,2,1]
arr = randn(2,4)
arr.take(inds,axis=1)
put
函数不接受axis参数,他只会在数组的扁平化进行索引
六、广播
定义:它指的是不同形状的数组之间的算术运算的执行方式。其中将标量值跟数组合并时就会发生最简单的广播。
这里我们说:在这个乘法运算中,标量值4
被广播到了其他元素上
arr = np.arange(5)
arr
Out:array([0, 1, 2, 3, 4])
arr*4
Out:array([ 0, 4, 8, 12, 16])
距平化处理,对数组的每一列进行减去列平均值的方式
arr = randn(4,3)
arr.mean(0)
demeaned = arr - arr.mean(0)
demeaned.mean(0)
Out:
array([ 0.00000000e+00, -1.11022302e-16, 1.04083409e-17])
arr
Out:
array([[-1.46031835, 1.3240841 , 0.1621479 ],
[-0.61236475, -0.50598835, -0.33708048],
[-1.2265001 , 0.85693646, 0.64020573],
[-1.13596485, -2.56214722, 0.12491285]])
row_means = arr.mean(1)
row_means.reshape((4,1))
demeaned = arr - row_means.reshape((4,1))
demeaned.mean(1)
Out:
array([-5.55111512e-17, 5.55111512e-17, 0.00000000e+00, 7.40148683e-17])
通过广播设置数组的值
arr = np.zeros((4,3))
arr[:]=5
用一个一维数组来设置目标数组的格列,其中np.newaxis
参数可以不用构造一个表示新形状的元组,就可以插入新轴:
col = np.array([1.28,-0.42,0.44,1.6])
arr[:] = col[:,np.newaxis]
arr
Out:
array([[ 1.28, 1.28, 1.28],
[-0.42, -0.42, -0.42],
[ 0.44, 0.44, 0.44],
[ 1.6 , 1.6 , 1.6 ]])
七、ufunc高级应用
Numpy
的各个二元ufunc
都有一些用于执行特定矢量化运算的特殊方法。
1、reduce 函数
reduce
可以接受一个数组参数,并通过一系列的二元运算对其值进行聚合,例如:我们可以用np.add.reduce
对各个元素进行求和
arr = np.arange(10)
np.add.reduce(arr)
arr.sum()
用np.logical_and
检查数组各行中的值是否是有序的:
arr = randn(5,5)
arr[::2].sort(1) #对部分进行排序
arr[:,:-1]<arr[:,1:]
np.logical_and.reduce(arr[:,:-1]<arr[:,1:],axis=1)
2、accumulate函数
产生一个跟原数组大小相同的中间“累计”值数组:
arr = np.arange(15).reshape((3,5))
arr
Out:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
np.add.accumulate(arr,axis=1)
Out:
array([[ 0, 1, 3, 6, 10],
[ 5, 11, 18, 26, 35],
[10, 21, 33, 46, 60]])
3、Outer函数
用于计算叉积
arr = np.arange(3).repeat([1,2,2])
np.multiply.outer(arr,np.arange(5))
4、reduceat函数
用于计算“局部简约”,就是一个对数据各切片进行聚合groupby
运算。
arr=np.arange(10)
np.add.reduceat(arr,[0,5,8])
5、自定义ufunc函数
numpy.frompyfunc
接受一个Python
函数以及两个分别表示输入参数数量的整数。
例如:下面是一个能够实现元素级加法的简单函数
- 方法一:
def add_elements(x,y):
return x+y
add_them = np.frompyfunc(add_elements,2,1)
add_them(np.arange(8),np.arange(8))
Out:
array([0, 2, 4, 6, 8, 10, 12, 14], dtype=object)
frompyfunc
创建的函数总是返回Python
对象数组,这一点很不方便。
- 方法二:
numpy.vectorize
,它在类型推断方面更加智能
add_them = np.vectorize(add_elements,otypes=[np.float64])
add_them(np.arange(8),np.arange(8))
Out:
array([ 0., 2., 4., 6., 8., 10., 12., 14.])
八、结构化和记录式数组
结构化数组是一种特殊的ndarray
,其中的各个元素可以被看作C语言中的结构体或SOL表中带有多个命名字段的行:
dtype = [('x',np.float64),('y',np.int32)]
sarr = np.array([(1.5,6),(np.pi,-2)],dtype=dtype)
最典型的定义结构化dtype
的方式就是元组列表,各元组的格式为(field_name, field_type)
sarr[0]
Out:(1.5, 6)
字段保存在dtype.names
属性中,在访问结构化数组的某个字段时,返回的是该数据的视图,所以不会发生数据的复制
sarr['x']
Out:array([1.5 , 3.14159265])
1、嵌套dtype和多维字段
可以再设置一个形状,来定义结构化dtype
dtype = [('x',np.int64,3),('y',np.int32)]
arr = np.zeros(4,dtype=dtype)
其中x
字段记录的是一个长度为3的数组
arr[0]['x']
这就能使我们单个数组的内存块存放复杂的嵌套结构
示范:嵌套dtype
dtype = [('x',[('a','f8'),('b','f4')]),('y',np.int32)]
data = np.array([((1,2),5),((3,4),6)],dtype=dtype)
data['x']
Out:array([(1., 2.), (3., 4.)], dtype=[('a', '<f8'), ('b', '<f4')])
2、为什么要用结构化数组
他可以将单个内存块解释为带有任意复杂嵌套列的表格型数据。由于数组中的每个元素在内存中都被表示为固定的字节数,所以结构化数组能够提供非常高效的磁盘数据读写
九、排序
ndarray
的sort
实例方法就是就地排序,数组内容的重新排列不会产生新数组。相反,numpy.sort
会为原数组创建一个已排序副本
arr = randn(5)
arr
Out:
array([1.04476665, 0.60057523, 1.34554912, 0.71409436, 3.10547606])
np.sort(arr)
Out:
array([0.60057523, 0.71409436, 1.04476665, 1.34554912, 3.10547606])
这个排序方法不可以被设置为降序,可以使用列表的反序列表
arr[::-1]
Out:array([3.10547606, 0.71409436, 1.34554912, 0.60057523, 1.04476665])
1、间接排序:argsort和lexsort
在数据分析工作中,常常需要一个或多个键对数据集进行排序。给定一个或多个键,你就可以得到一个由整数组成的索引数组(索引器)
values = np.array([5,0,1,3,2])
indexer = values.argsort()
indexer #返回排序索引
Out:array([1, 2, 4, 3, 0])
values[indexer]
Out:array([0, 1, 2, 3, 5])
- 根据数组的第一行对其排序
arr = randn(3,5)
arr[0] = values
arr
Out:
array([[ 5. , 0. , 1. , 3. , 2. ],
[-0.55049158, 1.37840367, 0.57288805, 1.76152185, -0.21042948],
[-0.57566978, 0.59815094, 0.79902713, -0.60928023, -1.18951258]])
arr[:,arr[0].argsort()]
Out:
array([[ 0. , 1. , 2. , 3. , 5. ],
[ 1.37840367, 0.57288805, -0.21042948, 1.76152185, -0.55049158],
[ 0.59815094, 0.79902713, -1.18951258, -0.60928023, -0.57566978]])
lexsort
跟argsort
差不多,只不过他可以一次性对多个键数组执行间接排序(字典序)
- 范例:对一些以姓和名标示的数组进行排序
first_name = np.array(['Bob','Jane','Steve','Bill','Barbara'])
last_name = np.array(['Jones','Arnold','Arnold','Jones','Walters'])
sorter = np.lexsort((first_name,last_name))
zip(last_name[sorter],first_name[sorter])
2、其他排序算法
稳定的排序算法会保持等价元素的相对位置。
values = np.array(['2:first','2:second','1:first','1:second','1:third'])
key = np.array([2,2,1,1,1])
indexer = key.argsort(kind='mergesort')
mergesort
(合并排序)是这些排序里唯一的稳定排序
values.take(indexer)
Out:
array(['1:first', '1:second', '1:third', '2:first', '2:second'],
dtype='<U8')
3、numpy.searchsorted:在有序数组中查找元素
searchsorted
是一个在有序数组上执行二分查找的数组方法,只要将值插入到他返回的那个位置就可以维持数组的有序性
arr = np.array([0,1,7,12,15])
arr.searchsorted(9)
Out:3
searchsorted
的另一个用法,假设有一个数据数组的值从0-1000之间,还有一个表示“面元边界”的数组,我能希望用它将数据数组拆分开
data = np.floor(np.random.uniform(0,10000,size=50))
bins = np.array([0,100,1000,5000,10000])
data
Out:
array([2557., 8042., 8472., 2732., 3676., 2779., 5785., 5268., 4572.,
6304., 7420., 5250., 4264., 1196., 5319., 9839., 8194., 9250.,
2153., 9223., 3687., 9561., 1498., 1830., 2767., 7918., 4045.,
3495., 7532., 1905., 4262., 881., 519., 3322., 3718., 5582.,
7482., 2577., 4142., 7973., 9000., 3225., 7391., 639., 2283.,
8425., 8584., 7463., 2980., 2393.])
为了得到个数据点所属区间的编号
labels = bins.searchsorted(data)
labels
Out:
array([3, 4, 4, 3, 3, 3, 4, 4, 3, 4, 4, 4, 3, 3, 4, 4, 4, 4, 3, 4, 3, 4,
3, 3, 3, 4, 3, 3, 4, 3, 3, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4, 3, 4, 2,
3, 4, 4, 4, 3, 3])
再通过pandas
的groupby
将结果拆分
Series(data).groupby(labels).mean()
Out:
2 679.666667
3 3002.416667
4 7620.739130
dtype: float64
Numpy
的digitize
也可以计算这种面元编号
np.digitize(data,bins)
十、高级数组的输入输出
内存映像文件
Numpy
实现了一个类似于ndarray
的memmap
对象,它允许将大文件分成小段进行读写,而不是一次性将整个数组读入内存。
使用np.memmap
函数传入一个文件路径、数据类型、文件模式以及形状,即可创建一个新的memmap
。
mmap = np.memmap('mymap',dtype='float64',mode='w+',shape=(10000,10000))
mmap
Out:
memmap([[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., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]])
其中memmap
的切片也是数据视图
section = mmap[:5]
section[:] = np.random.rand(5,10000)
mmap.flush()
mmap
Out:
memmap([[0.33831052, 0.02845899, 0.94490351, ..., 0.46454688, 0.15109669,
0.95387255],
[0.77214426, 0.02520478, 0.76379407, ..., 0.81475754, 0.65447479,
0.82608759],
[0.92651679, 0.82303761, 0.9384897 , ..., 0.35232664, 0.48599669,
0.28068128],
...,
[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ]])
del mmap
只要内存映像超出作用域,就会被垃圾回收期回收,之前对其所有的操作都会被写入磁盘,但是打开一个以及存在的内存映像,仍然需要指明数据类型和形状。
mmap = np.memmap('mymap',dtype='float64',shape=(10000,10000))
mmap
Out:
memmap([[0.16264806, 0.48319848, 0.82222971, ..., 0.49783845, 0.26486926,
0.42594865],
[0.00296158, 0.42102723, 0.35295437, ..., 0.29119252, 0.41847388,
0.01854104],
[0.40182186, 0.36677069, 0.81543187, ..., 0.66691882, 0.39284047,
0.11911527],
...,
[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ]])