numpy是Python一个非常强大的拓展库,专门用来处理数组和矩阵运算,并且提供数组和矩阵相关的大量内置函数库,其运行效率非常高,而专门进行数据分析的pandas库也是基于numpy的数据底层。可以说在处理大量数据的场景下,numpy有着绝对的应用场景,一般主要应用于以下场合:
- 处理数组和矩阵运算等基础运算
- 线性代数、傅里叶变换、大量随机数生成等基础应用场景
- 与scipy和matplotlib库组合使用,处理科学计算相关问题,可以基本替代matlab,并且因为其可以与Python其他库(网络和数据库等)完美融合,其潜力可能更大。
下面开始系统性介绍numpy库,掌握好该库是掌握pandas 、scipy甚至后续运用大数据分析和人工智能等相关框架的基础,如果掌握好了,会对后续学习有很大的帮助。
一、numpy基础
1.1 ndarray介绍
ndarray是numpy的核心概念,它是一系列同类型数据组成的数组,可以是一维或多维数组,可以认为是同类型数据的存储容器,不过相较于Python自身的列表、字典等对象数据类型,其要求存储在其中的数据类型需要相同,且运算效率极高。
而在后续要接触到的pandas中,Series类型数据可以近似认为就是ndarray,而pandas的DataFrame则本质是多个ndarray组成,其核心还是ndarray,只不过在此基础上进行了很多特性的封装,让更加适合处理类似excel表格类的数据集。
1.2 ndarray数组对象属性
属性 | 说明 | 示意 |
ndarray.ndim | 秩:即轴的数量或维度的数量,dim即dimention | ndarray.ndim→2 |
ndarray.shape | 形状:数组的维度,对于矩阵,n 行 m 列 | ndarray.shape→(n,m) |
ndarray.size | 数量:即数组或矩阵的元素总个数,为n*m | ndarray.size→n*m |
ndarray.dtype | 数据类型:即该数组内元素的数据类型 | ndarray.dtype→object |
ndarray.itemsize | 元素大小:即该数组内元素占据内存大小,字节单位 | ndarray.itemsize→8 |
ndarray.flags | 该数组的内存信息,一般用不到 | 无 |
ndarray.real | 数组元素的实部:即由数组内每个元素实部组成的相同形状的数组 | 无 |
ndarray.imag | 数组元素的虚部:即由数组内每个元素虚部组成的相同形状的数组 | 无 |
ndarray.data | 包含实际数组元素的缓冲区,由于一般通过数组的索引获取元素,所以通常不需要使用这个属性。 | 无 |
1.3 numpy数据类型
numpy可以认为是一种数据容器,在该容器内可以存放相同数据类型的数据,接下来会开始介绍numpy支持的常见数据类型,首先讲解numpy的dtype对象,因为numpy借由该对象来定义数据类型。也可以认为以下将要介绍的数据类型是dtype对象的实例。
1.3.1 dtype对象
#数据类型对象(numpy.dtype 类的实例)用来描述与数组对应的内存区域是如何使用,主要描述数据类型、大小等属性
#numpy已经内置(定义)了很多常用的数据类型对象实例,可以直接拿来使用,不过也可按照自己的需要,使用基础的数据类型来构造自己独特的数据类型对象实例,并赋值给array的dtype
#以下是dtype对象的构造函数
numpy.dtype(
object, #要转换为的数据类型对象,可以是基础内置数据类型、Python对象或其组合
align, #如果为 true,填充字段使其类似 C 的结构体。一般很少用到
copy ##复制 dtype 对象 ,如果为 false,则是对内置数据类型对象的引用,一般很少用到
)
1.3.2 内置数据类型
numpy已经根据实际需要,内置封装了很多数据类型,无需自己再次定义,直接用即可,以下为常用的数据类型及其用法
数据类型及取值范围 | 类型代码(字符) | 类型说明 | 示意 |
int8(-128 to 127) uint8(0 to 255) | i1、u1 | 有符号和无符号的8位(1字节)整型 | dtype='i1'、dtype='u1' dtype=np.int8、dtype=np.uint8 |
int16(-32768 to 32767) uint16(0 to 65535) | i2、u2 | 有符号和无符号的16位(2字节)整型 | dtype='i2'、dtype='u2' dtype=np.int16、dtype=np.uint16 |
int32 uint32 | i3、u3 | 有符号和无符号的32位(3字节)整型 | dtype='i3'、dtype='u3' dtype=np.int32、dtype=np.uint32 |
int64 uint64 | i4、u4 | 有符号和无符号的64位(4字节)整型 | dtype='i4'、dtype='u4' dtype=np.int64、dtype=np.uint64 |
float16 | f2 | 半精度浮点数,包括:1 个符号位,5 个指数位,10 个尾数位 | dtype='f2'或dtype=np.float16 |
float32 | f4或f | 标准精度浮点数,包括:1 个符号位,8 个指数位,23 个尾数位 | dtype='f4'或dtype='f'或dtype=np.float32 |
float64 | f8或d | 双精度浮点数,包括:1 个符号位,11 个指数位,52 个尾数位 | dtype='f8'或dtype='d'或dtype=np.float64 |
float128 | f16或g | 扩展精度浮点数 | dtype='f16'或dtype='g'或dtype=np.float128 |
complex64 complex128 complex256 | c8 c16 c32 | 32位浮点数分别表示实部和虚部,共64位的虚数 64位浮点数分别表示实部和虚部,共128位的虚数 128位浮点数分别表示实部和虚部,共256位的虚数 | dtype='c8'或dtype=np.complex64 dtype='c16'或dtype=np.complex128 dtype='c32'或dtype=np.complex256 |
bool | ? | 布尔型数据 | dtype=bool或dtype='?'或dtype=np.bool |
object | O | python对象类型 | dtype='O'或dtype=np.object |
string | S或a | 固定长度字符串数据类型(每个字符1个字节) 如要创建一个10个字符的字符串,则语法为S10 | dtype='S'或dtype=str或dtype='a'(自动调节字符串长度) dtype='S3'(字符串长度固定,多出的会截断)
|
unicode | U | 固定长度字符串数据类型(每个字符的字节数由所在平台或系统决定) 如要创建一个10个字符的字符串,则语法为S10 | dtype='U'或dtype=np.unicode_(自动调节字符串长度) dtype='U3'(字符串长度固定,多出的会截断) |
datetime64 | M | 日期时间格式,在使用datetime64字符指定时,也可指定日期时间格式的单位(Y、M、D、h、m、s) Y代表只截取到年,M代表截取到月,D代表截取到日,依次类推 | dtype='M'或dtype=np.datetime64(均根据数据自动找寻合适的单位) dtype='datetime64[M]'(强制截取到月) |
1.3.3 自定义结构化数据类型
#numpy的dtype对象,不仅可以使用内置的数据类型,还可按照实际需要,基于内置数据类型,创建结构化的数据类型,并在创建具体array时使用
#下面创建一个关于人的结构化数据类型,包括姓名、性别、是否入职、出生日期四个字段
import numpy as np
people=np.dtype([
('name','S20'),
('gender','S10'),
('entry','bool'),
('birthday','datetime64[D]',)
])
#使用people结构化数据类型创建array
peoples=np.array(
[
('dennis','female','1','2020-10-20'),
('jacky','male','0','2018-10-20'),
('tony','female','1','2000-10-20')
],
dtype=people
)
#访问其中某个字段
people['name'] →['dennis','jacky','tony']
1.3.4 复用其他ndarray数据对象
#也可以直接复用其他array的dtype对象,并创建新的array
import numpy as np
arr1=np.array(
[1,2,3],
dtype=np.float64
)
arr2=np.array(
[4,5,6],
dtype=arr1.dtype
)
二、数组创建
2.1 使用array构造器创建
2.1.1 概述
#一般使用array构造函数创建ndarray
import numpy as np
numpy.array(
object, #传入列表、元组来创建一维数组,或列表、元组的嵌套创建多维数组
dtype = None, #设置数组元素的数据类型,可以是内置的数据类型,也可是自定义的结构化数据类型
copy = True, #设置传入的对象是否需要复制,默认是True,如果设置为False,则会直接引用,一般用不到
order = None, #设置创建数组的样式,C为行方向,F为列方向,A为任意方向(默认),一般无需显示设置
subok = False, #默认返回一个与基类类型一致的数组,暂时还未用到过
ndmin = 0 #指定生成数组的最小维度,如果传入的object维度没达到最小维度,则会自定转变为最小维度的数组
)
2.1.2 具体创建
最基础的是使用np.array构造函数创建ndarray,下面展示具体创建方法
#1、创建一维数组
import numpy as np
np.array(
[1,2,3,4,5] #或(1,2,3,4,5)
)
#2、创建多维数组
np.array([
[1,2,3],
[4,5,6]
]) #会创建一个2*3的数组,即维数为2的数组,其他高维数组,依次类推
#3、创建一个显示指定数据类型的数组,比如复数
np.array(
[1,2,3,4,5],
dtype='c8'
)
2.2 使用存量数据创建
2.2.1 概述
#可以直接从存量的数据创建数组,需要使用asarray函数
#整体功能与arrray类似
numpy.asarray(
a, #任意形式的输入参数,可以是,列表, 列表的元组, 元组, 元组的元组, 元组的列表,多维数组
dtype = None, #数组的数据类型,枚举与array函数类似,不再赘述
order = None #一般用不到
)
2.2.2 从存量的数组或元组创建
import numpy as np
a=[1,2,3,4]
b=(2,2,2)
c=[(1,2,),(2,2)]
np.asarray(a) → array([1, 2, 3, 4])
np.asarray(b) → array([2, 2, 2])
np.asarray(c) → array([[1, 2],
[2, 2]])
2.2.3 从可迭代对象创建
#也可以通过传入python的迭代对象,来创建数组
numpy.fromiter(
iterable, #python的可迭代对象
dtype, #数据类型,不再赘述,必填
count=-1 #读取的数据数量,默认为-1,读取所有数据
)
list=range(1,100,2)
it=iter(list)
np.fromiter(it,float)
2.3 从数值范围创建
2.3.1 numpy.arange创建
#可类似使用Python的range函数,创建序列数组
numpy.arange(
start, #序列开始值
stop, #序列结束值,一般不包括该值
step, #步长,即序列之间数值差
dtype#数据类型,不再赘述
)
import numpy as np
np.arrange(1,10,2,dtype=float)
2.3.2 numpy.linspace创建一维等差数组
#可直接创建一维的由等差数列组成的数组,相较于arrange,该函数是指定数列范围、数列需要包含的数值个数,然后系统会自动生成包含对应数量的等差序列,两者应用场景不同
np.linspace(
start, #数列的开始值
stop, #数列的结束值,如果endpoint为true,该值包含于数列中,默认包含
num=50, #数列的数值个数,即要生成的等步长的样本数量,默认为50
endpoint=True, #该值为 true 时,数列中包含stop值,反之不包含,默认是True。
retstep=False, #如果为 True 时,生成的数组中会显示间距,反之不显示。
dtype=None #不再赘述
)
import numpy as np
np.linspace(10,100,20)
2.3.3 numpy.logspace创建一维等等比数组
#可直接创建一维的由等比数列组成的数组,可指定开始和结束数值,以及数列包含的数字数量
np.logspace(
start, #序列的起始值
stop, #序列的终止值为:base ** stop。如果endpoint为true,该值包含于数列中
num=50, #要生成的等步长的样本数量,默认为50
endpoint=True, #该值为 true 时,数列中中包含stop值,反之不包含,默认是True。
base=10.0, #对数 log 的底数。
dtype=None #不再赘述
)
import numpy as np
np.logspace(1,5,5,base=2)
2.4 内置特殊矩阵&数组的创建
2.4.1 创建指定大小的空数组
#numpy内置了较多在线性代数中可能比较常用的矩阵或数组,比如空数组
#空数组即数据或矩阵内的元素没有进行初始化赋值,其元素可能是任何值
numpy.empty(
shape, #元组或列表格式,指定需创建的数组或矩阵的大小,或者形状
dtype = float, #不再赘述
order = 'C' #一般用不到
)
import numpy as np
np.empty([2,3])
2.4.2 创建指定大小的零数组
#可创建元素全为0(近似为0)的数组或矩阵
numpy.zeros(
shape,
dtype = float,
order = 'C'
)
import numpy as np
np.zeros([2,3])
2.4.3 创建指定大小的一数组
#可创建元素全为1(近似为1)的数组或矩阵
numpy.ones(
shape,
dtype = float,
order = 'C'
)
import numpy as np
np.ones([2,3])
2.4.4 创建对角矩阵数组
#可创建对角线矩阵
np.eye(
N, #指定对角矩阵的行数
M=None, #指定对角矩阵的列数,默认与N相等
k=0, #对角线的索引,默认为0,即斜对角,如果赋予正值或负值,则会相对向右上或左下移动
dtype=float, #不再赘述
order='C'
)
import numpy as np
np.eye(5)
2.4.5 创建随机数组
#1、创建指定形状的,服从 X~N(0,1) 的正态分布随机数组
np.random.randn(2,3)#创建一个2*3的符合正态分布的随机数组
#2、创建指定形状的,随机分布整型数组或矩阵
randint(
low, #随机整数的下限,可以是整数或者数组形式的列表,如果是列表,则将直接决定最终生成的矩阵形状
high=None, #随机数的上限,可以是整数或者数组形式的列表,如果是列表,则将与low一起,决定最终生成的矩阵形状,会按照广播机制生成,此时,指定size基本无效
size=None, #一般用于在Low和high均为整数,此时该参数指定最终生成矩阵的形状
dtype=int #不再赘述
)
np.random.randint(2,10,(3,3)) #此种用法最常见
三、数组访问和查询
3.1 数组访问
3.1.1 切片和索引访问
#因矩阵或数组,其实本质是由列表组成,故也可以使用与访问Python列表元素相同的方式对数组进行切片和索引访问
import numpy as np
a=np.arange(1,21).reshape(4,5) #创建一个4*5的矩阵
#a为:
array([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]])
a[m,n] #m、n即可以是索引,又可以是切片,也可以是由索引组成的列表
#1、使用索引切片访问和提取数组的一部分
a[1:3,0:5:2] #会提取2到3行,1到6列每隔1列取一列,并组成一个2*3矩阵
array([[ 6, 8, 10],
[11, 13, 15]])
#2、提取指定的行、列
a[2] #提取第3行
array([11, 12, 13, 14, 15])
a[:,2]#提取第2列,并组成一维数组
array([ 3, 8, 13, 18])
#3、提取指定的元素
a[1,1] #提取第2行的第二列元素
#4、一维及更高维,同理,比如提取shape为3*4*5的矩阵,则即a[k,m,n],其中京k、m、n即可以是索引值,也可以是由索引值构成的切片
#5、切片中省略号的用法
#当要提取某一整行或整列时,其效果与:完全相同
a[:,1] #等同于a[...,1]
a[1,:] #等同于a[1,...]
#6、切片对象简介
s=slice(1,3)
a[s] #等同于a[1:3]
3.1.2 花式索引访问
#花式索引,其实就是指利用整数数组进行索引,对于使用一维整型数组作为索引,如果目标是一维数组,那么索引的结果就是对应位置的元素(类似索引访问);如果目标是二维数组,那么就是对应下标的行(类似直接取对应的行)
#花式索引主要有以下几种情况,最终结果一般都是另外一个数组
import numpy as np
#1、原数组为一维数组,传入1个一维数组作为索引
a=np.array([1,2,3,4,5,6])
a[[1,2,3]] #得到a的第2、3、4个元素并组成一个新的一维数组
#2、原数组为二维数组,传入1个一维数组作为索引
a=np.array([
[1,2,3],
[4,5,6],
[7,8,9]
])
a[[1,2]] #得到a的第2和第3行组成的2*3数组
#3、原数组为多维数组,传入多个一维数组作为索引
a=np.array([
[1,2,3],
[4,5,6],
[7,8,9]
])
a[[0,1],[0,1]] #得到a的第1行第1列、第2行第2列两个元素组成的一维数组
#4、原数组为多维数组,传入多个一维数组作为索引,且使用ix_函数
#pandas内的iloc索引方式,其本质即ix_的索引方式
a=np.array([
[1,2,3],
[4,5,6],
[7,8,9]
])
a[np.ix_([0,1],[0,1])] #得到由a的第1、2行,第1、2列交叉组成的2*2矩阵
array([[1, 2],
[4, 5]])
3.1.3 布尔索引访问
了解清楚布尔索引,会对后续学习pandas相关操作有较大帮助
#布尔索引,可以使用布尔表达式(条件表达式)来检索出某个数组符合指定条件的元素,并返回由这些元素组成的一维数组
#布尔索引在理解pandas相关条件检索操作时,很有用,可以说pandas的条件检索,本质就是布尔索引
#其本质的运算逻辑即对数组进行遍历,找出满足条件的元素对应的索引值,最后返回索引值对应的元素
import numpy as np
a=np.arange(2,10).reshape(2,4)
a[(a>2)&(a<8)] #会返回大于2且小于8的元组组成的一维数组
#其他比如非运算(~),或运算(|)也均可以使用并组成更加复杂的条件表达式
3.2 遍历和迭代
3.2.1 遍历行、列
import numpy as np
a=np.array([
[1,2,3],
[4,5,6],
[7,8,9]
])
#1、通过for语句,遍历以上数组的所有行
for row in a:
print(row) #会逐行输出以上数组
#2、通过for语句,遍历以上数组的所有列
for i in a.T:
print(i) #先将a进行转置,然后再逐行遍历转置后的数组
3.2.2 遍历元素
#如果需要遍历某数组的元素,则需要使用nditer函数,该函数提供了访问numpy元素的多种方式
#下面只罗列最为常见的操作
np.nditer(
a, #需遍历的数组
order = 'C', #遍历的方向或顺序,C代表从上到下逐行遍历,F代表从左到右逐列遍历
op_flags= ‘readwrite’, #可设置在遍历的同时,可以直接修改原数组内对应元素的值,默认只读
flags = ['external_loop'] #
)
#以下均使用下面的数据
import numpy as np
a=np.array([
[1,2,3],
[4,5,6],
[7,8,9]
])
#1、按行遍历元素
for i in np.nditer(a,order='C'):
print(i,end=',') #输出结果如下:
1,2,3,4,5,6,7,8,9,
#2、按列遍历元素
for i in np.nditer(a,order='F'):
print(i,end=',') #输出结果如下:
1,4,7,2,5,8,3,6,9,
#3、遍历的同时,修改元素,对大于6的元素,统一加1
for i in np.nditer(a,order='C',op_flags=['readwrite']):
if i>6:
i+=1 #最后a数组变为如下:
array([[ 1, 2, 3],
[ 4, 5, 6],
[ 8, 9, 10]])
#4、广播迭代,如果两个数组是可广播的,nditer 组合对象能够同时迭代它们
b=np.arange(10,26).reshape(4,4)
for i,j in np.nditer([a,b]):
print(i,j)
3.3 排序
#主要对指定数组进行排序,一般是从小到大的排序方法,此时需要用到sort函数,该函数会返回排序后的数组
numpy.sort(
a, #待排序的数组
axis, #指定排序的轴方向,axis=0 按列排序,axis=1 按行排序
kind, #指定排序的方法,默认'quicksort',一般无需设置
order #如果数组包含字段,则是要排序的字段,一般用不到
)
import numpy as np
a=np.array([
[1,3,2],
[4,8,6],
[7,10,9]
])
np.sort(a) #输出结果如下:
array([[ 1, 2, 3],
[ 4, 6, 8],
[ 7, 9, 10]])
3.4 条件抽取&筛选
以下方法,均是返回对应元素在原数组的索引,如果需要返回对应的元素,则可以使用索引引用
3.4.1 最大值最小值元素
#numpy.argmax() 和 numpy.argmin()
import numpy as np
a=np.array([
[ 1, 3, 2],
[ 4, 8, 6],
[ 7, 10, 9]
])
#1、指定数轴的最大值,会按照指定轴方向,返回沿着该轴最大值对应的索引
np.argmax(
a, #需找寻最大值的数组
axis = 0 #0指按行方向,1指按列方向
)
np.argmax(a,axis=0) #输入为如下
array([2, 2, 2])
#1、指定数轴的最小值,会按照指定轴方向,返回沿着该轴最小值对应的索引
np.argmin(
a, #需找寻最小值的数组
axis = 0 #0指按行方向,1指按列方向
)
np.argmin(a,axis=0) #输入为如下
array([0, 0, 0])
3.4.2 非零元素
#numpy.nonzero() 函数返回输入数组中非零元素的索引。
import numpy as np
a=np.array([
[ 1, 3, 2],
[ 4, 8, 6],
[ 7, 10, 9]
])
np.nonzero(a) #会返回非零元素构成的索引,如果传入的数组维数大于1,则会返回对应维数个数的由索引组成的数组,返回值为:
(array([0, 0, 0, 1, 1, 1, 2, 2, 2]), array([0, 1, 2, 0, 1, 2, 0, 1, 2]))
b=np.nonzero(a)
a[b] #会返回由非零元素组成的一维数组
array([ 1, 3, 2, 4, 8, 6, 7, 10, 9])
3.4.3 按布尔条件筛选
#numpy.where(),用来找寻满足指定条件元素对应的索引值
import numpy as np
a=np.array([
[ 1, 3, 2],
[ 4, 8, 6],
[ 7, 10, 9]
])
np.where(a>4) #输出结果如下:
(array([1, 1, 2, 2, 2]), array([1, 2, 0, 1, 2]))
b=np.where(a>4)
a[b] #输出为
array([ 8, 6, 7, 10, 9])