•   了解Numpy运算速度上的优势知道数组的属性,形状、类型应用Numpy实现数组的基本操作

  •   应用随机数组的创建实现正态分布应用

  •   应用Numpy实现数组的逻辑运算

  •   应用Numpy实现数组的统计运算

  •   应用Numpy实现数组之间的运算

先介绍如何在pycharm中执行jupyter中的代码:

先安装jupyter

pip install jupyter

新建一个jupyter notebook文件test.ipynb,点击run cell,浏览器访问:http://localhost:8888/tree

点击test.ipynb文件,进入如下界面:

输入代码后,按住shift+enter就会运行。

如果运行报错:

解决办法:重新建一个新的纯python项目,在新项目中运行代码即可。

一、numpy的优势

Numpy(Numerical Python)是一个开源的Python科学计算库,用于快速处理任意维度的数组。

Numpy支持常见的数组和矩阵操作。对于同样的数值计算任务,使用Numpy比直接使用Python要简洁的多。

Numpy使用ndarray对象来处理多维数组,该对象是一个快速而灵活的大数据容器。

ndarray介绍

NumPy提供了一个N维数组类型ndarray,它描述了相同类型的“items”的集合,

使用np.array创建numpy

import numpy as np
# 创建ndarray
score =np.array(
[[80,89,86,67,79],
[78,97,89,67,81],
[90,94,78,67,74],
[91,91,90,67,69],
[76,87,75,67,86],
[70,79,84,67,84],
[94,92,93,67,64],
[86,85,83,67,80]])
score

结果如下:

使用Python列表可以存储一维数组,通过列表的嵌套可以实现多维数组,那么为什么还需要使用Numpy的ndarray呢?

ndarray与Python原生list运算效率对比

在这里我们通过一段代码运行来体会到ndarray的好处

import random
import time
import numpy as np
a = []
for i in range(100000000):
    a.append(random.random())
# 通过%time魔法方法,查看当前行的代码运行一次所花费的时间
%time sum1=sum(a)
b=np.array(a)
%time sum2=np.sum(b)

其中第一个时间显示的是使用原生Python计算总和的时间,第二个内容是使用numpy计算总和的时间:

结果如下:

CPU times: total: 516 ms
Wall time: 553 ms
CPU times: total: 156 ms
Wall time: 120 ms

从中我们看到ndarray的计算速度要快很多,节约了时间,

机器学习的最大特点就是大量的数据运算,那么如果没有一个快速的解决方案,那可能现在python也在机器学习领域达不到好的效果。

Numpy专门针对ndarray的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于Python中的嵌套列表,数组越大,Numpy的优势就越明显。

ndarray为什么可以这么快?

ndarray到底跟原生python列表有什么不同呢,请看一张图:

从图中我们可以看出ndarray在存储数据的时候,数据与数据的地址都是连续的,这样就给使得批量操作数组元素时速度更快
这是因为ndarray中的所有元素的类型都是相同的,而Python列表中的元素类型是任意的,所以ndarray在存储元素时内存可以连续,而python原生list就只能通过寻址方式找到下一个元素,这虽然也导致了在通用性能方面Numpy的ndarray不及Python原生list,但在科学计算中,Numpy的ndarray就可以省掉很多循环语句,代码使用方面比Python原生list简单的多

ndarray支持并行化运算(向量化运算)

numpy内置了并行运算功能,当系统有多个核心时,做某种计算时,numpy会自动做并行计算

效率远高于纯Python代码

Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁)其对数组的操作速度不受Python解释器的限制,所以,其效率远高于纯Python代码。

二、N维数组-ndarray

1、ndarray的属性

数组属性反映了数组本身固有的信息。

2、ndarray的形状

首先创建一些数组。

import numpy as np
# 创建不同形状的数组
a = np.array([[1,2,3],[4,5,6]])
b = np.array([1,2,3,4])
c = np.array([[[1,2,3],[4,5,6]],[[1,2,3],[4,5,6]]])

结果:

(2,2,3)中第一个2表示三维数组内有两个二维数组,第二个2表示二维数组中有两个一维数组,3表示一维数组中有3个元素。

如何理解数组的形状?

二维数组

三维数组(三维数组就是二维数组的叠加):

3、ndarray的类型

type(c.dtype)

结果:

numpy.dtypes.Int32DType

dtype是numpy.dtype类型,先看看对于数组来说有哪些类型

创建数组的时候指定类型

import numpy as np
a =np.array([[1,2,3],[4,5,6]],dtype=np.float32)
type(a.dtype)

结果:numpy.dtypes.Float32DType

import numpy as np
arr = np.array(['python', 'tensorflow', 'scikit-learn', 'numpy'], dtype = np.string_)
arr

结果:

array([b'python', b'tensorflow', b'scikit-learn', b'numpy'], dtype='|S12')

注意:S表示字符串,12表示最长的字符串的字符个数为12

注意:若不指定,整数默认int64,小数默认float64

numpy的shape属性

import numpy as np
c = np.array([[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]])
print(c.shape) # # (1, 4, 3)
print(c.shape[:-1]) # (1, 4)
print(c.shape[:-1][::-1]) # (4, 1)

shape[:-1] 表示 获取0维和1维

shape[:-1][::-1]表示 获取0维和1维之后再反转

三、基本操作

创建数组

1、生成数组的方法

(1)、生成0和1的数组

 • np.ones(shape, dtype)

 • np.ones_like(a, dtype)

 • np.zeros(shape, dtype)

 • np.zeros_like(a, dtype)

import numpy as np
ones = np.ones([4,8])
ones

结果:

array([[1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1., 1., 1., 1.]])

 • np.zeros_like(a, dtype):用于创建一个与给定数组具有相同形状和类型的数组,并将所有元素初始化为 0。

import numpy as np
ones = np.ones([4,8])
np.zeros_like(ones)

