numpy是python中的一个基础工具包,主要用于科学计算和数据分析。
在使用numpy这个工具包的时候,使用最多的当属于数组array这种数据存储结构了。
numpy中的数组结构
接下来开始介绍numpy中的数组array。
想使用一个数组来存储数据,首先要创建一个数组。创建数组非常容易,使用numpy内置的array函数就可以直接创建一个数组。
例如,创建一个包含0到9十个数字的数组。
import numpy as np
nparray = np.array([0,1,2,3,4,5,6,7,8,9])
print(nparray)
[0 1 2 3 4 5 6 7 8 9]
如果我们想查看一个数组的形状,可以使用数组的shape属性,shape属性的返回结果是一个元组,元组中的每一个元素表示它对应的维度的长度。
print(nparray.shape)
(10,)
从上面的运行结果可以看出,nparray的shape属性返回的元组包含一个元素10,表示这是一个一维数组,第一维有10个元素。更多维度的数组,我们会在后面的课程内容中介绍,先不要着急,我们慢慢学。
#查看数组中的某个元素
nparray[3]
#通过元素下标修改元素的值
nparray[3]=200
print(nparray)
[ 0 1 2 200 4 5 6 7 8 9]
注意:numpy中的array不允许向数组中添加不同类型的元素,array中只能存储相同类型的元素
import numpy as np
#在array函数中直接传入一个列表,即可创建数组
nparray = np.array([0,1,2,3,4,5,6,7,8,9])
nparray[3]='guowenjing'
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-5-cd7f5e7dd233> in <module>
2 #在array函数中直接传入一个列表,即可创建数组
3 nparray = np.array([0,1,2,3,4,5,6,7,8,9])
----> 4 nparray[3]='guowenjing'
ValueError: invalid literal for int() with base 10: 'guowenjing'
如果想窥探array中存储的数据类型,可以使用array提供的dtype属性。
import numpy as np
# 在array函数中直接传入一个列表,即可创建一个数组
nparray = np.array([0,1,2,3,4,5,6,7,8,9])
nparray.dtype # 通过dtype得知nparray里存储的元素类型是64位整型
dtype('int32')
创建全0数组
1.使用zeros(num)函数创建一个全零数组,zeros函数需要传入一个参数num,num表示数组中有几个0,也就是元素个数。
import numpy as np
zero_array=np.zeros(10)
print(zero_array)
print(zero_array.dtype)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
float64
使用zeros函数创建数组,数组里的元素默认是float64浮点数。如果想设置数组中元素为其他类型,可以在zeros函数中传入dtype=类型
import numpy as np
zero_array=np.zeros(10,dtype=int)
print(zero_array)
print(zero_array.dtype)
[0 0 0 0 0 0 0 0 0 0]
int32
zeros函数传入的第一个参数是一个数字(这个数字表示数组的大小),创建出来的数组只有一行数据,我们称这样的数组为一维数组,或者行向量。向量是一个数学概念,在机器学习中经常会用到很多数学公式,所以在后面的学习中,只要提到行向量就是指一维数组。
zeros函数的第一个参数除了可以是一个数字之外,还尅是一个元组,用于创建多维数组,我们以创建二维数组为例。
import numpy as np
zeros_2=np.zeros((3,5))
print(zeros_2)
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
zeros_2.shape
(3, 5)
通过shape属性返回的元组中有两个元素,就表示zeros_2是一个二维数组,元组的第1个元素表示第一维的长度是3,也就是3行;元组的第2个元素表示第二维的长度是5,也就是有5列。 我们又发现一个规律:一个数组的shape属性返回的元组的元素个数就等于这个数组的维度。有了这个规律,就不用去掰手指数数组的中括号个数了。
其实还可以这样写:
import numpy as np
zeros_2=np.zeros(shape=(3,5))
print(zeros_2)
[[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
一维数组也被称为行向量,二维数组也被称为矩阵。
创建全1数组
在机器学习中,还会经常用到的一种数组是全1数组,跟全零素组的原理一样,只有数组中的元素从0变成了1.
import numpy as np
np.ones(10)
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
np.ones(shape=(3,5),dtype=int)
array([[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1]])
创建全num数组
上面井道的全0数组和全1数组都有专门的函数直接生产,如果我要生成一个全2、全3、全5…数组该怎么实现呢?
不用担心,numpy为我们提供了full函数,你可以用full函数自定义包含任何数字的全XX数组
使用full函数的语法:full(数组形状,填充的数字),函数的第一个参数数组的形状,一般要传入一个元组来表示数组的形状。
需求:创建一个全2的一维数组,数组的大小是10。
import numpy as np
print(np.full((10,),2))
np.full((10,2),3)
[2 2 2 2 2 2 2 2 2 2]
array([[3, 3],
[3, 3],
[3, 3],
[3, 3],
[3, 3],
[3, 3],
[3, 3],
[3, 3],
[3, 3],
[3, 3]])
对于使用full函数创建的一维数组,传入的第一个表示数组形状的参数可以不是元组,可以直接传入一个表示数组中元素个数的数字。
import numpy as np
np.full(10,2)
array([2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
arrage函数创建数组
如果我想在一个指定的区间范围内,快速创建一个数组,而且我不想自己写循环往数组里一个一个填数据,有没有什么简便的方法呢?
numpy这么强大,当然可以满足这样的小需求。
numpy提供了arrage函数可以在指定的区间范围内,快速生成一个数组,并且按照指定的步长自动填入数据
# 在[0,20)区间内(注意是左闭右开区间,不包含末尾的20),创建一个数组,数组中两个挨着的元素差值是1
# 也就是在[0,20)区间内创建一个数组,并且包含20个整数
np.arange(0,20)
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19])
arange函数中的参数介绍:
第1个参数:开始值,包含;
第2个参数:结束值,不包含;
第3个参数:步长,默认值1。
np.arange(1,20,2)
array([ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19])
我们还可以在调用arange函数的时候只传入一个数字num,这时候就会生成一个一维数组,数组里的元素默认是在[0,num)区间内,相邻两个元素之间的差值是1,也就是使用默认步长生成的数组元素,看个例子就明白了。
不能创建二维数组
np.arange(20)
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19])
randint随机数数组
接下来我要讲的一个知识点是随机数,随机数在我们的生活中经常使用,比如公司年会抽奖,在所有的员工中随机抽取一个幸运的员工获得年终大奖,为了保证公平公正,程序员小哥哥就要写个随机数程序,从全体员工中随机挑选一个员工。
numpy中有个专门模块用于处理随机数相关的操作,它就是random模块,random模块已经给我们实现好了一些生成随机数的函数,接下来我们学习几个常用的生成随机数的函数。
需求:在一个区间内生成整数随机数。
np.random.randint(1,20)
17
randint还可以传入第3个参数size,用于设置生成的随机数个数。 如果将size设置成一个整数,那么返回的是一个一维数组(向量)。 如果将size设置成一个元组,那么返回的是一个多维数组(矩阵)。
#生成一个一维数组
print(np.random.randint(1,20,size=4))
#写法2
print(np.random.randint(1,20,4))
#生成一个二维数组
np.random.randint(1,20,size=(3,4))
[16 6 18 16]
[18 12 17 12]
array([[10, 18, 5, 6],
[ 1, 4, 6, 3],
[ 7, 13, 13, 7]])
需求:要求每次生成的随机数相同
这个需求非常容易实现,numpy的random模块给我们提供了一个叫做随机种子的函数seed,在使用seed函数的时候需要传入一个数字,这个数字可以随意传,我们可以把这个数字理解成种子“编号”,这样就能够保证使用同一个“编号”的种子生成的随机数是相同的。
import numpy as np
np.random.seed(111)
print("使用111种子生成的随机向量:", np.random.randint(0,10,5))
np.random.seed(111)
print("使用111种子生成的随机向量:", np.random.randint(0,10,5))
#np.random.seed(21)
print("使用21种子生成的随机向量:", np.random.randint(0,10,5))
使用111种子生成的随机向量: [4 4 4 6 3]
使用111种子生成的随机向量: [4 4 4 6 3]
使用21种子生成的随机向量: [9 2 6 2 8]
reshape改变数组形状
之前我们将讲解过,通过数组的shape属性可以查看数组的形状,如果想改变数组的形状可以使用reshape函数,我们来看看reshape函数具体怎么来使用。
import numpy as np
# 创建一个包含6个数字的一维数组
x1 = np.random.randint(0,10,6)
print("x1 shape :", x1.shape)
print(x1)
# 将数组x1转换成一个2行3列的二维数组
x2 = x1.reshape(2,3)
print("-------------------------") #打印一个分割线,只为了方便看打印结果
print("x1 shape :", x1.shape)
print(x1)
print("x2 shape :", x2.shape)
print(x2)
x1 shape : (6,)
[7 9 7 1 0 8]
-------------------------
x1 shape : (6,)
[7 9 7 1 0 8]
x2 shape : (2, 3)
[[7 9 7]
[1 0 8]]
x1.shape返回的是一个包含1个元素的元组,表示x1是一个一维数组,数组里有6个元素。
x2.shape返回的是一个包含2个元素的元组,表示x2是一个两行三列的二维数组,x2是使用reshape函数在x1的基础上转换得到的新数组,原数组x1没有发生改变。
使用reshape函数改变数组形状生成一个新的数组的时候,原数组中的元素个数要正好填满新数组,例如,数组x1有6个元素,数组x2的形状是2行3列,要想填满数组x2就必须有6个元素,正好x1有6个元素,所以使用reshape转换数组形状能成功。
import numpy as np
# 创建一个包含6个数字的一维数组
x1 = np.random.randint(0,10,6)
# 错误例子1,x1的元素个数不够填满新的数组
x4 = x1.reshape(4,2)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-32-e1d4ce812591> in <module>
5
6 # 错误例子1,x1的元素个数不够填满新的数组
----> 7 x4 = x1.reshape(4,2)
ValueError: cannot reshape array of size 6 into shape (4,2)
import numpy as np
# 创建一个包含6个数字的一维数组
x1 = np.random.randint(0,10,6)
# 错误例子2,x1的元素个数超过了新数组需要的元素个数
x4 = x1.reshape(2,2)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-33-40152e3b359c> in <module>
5
6 # 错误例子2,x1的元素个数超过了新数组需要的元素个数
----> 7 x4 = x1.reshape(2,2)
ValueError: cannot reshape array of size 6 into shape (2,2)
在使用reshape的时候,如果你不关心转换之后有多少行或者有多少列,应该怎么操作呢?下面我用几个例子来演示下reshape的新用法。
需求1:x1是一个包含6个元素的一维数组,现在我基于x1使用reshape转换生成一个新数组,我要求新数组只要有2行就行,具体有几列我不关心
import numpy as np
# 创建一个包含6个数字的一维数组
x1 = np.random.randint(0,10,6)
x1.reshape(2,-1)
array([[3, 7, 9],
[3, 1, 6]])
从这个需求的代码实现可以看出,在使用reshape的时候,不关心的维度可以使用-1表示,让机器自动去判断。
需求2:x1是一个包含6个元素的一维数组,现在我基于x1使用reshape转换生成一个新数组,我要求新数组只要有2列就行,具体有几行我不关心
import numpy as np
# 创建一个包含6个数字的一维数组
x1 = np.random.randint(0,10,6)
x1.reshape(-1,2)
array([[8, 5],
[4, 1],
[1, 7]])
通过这个需求,再一次验证了上一个需求的结论,我不关心有多少行,那么控制行的参数就传-1,让机器自己去判断转换之后的新数组有多少行。
我们在前面学习了使用数组的shape属性可以查看数组的形状,除了shape属性之外,还有两个比较常用的属性,分别是查看数组维度的ndim属性和查看数组元素个数的size属性。
import numpy as np
# 创建一个包含6个数字的一维数组
x1 = np.random.randint(0,10,6)
x6 = np.random.randint(0,10,6)
print("x6是{}维数组".format(x6.ndim))
print("x6中有{}个元素".format(x6.size))
x7 = x6.reshape((3,2))
print("x7是{}维数组".format(x7.ndim))
print("x7中有{}个元素".format(x7.size))
x6是1维数组
x6中有6个元素
x7是2维数组
x7中有6个元素
数组切片
我们在前面学习了通过数组下标获取数组的某个元素,这种方式只能获取一个元素,如果想获取数组中的一批元素或者一个片段的元素应该怎么操作呢?
numpy为我们提供了好用到爆的切片功能,老规矩直接上例子代码。
import numpy as np
# 创建一个一维数组:[0 1 2 3 4 5 6 7 8 9]
arr1 = np.arange(10)
print(arr1)
[0 1 2 3 4 5 6 7 8 9]
需求1:获取arr1的前5个元素
import numpy as np
# 创建一个一维数组:[0 1 2 3 4 5 6 7 8 9]
arr1 = np.arange(10)
arr1[:5]
array([0, 1, 2, 3, 4])
我们只用了一行代码就完成了获取arr1的前5个元素的需求,通过这行代码我们也看出了切片的语法:数组名[开始下标:结束下标]
切片的语法有两个注意点: 1)如果切片是从第一个下标0开始,可以不写开始下标; 2)切片不包含结束下标对应的数组元素;
需求2:获取arr1的后3个元素
import numpy as np
# 创建一个一维数组:[0 1 2 3 4 5 6 7 8 9]
arr1 = np.arange(10)
arr1[7:]
array([7, 8, 9])
数组的下标从0开始,数组arr1中共有10个元素,arr1最后一个元素的脚标是9,要完成需求是获取arr1的后3个元素,那么脚标就是从7开始。
实现这个需求有个注意点:切片的结束下标如果不写的话,表示切片到数组的结尾。
需求3:获取arr1的第3个到第6个元素
import numpy as np
# 创建一个一维数组:[0 1 2 3 4 5 6 7 8 9]
arr1 = np.arange(10)
arr1[2:6]
array([2, 3, 4, 5])
数组的下标从0开始,第3个元素的下标是2,第6个元素的下标是5,要获取到第6个元素,也就是说切片的结果要包含第6个元素,由于切片是不包含结束下标对应的元素的,所以切片的结束下标要设置成第6个元素后一个下标的位置。
解释这么多!弄得这么绕!全怪数组的下标从0开始!
需求4:从数组arr1中,隔一个元素获取一个
小提示:切片带步长的语法是:数组名[开始下标:结束下标:步长]
import numpy as np
# 创建一个一维数组:[0 1 2 3 4 5 6 7 8 9]
arr1 = np.arange(10)
arr1[::2]
array([0, 2, 4, 6, 8])
因为是要从头取到尾,默认切片不设置开始和结束下标就表示从第1个元素到最后1个,上面的代码等价于设置开始和结束下标的方式:arr1[0:10:2]
由于需求是隔一个元素获取一个,中间要跳过一个元素,所以要设置步长为2。
需求5:从后向前获取数组arr1中的元素,相当于把数组arr1翻过来
import numpy as np
# 创建一个一维数组:[0 1 2 3 4 5 6 7 8 9]
arr1 = np.arange(10)
arr1[::-1]
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
步长是正数表示从左向右获取,步长是负数表示从右向左获取。需求是把数组翻过来,那就是从右向左获取,而且要获取原数组arr1的所有元素,所以步长设置为-1。
上面的5个需求的实现方法能够全部掌握了,基本上可以熟练地使用数组切片了。
切片不是一维数组的特有功能,多维数组同样可以使用切片,接下来我们以最常用的二维数组为例,讲解在二维数组中是如何使用切片的。
import numpy as np
# 先创建一个3行5列的二维数组
arr2 = np.arange(15).reshape((3,5))
print(arr2)
print("arr2是{}维数组".format(arr2.ndim))
print("arr2有{}个元素".format(arr2.size))
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
arr2是2维数组
arr2有15个元素
新需求1:获取数组arr2的前2行的前3列
import numpy as np
# 先创建一个3行5列的二维数组
arr2 = np.arange(15).reshape((3,5))
arr2[:2,:3]
array([[0, 1, 2],
[5, 6, 7]])
这里不再像df或者Series用loc或者iloc获取元素,直接使用数组名加中括号就可以定位到具体的元素
二维数组的切片语法:数组[第一维开始下标:第一维结束下标,第二维开始下标:第二维结束下标]
我们从二维数组的切片语法可以看出中括号内以逗号分隔,逗号前的开始/结束下标控制第一维切片,逗号后的开始/结束下标控制第二维。其他切片相关的语法规则跟前面一维数组的规则一样。
需求是先获取arr2的前2行,通过切片中设置第一维的开始/结束下标控制;然后再获取前两行的前三列,通过切片中设置第二维的开始/结束下标控制。
新需求2:隔一行取一行,再隔一列取一列
import numpy as np
# 先创建一个3行5列的二维数组
arr2 = np.arange(15).reshape((3,5))
arr2[::2,::2]
array([[ 0, 2, 4],
[10, 12, 14]])
通过这个需求的实现,我们可以得出一个结论,使用数组切片的时候,不管数组有多少维,每一个维度的切片都是使用相同语法规则的切片表达式控制。
作业
1.创建一个包含10个整数0的一维数组,创建完后修改数组中第5个元素的值为10
2.创建一个3行5列的二维数组,获取数组arr2的前2行的前3列
import numpy as np
zero_array=np.zeros(10,dtype=int)
zero_array[4]=10
print(zero_array.dtype)
zero_array
int32
array([ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0])
arr2=np.random.randint(0,10,(3,5))
arr2=np.random.randint(0,10,size=(3,5))
print(arr2)
#获取数组arr2的前2行的前3列
arr2[:2,:3]
[[4 3 5 0 8]
[2 2 8 1 1]
[6 3 8 9 4]]
array([[4, 3, 5],
[2, 2, 8]])