跳转至

NumPy

numpy为python提供了数组的支持

import numpy as np

类型

一个np数组只能存储相同的一种数据类型,int/float(字符串也是可以的,但是基本不用) 在int数组里修改元素为float会被截断

arr_float = arr.astype(float) 利用.astype()方法转换数组的类型 类似于c/cpp的类型提升,当整数型数组进行除法等涉及到浮点数的运算时,会转换成浮点型数组

打印时,数字之间是空格(列表是逗号)

维度

数组的维度信息,就是形状.shape,是一个元组

使用.reshape()方法,传入形状元组,可以在不同维度的数组之间转换

arr1 = np.array([1,2,3,4,5,6])
arr2 = arr1.reshape(2, -1) #两行三列

print(arr2.reshape(-1)) #转回一维数组,或者写(6)
可以留参数-1,让程序自行计算

创建

arr = np.array([1,2,3]) #创建的过程是把列表转换为np数组
arr = np.array( [[1,5,9], [8,8,8]] ) #二维数组,但是依旧传入一个列表
arr = np.arange(start, end, step) #创建递增数组,和range一样左闭右开

np.zeros() np.ones() 创建全0和全1的数组 第一个参数是形状的元组,第二个参数dtype=可以指定float/int

随机数组 np.random.random(shape) 0-1 np.random.randint(start, end, shape) 随机int np.random.normal(mean, standard_deviation, shape) 正态分布

注意numpy数组进行赋值只是绑定,类似于指针层面的浅拷贝 arr2 = arr1,修改arr2arr1也会同样被修改 需要拷贝,使用.copy()方法

访问

下标[]访问 二维数组一般使用[i,j]的形式访问,[i][j]也可以

切片

注意numpy切片得到的不是拷贝,是一个视图,这和py列表不一致 需要创建拷贝就使用arr[:, 0:2].copy()

向量的切片方法和列表一致 对于矩阵的切片,需要用两个参数分别对行列切片

arr = np.arange(20).reshape(4,-1)
# [[ 0  1  2  3  4]
#  [ 5  6  7  8  9]
#  [10 11 12 13 14]
#  [15 16 17 18 19]]
print(arr[1:3, 1:4]) #分别对行[1:3] 列[1:4]切片
# [[ 6  7  8]
#  [11 12 13]]

提取行列 切片的时候某个参数不写冒号,就可以提取矩阵的某一行列 比如arr[1, :]就是提取第1行,保留所有列(可以简写为arr[1]) 提取列的时候不会输出n*1的二维数组,而是一维向量

花哨索引

允许使用数组作为下标来访问元素,需要使用双层中括号(传入一或多个列表) 返回副本拷贝

arr = np.array([10, 20, 30, 40, 50])
print(arr[[3, 1, 4]])  #按 [3, 1, 4] 的顺序取值 输出: [40, 20, 50]

X = np.arange(12).reshape(3, 4)
# [[ 0,  1,  2,  3],
#  [ 4,  5,  6,  7],
#  [ 8,  9, 10, 11]]
rows = [0, 1, 2]
cols = [2, 0, 3]
print(X[rows, cols])  #取 (0,2), (1,0), (2,3) 三个点 输出: [2, 4, 11]

常用于数据清洗,特别是结合布尔数组的情况下

data = np.array([1, -2, 3, -4, 5])

mask = (data > 0) & (data < 4)  #data > 0得到的是一个相同形状的布尔数组
print(data[mask])               #[1, 3]

data[data < 0] = 0
print(data)                     #[1, 0, 3, 0, 5]

变形

.T转置矩阵方法 如果是向量(一维数组),先.reshape(1,-1)转换为1*n的矩阵(二维数组)

两个翻转函数 arr2 = np.flipud(arr) flip up-down上下翻转 np.fliplr left-right 左右 对于向量,只能使用上下翻转(数学上写成列向量)

前面提到的.reshape()方法,传入形状元组

拼接函数 np.concatenate( [arr, arr2], axis=0 ) 第二个参数axis是拼接的方向,默认为0在行的方向(行标增加的方向)上拼接

分割 arr1, arr2, arr3 = np.split(arr, [1,3]) 第二个参数传入切断位置的列表,代表切断成 0-1 1-3 3-end三部分,同样是左闭右开区间 如果是分割矩阵,第三个参数axis是分割的方向,默认为0,在行的方向进行分割 分割向量得到的是向量,分割矩阵得到的是矩阵,以元组的形式返回

运算

使用*进行乘法,是逐元素:

  • 数组和系数之间进行运算,运算会作用到每一个元素上
  • 数组和数组之间进行运算,也是逐元素运算

比如矩阵乘法是

[[0 1 2]
 [3 4 5]] 
*
 [[ 7  8  9]
 [10 11 12]]
=
[[ 0  8 18]
 [30 44 60]]

一般来说数组之间的运算需要形状相匹配,除了

  • 向量和矩阵进行运算,向量自动升级为行矩阵
  • 行/列矩阵和矩阵进行运算,行/列矩阵自动扩充到匹配的形状,这称之为广播(准确的说,是参与运算时,从最后一个维度开始对齐,缺失维度补 1,各维度取 max)

使用@乘法,才是矩阵乘法,相当于np.dot(arr1,arr2)函数 向量和向量做点积,返回标量;向量与矩阵相乘,返回向量,而不是列/行矩阵的形式;矩阵和矩阵相乘,就算数学上结果是标量,依旧会返回矩阵

常用函数

下面都是利用np.func()调用 abs cos sin tan exp log 注意log默认是e为底,自然对数

max min argmax argmin sum prod mean std 均值和标准差 对于矩阵,默认是求所有元素的最值/和/积;当给定参数axis=0/1的时候,会按行/列求出最值/和/积的向量 第二部分的函数如果遇到缺失值会报错,这是需要使用nanmin等安全版本

where

data = np.arange(18)
print(np.where(data > 10)) # 直接使用
# 打印 (array([11, 12, 13, 14, 15, 16, 17]),)
返回一个元组,打包符合条件的下标位置数组

布尔数组

在进行比较运算的时候,返回的就是逐元素比较结果构成的布尔数组

arr = np.arange(6).reshape(3,-1)
print(arr > 3)
#[[False False]
# [False False]
# [ True  True]]

在python中逻辑运算符是and/or/not,而numpy使用的是& | ~ 注意添加括号保证运算顺序

对布尔数组,np.sum()被重载为统计True的个数 np.any()np.all()和python内置类型的用法一样

在前面花哨索引的部分提到过,布尔数组可以作为掩码 注意这样的操作返回的是数组:

data = np.arange(18).reshape(3,-1)
print(data[data > 6])