本文目录
  1. Numpy
  2. 一、【了解】Numpy优势
    1. Numpy介绍
    2. ndarray介绍
    3. ndarray与Python原生list运算效率对比
    4. ndarray的优势
      1. 内存块风格
      2. ndarray支持并行化运算(向量化运算)
      3. 效率远高于纯Python代码
  3. 二、ndarray数据结构
    1. 掌握内容
    2. ndarray的属性
    3. ndarray的形状
    4. ndarray的类型
  4. 三、基本操作
    1. 掌握内容
    2. 生成数组的方法
      1. 生成0和1的数组
      2. 从现有数组生成
      3. 生成固定范围的数组
        1. np.linspace (start, stop, num, endpoint)
        2. np.arange(start,stop, step, dtype)
        3. np.logspace(start,stop, num)
      4. 生成随机数组
        1. 使用模块介绍
    3. 数组的索引、切片
    4. 形状修改
      1. ndarray.reshape(shape)
      2. np.resize(数组对象, new_shape)
      3. ndarray.T
    5. 类型修改
      1. ndarray.astype(type)
      2. ndarray.tobytes()
    6. 数组的去重
      1. np.unique()
  5. 四、ndarray运算
    1. 掌握内容
    2. 逻辑运算
    3. 通用判断函数
    4. np.where(三元运算符)
    5. 统计运算
      1. 统计指标
      2. 案例:学生成绩统计运算
  6. 五、数组间运算
    1. 掌握内容
    2. 数组与数的运算
    3. 矩阵运算
    4. 【了解】数组与数组的运算
      1. 思考
      2. 广播机制

分类: python | 标签: numpy

numpy

发表于: 2026-05-25 16:49:28 | 字数统计: 5.8k | 阅读时长预计: 28分钟

Numpy

一、【了解】Numpy优势

Numpy介绍

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

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

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

ndarray:n dimension array

ndarray介绍

NumPy provides an N-dimensional array type, the ndarray, 
which describes a collection of “items” of the same type.

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

image-20260525165020850

用ndarray进行存储:

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

返回结果:

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]])

提问:

使用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())

start = time.time()
sum(a)
print("python运行耗时:",time.time()-start)

print("-"*50)

b = np.array(a)

start = time.time()
np.sum(b)
print("numpy运行耗时:",time.time()-start)

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

python运行耗时: 0.7574722766876221
--------------------------------------------------
numpy运行耗时: 0.11530804634094238

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

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

计算量大

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

思考:

ndarray为什么可以这么快?

ndarray的优势

内存块风格

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

image-20260525165059671

从图中我们可以看出ndarray在存储数据的时候,数据与数据的地址都是连续的,这样就给使得批量操作数组元素时速度更快。

这是因为ndarray中的所有元素的类型都是相同的,而Python列表中的元素类型是任意的,所以ndarray在存储元素时内存可以连续,而python原生list就只能通过寻址方式找到下一个元素,这虽然也导致了在通用性能方面Numpy的ndarray不及Python原生list,但在科学计算中,Numpy的ndarray就可以省掉很多循环语句,代码使用方面比Python原生list简单的多。

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

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

效率远高于纯Python代码

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

二、ndarray数据结构

掌握内容

属性
    shape
    dtype

ndarray的属性

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

属性名字属性解释
ndarray.shape数组维度的元组
ndarray.ndim数组维数
ndarray.size数组中的元素数量(个数)
ndarray.itemsize一个数组元素的长度(字节)
ndarray.dtype数组元素的类型

ndarray的形状

首先创建一些数组。

arr1 = np.array([11, 22, 33, 44])
print("arr1的维度个数:",arr1.ndim)
print("arr1的形状:",arr1.shape)    # 4个元素
print("arr1的元素数据类型:",arr1.dtype)


arr2 = np.array([
    [1,2,3],
    [4,5,6]
])
print("arr2的维度个数:",arr2.ndim) # 2
print("arr2的形状:",arr2.shape) # 2行3列
print("arr2的元素数据类型:",arr2.dtype)


arr3 = np.array([
    [
        [1,2,3],
        [4,5,6]
    ],
    [
        [1,2,3],
        [4,5,6]
    ]
])
print("arr3的元素个数:",arr3.size)   # 12个元素
print("arr3的维度个数:",arr3.ndim) # 3
print("arr3的形状:",arr3.shape) # 2,2,3
print("arr3的元素数据类型:",arr3.dtype)

如何理解数组的形状?

二维数组:

image-20260525165119841

三维数组:

image-20260525165131498

ndarray的类型