结果:

array([[0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0.]])

2、 从现有数组生成

生成方式:

 • np.array(object, dtype):深拷贝

 • np.asarray(a, dtype):浅拷贝

关于array和asarray的不同

 3、 生成固定范围的数组

(1)、创建等差数组 -指定数量

np.linspace (start, stop, num, endpoint)

  •  参数:

  start:序列的起始值

  stop:序列的终止值

  num:要生成的等间隔样例数量,默认为50

  endpoint:序列中是否包含stop值,默认为ture

import numpy as np
# 生成等间隔的数组
a = np.linspace(0,100,11)
a

结果:array([ 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.])

(2)、创建等差数组 - 指定步长

np.arange(start,stop, step, dtype)

  •  参数
  step:步长,默认值为1

import numpy as np
a = np.arange(10,50,2)
a

结果:array([10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48])

(3)、创建等比数列

np.logspace(start,stop, num)

参数:
  num:要生成的等比数列数量,默认为50

import numpy as np
#生成10^x
a = np.logspace(0,2,3)
a

结果:array([ 1., 10., 100.])

4、 生成随机数组

使用模块介绍:np.random模块

正态分布

基础概念复习:正态分布(理解)

什么是正态分布?

正态分布是一种概率分布。正态分布是具有两个参数μ和σ的连续型随机变量的分布,第一参数μ是服从正态分布的随机变量的均值,第二个参数σ是此随机变量的方差,所以正态分布记作N(μ,σ)。

正态分布的应用

生活、生产与科学实验中很多随机变量的概率分布都可以近似地用正态分布来描述

正态分布特点

μ决定了其位置,其标准差σ决定了分布的幅度。当μ=0,σ=1时的正态分布是标准正态分布

标准差如何来?

方差(标准差的平方):是在概率论和统计方差衡量一组数据时离散程度的度量

从式中可以知道:当σ越小,数据越集中。σ决定了图形是瘦高还是矮胖。

其中M为平均值,n为数据总个数,σ为标准差,σ^2可以理解一个整体为方差

标准差与方差的意义:可以理解成数据的一个离散程度的衡量

正态分布创建方式

(1)、np.random.randn(d0, d1, ..., dn)

从标准正态分布中返回一个或多个样本值

(2)、np.random.normal(loc=0.0,scale=1.0,size=None)

loc:float,此概率分布的均值(对应着整个分布的中心centre)

scale: float,此概率分布的标准差(对应于分布的宽度,scale越大越矮胖,scale越小,越瘦高)

size: int or tuple of ints,输出的shape,默认为None,只输出一个值

(3)、np.random.standard normal(size=None)

返回指定形状的标准正态分布的数组,

举例1:生成均值为1.75,标准差为1的正态分布数据,100000000个

import numpy as np
x1 = np.random.normal(1.75,1,100000000)
x1

结果:

array([2.66408375, 0.91945128, 2.78218299, ..., 3.16938642, 2.12794155,
       0.78719875])

代码

import matplotlib.pyplot as plt
import numpy as np
# 生成均匀分布的随机数
x1 = np.random.normal(1.75,1,100000000)
# 画图看分布状况
# 1)创建画布
plt.figure(figsize=(20,10),dpi=100)
#2)绘制直方图
plt.hist(x1,1000)
# 3)显示图像
plt.show()

结果:

例如:我们可以模拟生成一组股票的涨跌幅的数据

举例2:随机生成4支股票1周的交易日涨幅数据

4支股票,一周(5天)的涨跌幅数据,如何获取?

随机生成涨跌幅在某个正态分布内,比如均值0,方差1

股票涨跌幅数据的创建

import numpy as np
# 创建符合正态分布的4只股票5天的涨跌幅数据
stock_change = np.random.normal(0,1,(4,5))
stock_change

结果:

array([[ 0.73334139, -0.58521962, -1.46506676,  1.54290679,  0.07188718],
       [-1.76524248, -0.879684  , -1.05984393,  0.19934241, -0.61507942],
       [ 1.77756534, -1.54731467,  0.44325793, -0.4820808 , -0.27386622],
       [ 0.66783457, -0.7253537 ,  0.52598072,  0.26541225,  0.62257258]])

均匀分布

(1)、np.random.rand(d0, d1,..., dn)

返回[0.0,1.0)内的一组均匀分布的数。

(2)、np.random.uniform(low=0.0,high=1.0, size=None)

功能:从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开,即包含low,不包含high

参数介绍:

low:采样下界,float类型,默认值为0;

high:采样上界,float类型,默认值为1;

size: 输出样本数目,为int或元组(tuple)类型,例如,size=(m,n,k),则输出mnk个样本,缺省时输出1个值。

返回值:ndarray类型,其形状和参数size中描述一致。

(3)、np.random.randint(low,high=None, size=None, dtype='!')

从一个均匀分布中随机采样,生成一个整数或N维整数数组

取数范围:若high不为None时,取llow,high)之间随机整数,否则取值[0,low)之间随机整数

import numpy as np
# 生成均匀分布的随机数
x2 = np.random.uniform(-1,1,100000000)
x2

结果:

array([ 0.65996862,  0.56493368,  0.86964756, ...,  0.80140967,
       -0.46280982,  0.2130456 ])

画图看分布状况:

import matplotlib.pyplot as plt
import numpy as np
# 生成均匀分布的随机数
x2 = np.random.uniform(-1,1,100000000)
# 画图看分布状况
#1)创建画布
plt.figure(figsize=(10,10),dpi=100)
#2)绘制直方图
plt.hist(x=x2,bins=1000)#x代表要使用的数据,bins表示要划分区间数
# 3)显示图像
plt.show()

结果:

数组的索引、切片

一维、二维、三维的数组如何索引?

(1)、直接进行索引,切片
(2)、对象[:,:]--先行后列

