Numpy使用小结
一、创建数组
1.从python列表中创建数组
np.array([1,2,3,4],dtype='int32') #明确指定数组中元素的类型
#### 2.创建数组的各种方式
#创建一个长度为10的数组,其值都为0
np.zeros(10,dtype=int)
#创建一个3*5的浮点型数组,其值均为1
np.ones((3,5),dtype=float)
#若需要指定创建是数字的值,那么可以使用full方法,创建一个3*5的浮点型数组,其值均为3.14
np.full((3,5),dtype=3.14)
#创建一个线性数组,从0开始,20结束,步长为2
np.arange(0,20,2)
#创建一个5个元素的数组,这5个数均匀的分配到(0,1)
np.linspace(0,1,5)
#创建一个3*3的随机数组
np.random.random((3,3))
#创建一个3*3的,均值为0,标准差为1的正态分布随机数组,normal()方法
np.random.normal(0,1,(3,3))
#创建一个3*3的,[0,10]区间的随机整型数组,randint()方法
np.random.randint(0,10,(3,3))
#创建一个3*3的单位矩阵
np.eye(3)
#创建一个空的数组,其值未被初始化,可以是内存中的任意值
np.empty(3)
二、numpy数组的基本操作方式
先给出大纲,主要是以下的操作方式,
数组的属性:确定数组的大小,形状,存储大小,数据类型
数组的索引:获取和设置数组各元素的值
数组的切分:在大的数组中获取或者设置更小的子数组
数组的变形:改变数组的形状
数组的拼接和分裂:将多个数组拼接为1个数组,或者将一个数组分裂为多个数组
1.数组的属性
数组的属性主要可以获取以下类型:
ndim:数组的维度
shape:数组中每个维度的大小
size:数组的总大小(元素的总个数)
dtype:查看数组的数据类型
itemsize:每个数组元素字节的大小
nbytes:数组总字节大小的属性,一般为itemsize和size的乘积
2.数组的索引
索引数组的方式主要有两种,一种是获取单个元素,一种是获取多个元素
2.1获取单个元素
在一维数组中,可以按照下标的方式进行索引,同时,为了获取数组的末尾索引,也可以采用负值索引。
如x1=np.array([5,0,3,3,7,9])
获取时,可以采用这样的方法:x1[0]、x1[-1]等
在多维数组中,就是按照下标进行索引,如x[0,0]
2.2获取多个元素(在数组的切片过程中讲到)
3.数据切片:获取子数组
一般情况下,采用切片(slice)来获取子数组,切片符号采用冒号(:)来表示,切片的语法是:
X[start:stop:step]
如果以上3个参数都未指定,那么它们会被分别设置默认值start=0,stop=维度的大小和step=1
3.1.一维子数组
x=np.arange(0,10)
#获取前5个数据
x[:5]
#获取5之后的元素
x[5:]
#每隔一个元素获取
x[::2]
#同时可能存在步长为负数的情况,这种请款自己多注意一下
#逆序打印出所有的元素
x[::-1]
3.2.多维子数组
多维切片也可以采用同样的方式进行处理,用逗号分隔
x2=np.random.randint(0,10,size=(3,4)) #一般情况下0页可以省略
#获取两行三列
x2[:2,:3]
#获取所有的行,每隔一列
x2[:3,::2]
#当然,子数组维度也可以同样被逆序
x2[::-1,::-1]
3.3.获取数组的行和列
一种常见的需求是获取数组的单行和单列
#获取x2的第一列
x2[:,0]
#获取x2的第一行
x2[0,:]
#在获取行时,出于语法的简洁考虑,可以省略空白的切片
x2[0] #等同于x2[0,:]
3.4.非副本视图的子数组
当存在一个非常大的dataset的时候,我们想要去修改某些固定的值,那么就可以采用以下的方法。因为数组切片返回的是数组的视图(即切片所返回的数组和原数组的指向的是 同一个内容),下面举例说明:
#抽取x2中的2行2类数据
x2_sub=x2[:2,:2]
#如果修改这个子数组,比如要修改x2_sub[0,0]=99
x2_sub[0,0]=99
#那么再次打印x2的数组,那么会发现x2[0,0]=99, 这就是非副本视图的子数组
3.5.创建数组的副本
尽管数组视图有一些非常好的特性,但是在有些时候明确的复制数组里面的数据也是非常有用的。这种操作是可以通过copy()方法实现
x2_sub=x2[:2,:2].copy()
#再次修改x2_sub[0,0]的值不会对x2有任何影响
4.数组的变形
数组的变形主要是存在两种形式:
1.如果希望将数字1-9放入一个3*3的矩阵中,那么可以采用如下的方式:
grid=np.arange(1,10).reshape((3,3))
2.将一个一维数组变成二维的行或者列的矩阵。
x=np.array([1,2,3])
#通过变形,将想变成行向量
x.reshape((1,3))
#通过newaxis获得的行向量,这个和reshape()方法功能相同
x[np.newaxis,:]
#output:[[1 2 3]]
#同样的将x变为列向量同样存在上述的两种方法
5.数组的拼接分裂
5.1数组拼接的三个函数
数组的拼接主要是借助于三个函数来实现,np.concatenate,np.vstack(垂直栈)和np.hstack(水平栈)
x=np.array([1,2,3])
y=np.array([3,2,1])
#将x,y拼接在一起
np.concatenate([x,y])
# output:[1 2 3 3 2 1]
#同样的concatenate()方法也支持二维数组的拼接(在这种方法情况下注意要指定axis,若未指定,则是默认第0轴)
grid=np.array([[1,2,3],[4,5,6]])
#若指定第0轴进行拼接
np.concatenate([grid,grid],axis=0)
#output:[[1 2 3]
# [4 5 6]
# [1 2 3]
# [4 5 6]]
#同样的情况下,也可以指定对第1轴进行拼接
np.concatenate([grid,grid],axis=1)
而对于np.vstack()和np.hstack()这两个方法来说,我们可以看做是concatenate()指定特殊维度的方法
vstack():垂直轴方向进行拼接,那么就是等于concatenate()指定axis=0
np.vstack([grid,grid])
hstack():水平轴方向进行拼接,那么就是等于concatenate()指定axis=1
np.hstack([grid,grid])
np.dstack将会沿着第三个维度拼接数组
5.2数组分裂的三个函数
同样的数组的分裂与数组的拼接类似,主要是涉及np.split、np.hsplit、np.vspkit方法来实现
np.dsplit也会按照第三个维度分裂数组
三、Numpy数组的计算:通用函数
1通用函数介绍
通用函数的主要目的是对Numpy数组中的值执行更快的重复操作。一般情况下,向量的操作都是比较快的,而numpy中的向量操作是通过通用函数来实现的。
2numpy中常见的通用函数
2.1.数组的运算
numpy通用函数的使用方式非常自然,因为它用到了python中的原生字符串,标准的加减乘除都可以使用,也可以使用函数的形式
运算符 | 对应的通用函数 | 描述 |
---|---|---|
+ | np.add | 加法 |
- | np.subtract | 减法 |
* | np.multiply | 乘法 |
/ | np.divide | 除法 |
- | np.negative | 负数运算 |
// | np.floor_divide | 向下整除运算即3//2=1 |
** | np.power | 指数运算 |
% | np.mod | 模/余数 |
3.2.2.绝对值
对应于numpy的通用函数的np.absolute(),python中的内置函数为abs()
注意:numpy中的这个函数也是可以处理负数的,当处理负数时,绝对值返回的是该负数的模
eg. x=np.array([3-4j,4-3j,2+0j,0+1j])
np.abs(x)
返回的值是:[5.,5.,2.,1.]
2.3.三角函数
支持常见的各种三角函数、反三角函数、以及双曲三角函数等等
2.4.指数和对数
指数运算
x=[1,2,3]
print("x=",x)
print("e^x=",np.exp())
print("2^x=",np.exp2(x))
print("3^x=",np.power(3,x)) #没有exp3(x)这个方法
对数运算
x=[1,2,4,10]
print("x=",x)
print("ln(x)=",np.log(x))
print("log2(x)=",np.log2(x))
print("log10(x)=",np.log10(x))
2.5.专用的通用函数
另外常见的通用函数还有:比特位运算、比较运算符、弧度转换为角度的运算,取整和求余运算
另外对于常用的统计函数自己可以到scipy.specal 的模块里面去看。
3高级的通用函数特性
3.1.指定输出
在进行大量运算时,有时候指定一个用于存放运算结果的数组是非常有用的。
所有的通用函数都可以通过out参数来指定计算结果的存放位置
x=np.arange(5)
y=np.empty(5)
np.multiply(x,10,out=y)
print(y)
3.2.聚合(下个模块更加详细的介绍)
如果我们希望用一个特定的运算reduce一个数组,那么可以用任何通用函数的reduce方法。
如:对add函数调用reduce()方法会返回数组中所有元素的和。
x=np.arange(1,6)
np.add.reduce(x)
#output:15
同样的,对multiply()函数调用reduce()方法也就会返回所有元素的乘积。
但是,如果你想要存储每次计算的中间结果,那么可以使用accumulate()函数
x=np.arange(1,6)
np.add.accumulate(x)
#output:[ 0. 1. 3. 6. 10.]
3.3.外积
任何通用函数都可以用outer()
方法获取两个不同输入数组所有元素对的函数运算结果。直接看示例:
x=np.arange(1,4)
np.multiply.outer(x,x)
#output:[[1 2 3]
[2 4 6]
[3 6 9]]
4.聚合:最大值、最小值
4.1数组值求和
就是sum()
方法的使用
import numpy as np
L=np.random.random(100)
np.sum(L)
注意:np.sum()和python自带的sum函数并不等同
4.2最大值和最小值
和python内置的函数一样,numpy也内置了相同的max()
、min()
函数
存在两种形式的调用,
一种是作为参数传递:np.max(L)
、np.min()
另外一种的调用方式是:直接通过对象进行调用,但是需要确保是执行numpy版本的聚合。L.max()
4.3多维度聚合
当采用以上的聚合函数的时候,如果需要操作的是二维数组或者是更高维度的数组,那么可以采用指定维度的方式来进行聚合。
M=np.random.random((3,4))
#返回所有数据的和
M.sum()
#如果需要返回的是每一列的和
M.sum(axis=0)
#如果需要返回每一行数据的和
M.sum(axis=1)
其他的聚合方式,如求最大值,最小值操作和上面的一样
四、数组的计算:广播
1.广播的介绍
广播用以描述numpy中对两个形状不同的阵列进行数学计算的处理机制。较小的阵列“广播”到较大阵列相同的形状尺度上,使它们对等以可以进行数学计算。广播提供了一种向量化阵列的操作方式,因此Python不需要像C一样循环。广播操作不需要数据复制,通常执行效率非常高。然而,有时广播是个坏主意,可能会导致内存浪费以致计算减慢。
2广播的规则
Numpy的广播遵循一组严格的规则,设定这组规则是为了决定两个数组间的操作,下面阐述规则:
规则1:如果两个数组的维度数不相同,那么小维度数组的形状将会在` 最左边补1(强调是左边)
规则2:如果两个数组的形状在任何一个维度上都不匹配,那么数组的形状会沿着维度为1的维度扩展以匹配另外一个数组的形状
规则3:如果两个数组的形状在任何一个维度上都不匹配并且没有任何一个维度等于1,那么会引发异常。
接下来举例说明各种各样的形式:
import numpy as np
a=np.array([0,1,2])
b=bp.array([5,5,5])
c=a+b
d=5
f=a+d
print(c)
#根据规则1 ,广播的规则,那么在结果上面c和f是等同的。a+d这样的形式是可以存在的
M=np.ones((2,3))
a=np.arange(3)
#那么我们可以分析这两个数组的加法规则
M.shape=(2,3)
a.shape=(3,)
#根据规则1,那么a数组会在左边补1,即之后 a.shape=(1,3) ,a之后的类型如下:
# a:[[0,1,2]]
#再根据规则2,可以再进行扩充,那么a就变为:[[0,1,2],[0,1,2]],那么就可以进行相加了
这个知识点并不难,就是要按照规则去推算,最后得到结果即可。
3.广播的实际应用
3.1 数组的归一化
数组的归一化,说明白点就是实现数组的均值为0.
# 将矩阵做归一化处理
import numpy as np
x=np.random.random((10,3))
# 采用的是省略的写法,表示对第一个维度,就是列做归一处理
Xmean=x.mean(0)
Xcentered=x-Xmean
# 在满足机器精度的情况下,这些值均为0,第一维度均值为0
print(Xcentered.mean(0))
3.2二维函数的可视化
这里先提前介绍直方图的作用:
直方图是为了表名数据的分布情况。通俗的就是说哪一块数据所占比例或者出现的次数较高,哪一块出现的次数较少。画直方图主要是应用hist()
函数
hist(Y,n):将向量Y中的元素分到n个间隔的范围内(如果不指定n,那么n默认为10),并且返回每个范围内元素的个数作为一行向量
# 画一个二维函数示例
import matplotlib.pyplot as plt
import numpy as np
# 体现扩展(广播的作用)
x=np.linspace(0,5,50)
y=np.linspace(0,5,50)[:,np.newaxis]
z=np.sin(x)**10+np.cos(10+y*x)*np.cos(x)
plt.imshow(z,origin='lower',extent=[0,5,0,5],cmap='viridis')
plt.colorbar()
plt.show()
五、比较、掩码和布尔逻辑
使用地点:当你想基于某些准则来抽取、修改、计数或对一个数组中的值进行其他操作时,掩码就可以派上用场了。另外你可能想要统计数组中有多少值大于某一个给定值,或者删除所有超出threshold的异常值,那么在Numpy中,布尔掩码通常是最好的方式。
1.和通用函数类似的比较操作
除了加减乘除这些运算以外,Numpy还实现了逐元素比较(如<,>等),这些比较运算的结果是一个布尔数据类型的数组。一共存在六种标准的比较操作符
标准的比较操作符:<(小于)、>(大于) <=(小于等于) >=(大于等于) !=(不等于) ==(等于)
接下来给出一个操作示例:
x=np.array([1,2,3,4,5])
print(x<3)
#output:[ True True False False False]
当然,这些比较运算通用函数也可以用于任何形状、大小的数组。示例和上面的一致。
2.操作布尔数组
2.1 统计记录的个数
首先定义:
rng=np.random.RandomState(0)
x=rng.randint(10,size=(3,4))
print(x)
#output:[[5 0 3 3]
# [7 9 3 5]
# [2 4 7 6]]
假设需要统计布尔数组中True记录的个数,可以使用np.count_nonzero()
np.count_nonzero(x<6)
#output:8
另外一种通用的实现的方式是借助于sum()
方法,在这种实现方式中,False会被解析成0,True会被解析成为1
np.sum(x<6)
#output:8
使用sum()
方法一个好处时,可以另外指定行或者列进行聚合。
np.sum(x<6,axis=0) #按照列进行聚合
#output:[2 2 2 2]
如果需要快速检查任意或者所有这些值是否为True,那么可以采用np.all()
、np.any()
#判断有没有值大于8?
np.any(x>8)
#output:True
#判断是否所有值均小于10
np.all(x<10)
#output:True
#是否所有值均等于6
np.all(x==6)
#output:False
同时可以提醒一下:上面的all()
any()
方法也可以指定维度参数axis
2.2 布尔运算符
加入我们需要统计x中的大于2,小于6的元素的个数,那么可以采用Numpy中的逐位逻辑运算符。
逐位运算符(bitwise logic operator):&、|、^、~
如果需要实现上述的功能,那么可以采用以下的方式:
np.sum((x>2)&(x<6))
#output:6
#统计小于2或者大于6的元素的个数
np.sum((x<2)|(x>6))
#output:4
3. 将布尔数组作为掩码
除了上面所采用的聚合方式,我们也可以采用布尔数组作为掩码,通过该掩码选择数据的子数据集。
#选出数组中小于5的数据
x[x<5]
#output:[0 3 3 3 2 4]
#下面来分析这个过程:首先是x<5 这个操作
z=x<5
#output :[[False True True True]
# [False False True False]
# [ True True False False]]
x[z]
#output:[0 3 3 3 2 4]
#所以,实际是对掩码进行操作,即对False 或者True进行操作
小考点:
使用关键字 and/or 与使用逻辑操作运算符 &/|的区别:
and和or对整个对象执行布尔运算,而&和|对一个对象的内容(单个比特或者字符)执行多个布尔运算
六、索引
1.花哨的索引
花哨的索引(fancy indexing):和一般的简单索引类似,但花哨的索引里面传递的是索引数组,而不是单个标量。利用花哨的索引,结果的形状与索引数组的形状一致,而不是与被索引数组的形状一致。
import numpy as py
rand=np.random.RandomState(42)
x=rand.randint(100,size=10)
#假设我们希望获取三个不同的元素,那么可以采取以下的方式:
[x[3],x[7],x[2]]
#output:[71, 86, 14]
#另一种方法是通过传递索引的单个列表或数组来获得同样的结果
index=[3,7,2]
x[index]
#output:[71, 86, 14]
ind=np.array([[3,7],[4,5]])
x[ind]
#output:[[71 86]
# [60 20]]
#这个结果印证了上面的高亮的一句话的含义,花哨的索引返回的值反应的是广播后的索引数组的形状、
X=np.arange(12).reshape((3,4))
row=np.array([0,1,2])
col=np.array([2,1,3])
print(X[row[:,np.newaxis],col])
#output:[[ 2 1 3]
# [ 6 5 7]
# [10 9 11]]
#这里主要是通过广播的形式去解答,首先row[:,np.newaxis]变成二维数组[[0] [1] [2]],最后要在X中取值,那么row[:,np.newaxis],col都要根据广播的规则进行扩展,最后根据对应的index去取值就得到这样的结果。
2. 组合索引
可以将各种索引的方式组合在一起去索引元素,这就是组合索引
#将花哨的索引和简单的索引组合使用
X[2,[2,0,1]]
#也可以将花哨的索引和切片组合使用
X[1:,[2,0,1]]
3. 示例 选择索引点
import numpy as np
import matplotlib.pyplot as plt
import seaborn
seaborn.set()
rand=np.random.RandomState(42)
mean=[0,0]
cov=[[1,2],[2,5]]
# 生成一个多元正态分布矩阵
X=rand.multivariate_normal(mean,cov,100)
# print(X.shape)
# 将这些点用散点图的形式可视化
# plt.scatter(X[:,0],X[:,1])
# plt.show()
# 我们可以利用花哨的索引随机选取20个点-选取 20个随机的,不重复的索引值,并且利用这些索引值选取到原始数组中对于的值:
indices=np.random.choice(X.shape[0],20,replace=False)
# print(indices)
selection=X[indices]
# 我们可以看到哪些点被选中了,在图上使用大圆圈标记出来
# alpha的参数是显示所标记点的颜色的深浅
plt.scatter(X[:,0],X[:,1],alpha=0.3)
# facecolor主要是填充的颜色,edgecolors 外圈标记点的颜色,s是所画的圆圈的大小
plt.scatter(selection[:,0],selection[:,1],facecolor='none',edgecolors='r',s=100)
plt.show()
4. 用花哨的索引修改值
x=np.arange(10)
i=np.array([2,1,8,4])
x[i]=99
#完成了值的修改
print(x)
#output :[ 0 99 99 3 99 5 6 7 99 9]
5. 数据区间划分
假定我们有1000个值,希望快速统计分布在每个区间中的数据频次。
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
x=np.random.randn(100)
# print(x)
# 手动计算直方图
bins=np.linspace(-5,5,20)
# 关于zeros_like():Return an array of zeros with the same shape and type as a given array.
counts=np.zeros_like(bins)
# print(counts)
# 为每个x找到合适的区间
# Find indices where elements should be inserted to maintain order.
i=np.searchsorted(bins,x)
# print(i)
# 为每个区间加上1
# at()函数在这里对给定的操作、给定的索引(这里是i)以及给定的值(这里是1)执行的是就地操作
np.add.at(counts,i,1)
#画出结果
# plt.plot(bins,counts,linestyle='steps')
# plt.show()
# 另外一种解决的方式是直接调用方法,step参数是画外面的线,里面的颜色不填充
plt.hist(x,bins,histtype='step')
plt.show()
七、数组的排序
1. 实现选择排序
import numpy as np
def selection_sort(x):
for i in range(len(x)):
swap=i+np.argmin(x[i:])
(x[i],swap)=(x[swap],x[i])
return x
x=np.array([2,1,4,3,5])
selection_sort(x)
2.Numpy中的快速排序:np.sort和np.argsort
如果想在不修改原始输入数组的基础上返回一个排好序的数组,可以使用np.sort:
x=np.array([2,1,4,3,5])
np.sort(x)
#output:[1,2,3,4,5]
# 如果希望用排好序的数组代替原始数组,可以使用数组的sort方法:
x.sort()
print(x)
#output:[1,2,3,4,5]
另一个相关的函数是argsort,该函数返回的是原始数组排好序的索引值:
x=np.array([2,1,4,3,5])
#返回的是原始数组排好序的索引值
i=np.argsort(x)
print(i)
#output:[1,0,3,2,4]
#上面的结果的第一个元素是数组中最小元素的索引值,第二个值是给出的是次小元素的索引值,依次类推
当需要沿着行或者列排序的时候,需要指定axis参数。
3.部分排序:间隔
有的时候我们不希望对整个数组进行排序,仅仅需要找到数组中第K小的值,Numpy中的np.partition()方法提供了该功能。np.partition()的输入是数组和数字K,输出结果是一个新数组,根据源代码里面给出的内容,K值的含义应该是分割点,这个函数就是类似于快速排序的特点,K位置左边的值均小于K位置右边的值
x=np.array([7,2,3,1,6,5,4])
np.partition(x,3)
#output:[2 1 3 4 6 5 7]
#排完序的数组存在这样的特点:结果数组中前三个值是数组中最小的三个值,剩下的位置是原始数组中剩下的值
3.1 K个最邻近
下面的示例展示如何利用argsort()函数沿着多个轴快速找到集合中每个点的最邻近。
import numpy as np
import matplotlib.pyplot as plt
import seaborn
seaborn.set()
rand=np.random.RandomState(42)
X=rand.rand(10,2)
# 画出这些点的分布
# plt.scatter(X[:,0],X[:,1],s=100)
# plt.show()
# 求解两个点之间的距离等于每个维度的距离差的平方的和
# 在坐标系中计算每对点的差值
differences=X[:,np.newaxis,:]-X[np.newaxis,:,:]
# print(differences.shape)
# 求出差值的平方
sq_differences=differences**2
# 将差值求和获得平方距离
dist_sq=sq_differences.sum(-1)
# 查看对角线矩阵上面的值,应该都是0,自己到自己的距离为0
# print(dist_sq.diagonal())
# 我们已经得到了一个转化为两点间的平方的距离的矩阵,那么可以利用argsort()沿着每行进行排序,最左边给出的列就是最邻近
nearest=np.argsort(dist_sq,axis=1)
# print(nearest)
# 求每个点的两个最邻近点
K=2
nearest_partition=np.argpartition(dist_sq,K+1,axis=1)
plt.scatter(X[:,0],X[:,1],s=100)
# 将每个点与他的两个最邻近连接起来
for i in range(X.shape[0]):
for j in nearest_partition[i,:K+1]:
#画一条从X[i]到X[j]的线段
plt.plot(*zip(X[j],X[i]),color='red')
plt.show()