名称描述简写
np.bool用一个字节存储的布尔类型(True或False)‘b’
np.int8tinyint一个字节大小,-128 至 127‘i’
np.int16smallint整数,-32768 至 32767‘i2’
np.int32int整数,-2^31​ 至 2^32 -1‘i4’
np.int64bigint整数,-2^63 至 2^63 - 1‘i8’
np.uint8tinyint unsigned==无符号==整数,0 至 255‘u’
np.uint16smallint unsigned无符号整数,0 至 65535‘u2’
np.uint32无符号整数,0 至 2^32 - 1‘u4’
np.uint64无符号整数,0 至 2^64 - 1‘u8’
np.float16半精度浮点数:16位,正负号1位,指数5位,精度10位‘f2’
np.float32float单精度浮点数:32位,正负号1位,指数8位,精度23位‘f4’
np.float64double双精度浮点数:64位,正负号1位,指数11位,精度52位‘f8’
np.complex64复数,分别用两个32位浮点数表示实部和虚部‘c8’
np.complex128复数,分别用两个64位浮点数表示实部和虚部‘c16’
np.object_python对象‘O’
np.string_字符串‘S’
np.unicode_unicode类型(字符串)‘U’

常用的几个:

无符号:只有正数,没有负数,也就是>=0

**np.int32**:32位整数,是最常用的整数类型,适用于大多数整数运算。

**np.float64**:64位浮点数,是默认的浮点数类型,广泛用于科学计算。

**np.bool_**:布尔类型,用于表示True或False,常用于条件判断和逻辑操作。

**np.string_/np.unicode_**:定长字符串类型,常用于二进制数据 或 多语言文本数据

**np.object_**:用于存储任意Python对象,特别是在处理混合类型数据或需要灵活性的时候。

np.string_只支持ASCII编码,不支持Unicode,而np.unicode_支持Unicode字符。

np.string_更适合处理旧有的二进制数据,而np.unicode_更适合处理现代文本数据。

创建数组的时候指定类型

# 小数转成整数,直接舍弃所有的小数内容,只保留整数部分
arr5 = np.array([[1.999, 2.123, 3.456],[4.5, 5.6, 6.7]], dtype=np.int32)
arr5,arr5.dtype

# S15:S是string的首字母;15表示的是最长字符串的长度
arr6 = np.array(['python', 'tensorflow', 'scikit-learnabc', 'numpy'], dtype=np.string_)
arr6,arr6.dtype

arr7 = np.array([[1, 2, 3],[4, 5, 6]], dtype=np.double) # double就是float64
arr7,arr7.dtype
  • 注意:若不指定,整数默认int64,小数默认float64

三、基本操作

掌握内容

生成数组
    ones
    zeros
    array
    arange
    np.random.randint
    
索引切片
    二维数组[行索引开始:行索引结束:步长, 列索引开始:列索引结束:步长],注意是左闭右开的区间
    
形状修改
    reshape
    T
    
去重
    unique

生成数组的方法

生成0和1的数组

  • **np.ones(shape, dtype)**:创建形状指定的,并且元素值都为1的数组
  • np.ones_like(a, dtype) :用于创建一个与数组 a 形状相同且所有元素都为1的数组的函数。
  • **np.zeros(shape, dtype)**:创建形状指定的,并且元素值都为0的数组
  • np.zeros_like(a, dtype) : :用于创建一个与数组 a 形状相同且所有元素都为0的数组的函数。
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(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.]])

从现有数组生成

  • **np.array(object, dtype)**:新建一个,相当于深拷贝
  • **np.asarray(a, dtype)**:没有新建,引用指向同一片内存区域,相当于浅拷贝
import numpy as np

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

# 从现有的数组当中创建
a1 = np.array(a)
id(a1)

# 相当于浅拷贝,并没有真正的创建一个新的
a2 = np.asarray(a)
id(a2)

# 修改值
a[1,1] = 99

print(f"a--->{a}")
print(f"a1--->{a1}")
print(f"a2--->{a2}")

生成固定范围的数组

类似于之前讲过的range()

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

  • 创建等差数组 — ==指定数量==
  • 参数:
    • start:序列的起始值
    • stop:序列的终止值
    • num:要生成的等间隔样例数量,默认为50
    • endpoint:序列中是否包含stop值,默认为True
"""
    参数解释:
        start:开始值
        stop:结束值
        endpoint:stop是否包含在内
        num:等差数组中元素的个数
"""
arr9 = np.linspace(start=1,stop=10,num=10,endpoint=True) 
arr9    # 产生的是 10行1列 的 列向量(既有大小,也有方向)

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

  • 创建等差数组 — ==指定步长==
  • 参数
    • step:步长,默认值为1