score[6:,0:5]表示从索引为6的行开始到最后一行,列从索引为0的列开始到索引为5的列。

score[:, 0::2]是对所有行的偶数列进行赋值。这里0::2是从第0列开始,每隔一列取一列,也就是0,2,4,...这些列。1::2是奇数列

二维数组索引方式:

举例:获取第一个股票的前3个交易日的涨跌幅数据

三维数组索引方式:

import numpy as np
#三维
a1 =np.array([[[1,2,3],[4,5,6]],[[12,3,34],[5,6,7]]])# 返回结果
#索引、切片
a1[0,0,1]

结果:2

数组形状改变

6、形状修改

(1)、ndarray.reshape(shape, order)

 • 返回一个具有相同数据域,但shape不一样的视图,即生成一个新的数组。
 • 行、列不进行互换

import numpy as np
# 创建符合正态分布的4只股票5天的涨跌幅数据
stock_change = np.random.normal(0,1,(4,5))
# 在转换形状的时候,一定要注意数组的元素匹配
stock_change.reshape([5,4])

结果:

array([[-0.43163947, -0.60885818, -1.54730667,  0.20170622],
       [ 0.41326328, -1.0658904 ,  0.81301686, -1.25696051],
       [-1.04780744,  2.55363561, -0.93228075,  0.49892247],
       [ 0.9194214 ,  0.35874845,  0.19040156,  0.3208775 ],
       [-0.35759115,  0.3505183 ,  0.40907475, -0.76912757]])

此时查看stock_change是否发生变化

array([[ 1.34968512, -0.51723375,  1.0640306 ,  0.23750504, -0.9396212 ],
       [ 0.48598086,  0.02911366,  0.65421674,  1.07225325,  0.51867497],
       [-2.34449444, -1.08805   ,  0.36916812,  0.17638552, -1.22539747],
       [-2.76901179,  0.57442076,  0.14511709,  0.01467308, -0.684959  ]])

发现没有变化。

修改为:(2,10),-1表示不知道多少行,但是确定是10列。

#数组的形状被修改为:(210),-1:表示通过待计算
stock_change.reshape([-1,10])

结果:

array([[-0.43163947, -0.60885818, -1.54730667,  0.20170622,  0.41326328, -1.0658904 ,  0.81301686, -1.25696051, -1.04780744,  2.55363561],
       [-0.93228075,  0.49892247,  0.9194214 ,  0.35874845,  0.19040156, 0.3208775 , -0.35759115,  0.3505183 ,  0.40907475, -0.76912757]])

(2)、ndarray.resize(new_shape)

 • 修改数组本身的形状(需要保持元素个数前后相同)

 • 行、列不进行互换

import numpy as np
# 创建符合正态分布的4只股票5天的涨跌幅数据
stock_change = np.random.normal(0,1,(4,5))
stock_change.resize([5,4])
stock_change

结果:

array([[ 0.15792575,  0.03846806,  0.79963431, -0.9257138 ],
       [-0.75702453, -0.06340506,  0.1291326 ,  0.51758646],
       [-0.56561087, -1.67614669,  1.25599246, -0.42486217],
       [-0.70856964,  1.31609015, -0.44146199, -0.4705198 ],
       [ 0.99493424, -0.29712594,  1.50867695,  1.08504095]])

此时stock_change已经改变。

(3)、ndarray.T

 • 数组的转置

 • 将数组的行、列进行互换

import numpy as np
# 创建符合正态分布的4只股票5天的涨跌幅数据
stock_change = np.random.normal(0,1,(4,5))
stock_change

结果:

array([[-0.02497122,  0.85785156,  0.04953773,  0.60534246, -0.39896299],
       [-1.03964377, -0.52913746,  0.61326595, -0.81828299, -1.15005551],
       [ 1.05733382, -0.7973569 ,  0.92890375,  0.81121572,  1.04946328],
       [-1.94349809,  0.38870125, -1.30038201,  0.58364877,  0.62247386]])

转置

stock_change.T

结果:

array([[-0.02497122, -1.03964377,  1.05733382, -1.94349809],
       [ 0.85785156, -0.52913746, -0.7973569 ,  0.38870125],
       [ 0.04953773,  0.61326595,  0.92890375, -1.30038201],
       [ 0.60534246, -0.81828299,  0.81121572,  0.58364877],
       [-0.39896299, -1.15005551,  1.04946328,  0.62247386]])

查看形状

stock_change.T.shape

结果:(5, 4)

7、类型修改

(1)、ndarray.astype(type)

返回修改了类型之后的数组

stock_change.astype(np.int32)

例子:

import numpy as np
# 创建符合正态分布的4只股票5天的涨跌幅数据
stock_change = np.random.normal(0,1,(4,5))
stock_change

结果:

array([[ 2.04610991e-01,  8.96789667e-01,  2.13669533e+00,
         2.07912938e-02, -7.41949060e-01],
       [ 1.12828295e+00,  1.04234294e+00, -2.04295814e+00,
         9.34883660e-01,  9.31591181e-02],
       [-1.07039839e+00,  1.55111547e-03,  5.29354564e-01,
         1.43609973e+00,  8.55359600e-01],
       [ 6.40414179e-01,  1.23344510e+00, -2.77598450e-01,
        -1.52502070e-01,  1.46172723e+00]])

修改为int32

stock_change.astype(np.int32)

结果:

array([[ 0,  0,  2,  0,  0],
       [ 1,  1, -2,  0,  0],
       [-1,  0,  0,  1,  0],
       [ 0,  1,  0,  0,  1]])

(2)、ndarray.tostring([order])或者ndarray.tobytes([order])

构造包含数组中原始数据字节的Python字节

import numpy as np
arr=np.array([[[1,2,3],[4,5,6]],[[12,3,34],[5,6,7]]])
arr.tostring()

