1.ndarray 的创建
numpy 的最常用对象就是 多维数组, numpy 可以将 python的其他元素转为 array 对象。
ndarray 天然所带的两个属性就是 dtype 和 shape,也就是多维数组的数据类型和数组的维度。下面来看多维数组的创建
import numpy as np
import random
a=np.array([1,2,3,4])
b=[5,6.6,7]
b=np.array(b)
c=[[1,2,3],[4,5,6]]
c=np.array(c)
print(a)
print(b)
print(c)
print(c.shape)
print(b.dtype)
可以看到的是,对于一个多维数组来说,其里面虽然含有多个数据,但是其数据类型是唯一的,会像下兼容。
除了直接创建多维数组,也可以采用常见的函数进行创建
a1=np.zeros(10)
b1=np.ones(8)
c1=np.empty(9)
d1=np.arange(10)
e1=np.ones_like(d1)
print(a1,b1,c1,d1,e1)
其中使用到的函数有,ones,zeros,empty 分别是创建全1,全0,垃圾值,对于empty函数,创建的值其实倒真是一个随机值,比random随机多了。如果要创立一个和已知数组相同 尺寸的新数组,可以采用,onesl_like和zeros_like
可以看到的是 zeros和ones创建的数组默认为浮点数,而arange创建的则是整数。并且需要指出的是 ones_like和zeros_like 两个函数再创建新数组的时候,也会在数据类型上保持一致。
前面说到一个多维数组,有两个属性,一个是数据类型一个是尺寸。还有别的一些属性。
a2=np.arange(20).reshape(4,5)
print(a2)
print(a2.ndim)
print(a2.shape)
print(a2.size)
print(a2.dtype)
print(a2.itemsize)
ndim表示改数据的维度,也就所谓的轴的数量,这就是一个二维数组;
size 表示所有的数据的长度,这和 len 不一样;
itemsize 表示一个元素所占的字节大小。
对于一个数组的数据类型,在创建的时候其实就已经可以进行指定。但是不能说数组中有浮点数,然后指定数组的类型为整型,反之可以操作。
a3=np.arange(20,dtype='float64')
print(a3.dtype)
b3=np.arange(10,dtype=np.float64)
print(b3.dtype)
对于指定类型而言,‘float64’和np.float64 的作用是一样的。
数组在创建的时候就已经有了,但是在后续过程中也可以进行重建,在之前使用的 reshape函数就是对数组进行尺寸上的修改。
在这里需要说明的是 reshape 中的参数就是重建后的尺寸,但是对于一些时候,不知道所有轴的尺寸就可以使用-1来进行自动调整。
a4=np.arange(24)
a4.reshape(3,2,-1)
对于一个三维的数组,可以发现其 第三个轴有 4个数,但这个数是自动算出来的。
对于多个数组也可以进行合并与拆分操作
a4=np.arange(12).reshape(3,4)
b4=np.arange(12,24).reshape(3,4)
np.vstack((a4,b4)),np.hstack((a4,b4))
当然这里是简单的一个水平合并和竖直合并,一个隐形的条件就是竖直合并时,需要保证两个数组第二个轴的大小一致,水平合并时需要保证两个数组的第一个轴的大小一致。
a4=np.arange(12).reshape(3,4)
b4=np.arange(12,24).reshape(3,4)
np.concatenate([a4,b4],axis=0),np.concatenate([a4,b4],axis=1)
对于两个数组可以进行合并,那么一个数组也可以拆成两个数组,使用split进行拆分:
a5=np.arange(36).reshape(2,2,9)
b5,c5,d5=np.split(a5,[3,6],axis=2)
b5,c5,d5
首先需要说明的是,split的参数有 要拆分的数组,在哪个位置进行拆分,[3,6]表示在3,4之间,6,7之间进行拆分, axis=2 表示对第三个轴进行拆分。
这样,关于轴的拆分就很清楚了,其实在后面关于轴的加法也是类似的道理。
数组类似于线性代数中的矩阵和张量,其也可以进行转置和轴对换,采用T,transpose和swapaxess进行操作
T是类比于矩阵中的转置符号
a6=np.arange(24).reshape(3,2,4)
a6.T
可以发现它的尺寸 变成了 423了,也就是T操作是把原来的尺寸整个倒过来。
a6.swapaxes(1,0)
swapaxes传进去的参数是需要调换的两个轴,对于三个轴进行混合调换,比如012换成120,swapaxes对此无法解决。
a6.transpose((1,0,2))
对于一般意义下的转置运算,transpose传进去的参数是要显示的轴的顺序,012换成120这种要求就可以达到。需要说明的是转置运算只是一个视图,并没有实际存在,除非新赋值一个数组。
在数据处理和数据分析中有关随机数函数的应用十分之广泛。
在创建数组的时候,除了之前说的创建0和1 数组,也可以创建一个指定尺寸的随机数组。
a7=np.random.randint(100,200,size=(3,4))
上面的代码表示,使用的是random 这个函数类中的 randint 函数,这个函数的参数含有,随机数的范围和尺寸,对于size这个参数,默认是1。random函数类中除了randint函数,还有一些常用的随机函数,比如以下:
a7=np.random.randint(100,200,size=(3,4))
a8=np.random.rand(3,4)
a9=np.random.randn(3,4)
a10=np.random.normal(4,5,size=(3,4))
其中 randint 和normal 可以指定范围,分别表示指定范围的随机整数和随机正态分布取样,而rand 和randn 表示标准的均匀分布和正态分布取样。需要补充的是
a10=np.random.normal(4,5,size=(3,4))
a11=np.random.uniform(4,5,size=(3,4))
a12=np.random.poisson(3,size=(3,4))
分别表示指定范围内的指定尺寸的正态分布,均匀分布和泊松分布,其中的normal 的参数为均值和标准差,uniform的参数为区间,poisson 的参数为 lamda。
除了这些随机函数和随机分布的创建,还需要说明的是两个函数,permutation 和shuffle,看一个例子
a13=np.random.permutation(a12)
创建一个和已知数组尺寸相同的随机数组,并不改变原数组,再看shuffle 的用法
a14=np.random.shuffle(a12)
a14,a12
但是结果却是
a14中并没有数据,原因是shuffle并没有生成新的数组,仅仅是把原来的数组打乱顺序
np.random.shuffle(a12)
a12
到这里呢,就把基础的有关np中的多维数组的创建与相关基本属性的操作讲完了,但更多的其实是数组的索引和切片,这其实和python中list 的索引和切片差不多。
2.数组的索引和切片
对于一维的array ,其索引就和py 中的的list 的索引是一样的。
a13=np.arange(10
a13[2],a13[-2],a13[3:5]
始终记住的就是 python 中的索引都是从0开始,
其次需要说明的是,数组的切片其实就是原来数组的一个视图,切片操作并不会产生新的数据但是,在视图上操作却会改变原来的数组。
a13=np.arange(10)
print(a13)
a14=a13[2:3]
a14[:]=77
print(a13)
但是下面这种形式,使用单个的索引似乎就达不到类似的效果
a13=np.arange(10)
print(a13)
a14=a13[3]
a14=77
print(a13)
对于高维的数组进行索引和切片时也是一样的道理。
a15=np.arange(12).reshape(3,4)
print(a15)
a15[2][3]
而对于单个的行索引为:
a15[2]
若要对列索引,可以采用
a15[:,2]
对于高维数组的切片,可以采用
a15=np.arange(12).reshape(3,4)
print(a15)
a15[1:3,2:4]
这就很直观了,需要说明的是,单个的冒号表示选遍整个轴。
除了在尺寸上进行索引和切片,也可以在逻辑上使用布尔类型的索引
a16=np.arange(12).reshape(4,3)
a17=a16>8
a17,a16[a17]
这串代码其实就是把0-11这12个数中大于8的数挑了出来
其中第2行返回的是一个布尔型数组,在进行布尔索引时要确定索引的布尔数组和原来要被索引的数组保持尺寸一致。由于布尔型索引的方法,对于算术运算符和逻辑运算符也可以返回一个布尔数组然后进行索引。
有了布尔索引后,我们就可以把布尔索引和尺寸索引结合起来用。最后还有一个花式索引
a16=np.arange(12).reshape(3,4)
print(a16)
a16[[0,2]][:,[2,1]]
本质上其实和 mysql 中的select 差不多,可以看成是两次索引。
3.数组的运算
数组的灵魂就在于可以进行批量的运算而不是要在循环里面进行元素的运算:
a17=np.arange(10)
a17*10
而对于数组的乘法可以类比于向量之间的克罗内科积,这对于基本的加减乘除运算总是有效的
当然更常见的是一些数学函数比如,abs,square,minumum
a18=np.random.randint(1,10,size=(3,5))
a19=np.random.normal(5,1,size=(3,5))
np.add(a18,a19)
a18=np.random.randint(1,10,size=(3,5))
a19=np.random.normal(5,1,size=(3,5))
np.minimum(a18,a19)
对于这些数学函数,有一元函数,就是传入一个数组,二元函数,传入多个数组。
除了常见的算术运算和函数运算,还有一类就是条件逻辑运算
a20=np.array([1,2,3,4])
a21=np.array([5,6,7,8])
cond=np.array([1,0,0,1])
# 如果要实现根据 con 的值来选择 a20 和 a21 之间的数,一般的想法还是通过 if else 来进行条件控制
result=np.where(cond,a20,a21)
result
其中需要着重介绍的就是 where 这个函数,where 传进去的有三个参数,第一个就是 condition,第二个和第三个参数和可以是数组,也可以是标量,来看这样一个题,对于随机生成的一个符合正态分布的数组,通过数据处理将正值替换为1,负值替换为-1
a22=np.random.randn(4,4)
a33=np.where(a22>0,1,-1)
a33
从这个例子就能看出来where 的具体用法了。在面临多个条件时,可以对where使用嵌套,多个嵌套的使用
a24=np.random.randint(1,300,size=(3,3))
a25=np.where(a24>200,1,np.where(a24>100,0.5,0))
a25
上述代码实现的就是200-300的数字赋值为1,100-200的数字赋值为0.5,100以下的数字赋值为0.
在函数运算那一块中讲过了 minimum的用法,返回的是两个数组中元素较小的那一个,那其实还有统计意义下的最小值计算。在统计运算中,常见的一些函数在 numpy 中也是有运算的,比如平均数,最大值,求和,标准差
a26=np.random.randn(4,4)
a26.sum(),np.sum(a26)
给出了函数的两种表示方法,其实说 sum 也是数组的一个属性。一个比较麻烦的是与数组关于某个轴合并一样,多个数组也可以关于某个轴进行求和。
a26=np.random.randn(3,4,2)
a26
a26.mean(axis=0),a26.mean(axis=1)
其实一个比较好记得办法就是 对哪个轴求和,哪个轴的尺寸就会消失。
cumsum 和cumpod 会计算出累计和与累计乘积,
也是可以指定轴的,和之前的指定轴求和没啥区别,对于统计函数可以做个小总结,就是sum,mean,std,var,min,max,argmin,argmax,cumsum,cumprod看函数的名称就知道是干嘛的。
对于布尔型的数组,也可以进行运算,需要说明的是在进行运算的时候会强制把布尔数据转为0,1 看下面的一个例子。
(a26>0).sum()
这就求出了a26 中有多少个数据是大于0的,其次还有两个函数,any 和all 用来检测布尔数组中是否有 true 和检测数组中是否都是true.
对于一个整数型的列表,可以进行排序,而对于多维数组也可以进行排序,使用sort 的办法。
a26.sort()
通常多维数组的数据类型都是整数型或者是浮点数,有的时候也是字符串的形式,可以使用unique函数对其进行去重。
a27=np.array([2,2,3,4,3,4,5,5,6,7,8,9])
np.unique(a27)
很令我苦恼的一点就是,计算一个数组的某些特征的时候,有时候是用 array.feature()来算,有的时候是用 np.feature(array)来算,并没有一个好的方法去分辨,只能记住每一个特征究竟是函数还是属性。
np.inld 可以检测数组中有没有包含目标值
a27=np.array([2,2,3,4,3,4,5,5,6,7,8,9])
# np.unique(a27)
np.in1d(a27,[3,4])
in1d(x,y)用来检测x的元素是否在y中出现,所以返回的是一个和x尺寸相同的布尔数组。
常见的集合运算函数
函数名 | 用法 |
---|---|
unique(x) | 返回x中的唯一值 |
intersect1d(x,y) | 两个数组的公共元素 |
union1的(x,y) | 两个数组的并集 |
in1d(x,y) | x中的元素是否在y中出现 |
setdiff1d(x,y) | 集合的差,x-y |
numpy 处理的很多情形的下都是矩阵,所以我们更关心线性代数的很多值是否能直接计算。 | |
需要使用的是linalg这个类 |
a28=np.random.randint(1,100,size=(3,3))
print(a28)
np.linalg.det(a28)
计算了矩阵的行列式
4.数据的存取
很简单的两个命令,一个是savetxt,一个是loadtxt
np.savetxt('linshi.csv',a28,fmt='%d',delimiter=',')
a29=np.loadtxt('linshi.csv',delimiter=',')
a29
总结
在这篇文章中,我们快速把 numpy 中的数据结构和常用的函数操作过完了一遍,其中包括以下的内容
- 创建:zeros,ones,empty,arange,包括 ones_like,zeros_like
- 属性:ndim,sahpe,size,dtype
- 数据类型,int,float,boo,string
- 数组变换:reshape,concatenate,split,transpose
- 随机函数类:rand,randint,randn,uniform,normal,poission,permutation,shuffle
- 条件逻辑运算:where
- 统计运算:mean,sum,std,var,argmin,argmax,cummax,cumprod
- 集合运算:sort,unique,intersect1d,union1d,in1d,setdiff1d,setor1d,
- 线性函数类:linalg
10.存取:savetxt,loadtxt