# np.arange:使用和参数与range函数完全一样
arr10 = np.arange(start=1,stop=6,step=2)
arr10

np.logspace(start,stop, num)

  • 创建等比数列

  • 参数:

    • num:要生成的等比数列数量,默认为50
"""
    参数解释: 
        start:开始值
        stop:结束值
        endpoint:stop是否包含在内
        num:等差数组中元素的个数
        base:幂次方的底。a**x,也就是这里的a的值
"""
arr11 = np.logspace(start=0,stop=5,num=6,endpoint=True,base=10)
arr11 = np.logspace(start=0,stop=5,num=6,endpoint=True,base=2)
arr11

生成随机数组

使用模块介绍

  • np.random模块
# 设置随机数种子
np.random.seed(523)

"""
    np.random.rand():产生0-1的随机小数;结果数据是均匀分布的
"""
arr12 = np.random.rand(2,4)
arr12


"""
    np.random.randint:生成指定范围的随机整数
    参数解释:
        low:范围下限。包含
        high:范围上限。不包含
        size:数组的形状
"""
arr13 = np.random.randint(low=1,high=7,size=(2,3,4))
arr13


"""
    np.random.randn:生成随机小数,结果符合标准正态分布
    标准正态分布:也称之为高斯分布。均值mean=0,标准差std=1
"""
arr14 = np.random.randn(10,10,10)
arr14

数组的索引、切片

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

  • 直接进行索引,切片
  • 二维数组[行索引开始:行索引结束:步长, 列索引开始:列索引结束:步长],含头不含尾

基本索引:

import numpy as np

# 创建一个 2x3 的数组
arr = np.array([[1, 2, 3], [4, 5, 6]])

# 访问第1行第2列的元素(注意:索引从0开始)
element = arr[0, 1]
print("第1行第2列的元素:", element)  # 输出: 2

# 访问第2行第3列的元素
element = arr[1, 2]
print("第2行第3列的元素:", element)  # 输出: 6

切片操作

二维数组:可以通过 [row, column] 进行索引和切片,提取特定的行、列或子矩阵。

三维数组:可以通过 [depth, row, column] 进行索引和切片,提取特定的层、行、列或子阵列。

import numpy as np

# 创建一个 7x7 的数组
# 二维数组[行索引开始:行索引结束:步长, 列索引开始:列索引结束:步长]
arr = np.array(
    [
        [10, 2, 3, 73, 8, 7, 6],
        [11, 5, 6, 74, 8, 9, 10],
        [22, 5, 6, 75, 8, 9, 10],
        [33, 5, 6, 76, 8, 9, 10],
        [44, 5, 6, 77, 8, 9, 10],
        [55, 5, 6, 78, 8, 9, 10],
        [66, 5, 6, 79, 8, 9, 10],
    ]
)

sub_array = arr[1:6:2, 0:6:3]
print(sub_array)

# 提取第1行和第2行的所有列
sub_array = arr[0:2, :]
print("第1行和第2行的所有列:\n", sub_array)

# 提取第2列和第3列的所有行
sub_array = arr[:, 1:3]
print("第2列和第3列的所有行:\n", sub_array)

# 提取第1行第2列到第3列的元素
sub_array = arr[0, 1:3]
print("第1行第2列到第3列的元素:", sub_array)
  • 三维数组索引方式:
# 三维
a1 = np.array([[[1,2,3],[4,5,6]], [[12,3,34],[5,6,7]]])
# 返回结果
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[12,  3, 34],
        [ 5,  6,  7]]])

# 索引、切片
sub_array = a1[0, 0, 1]
print(sub_array) # 输出: 2

形状修改

ndarray.reshape(shape)

  • 修改前后元素个数不能变
arr2 = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
arr2

# reshape前后的元素个数不能变,不能变多不能变少
# reshape中的-1表示Numpy自动的帮我们计算形状
# reshape还可以升维或者降维
arr3 = arr2.reshape(3,3)
arr3 = arr2.reshape(1,9)
arr3 = arr2.reshape(1,-1)
# arr3 = arr2.reshape(1,3,3) # 升维
arr3 = arr2.reshape(9,1,1) # 升维

# 下面的都是错误的写法
# arr3 = arr2.reshape(2,4)      # 元素个数少了
# arr3 = arr2.reshape(2,5)      # 元素个数多了
# arr3 = arr2.reshape(2,4.5)    # 传递小数

arr3

np.resize(数组对象, new_shape)

  • 修改前后元素个数可以不同