结果:

b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x0c\x00\x00\x00\x03\x00\x00\x00"\x00\x00\x00\x05
\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00
'

数组去重

np.unique()

import numpy as np
temp =np.array([[1,2,3,4],[3,4,5,6]])
np.unique(temp)

结果:array([1, 2, 3, 4, 5, 6])

四、ndarray运算

如果想要操作符合某一条件的数据,应该怎么做?

1、逻辑运算

import numpy as np
# 生成10名同学,5门功课的数据
score = np.random.randint(40,100,(10,5)) # 最小值40,最大值100
score

结果:

array([[76, 98, 87, 57, 44],
       [64, 82, 55, 51, 58],
       [88, 55, 95, 72, 41],
       [48, 55, 48, 72, 98],
       [81, 82, 44, 82, 82],
       [77, 55, 50, 51, 96],
       [75, 51, 82, 77, 45],
       [86, 40, 89, 40, 48],
       [59, 91, 99, 70, 77],
       [55, 71, 78, 49, 57]])

取出最后4名司学的成绩,用于逻辑判断

# 取出最后4名司学的成绩,用于逻辑判断
test_score =score[6:,0:5]
test_score

结果:

array([[75, 51, 82, 77, 45],
       [86, 40, 89, 40, 48],
       [59, 91, 99, 70, 77],
       [55, 71, 78, 49, 57]])

逻辑判断

test_score >60

结果:

array([[ True, False,  True,  True, False],
       [ True, False,  True, False, False],
       [False,  True,  True,  True,  True],
       [False,  True,  True, False, False]])

B00L赋值,将满足条件的设置为指定的值-布尔索引

test_score[test_score>60]=1
test_score

结果:

array([[ 1, 51,  1,  1, 45],
       [ 1, 40,  1, 40, 48],
       [59,  1,  1,  1,  1],
       [55,  1,  1, 49, 57]])

2、通用判断函数

(1)、np.all()

import numpy as np
# 生成10名同学,5门功课的数据
score = np.random.randint(40,100,(10,5))
score

结果:

array([[68, 95, 66, 67, 84],
       [81, 72, 57, 97, 91],
       [71, 84, 47, 97, 83],
       [52, 94, 76, 63, 73],
       [82, 46, 83, 76, 45],
       [87, 41, 72, 63, 62],
       [93, 96, 41, 59, 96],
       [83, 80, 78, 65, 85],
       [96, 68, 88, 61, 73],
       [96, 74, 58, 84, 77]])

判断两名同学的成绩[0:2,:]是否全及格

np.all(score[0:2,:]>60)

结果:False

(2)、np.any()

判断前两名同学的成绩[0:2,:]是否有大于90分的

np.any(score[0:2,:]>90)

结果:True

3、np.where(三元运算符)

通过使用np.where能够进行更加复杂的运算

(1)、np.where()

import numpy as np
# 生成10名同学,5门功课的数据
score = np.random.randint(40,100,(10,5))
score

结果:

array([[86, 72, 79, 91, 92],
       [65, 95, 70, 42, 49],
       [96, 99, 85, 46, 89],
       [53, 60, 42, 43, 41],
       [96, 97, 97, 76, 81],
       [66, 86, 52, 58, 53],
       [75, 84, 58, 93, 76],
       [81, 89, 73, 56, 70],
       [45, 96, 72, 44, 59],
       [66, 63, 68, 68, 46]])

判断前四名学生,前四门课程中,成绩中大于60的置为1,否则为0

temp =score[:4,:4]
np.where(temp>60,1,0)

结果:

array([[1, 1, 1, 1],
       [1, 1, 1, 0],
       [1, 1, 1, 0],
       [0, 0, 0, 0]])

(2)、复合逻辑需要结合np.logical and和np.logical or使用

# 判断前四名学生,前四门课程中,成绩中大于60且小于90的换为1,否则为0
np.where(np.logical_and(temp>60,temp<90),1,0)

结果:

array([[1, 1, 1, 0],
       [1, 0, 1, 0],
       [0, 0, 1, 0],
       [0, 0, 0, 0]])

判断前四名学生,前四门课程中,成绩中大于90或小于60的换为1,否则为0

np.where(np.logical_or(temp>90,temp<60),1,0)

结果:

array([[0, 0, 0, 1],
       [0, 1, 0, 1],
       [1, 1, 0, 1],
       [1, 0, 1, 1]])

4、统计运算

如果想要知道涨幅或者跌幅最大的数据,应该怎么做?

(1)、在数据挖掘/机器学习领域,统计指标的值也是我们分析问题的一种方式。常用的指标如下:

median是中位数,mean是平均值,std是标准差,var是方差

案例:学生成绩统计运算

进行统计的时候,axis 轴的取值并不一定,Numpy中不同的API轴的值都不一样,在这里,axis 0代表列,axis 1代表行去进行统计

import numpy as np
# 生成10名同学,5门功课的数据
score = np.random.randint(40,100,(10,5))
score

结果:

array([[65, 87, 67, 42, 69],
       [87, 86, 99, 62, 50],
       [55, 87, 76, 49, 94],
       [47, 44, 89, 89, 87],
       [77, 82, 85, 90, 50],
       [79, 82, 94, 60, 70],
       [47, 45, 93, 76, 64],
       [69, 47, 42, 95, 90],
       [77, 69, 54, 40, 95],
       [40, 99, 97, 45, 89]])

获取统计信息

# 接下来对于前四名学生,进行一些统计运算
#指定列 去统计
temp =score[:4,0:5]
print("前四名学生,各科成绩的最大分:{}",format(np.max(temp,axis=0)))
print("前四名学生,各科成绩的最小分:{}",format(np.min(temp,axis=0)))
print("前四名学生,各科成绩波动情况:{}",format(np.std(temp,axis=0)))
print("前四名学生,各科成绩的平均分:{}",format(np.mean(temp,axis=0)))

