numpy是Python一个非常强大的拓展库,专门用来处理数组和矩阵运算,并且提供数组和矩阵相关的大量内置函数库,其运行效率非常高,而专门进行数据分析的pandas库也是基于numpy的数据底层。可以说在处理大量数据的场景下,numpy有着绝对的应用场景,一般主要应用于以下场合:

  1. 处理数组和矩阵运算等基础运算
  2. 线性代数、傅里叶变换、大量随机数生成等基础应用场景
  3. 与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])