# 【了解】resize:调整形状前后的元素个数可以不一致
arr3 = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
arr3

# 注意:resize需要通过np来进行调用,不能直接通过具体的array数组调用
arr4 = np.resize(a=arr3,new_shape=(3,3))
arr4 = np.resize(a=arr3,new_shape=(3,4))
arr4 = np.resize(a=arr3,new_shape=(3,2))
arr4

ndarray.T

  • 数组的转置
  • 将数组的行、列进行互换
import numpy as np

# 创建一个 2x3 的二维数组
arr = np.array([[1, 2, 3],
                [4, 5, 6]])

# 对数组进行转置
transposed_arr = arr.T

print("原始数组:\n", arr)
print("转置后的数组:\n", transposed_arr)

运行结果
原始数组:
 [[1 2 3]
  [4 5 6]]
转置后的数组:
 [[1 4]
  [2 5]
  [3 6]]

类型修改

ndarray.astype(type)

  • 返回修改了类型之后的数组
import numpy as np

# 创建一个浮点数类型的数组
arr = np.array([1.1, 2.2, 3.3, 4.4, 5.5])

# 使用 .astype(np.int32) 将数组的元素类型转换为 int32
arr_int32 = arr.astype(np.int32)

print("原始数组:", arr)
print("原始数组的类型:", arr.dtype)

print("转换后的数组:", arr_int32)
print("转换后的数组类型:", arr_int32.dtype)

ndarray.tobytes()

  • 构造包含数组中原始数据字节的Python字节
arr = np.array([[[1, 2, 3], [4, 5, 6]], [[12, 3, 34], [5, 6, 7]]])
arr.tobytes()

为什么转二进制?方便网络传输

数组的去重

np.unique()

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

四、ndarray运算

掌握内容

all
any
argmax:与idxmax的作用完全一致,取最大值对应的索引

逻辑运算

# 过滤出分数超过60分,并且小于90分的成绩
score = np.random.randint(40, 100, (10, 5))
six_stu = score[6:, :]
six_stu

# 过滤出分数超过60分,并且小于90分的成绩。将对应成绩设置为1
six_stu[ (six_stu>60) & (six_stu<90)] = 1
six_stu

通用判断函数

  • np.all()

    当你需要检查数组中的所有元素是否都满足条件时使用,如果所有元素都满足条件,返回 True,否则返回 False

  • np.any()

    当你需要检查数组中是否至少有一个元素满足条件时使用,如果有一个元素满足条件,返回 True,否则返回 False

# 判断前4名同学的成绩[0:4, :]是否全及格
four_stu = score2[0:4, :]
four_stu>=60

# 所有学生的所有成绩,全部都>=60分的时候,结果才是True;否则,只要出现一个成绩<60,结果就是False
np.all(four_stu>=60)

# 判断前4名同学的成绩[0:4, :]是否有大于90分的
# 只要能够找出一个值满足条件,结果就是True;否则,全部都不满足的时候才是False
np.any(four_stu>90)

np.where(三元运算符)

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

  • np.where() => 类似Python中的if…else结构

  • 复合逻辑需要结合np.logical_and和np.logical_or使用

np.random.seed(523)
score3 = np.random.randint(40, 100, (10, 5))
score3

# 判断前四名学生,前四门课程中,成绩中大于60的置为1,否则为0
arr1 = np.where(score3[:4, :4]>60, 1,0)
arr1

# 判断前四名学生,前四门课程中,成绩中大于60且小于90的换为1,否则为0
# 写法一:推荐
arr2 = np.where((score3[:4, :4]>60) & (score3[:4, :4]<90), 1,0)

# 写法二:使用函数包起来
arr2 = np.where(np.logical_and(score3[:4, :4]>60, score3[:4, :4]<90), 1,0)
arr2

# 判断前四名学生,前四门课程中,成绩中大于90或小于60的换为1,否则为0
# 写法一:推荐
arr3 = np.where((score3[:4, :4]<60) | (score3[:4, :4]>90), 1,0)

# 写法二:使用函数包起来
arr3 = np.where(np.logical_or(score3[:4, :4]<60, score3[:4, :4]>90), 1,0)
arr3

统计运算

如果想要知道学生成绩最大的分数,或者做小分数应该怎么做?

统计指标

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

  • min(a, axis)
    • Return the minimum of an array or minimum along an axis.
  • max(a, axis])
    • Return the maximum of an array or maximum along an axis.
  • median(a, axis)
    • Compute the median along the specified axis.
  • mean(a, axis, dtype)
    • Compute the arithmetic mean along the specified axis.
  • std(a, axis, dtype)
    • Compute the standard deviation along the specified axis.
  • var(a, axis, dtype)
    • Compute the variance along the specified axis.