结果:

前四名学生,各科成绩的最大分:{} [87 87 99 89 94]
前四名学生,各科成绩的最小分:{} [47 44 67 42 50]
前四名学生,各科成绩波动情况:{} [14.99166435 18.47971861 12.2142335  17.95132307 17.07337108]
前四名学生,各科成绩的平均分:{} [63.5  76.   82.75 60.5  75.  ]  

如果需要统计出某科最高分对应的是哪个同学?

np.argmax(temp,axis=)
np.argmin(temp, axis=)

print("前四名学生,各科成绩最高分对应的学生下标:{}",format(np.argmax(temp,axis=0)))

结果:

前四名学生,各科成绩最高分对应的学生下标:{} [1 0 1 3 2]

如果不传axis,则从所有的数中进行统计,如果传axis,则从行或列中进行统计。

五、数组间运算

目标
知道数组与数之间的运算
知道数组与数组之间的运算
说明数组间运算的广播机制

1、数组与数的运算

import numpy as np
arr=np.array([[1,2,3,2,1,4],[5,6,1,2,3,1]])
arr +1

结果:

array([[2, 3, 4, 3, 2, 5],
       [6, 7, 2, 3, 4, 2]])

除以2

arr / 2 

结果:

array([[0.5, 1. , 1.5, 1. , 0.5, 2. ],
       [2.5, 3. , 0.5, 1. , 1.5, 0.5]])

可以对比python列表的运算,看出区别

a=[1,2,3,4,5]
a*3

结果:[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

2、数组与数组的运算(加减乘除幂等)要满足广播机制

arr1=np.array([[1,2,3,2,1,4],[5,6,1,2,3,1]])
arr2=np.array([[1,2,3,4],[3,4,5,6]])
arr1+arr2

结果:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[58], line 3
      1 arr1=np.array([[1,2,3,2,1,4],[5,6,1,2,3,1]])
      2 arr2=np.array([[1,2,3,4],[3,4,5,6]])
----> 3 arr1+arr2

ValueError: operands could not be broadcast together with shapes (2,6) (2,4) 

我们让形状相等试试:

arr1=np.array([[1,2,3,2],[5,6,1,2]])
arr2=np.array([[1,2,3,4],[3,4,5,6]])
arr1+arr2

结果:

array([[ 2,  4,  6,  6],
       [ 8, 10,  6,  8]])

三维数组相加

arr1=np.array([[[1, 2, 3],[4, 5, 6]]])
arr2=np.array([[[1, 1, 1],[1, 1, 1]], [[2, 2, 2],[2, 2, 2]]])
print(arr1.shape) # (1, 2, 3)
print(arr2.shape) # (2, 2, 3)
print(arr1+arr2) # [[[2 3 4], [5 6 7]], [[3 4 5], [6 7 8]]]

广播机制

注意:这是Numpy中的一种人为设定的规则(机制),数学中并不存在这样的定义

数组在进行矢量化运算时,要求数组的形状是相等的。

当形状不相等的数组执行算术运算的时候,就会出现广播机制,该机制会对数组进行扩展,使数组的shape属性值一样,这样,就可以进行矢量化运算了。下面
通过一个例子进行说明:

arr1 = np.array([[0],[1],[2],[3]])
arr1.shape
#(41)
arr2 = np.array([1,2,3])
arr2.shape
#(3,)
arr1+arr2

结果:

array([[1, 2, 3],
       [2, 3, 4],
       [3, 4, 5],
       [4, 5, 6]])

上述代码中,数组arr1是4行1列,arr2是1行3列。这两个数组要进行相加,按照广播机制会对数组arr1和arr2都进行扩展,使得数组arr1和arr2都变成4行3列

广播机制实现了对两个或两个以上数组的运算,即使这些数组的shape不是完全相同的,只需要满足如下任意一个条件即可。

1.两个数组形状完全相同,即两个数组维度均为n*m。
2.其中一个数组的某一维度为1。其中一个数组维度为n*m,另一个数组维度为n*1或1*m

上面第一个数组为4行1列,第二个数组为1行3列,故符合第二个条件。

广播机制需要扩展维度小的数组,使得它与维度最大的数组的shape值相同,以便使用元素级函数或者运算符进行运算。

如果是下面这样,则不匹配:

A(1d array):10
B(1d array): 12

答:不满足上面的任何一个条件。

A (2d array): 2 x 1
B (3d array):8x4x3

答:不可以

思考:下面两个ndarray是否能够进行运算?

import numpy as np
arr1=np.array([[1,2,3,2,1,4],[5,6,1,2,3,1]])
arr2 = np.array([[1],[3]])
arr1+arr2

结果:

array([[2, 3, 4, 3, 2, 5],
       [8, 9, 4, 5, 6, 4]])

六、数学:矩阵

目标
  知道什么是矩阵和向量
  知道矩阵的加法,乘法
  知道矩阵的逆和转置
  应用np.matmul、np.dot实现矩阵运算

1、矩阵和向量

矩阵

矩阵,英文matrix,和array的区别矩阵必须是2维的,但是array可以是多维的。

如图:这个是 3x2 矩阵,即3行2列,如 m 为行,n 为列,那么 mxn 即 3x2

矩阵的维数即行数x列数

矩阵元素(矩阵项):

Aij 指第i行,第j列的元素,

向量

向量是一种特殊的矩阵,讲义中的向量一般都是列向量,下面展示的就是三维列 向量(3x1)。)

2、加法和标量乘法

矩阵的加法:行列数相等的可以加。例:

矩阵的乘法:每个元素都要乘。例:

组合算法也类似。

3、矩阵向量乘法

矩阵和向量的乘法如图:mxn 的矩阵乘以 nx1 的向量,得到的是 mx1 的向量。例:

