8.6 书生大模型实战营2-Numpy基础

1. numpy Ndarry和创建数组的方式

NumPy数组(ndarray)是NumPy库的核心数据结构,它是一系列同类型数据的集合,以 0 下标为开始进行集合中元素的索引。

ndarray本质上是一个存放同类型元素的多维数组,其中的每个元素在内存中都有相同存储大小的区域。

创建numpy数组最常见的方法就是将一个列表使用np.array()函数转成ndarray。

import numpy as np
array1 = np.array([1,2,3,4,5])
#np.array函数全部的参数有
#np.array(object, dtype=None, *, copy=True, order='K', subok=False, ndmin=0,like=None)
#具体可以尝试上面提到的查看函数提示的方法或者去看numpy的文档
#dtype参数表示array中元素的类型,这个参数在numpy中很常见。
print('array1: \n',array1)
array2 = np.array([[1,2],[3,4]])
print('array2: \n ',array2)

array1: 
 [1 2 3 4 5]
array2: 
  [[1 2]
 [3 4]

#array常用属性
#查看array数据类型
print("array1.dtype : ",array1.dtype)
#查看数组的维度
print("array1.ndim : ",array1.ndim)
print("array2.ndim : ",array2.ndim)
#查看array的形状
print("array1.shape : ",array1.shape)
print("array2.shape : ",array2.shape)
#查看array中的元素总个数
print("array2.size : ",array2.size)

array1.dtype :  int32
array1.ndim :  1
array2.ndim :  2
array1.shape :  (5,)
array2.shape :  (2, 2)
array2.size :  4

Numpy还有其他很方便的创建元素值相同的数组的函数:

  • np.empty(shape,dtype)创建一个指定形状(shape)、数据类型(dtype)且未初始化的数组
  • np.zeros(shape,dtype)创建指定形状(shape),数组元素以 0 来填充
  • np.ones(shape,dtype)创建指定形状(shape),数组元素以 1 来填充
  • np.zeros_like(array,dtype)创建一个与给定数组具有相同形状的数组,数组元素以 0 来填充
  • np.ones_like(array,dtype)创建一个与给定数组具有相同形状的数组,数组元素以 1 来填充

1.1 从数值范围创建数组

在python for循环中的range(start,stop,step)函数,他能生成一个迭代器。Numpy也有类似的函数,能够直接生成一个数组。

np.arange(start, stop, step, dtype),他的使用方法与range()一模一样。

print("np.arange(20)\n",np.arange(20))
print("np.arange(1,20,2)\n",np.arange(1,20,2))

np.arange(20)
 [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
np.arange(1,20,2)
 [ 1  3  5  7  9 11 13 15 17 19]

此外,numpy还提供一个构建等差数列的函数,只需要设定start和stop,以及要生成的等步长样本数量Num。

np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)

  • start 序列的起始值
  • stop 序列的终止值,如果endpoint为true,该值包含于数列中
  • num 要生成的等步长的样本数量,默认为50
  • endpoint 该值为 true 时,数列中包含stop值,反之不包含,默认是True。
  • retstep 如果为 True 时,生成的数组中会显示间距,反之不显示。
  • dtype ndarray 的数据类型
print("np.linspace(1,100,50)\n",np.linspace(0,100,30))

np.linspace(1,100,50)
 [  0.           3.44827586   6.89655172  10.34482759  13.79310345
  17.24137931  20.68965517  24.13793103  27.5862069   31.03448276
  34.48275862  37.93103448  41.37931034  44.82758621  48.27586207
  51.72413793  55.17241379  58.62068966  62.06896552  65.51724138
  68.96551724  72.4137931   75.86206897  79.31034483  82.75862069
  86.20689655  89.65517241  93.10344828  96.55172414 100.        ]

这里说明一下:

np.arange()和np.linspace()的区别在于前一个是确定了步长,从start开始,每次步长确定;而np.linspace()是为了构建等差数列的函数,使得生成的序列从start到end是一个等步长的序列,步长的确定由样本数量间接确定。

2. 数组操作

2.1 数组运算

numpy array之前的运算均为元素位置一一对应进行计算,需要两个数组维数相同,且各维度的长度也相同。此外,numpy的广播机制还可以把该条件放宽到两个数组在某个维度上的长度相同即。

普通的数组运算:

array1 = np.array([[1,2,3],[4,5,6]])
array2 = np.array([[2,4,6],[3,5,7]])
print('array1 :\n',array1)
print('array2 :\n', array2)
print('array1+array2:\n',array1+array2)
print('array1*array2:\n',array1*array2)

array1 :
 [[1 2 3]
 [4 5 6]]
array2 :
 [[2 4 6]
 [3 5 7]]
array1+array2:
 [[ 3  6  9]
 [ 7 10 13]]
array1*array2:
 [[ 2  8 18]
 [12 25 42]]

2.2 广播机制

NumPy的广播机制是NumPy库中一个非常强大的功能,它允许我们对形状不完全相同的数组进行算术运算。在广播过程中,NumPy会自动调整数组的形状,以便它们可以在元素级别上进行运算。这种机制使得NumPy在进行数组操作时更加灵活和高效。

广播的基本规则:
  1. 规则一:维度对齐

    • 如果两个数组的维度数不同,较小的数组会在其前面补1,直到两个数组的维度数相同。
    • 维度对齐后,从最后一个维度(也称为“后缘维度”)开始比较两个数组的形状。
  2. 规则二:兼容维度

    • 如果两个数组在某个维度上的大小相同,或者其中一个数组在该维度上的大小为1,则这两个数组在该维度上是兼容的。
    • 如果两个数组在所有维度上都是兼容的,那么它们就可以进行广播。
  3. 规则三:输出形状

    • 广播后的输出数组的形状是输入数组形状在各个维度上的最大值。

假设我们有两个数组ab,我们想要对它们进行加法运算。

import numpy as np

a = np.array([1, 2, 3])  # 形状为 (3,)
b = np.array([[1], [2], [3]])  # 形状为 (3, 1)

# 使用广播机制进行加法运算
result = a + b
print(result)  # 输出: [[2, 3, 4], [3, 4, 5], [4, 5, 6]]

[[2 3 4]
 [3 4 5]
 [4 5 6]]

  • 在这个例子中,a的形状是(3,),而b的形状是(3, 1)
  • 根据广播规则一,首先由于a,b 的维度不同a的形状在逻辑上被扩展为(1, 3),以便与b 的形状(3, 1)对齐。
  • 然后,根据规则二,两个数组在所有维度上都是兼容的,因为a在第一个维度上的大小为1(逻辑上扩展的),而b在该维度上的大小为3;同时,在第二个维度上,a的维度大小为3,b的维度大小为1。
  • 因此,可以进行广播,加法运算在元素级别上进行。a广播之后形状是(3,3),即将(1,3)进行“复制“得到(3,3);而b进行广播之后形状是(3,3)。即将(3,1)进行“复制“得到(3,3)
  • 结果数组result的形状是(3, 3),这是两个输入数组形状在各个维度上的最大值。

具体的实现步骤见如下所示:

如果我们不使用NumPy的广播机制,而是手动模拟这一过程,我们可以这样做:

# 手动模拟广播机制
a = np.array([1, 2, 3])  # 形状为 (3,)
b = np.array([[1], [2], [3]])  # 形状为 (3, 1)
print('a\n',a)
print('b\n',b)

a = np.expand_dims(a,0)
#先让两边维数相等
print('\na的形状在逻辑上被扩展为(1,3),a为\n',a)
print('此时a与b的形状为: a {},b {}'.format(a.shape,b.shape))
#此时a与b的形状为: a (1, 3),b (3, 1)
#a会在第0轴上重复三次变成(3,3)
a_broadcast = np.repeat(a,3,0)
#a会在第1轴上重复三次变成(3,3)
b_broadcast =np.repeat(b,3,1)
print('\n广播后的a_broadcast为\n',a_broadcast)
print('广播后的b_broadcast为\n',b_broadcast)
print('\na+b经过广播后就是a_broadcast+b_broadcast\n',a_broadcast+b_broadcast)

a
 [1 2 3]
b
 [[1]
 [2]
 [3]]

a的形状在逻辑上被扩展为(1,3),a为
 [[1 2 3]]
此时a与b的形状为: a (1, 3),b (3, 1)

广播后的a_broadcast为
 [[1 2 3]
 [1 2 3]
 [1 2 3]]
广播后的b_broadcast为
 [[1 1 1]
 [2 2 2]
 [3 3 3]]

a+b经过广播后就是a_broadcast+b_broadcast
 [[2 3 4]
 [3 4 5]
 [4 5 6]]

2.3 修改数组形状

ndarray.reshape(newshape, order='C')不改变数据的条件下修改形状

  • newshape:整数或者整数数组,新的形状应当兼容原有形状
  • order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'k' -- 元素在内存中的出现顺序。

返回修改好形状的数组,并不直接对原数组进行修改

array1 = np.array([[1,2,3],[4,5,6]])
print('array1 :\n',array1)
print('array1.reshape((3,2)) :\n',array1.reshape((3,2)))
print('array1.reshape((6,1)) :\n',array1.reshape((6,1)))

array1 :
 [[1 2 3]
 [4 5 6]]
array1.reshape((3,2)) :
 [[1 2]
 [3 4]
 [5 6]]
array1.reshape((6,1)) :
 [[1]
 [2]
 [3]
 [4]
 [5]
 [6]]

这里order=“C”可以理解为按照原数组行的顺序对新数组的每一行进行排列

这里order=“F”可以理解为按照原数组列的顺序对新数组的每一列进行排列

ndarray.flatten(order='C')将数组展平成一维数组

  • order:'C' -- 按行,'F' -- 按列,'A' -- 原顺序,'K' -- 元素在内存中的出现顺序。
array1 = np.array([[1,2,3],[4,5,6],[7,8,9]])
print('array1 :\n',array1)
print('array1.flatten():\n',array1.flatten())

array1 :
 [[1 2 3]
 [4 5 6]
 [7 8 9]]
array1.flatten():
 [1 2 3 4 5 6 7 8 9]

2.4 翻转数组

对于二维数组就是转置,但对于更高维的数组可以指定axes列表来确定维度的变化。原始数组的维度[1,2],可以指定axes为[2,1]。

array1 = np.array([[1,2,3],[4,5,6]])
print('array1.traspose() 等价于array1.transpose([1,0]) \n'\
        ,array1.transpose())

array1.traspose() 等价于array1.transpose([1,0]) 
 [[1 4]
 [2 5]
 [3 6]]

2.5 修改数组的维度

numpy有两个常用的修改数组维度的函数,一个用于增加维度一个用于减少维度。修改数组维度经常会在进行高维tesnor计算时用于对其维数用到,经过增加一维或者减少一维数来实现。 numpy.expand_dims(arr, axis)通过在指定位置插入新的轴来扩展数组形状

  • arr:输入数组
  • axis:新轴插入的位置
array1 = np.array([[1,2,3],[4,5,6]])
print('array1\n',array1)
print('array1.shape\n',array1.shape)
array2 = np.expand_dims(array1,0)
print('array2\n',array2)
print('array2.shape\n',array2.shape)
array3 = np.expand_dims(array1,1)
print('array3\n',array3)
print('array3.shape\n',array3.shape)

array1
 [[1 2 3]
 [4 5 6]]
array1.shape
 (2, 3)
array2
 [[[1 2 3]
  [4 5 6]]]
array2.shape
 (1, 2, 3)
array3
 [[[1 2 3]]

 [[4 5 6]]]
array3.shape
 (2, 1, 3)

numpy.squeeze(arr, axis)从给定数组的形状中删除一维的条目(即删除维度大小=1的维度)

  • arr:输入数组
  • axis:整数或整数元组,用于选择形状中一维条目的子集
array2 = np.expand_dims(array1,0)
print('array2\n',array2)
print('array2.shape\n',array2.shape)
array4 = np.squeeze(array2)
print('array4=np.squeeze(array2)\n',array4)
print('array4.shape\n',array4.shape)

array3 = np.expand_dims(array1,1)
print('array3\n',array3)
print('array3.shape\n',array3.shape)
array5 = np.squeeze(array3)
print('array5=np.squeeze(array3)\n',array5)
print('array5.shape\n',array5.shape)

array2
 [[[1 2 3]
  [4 5 6]]]
array2.shape
 (1, 2, 3)
array4=np.squeeze(array2)
 [[1 2 3]
 [4 5 6]]
array4.shape
 (2, 3)
array3
 [[[1 2 3]]

 [[4 5 6]]]
array3.shape
 (2, 1, 3)
array5=np.squeeze(array3)
 [[1 2 3]
 [4 5 6]]
array5.shape
 (2, 3)

2.6 拼接数组

numpy有四种拼接数组的函数:

numpy.concatenate(arrays,axis)沿指定轴连接相同形状的两个或多个数组

  • arrays:(a1,a2,a3...)相同类型的数组
  • axis:沿着它连接数组的轴,默认为 0

numpy.stack(arrays, axis)沿新轴连接数组序列

  • arrays相同形状的数组序列
  • axis:返回数组中的轴,输入数组沿着它来堆叠

numpy.hstack(arrays) np.concatenate的特殊情况,相当于axis=1,通过水平堆叠来生成数组

numpy.vstack(arrays) np.concatenate的特殊情况,相当于axis=0,通过垂直堆叠来生成数组

所有用于拼接的数组在拼接的轴上长度必须相同

axis=0代表列,axis=1代表行

array1 = np.array([[1,2,3],[4,5,6]])
array2 = np.array([[2,4,6],[3,5,7]])
print("np.concatenate((array1,array2),0)\n",np.concatenate((array1,array2),0))
print("np.concatenate((array1,array2),1)\n",np.concatenate((array1,array2),1))

print("np.stack((array1,array2),0)\n",np.stack((array1,array2),0))
print("np.stack((array1,array2),1)\n",np.stack((array1,array2),1))

print("np.hstack((array1,array2))\n",np.hstack((array1,array2)))
print("np.vstack((array1,array2),1)\n",np.vstack((array1,array2)))

np.concatenate((array1,array2),0)
 [[1 2 3]
 [4 5 6]
 [2 4 6]
 [3 5 7]]
np.concatenate((array1,array2),1)
 [[1 2 3 2 4 6]
 [4 5 6 3 5 7]]
np.stack((array1,array2),0)
 [[[1 2 3]
  [4 5 6]]

 [[2 4 6]
  [3 5 7]]]
np.stack((array1,array2),1)
 [[[1 2 3]
  [2 4 6]]

 [[4 5 6]
  [3 5 7]]]
np.hstack((array1,array2))
 [[1 2 3 2 4 6]
 [4 5 6 3 5 7]]
np.vstack((array1,array2),1)
 [[1 2 3]
 [4 5 6]
 [2 4 6]
 [3 5 7]]

这里我自己在学习的过程中主要对np.stack()有一些疑惑,这里主要说一下np.stack():

这里最重要的是在新轴上堆叠新轴!新轴!重要的事情说三遍

np.stack((array1,array2),0):

array1的维度是(2,3),array1的维度也是(2,3),在axis=0的维度堆叠的维度是(2,2,3),即相当于整体堆叠了。

np.stack((array1,array2),1):

array1的维度是(2,3),array1的维度也是(2,3),在axis=1的维度堆叠的维度是(2,2,3),即相当于将每个数组切分,在行的角度堆叠。

np.stack((array1,array2),1):

array1的维度是(2,3),array1的维度也是(2,3),在axis=2的维度堆叠的维度是(2,3,3)。具体结果如下:

np.stack((array1,array2),2)
 [[[1 2]
  [2 4]
  [3 6]]

 [[4 3]
  [5 5]
  [6 7]]]

2.7 索引与切片

numpy数据一样有索引与切片,只是其索引的写法与嵌套列表可能会有一点点不一样。

numpy的索引用[]表示,用,隔开每个轴上的索引,比如对于一个二位数组要取第i行第j列的元素的索引就为[i,j]

在索引切片上numpy数组依旧遵循start:stop:step格式,在每个轴上的切片用,隔开。:或者...可以表示取整个轴上的全部元素。

array1 = np.array([[1,2,3],[4,5,6],[7,8,9]])
print()
print("取array1第1行第3个元素,array1[0,2]=",array1[0,2])
print("取array1第1行前两个元素,array1[0,:2]=",array1[0,:2])
print("取array1每一行前两个元素,array1[:,:2]=\n",array1[:,:2])

取array1第1行第3个元素,array1[0,2]= 3
取array1第1行前两个元素,array1[0,:2]= [1 2]
取array1每一行前两个元素,array1[0:,:2]=
 [[1 2]
 [4 5]
 [7 8]]

3. 排序条件筛选

ndarray.sort(axis=-1, kind=None, order=None)

  • axis: 沿着它排序数组的轴,如果没有数组会被展开,沿着最后的轴排序, axis=0 按列排序,axis=1 按行排序
  • kind: 默认为'quicksort'(快速排序)
  • order: 如果数组包含字段,则是要排序的字段
array1 = np.random.randint(1,10,(3,4))
print('array1',array1)
array1.sort(axis=0)
print('对列内数字进行排序,array1.sort(axis=0)\n',array1)
array1.sort(axis=1)
print('对行内数字进行排序,array1.sort(axis=1)\n',array1)

array1 [[7 3 7 6]
 [8 9 3 2]
 [9 9 2 2]]
对列内数字进行排序,array1.sort(axis=0)
 [[7 3 2 2]
 [8 9 3 2]
 [9 9 7 6]]
对行内数字进行排序,array1.sort(axis=1)
 [[2 2 3 7]
 [2 3 8 9]
 [6 7 9 9]]

ndarray.argmax(axis)与ndarray.argmin(axis)是求数组对应轴上最大或者最小的元素的索引。

array1 = np.random.randint(1,10,(3,4))
print('array1',array1)
print('求每一列最大元素的索引,array1.argmax(axis=0)\n',array1.argmax(axis=0))
print('求每一行最大元素的索引,array1.argmax(axis=1)\n',array1.argmax(axis=1))

array1 [[6 7 8 9]
 [7 2 9 2]
 [6 7 3 4]]
求每一列最大元素的索引,array1.argmax(axis=0)
 [1 0 1 0]
求每一行最大元素的索引,array1.argmax(axis=1)
 [3 2 1]

numpy数组也支持比较运算,运算结果为bool numpy数组。可以使用np.where()获取其中为ture的元素的索引。

np.where(array1 > 1) 返回一个元组,其中包含两个数组,分别代表满足条件的元素的行和列索引。

array1 = np.random.randint(1,10,(3,4))
print('array1',array1)
np.where(array1>1)

array1 [[2 7 7 7]
 [8 1 3 1]
 [1 2 2 9]]

(array([0, 0, 0, 0, 1, 1, 2, 2, 2], dtype=int64),
 array([0, 1, 2, 3, 0, 2, 1, 2, 3], dtype=int64))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值