var方差是衡量数据点离平均值的平方偏差程度。方差的值总是非负的,方差越大,数据越分散。

std标准方差是衡量数据点离平均值的平均偏差程度。值越小,数据越集中;值越大,数据越分散。

案例:学生成绩统计运算

score = np.random.randint(40, 100, (10, 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)))

==axis=0==:表示沿着行的方向(垂直向下)跨越,对每一列进行操作,结果是行数被修改了。

==axis=1==:表示沿着列的方向(水平向右)跨越,对每一行进行操作,结果是列数被修改了。

==该参数的使用和参数值含义,与Pandas中完全一样。==

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

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

五、数组间运算

掌握内容

矩阵运算
    np.matmul(矩阵A, 矩阵B)   或者  矩阵A@矩阵B
    前提:前一个矩阵的列数,与后一个矩阵的行数相同,才能计算。矩阵运算没有广播机制

数组与数的运算

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

# 可以对比python列表的运算,看出区别 => 列表是整体操作,numpy是每个元素单独操作
a = [1, 2, 3, 4, 5]
a * 3

矩阵运算

矩阵运算的前提:前一个矩阵的列数,与后一个矩阵的行数相同,才能计算

"""
    矩阵乘法运算总结:
        1- A @ B = C,  A的列数 必须 与 B的行数 相同
        2- A[m行,n列] B[p行,q列],那么结果C的形状是[m行,q列]
"""

np.random.seed(520)
arr3 = np.random.randint(low=1,high=5,size=(2,4))
arr3


np.random.seed(520)
arr4 = np.random.randint(low=1,high=5,size=(4,3))
arr4


# 写法一:推荐。使用@符号
result_1 = arr3 @ arr4
# 写法二:使用matmul函数。matrix multiply
result_1 = np.matmul(arr3, arr4)
result_1

【了解】数组与数组的运算

广播机制总结:
    1- 是Numpy底层内部自动进行的一种机制,我们无法人为的控制
    2- 价值:实现形状不同数组之间的运算,核心就是通过广播机制自动的达到数组形状完全相同的前提要求

思考

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

上面这个能进行加法运算吗,结果是不行的!

广播机制

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

arr1 = np.array([[0],[1],[2],[3]])  # 4 x 1
arr1.shape
# (4, 1)

arr2 = np.array([1,2,3])  # 1 x 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列。

下面通过一张图来描述广播机制扩展数组的过程:

image-20190620005224076

什么情况下才能进行广播机制:

  • 形状完全相等
    两个数组在该维度上的大小完全相同。
import numpy as np

# 数组 A: 形状 (2, 3)
A = np.array([[1, 2, 3],
              [4, 5, 6]])

# 数组 B: 形状 (2, 3)
B = np.array([[10, 20, 30],
              [40, 50, 60]])

# 运算
result = A + B

print("结果:\n", result)

运行结果:
[[11 22 33]
[44 55 66]]

  • 其中至少一个维度大小为 1
    两个数组中,至少有一个在该维度上的大小为 1。NumPy 会自动将这个大小为 1 的维度“拉伸”以匹配另一个数组。
import numpy as np

# 数组 A: 形状 (2, 3)
A = np.array([[1, 2, 3],
              [4, 5, 6]])

# 数组 B: 形状 (1, 3)
B = np.array([[10, 20, 30]])

# 运算
result = A + B

print("结果:\n", result)

运行结果:

[[11 22 33]
[14 25 36]]

  • 其中一个数组缺少该维度
    如果两个数组的维度数量不同,维度较少的数组在前面会被视为用 1 来填充,然后再进行比较。
    • 例如:形状为 (3, 4) 的数组和形状为 (4,) 的数组比较时,(4,) 会被视为 (1, 4),因此它们是兼容的。
import numpy as np

# 1. 创建一个形状: (3, 4)
arr1 = np.array([
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12]
])

# 2. 创建一个形状: (1,4)
arr2 = np.array([10, 20, 30, 40])

# 3. 进行加法运算
result = arr1 + arr2

print("运算结果:\n", result)

运行结果:
[[11 22 33 44]
[15 26 37 48]
[19 30 41 52]]

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

arr1 = np.array([[1, 2, 3, 2, 1, 4], [5, 6, 1, 2, 3, 1]])   # (2, 6)
arr2 = np.array([[1], [3]])  # (2, 1)
------ 本文结束,感谢您的阅读 ------
本文作者: 程序员青阳
版权声明: 本文采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。