1*1+3*5 = 16
4*1+0*5 = 4
2*1+1*5 = 7

矩阵乘法遵循准则:

(M行,N列)*(N行,L列)=(M行,L列)

4、矩阵乘法

矩阵乘法:mxn 矩阵乘以 nxo 矩阵,变成 mxo 矩阵。

举例:比如说现在有两个矩阵 A和 B,那 么它们的乘积就可以表示为图中所示的形式

5、矩阵乘法的性质

矩阵的乘法不满足交换律:AxB≠BxA

矩阵的乘法满足结合律。即:Ax(BxC)=(AxB)xC

单位矩阵:在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,我们称 这种矩阵为单位矩阵,它是个方阵,一般用I或者E表示,从 左上角到右下角的对角线(称为主对角线)上的元素均为1以外全都为 0。如:

6、逆、转置

矩阵的逆:如矩阵A是一个 mxm 矩阵(方阵),如果有逆矩阵,则:AA-1=A-1A=I。即一个矩阵乘以另一个矩阵等于单位矩阵

 

低阶矩阵求逆的方法:
1.待定系数法
2.初等变换
矩阵的转置:设A为 mxn 阶矩阵(即m行n列),第i行j列的元素是 a(i,j),即:A=a(i,j)

定义 A的转置为这样一个 nxm 阶矩阵 B,满足 B=a(j,i),即 b(i,j)=a(j,i)(B 的第i行第j列元素是A 的第j行第i列元素),记 AT=B。

直观来看,将 A的所有元素绕着一条从第1行第1列元素出发的右下方 45 度的射线作 镜面反转,即得到 A的转置。例:

7、矩阵运算

 矩阵乘法api

(1)、np.matmul 矩阵乘法

import numpy as np
a= np.array([[80, 86],
[82, 80],
[85, 78],
[90, 90],
[86, 82],
[82, 90],
[78, 80],
[92, 94]])
b= np.array([[0.7],[0.3]])
np.matmul(a,b)

结果:

array([[81.8],
       [81.4],
       [82.9],
       [90. ],
       [84.8],
       [84.4],
       [78.6],
       [92.6]])

(2)、np.dot 点乘

既支持矩阵与矩阵相乘,还支持矩阵和数字相乘

np.dot(a,b)

结果:

array([[81.8],
       [81.4],
       [82.9],
       [90. ],
       [84.8],
       [84.4],
       [78.6],
       [92.6]])

np.matmul和np.dot的区别:
二者都是矩阵乘法。 np.matmul中禁止矩阵与标量的乘法。在矢量乘矢量的內积运算中,np.matmul与np.dot没有区别。

逐元素乘法

import numpy as np

a = np.array([[1, 2, 3]])  # shape (1,3)
b = np.array([[4, 5, 6]])  # shape (1,3)

# 逐元素乘法(要求两个数组形状完全相同)
result = a * b
print(result)
# 输出:[[ 4 10 18]]

矩阵乘法与逐元素乘法的区别

矩阵乘法(Matrix Multiplication)和逐元素乘法(Element-wise Multiplication)在数学和编程中都是常见的操作,但应用场景和计算方式完全不同。用户可能在实际编程中遇到了混淆,特别是在使用像NumPy这样的库时,因为运算符不同(*和@),导致结果差异很大。

1、定义和运算规则

矩阵乘法遵循行乘列累加的规则,而逐元素乘法则是相同位置的元素相乘。例如,两个形状相同的矩阵进行逐元素乘法,结果每个元素都是原矩阵对应位置的乘积;而矩阵乘法需要满足第一个矩阵的列数等于第二个矩阵的行数,结果矩阵的行数等于第一个矩阵的行数,列数等于第二个矩阵的列数。

2、数学符号与编程实现

在数学中,矩阵乘法通常用点号或省略乘号表示,而在NumPy中使用`@`或`np.dot()`;逐元素乘法在数学中没有特殊符号,编程中用`*`表示

3、应用场景

矩阵乘法用于线性变换,如神经网络中的全连接层;逐元素乘法用于调整特定元素,如激活函数应用或调整权重。

4、形状要求

矩阵乘法有严格的形状匹配要求,而逐元素乘法要求两个矩阵形状完全相同或者满足广播机制的条件。

 

七、Numpy的常用方法

1、meshgrid

语法:

X,Y = numpy.meshgrid(x, y)

输入的x,y,就是网格点的横纵坐标列向量(非矩阵)
输出的X,Y,就是坐标矩阵。

案例1:

import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

x = np.array([[0, 1, 2], [0, 1, 2]]) # 2行3列的二维数组
y = np.array([[0, 0, 0], [1, 1, 1]]) # 2行3列的二维数组
plt.plot(x, y,
         color='red',  # 全部点设置为红色
         marker='.',  # 点的形状为圆点
         linestyle='')  # 线型为空,也即点与点之间不用线连接
plt.grid(True)
plt.show()

结果:

案例2:

import torch
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
# 用矩阵的形式(坐标矩阵)来批量描述这些点的坐标 x
= np.array([[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]) y = np.array([[0, 0, 0, 0], [1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]) plt.plot(x, y, marker='.', # 点的形状为圆点 markersize=10, # 点设置大一点,看着清楚 linestyle='-.') # 线型为点划线 plt.grid(True) plt.show()

结果:

坐标矩阵:横坐标矩阵x中的每个元素,与纵坐标矩阵y中对应位置元素,共同构成一个点的完整坐标。

那么问题来了,如果需要的图比较大,需要大量的网格点该怎么办呢?

案例3:

import torch
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
x = np.arange(0,10,0.1) # arange 创建等差数组 - 指定步长
print(x)
print(x.shape) # (200,) 表示1维数组

y = np.arange(0,10,0.1)
print(y)
print(y.shape)
xx,yy=np.meshgrid(x,y) # xx为横坐标矩阵,yy为纵坐标矩阵
print(xx) # xx为横坐标矩阵
print(yy) # yy为纵坐标矩阵
plt.plot(xx, yy,
         marker='.',  # 点的形状为圆点
         markersize=0.5,  # 点设置大一点,看着清楚
         linestyle='')  # 线型为点划线
plt.grid(True)
plt.show()

结果:

[0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.  1.1 1.2 1.3 1.4 1.5 1.6 1.7
 1.8 1.9 2.  2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.  3.1 3.2 3.3 3.4 3.5
 3.6 3.7 3.8 3.9 4.  4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5.  5.1 5.2 5.3
 5.4 5.5 5.6 5.7 5.8 5.9 6.  6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.  7.1
 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 8.  8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9
 9.  9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9]
(100,)
[0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.  1.1 1.2 1.3 1.4 1.5 1.6 1.7
 1.8 1.9 2.  2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.  3.1 3.2 3.3 3.4 3.5
 3.6 3.7 3.8 3.9 4.  4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5.  5.1 5.2 5.3
 5.4 5.5 5.6 5.7 5.8 5.9 6.  6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.  7.1
 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 8.  8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9
 9.  9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9]
(100,)
[[0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 ...
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]]
[[0.  0.  0.  ... 0.  0.  0. ]
 [0.1 0.1 0.1 ... 0.1 0.1 0.1]
 [0.2 0.2 0.2 ... 0.2 0.2 0.2]
 ...
 [9.7 9.7 9.7 ... 9.7 9.7 9.7]
 [9.8 9.8 9.8 ... 9.8 9.8 9.8]
 [9.9 9.9 9.9 ... 9.9 9.9 9.9]]

2、ravel()将多维数组转化为一维数组

案例1:

import torch
import numpy as np

a = np.arange(12).reshape(3,4)
print(a)
print(a.ravel())

结果:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[ 0  1  2  3  4  5  6  7  8  9 10 11]

案例2:

import torch
import numpy as np

x = np.arange(0,10,0.1) # arange 创建等差数组 - 指定步长
y = np.arange(0,10,0.1)
xx,yy=np.meshgrid(x,y) # xx为横坐标矩阵,yy为纵坐标矩阵
print(xx) # xx为横坐标矩阵
print(xx.ravel()) # 将横坐标矩阵转为一维数组
print(xx.ravel().shape)
print(yy) # yy为纵坐标矩阵
print(yy.ravel()) # 将纵坐标矩阵转为一维数组
print(yy.ravel().shape)

结果:

[[0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 ...
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]
 [0.  0.1 0.2 ... 9.7 9.8 9.9]]
[0.  0.1 0.2 ... 9.7 9.8 9.9]
(10000,)
[[0.  0.  0.  ... 0.  0.  0. ]
 [0.1 0.1 0.1 ... 0.1 0.1 0.1]
 [0.2 0.2 0.2 ... 0.2 0.2 0.2]
 ...
 [9.7 9.7 9.7 ... 9.7 9.7 9.7]
 [9.8 9.8 9.8 ... 9.8 9.8 9.8]
 [9.9 9.9 9.9 ... 9.9 9.9 9.9]]
[0.  0.  0.  ... 9.9 9.9 9.9]
(10000,)

3、np.c_是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等

案例1:

import torch
import numpy as np

d = np.array([7, 8, 9]) # 此处一维数组为列向量
e = np.array([1, 2, 3])
print(np.c_[d, e])

结果:

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

案例2:

import torch
import numpy as np

a = np.array([[1, 2, 3],[7,8,9]])
b=np.array([[4,5,6],[1,2,3]])
print(np.c_[a, b])

结果:

[[1 2 3 4 5 6]
 [7 8 9 1 2 3]]

4、np.r_是按列连接两个连接,就是把两矩阵上下相加,要求列数相等

案例1:

import torch
import numpy as np

d = np.array([7, 8, 9]) # 列向量
e = np.array([1, 2, 3])
print(np.r_[d, e])

结果:[7 8 9 1 2 3]

案例2:

import torch
import numpy as np

a = np.array([[1, 2, 3], [7, 8, 9]])
b=np.array([[4, 5, 6], [1, 2, 3]])
print(np.r_[a, b])

结果:

[[1 2 3]
 [7 8 9]
 [4 5 6]
 [1 2 3]]

5、view()浅拷贝

import numpy as np
A = np.random.randint(0, 10, (3, 4))
print(A)
import torch
B = torch.Tensor(A)
print(A.shape[1])
C = A.view() # 浅拷贝
print(C)
D = A.view(np.byte)
print(D)
B = B.view(-1, 2, 2)
print(B)

结果:

[[4 6 5 6]
 [8 5 2 8]
 [1 0 7 6]]
4
[[4 6 5 6]
 [8 5 2 8]
 [1 0 7 6]]
[[4 0 0 0 6 0 0 0 5 0 0 0 6 0 0 0]
 [8 0 0 0 5 0 0 0 2 0 0 0 8 0 0 0]
 [1 0 0 0 0 0 0 0 7 0 0 0 6 0 0 0]]
tensor([[[4., 6.],
         [5., 6.]],

        [[8., 5.],
         [2., 8.]],

        [[1., 0.],
         [7., 6.]]])

关键概念解析

  1. 浅拷贝 (img2 = img.view())

    • 共享数据内存img2 是原数组的视图,与 img 共享同一块数据内存。

    • 同步修改:修改 img 的内容会直接影响 img2,反之亦然。

    • 适用场景:需要高效访问同一数据的不同表示(如类型转换),但需谨慎处理数据修改。

  2. 深拷贝 (img3 = img.copy())

    • 独立数据内存img3 是原数组的完整副本,数据与原数组完全隔离。

    • 数据隔离:修改 img 或 img3 不会相互影响。

    • 适用场景:需要完全独立的图像副本,避免意外修改传播

  • 性能权衡:浅拷贝节省内存但风险高;深拷贝安全但占用更多内存。

下面通过案例来看看浅拷贝和深拷贝的区别

import cv2
import numpy as np

img = cv2.imread('./dog.jpg') # 将图像转为数组

#浅拷贝:创建视图(共享数据)
img2 = img.view()

#深拷贝:创建独立副本(数据隔离)
img3 = img.copy()
# 修改原图 img 的局部区域(BGR 格式的蓝色) 表示从索引为10的行开始到100行,列从索引为10的列开始到索引为100的列
img[10:100, 10:100] = [0, 0, 255] # OpenCV 使用 BGR 通道顺序,[0, 0, 255] 表示蓝色(而非红色)

cv2.imshow('img', img) # 显示被修改的原图(带蓝色矩形)
cv2.imshow('img2', img2) # 浅拷贝,共享数据,同样显示蓝色矩形
cv2.imshow('img3', img3) # 深拷贝,保持原始图像,无矩形

cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows() # 关闭所有窗口

结果:

6、np.full

numpy.full(shape, fill_value, dtype=None, order=‘C’)

其中:

shape 参数指定了输出数组的形状,可以是一个整数、元组或列表,表示数组的维度大小。
fill_value 参数用于指定填充数组的元素值,可以是任何Python数据类型,包括数值、字符串、布尔值等。
dtype 参数用于指定输出数组的数据类型,如果未指定,则根据 fill_value 的类型自动推断。
order 参数用于指定数组的存储顺序,默认为 ‘C’,表示按行优先顺序存储。

import numpy as np
arr1 = np.full((3, 3), True)
print(arr1)
arr2 = np.full((2, 2), 10)
print(arr2)
arr3 = np.full((2, 2), [1, 2])
print(arr3)

结果:

[[ 1.  1.  1.]
 [ 1.  1.  1.]
 [ 1.  1.  1.]]
[[ 10.  10.]
 [ 10.  10.]]
[[ 1.  2.]
 [ 1.  2.]]

7、np.hstack()

np.hstack()函数将两个二维数组在水平方向上堆叠起来

import numpy as np
# 创建两个二维数组,确保它们具有相同的行数
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
# 使用np.hstack()函数在水平方向上堆叠数组
c = np.hstack((a, b))
print(c)

结果:

[[1 2 5 6]
 [3 4 7 8]]

对于二维数组,二维数组中有两个一维数组,将一维数组中的元素拼接起来。

对于三维数组,三维数组中有二维数组,二维数组中是一维数组,将一维数组拼接起来

import numpy as np
c = np.array([[[1,2,3],[4,5,6]],[[1,2,3],[4,5,6]]])
d = np.array([[[1,1,1],[2,2,2]],[[3,3,3],[4,4,4]]])
print(np.hstack((c, d)))

结果:

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

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

对于图像,相当于将两个图片左右并排着排列。

8、np.repeat()

import numpy as np
a = np.array([1, 2, 3])
print(np.repeat(a, 3))  # [1 1 1 2 2 2 3 3 3]
b = np.array([[1, 2, 3], [6 ,7, 8]])
print(b)
# repeat如果不指定维度,则二维变成一维的了
print(np.repeat(b, 3)) # [1 1 1 2 2 2 3 3 3 6 6 6 7 7 7 8 8 8]
c = np.repeat(b, 3, 0)
print(c) # 对0维进行重复
print(np.repeat(c, 3, 1))

结果:

[1 1 1 2 2 2 3 3 3]
[[1 2 3]
 [6 7 8]]
[1 1 1 2 2 2 3 3 3 6 6 6 7 7 7 8 8 8]
[[1 2 3]
 [1 2 3]
 [1 2 3]
 [6 7 8]
 [6 7 8]
 [6 7 8]]
[[1 1 1 2 2 2 3 3 3]
 [1 1 1 2 2 2 3 3 3]
 [1 1 1 2 2 2 3 3 3]
 [6 6 6 7 7 7 8 8 8]
 [6 6 6 7 7 7 8 8 8]
 [6 6 6 7 7 7 8 8 8]]

9、对一维数组进行sigmoid函数运算

import numpy as np

# ====================
# 基础实现(适合理解原理)
# ====================
def sigmoid_basic(x_array):
    """
    Sigmoid函数基础实现
    公式:sigmoid(x) = 1 / (1 + e^(-x))
    """
    result = []
    for x in x_array:
        result.append(1 / (1 + np.exp(-x)))
    return np.array(result)

# ====================
# 向量化实现(推荐,高效简洁)
# ====================
def sigmoid_vectorized(x_array):
    """
    使用NumPy的向量化运算实现
    适用于处理数组/矩阵的高效计算
    """
    return 1 / (1 + np.exp(-x_array))

# ====================
# 示例使用
# ====================
if __name__ == "__main__":
    # 创建示例数组
    input_array = np.array([-2, -1, 0, 1, 2])
    
    # 基础实现
    basic_result = sigmoid_basic(input_array)
    
    # 向量化实现
    vectorized_result = sigmoid_vectorized(input_array)
    
    # 结果对比
    print("输入数组:", input_array)
    print("基础实现结果:", basic_result.round(4))
    print("向量化实现结果:", vectorized_result.round(4))
    print("结果是否一致:", np.allclose(basic_result, vectorized_result))

结果:

输入数组: [-2 -1  0  1  2]
基础实现结果: [0.1192 0.2689 0.5    0.7311 0.8808]
向量化实现结果: [0.1192 0.2689 0.5    0.7311 0.8808]
结果是否一致: True

 

posted on 2025-02-07 17:35  周文豪  阅读(79)  评论(0)    收藏  举报