全部文章

5-NumPy

NumPy历史

  • 1995年Jim HugUNin开发了Numeric
  • 随后Numarray包诞生
  • Travis Oliphants整合Numeric和Numarray,开发NumPy,2006年第一个版本诞生
  • 使用Anaconda发行版的Python,已经安装好NumPy模块,所以可以不用再安装NumPy模块了。
  • 依照标准的NumPy标准,习惯使用import numpy as np的方式导入该模块。

NumPy简介

NumPy:Numerical Python,即数值Python包,是Python进行科学计算的一个基础包,用于快速处理任意维度的数组。一定需要先掌握该包的主要使用方式。

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

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

官网:http://www.numpy.org/

官方文档:https://docs.scipy.org/doc/numpy/user/index.html

NumPy模块是Python的一种开源的数值计算扩展,是一个用python实现的科学计算包,主要包括:

  • ndarray(N-dimensional array object):一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组
  • ufunc(universal function object):用于对整组数据进行快速运算的标准数学函数
  • 实用的线性代数、傅里叶变换和随机数生成函数。

NumPy和稀疏矩阵的运算包Scipy配合使用更加方便。

ndarray介绍

NumPy的数组类被称作ndarray,它是numpy的核心数据结构。通常被称作数组(或多维数组)。

NumPy provides an N-dimensional array type, the ndarray,which describes a collection of “items” of the same type.
NumPy提供了一个N维数组类型ndarray,它描述了相同类型的“items”的集合。
 
是一种由相同类型的元素组成的多维数组,元素数量是事先给定好的:数组一旦创建完成,其​​形状(shape)和元素数量无法直接修改​​。

注意numpy.array和标准Python库类array.array并不相同,后者只处理一维数组和提供少量功能。

 

特性​​ ​​Python 列表​​ ​​NumPy 数组​​
元素类型 可以混合不同类型(如 [1, "a"] 所有元素必须类型相同
内存存储 非连续存储 连续存储,支持高效计算
​​动态扩展性​​ 支持动态增删元素(如 append() ​​元素数量固定​​,不可直接修改

元素的数据类型dtype(data-type)对象来指定,每个ndarray只有一种dtype类型

ndarray的大小固定,创建好数组后数组大小是不会再发生改变的

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

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

在这里我们通过一段代码运行来体会到ndarray的好处
import random
import numpy as np
# 构建普通数组
list=[]
for i in range(100000000):
    list.append(random.random())
    
#构建多维数组
arr=np.array(list)
# 魔法命令统计传统数组求和耗时
%time sum1=sum(list)
# 统计多维数组求和耗时
%time sum2=np.sum(arr)

print(sum1,sum2)
其中第一个时间显示的是使用原生Python计算时间,第二个内容是使用numpy计算时间:
CPU times: user 852 ms, sys: 262 ms, total: 1.11 s
Wall time: 1.13 s
CPU times: user 133 ms, sys: 653 µs, total: 133 ms
Wall time: 134 ms
从中我们看到ndarray的计算速度要快很多,节约了时间。
机器学习的最大特点就是大量的数据运算,那么如果没有一个快速的解决方案,那可能现在python也在机器学习领域达不到好的效果。
思考:
ndarray为什么可以这么快?

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代码。

小结

一个开源的Python科学计算库
计算起来要比python简洁高效
Numpy使用ndarray对象来处理多维数组
NumPy提供了一个N维数组类型ndarray,它描述了相同类型的“items”的集合。
ndarray的优势【掌握】
  • 内存块风格
    • list -- 分离式存储,存储内容多样化
    • ndarray -- 一体式存储,存储类型必须一样
  • ndarray支持并行化运算(向量化运算)
  • ndarray底层是用C语言写的,效率更高,释放了GIL

创建ndarray多维数组的函数

可以通过numpy模块中的常用的几个函数进行创建ndarray多维数组对象,主要函数如下:

  • array函数:接收一个普通的python序列,并将其转换为ndarray
  • zeros函数:创建指定长度或者形状的全零数组。
  • ones函数:创建指定长度或者形状的全1数组。
  • empty函数:创建一个没有任何具体值的数组(准确地说是创建一些未初始化的ndarray多维数组)

帮助文档

 array函数

array函数:接收一个普通的python序列或者ndarray,并将其转换为ndarray

从现有数组生成

np.array(object, dtype)
np.asarray(a, dtype)
a = np.array([[1,2,3],[4,5,6]])
# 从现有的数组当中创建
a1 = np.array(a)#深拷贝
# 相当于索引的形式,并没有真正的创建一个新的,修改会影响原数组
a2 = np.asarray(a)#浅拷贝
关于array和asarray的不同:

zeros函数

zeros函数:创建指定长度或者形状的全0数组。

np.zeros(5)
np.zeros([5,5])
np.zeros((5,5))

ones函数

ones函数:创建指定长度或者形状的全1数组。

0和1数组其他创建方法

np.ones(shape, dtype)
np.ones_like(a, dtype)
np.zeros(shape, dtype)
np.zeros_like(a, dtype)
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.]])

empty函数

empty函数:创建一个没有任何具体值的数组(准确地说是创建一些未初始化的ndarray多维数组)

  • 只是分配内存空间,但不会初始化数组元素
  • 数组中的值是内存空间中已存在的随机 "垃圾值"
  • 它的优势是创建速度更快,因为省去了初始化步骤
np.empty((5,5))

array([[1.25318603e-311, 1.25318814e-311, 9.18225940e-072,1.25304780e-311, 1.25304780e-311],
       [1.25304780e-311, 1.25304771e-311, 1.25304771e-311,1.25304771e-311, 1.25304999e-311],
       [1.25304991e-311, 1.25304991e-311, 1.25305004e-311,1.25305009e-311, 1.25305004e-311],
       [1.25305478e-311, 1.25305477e-311, 1.25306303e-311,1.25315601e-311, 1.25315598e-311],
       [1.25315775e-311, 1.25315775e-311, 1.25315760e-311,1.25315869e-311, 1.25315932e-311]])

如果你需要初始值为 1 的数组,应该使用np.ones();如果你只需要一块指定大小的内存空间(并且会立即覆盖其中的值),可以使用np.empty()以获得更好的性能。

生成固定范围的数组

arange(start,stop, step, dtype)

创建等差数组 — 指定开始值、终值和步长。类似python的range函数
  • 参数:
    • step:步长,默认值为1

注意:最终创建的数组不包含终值

linspace(start, stop, num, endpoint)

创建等差数组 — 指定开始值、终值和元素个数
  • 参数:
    • start:序列的起始值
    • stop:序列的终止值
    • num:要生成的等间隔样例数量,默认为50
    • endpoint:序列中是否包含终值,默认为ture
#help说明:
linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
    Return evenly spaced numbers over a specified interval.

# 生成等间隔的数组
np.linspace(0, 100, 11)
返回结果:
array([ 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.])

注:需要生成连续数据,绘制平滑函数图像时,特别有用

logspace(start,stop, num)

和linspace函数类似,不过创建的是等比数列数组

logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0)
    Return numbers spaced evenly on a log scale.

生成随机数组

使用模块介绍
np.random模块

正态分布随机数组

关于正态分布的知识请移步数学和统计学基础概念了解。

np.random.randn(d0, d1, …, dn)

numpy.random.randn 是 NumPy 中用于生成服从 标准正态分布(均值为 0,标准差为 1) 的随机数数组的函数。它广泛用于模拟正态分布数据、初始化神经网络权重、统计分析等场景。

 

应用场景

  1. 模拟数据

    # 模拟 100 个正态分布的样本
    samples = np.random.randn(100)
  2. 机器学习初始化

    # 初始化神经网络权重(均值为 0,标准差为 1)
    weights = np.random.randn(64, 32)  # 64x32 的权重矩阵
  3. 统计分析

    # 生成两组正态分布数据,进行 t 检验
    group1 = np.random.randn(50)
    group2 = np.random.randn(50) + 0.5  # 均值偏移 0.5
    from scipy.stats import ttest_ind
    t_stat, p_value = ttest_ind(group1, group2)

  • 生成非标准正态分布
    若需生成均值为 μ、标准差为 σ 的正态分布,可基于 randn 转换:

    μ, σ = 5, 2
    data = μ + σ * np.random.randn(100)  # 均值为 5,标准差为 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,只输出一个值
normalarr=np.random.normal(1,5,100000000)
plt.hist(normalarr,1000)
plt.axvline(x=1, color='red', linestyle='--')#生成竖线
plt.show()

例如:我们可以模拟生成一组股票的涨跌幅的数据
举例2:随机生成4支股票1周的交易日涨幅数据
4支股票,一周(5天)的涨跌幅数据,如何获取?
  • 随机生成涨跌幅在某个正态分布内,比如均值0,方差1
股票涨跌幅数据的创建
# 创建符合正态分布的4只股票5天的涨跌幅数据
stock_change = np.random.normal(0, 1, (4, 5))
stock_change
返回结果:
array([[ 0.0476585 , 0.32421568, 1.50062162, 0.48230497, -0.59998822],
[-1.92160851, 2.20430374, -0.56996263, -1.44236548, 0.0165062 ],
[-0.55710486, -0.18726488, -0.39972172, 0.08580347, -1.82842225],
[-1.22384505, -0.33199305, 0.23308845, -1.20473702, -0.31753223]])

np.random.normal() 和 np.random.randn() 都是 NumPy 中生成正态分布随机数的函数,但它们在参数设置和默认行为上有显著区别。以下是详细对比:

1. 核心区别总结​​

​​特性​​ ​​np.random.normal()​​ ​​np.random.randn()​​
​​分布参数​​ 可自定义均值 (loc) 和标准差 (scale) ​​固定为标准正态分布​​(均值=0,标准差=1)
​​输入参数​​ loc(均值), scale(标准差), size(输出形状) d0, d1, ..., dn(维度参数,直接指定形状)
​​灵活性​​ 高(支持任意均值和标准差) 低(仅生成标准正态分布)
​​接口设计​​ 通用型函数 快捷函数(针对标准正态分布的简化版本)

​​2. 函数参数对比​​

np.random.normal()

np.random.normal(
    loc=0.0,      # 均值 (默认0)
    scale=1.0,    # 标准差 (默认1)
    size=None     # 输出形状 (默认单个值)
)

np.random.randn()

np.random.randn(
    d0, d1, ..., dn  # 维度参数(直接指定形状)
)

​3. 使用场景示例​​

场景 1:生成标准正态分布数据(均值为0,标准差为1)

​​np.random.normal()

data = np.random.normal(loc=0, scale=1, size=1000)

np.random.randn()​​更简洁

data = np.random.randn(1000)  # 等效写法

场景 2:生成非标准正态分布数据(如均值=5,标准差=3)

​​仅能用 np.random.normal()

data = np.random.normal(loc=5, scale=3, size=1000)

场景 3:生成多维数组

​​np.random.normal()

data = np.random.normal(loc=0, scale=1, size=(2, 3))  # 2行3列

​​np.random.randn()​​

data = np.random.randn(2, 3)  # 等效写法

np.random.standard_normal(size=None)

返回指定形状的标准正态分布的数组。
# randn 内部调用的是 standard_normal
两者生成的随机数分布完全一致,​​底层实现也相同​​
arr=np.random.standard_normal(10000000)
plt.hist(arr,1000)
plt.show()


均匀分布随机数组

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

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

np.random.rand(5,5)
  • 与 randn 的区别

    • np.random.rand:生成 [0,1) 均匀分布的随机数。

    • np.random.randn:生成 标准正态分布 的随机数。

验证是否均匀分布:

arr=np.random.rand(10000000)
plt.hist(arr,1000)
plt.show

np.random.random()

使用numpy.random中的random()函数来创建0-1之间的随机数元素,数组包含的元素数量由参数决定(np.random.random_sample的别名)

numpy.random.random() 和 numpy.random.rand() 功能完全相同,唯一区别在于参数传递方式,两者在性能和结果上无差异。

 

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中描述一致。
arr=np.random.uniform(1,10,5)#生成范围在1~10的随机数五个
arr
array([9.369543  , 4.81580831, 7.02010646, 4.96196996, 4.93288665])

np.random.randint(low=0.0, high=1.0, size=None)

  • 从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开,即包含low,不包含high,生成一个整数或N维整数数组,
  • 取数范围:若high不为None时,取[low,high)之间随机整数,否则取值[0,low)之间随机整数。

np.random.choice(a, size=None, replace=True, p=None)

从给定的一维数组(或整数范围)中生成​​随机样本​​,支持以下特性:

  • ​​均匀分布​​或​​自定义概率分布​​(通过 p 参数)。
  • ​​有放回抽样​​(replace=True,默认)或​​无放回抽样​​(replace=False)。
  • 灵活指定输出形状(size 参数)。
categories = np.random.choice(['A', 'B', 'C'], 50)
print(categories )
['A' 'B' 'A' 'A' 'C' 'A' 'C' 'B' 'A' 'C' 'C' 'B' 'A' 'A' 'C' 'A' 'C' 'A'
 'C' 'A' 'B' 'A' 'A' 'B' 'A' 'B' 'A' 'C' 'C' 'A' 'A' 'A' 'C' 'A' 'C' 'B'
 'C' 'C' 'B' 'C' 'A' 'A' 'C' 'A' 'C' 'A' 'A' 'C' 'A' 'C']
参数​​ ​​类型​​ ​​说明​​
a 1D数组或整数 若为整数 n,等价于从 np.arange(n) 中抽样。
size 整数或元组 输出形状,如 (2,3) 表示生成 2 行 3 列的数组。默认返回单个值。
replace 布尔值 True 表示有放回抽样(可重复抽取),False 表示无放回。
p 1D数组(概率) 每个元素的抽取概率,长度需与 a 一致,且概率和为 1。默认均匀分布。

应用场景​​

  • ​​数据抽样​​:从数据集中随机选取训练/测试样本。
  • ​​蒙特卡洛模拟​​:按概率分布生成随机事件。
  • ​​增强随机性​​:在游戏或算法中生成随机选择。

固定随机种子

若需结果可复现,需设置随机种子:

np.random.seed(42)  # 固定随机性
data = np.random.randn(3)

ndarray对象属性

ndim (‌ndim是“number of dimensions”的缩写‌)数组轴(维度)的个数,轴的个数被称作秩;就是告诉你这个数组是几维数组

shape 数组的维度, 例如一个2排3列的矩阵,它的shape属性将是(2,3),这个元组的长度显然是秩,即维度或者ndim属性

size 数组元素的总个数,等于shape属性中元组元素的乘积。

dtype 一个用来描述数组中元素类型的对象,可以通过创造或指定dtype使用标准Python类型。不过NumPy提供它自己的数据类型。

itemsize 数组中每个元素的字节大小。例如,一个元素类型为float64的数组itemsiz属性值为8(=64/8),又如,一个元素类型为complex32的数组itemsize属性为4(=32/8).

NumPy基本数据类型

 数值型dtype的命名方式为:一个类型名称(eg:int、float等),后接一个表示各个元素位长的数字。

比如Python的float数据类型(双精度浮点值),需要占用8个字节(64位),因此在NumPy中记为float64。

 每个数据类型都有一个类型代码,即简写方式。

 

数据类型

类型简写

说明

整数类型

int_

 

默认整形;整数默认类型为 int32(32 位系统)或 int64(64 位系统)。

intc

 

等价于long的整形

int8

i1

字节整形,1个字节,范围:[-128,127]

int16

i2

整形,2个字节,范围:[-32768,32767]

int32

i3

整形,4个字节,范围:[-2^31, 2^31-1](-2147483648 到 2147483647)

int64

i4

整形,8个字节,范围:[-2^63, 2^63-1]

uint8

u1

8 位无符号整数, 1个字节, 范围:[0,255](0 到 255),常用于图像数据(像素值范围 0-255)

uint16

u2

无符号整形, 2个字节, 范围:[0,65535]

uint32

u3

无符号整形, 1个字节, 范围:[0, 2^32-1](0 到 4294967295)

uint64

u4

无符号整形, 1个字节, 范围:[0,2^64-1]

浮点类型(详参float数据类型详解

float_

 

float64简写形式;浮点数默认类型为 float64

float16

f2

半精度浮点型(2字节)1符号位+5位指数+10位的小数部分

float32

f4或者f

单精度浮点型(4字节)1符号位+8位指数+23位的小数部分(十进制有效数字:~6-7 位)

float64

f8或者d

双精度浮点型(8字节)1符号位+11位指数+52位的小数部分(十进制有效数字:~15-17 位)

复数类型

complex_

c16

complex128的简写形式

complex64

c8

复数,由两个32位的浮点数来表示;由两个 float32 组成的复数(实部和虚部分别为 32 位)

complex128

c16

复数,由两个64位的浮点数来表示;由两个 float64 组成的复数(默认复数类型)

布尔类型

bool_  b 以一个字节形成存储的布尔值(True或者False)

字符串类型

String_(str_)

S

固定长度的字符串类型(每个字符1个字节),比如:要创建一个长度为8的字符串,应该使用S8

Unicode_

U

固定长度的unicode类型的字符串(每个字符占用字节数由平台决定),长度定义类似String_类型

固定长度字符串(例如 S10 表示最多 10 字节,U10 表示最多 10 Unicode)

其他类型

object O Python对象类型
datetime64

 

M

日期时间类型(支持年、月、日、小时等单位,如 M[D] 表示天级精度)

用于时间序列分析(如 np.datetime64('2023-10-01'))。

timedelta64 m 时间间隔类型(与 datetime64 配合使用)

 

 创建numpy数组的时候可以通过属性dtype显式指定数据类型,如果不指定的情况下,numpy会自动推断出适合的数据类型,所以一般不需要显示给定数据类型。

 NumPy数组基本操作

修改数组形状

ndarray.shape(new_shape)

 通过shape()修改数组本身形状

ndarray.resize(new_shape)

修改数组本身的形状和大小(不支持“-1”)

ndarray.resize() 是​​原地修改数组形状和大小​​的方法,允许调整后的元素数量(总大小)与原数组不同。

  • ​​扩展数组​​:新形状元素数更多时,填充 0(数值类型)或空值(对象类型)。
  • ​​缩减数组​​:新形状元素数更少时,​​截断末尾元素​​(按内存顺序保留前面的元素)。
参数​​ ​​类型​​ ​​说明​​
new_shape 元组/整数 新形状的维度。例如 (2,3) 或 2,3(非关键字参数形式)。
refcheck 布尔值 默认 True:检查数组是否被其他对象引用。若存在引用,拒绝调整大小。

 

ndarray.resize()​​ 和直接设置 ​​ndarray.shape​​ 都可以修改数组的形状,但它们在​​元素总数处理​​、​​内存操作​​和​​数据填充方式​​上有本质区别。

​​特性​​ ndarray.resize(new_shape) ndarray.shape = new_shape
​​元素总数是否可变​​ ✅ 可以改变(自动填充或截断数据) ❌ 必须与原数组元素总数一致,否则报错
​​是否原地操作​​ ✅ 直接修改原数组 ✅ 直接修改原数组
​​填充数据​​ 填充 0(数值类型)或空值(对象类型) 无填充,必须保持元素总数一致
​​适用场景​​ 需要扩展或缩减数组大小 仅调整形状,不改变元素总数

 ndarray.resize()​​与 np.resize() 函数的区别​

特性​​ ​​ndarray.resize()​​ ​​np.resize() 函数​​
​​操作类型​​ 原地修改原数组 返回新数组,原数组不变
​​填充行为​​ 填充 0 或空值 重复原数据填充至新形状大小
​​适用场景​​ 需要直接修改原数组 需保留原数组并生成新数组

ndarray.reshape(shape, order)

返回一个具有相同数据域,但shape不一样的视图
对于一个已经存在的ndarray数组对象而言,可以通过reshape()方法改变数组的形状,注意:原数组不变,返回的是一个新数组。

修改数组ndarray的shape值, 要求修改后乘积不变(元素总数不变),否则会报错。

直接使用reshape函数创建一个改变尺寸的新数组,原数组的shape保持不变,但是新数组和原数组共享一个内存空间,也就是修改任何一个数组中的值都会对另外一个产生影响,另外要求新数组的元素个数和原数组一致。

当指定某一个轴为-1的时候,表示将根据数组元素的数量自动计算该轴的长度值。

  • ndarray.reshape(shape):创建并返回新形状的数组(视图或副本),原数组不变,适合需要保留原数组的场景。
  • ndarray.shape = new_shape:直接修改原数组的形状,无返回值,适合明确要改变原数组且追求内存效率的场景。
两者核心功能一致(调整形状),但选择需根据是否保留原数组、内存使用需求和代码可读性决定。

ndarray修改数据类型

ndarray.astype(type)

 如果需要更改一个已经存在的数组的数据类型,可以通过astype方法进行修改从而得到一个新数组(原数组不变)。

ndarray.tobytes([order])

等价写法:ndarray.tostring([order]),不过ndarray.tostring([order])即将过期不建议使用

  • ndarray.tobytes() 将数组的​​原始数据​​转换为 Python 的 bytes 对象,直接反映内存中的二进制内容。

参数​​ ​​类型​​ ​​可选值​​ ​​说明​​
order 字符串 'C''F''A' 'C':按行优先(C 顺序)展开。
'F':按列优先(Fortran 顺序)展开。
'A':自动选择数组的连续顺序。
  • ​​数据顺序​​:默认按 ​​C 顺序(行优先)​​ 展开,但可通过 order 参数控制。
  • ​​数据类型影响​​:字节序列的表示形式由数组的 dtype 决定(例如 int16 占 2 字节,float32 占 4 字节)。
x = np.array([[1, 2], [3, 4]], dtype='<u2')  # 小端存储的 uint16 类型:每个字符占两个字节
bytes_c = x.tobytes(order='C')#行优先
print(bytes_c) 

默认 C 顺序​:

字节解析​​(每个元素占 2 字节,小端存储):

  • 1 → 01 00
  • 2 → 02 00
  • 3 → 03 00
  • 4 → 04 00

内存展开顺序:逐行排列 → 0, 1, 2, 3

 Fortran 顺序​:

​字节解析​​:

  • 按列优先展开:1, 3, 2, 4→ 字节序列为01 00 (1), 03 00 (3), 02 00 (2),04 00 (4)。

与 tostring() 的兼容性​​

  • ​​历史背景​​:tobytes() 替代了已弃用的 tostring() 方法,功能完全一致。
  • ​​推荐使用​​:始终优先使用 tobytes()

​​反向操作:np.frombuffer()

可将 bytes 数据重新转换为数组(需指定正确的 dtype 和 order):

# 从 bytes_c 恢复数组
restored = np.frombuffer(bytes_c, dtype='<u2').reshape(2, 2)
print(restored)
# 输出:
# [[1 3]
#  [2 4]]

应用场景​​

  • ​​二进制文件 I/O​​:将数组保存为二进制文件。
  • ​​网络传输​​:序列化数组数据。
  • ​​内存共享​​:与其他语言(如 C/C++)交互时传递原始内存数据。
注意:使用ndarray.tobytes([order])时jupyter输出太大可能导致崩溃问题【了解】
如果遇到
IOPub data rate exceeded.
The notebook server will temporarily stop sending output
to the client in order to avoid crashing it.
To change this limit, set the config variable
`--NotebookApp.iopub_data_rate_limit`.
这个问题是在jupyer当中对输出的字节数有限制,需要去修改配置文件
创建配置文件
jupyter notebook --generate-config
vi ~/.jupyter/jupyter_notebook_config.py
取消注释,多增加
## (bytes/sec) Maximum rate at which messages can be sent on iopub before they
# are limited.
c.NotebookApp.iopub_data_rate_limit = 10000000
但是不建议这样去修改,jupyter输出太大会崩溃

数组与标量、数组之间的运算

数组不用循环即可对每个元素执行批量的算术运算操作,这个过程叫做矢量化,即用数组表达式代替循环的做法。

 矢量化数组运算性能比纯Python方式快上一两个数据级。

 大小相等的两个数组之间的任何算术运算都会将其运算应用到元素级上的操作。

 元素级操作:在NumPy中,大小相等的数组之间的运算,为元素级运算,即只用于位置相同的元素之间,所得的运算结果组成一个新的数组,运算结果的位置跟操作数位置相同。

数组与标量运算

数组与数组运算

数组的矩阵积(matrix product)

 矩阵积(matrix product):两个二维矩阵(行和列的矩阵)满足第一个矩阵的列数第二个矩阵的行数相同,那么可以进行矩阵的乘法,即矩阵积,矩阵积不是元素级的运算。也称为点积、数量积。

一句话总结:矩阵积的结果数组的形状是(arr1的行数,arr2的列数)。

案例:根据学生的平时成绩(权重0.3)和期末成绩(权重0.7),求出最终成绩

 需求拆解:其实就是矩阵的乘法:

运算拆解=(80*0.3+86*0.7)+(82*0.3+80*0.7)+(85*0.3+78*0.7)+(...*0.3+...*0.7)

numpy.dot(arr1,arr2)

补充

在 NumPy 中,np.dot(arr1, arr2) 并不仅限于二维数组的矩阵乘法。它是一个​​广义的点积函数​​,支持不同维度的数组运算,具体行为取决于输入数组的维度:

​1. 不同维度的行为总结​​

​​输入数组维度​​ ​​运算规则​​
​​两个一维数组​​ 计算向量内积(标量):所有对应元素乘积之和
​​一个二维 + 一个一维​​ 矩阵与向量乘法,结果为一维数组
​​两个二维数组​​ 标准矩阵乘法
​​高维数组(≥3维)​​ 执行张量收缩(沿特定轴的点积求和),结果维度由输入形状决定

具体示例​​

​​(1) 一维数组 → 标量内积​

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.dot(a, b)  # 1 * 4 + 2 * 5 + 3 * 6 = 32
print(result)  # 输出: 32

二维数组与一维数组 → 矩阵乘向量​(其实也是特殊的矩阵积:就好比把一维数组转成二维:(竖起来))

A = np.array([[1, 2], [3, 4]])
v = np.array([5, 6])
result = np.dot(A, v)  # [1 * 5 + 2 * 6, 3 * 5 + 4 * 6] = [17, 39]
print(result)  # 输出: [17 39]

二维数组 → 矩阵乘法​

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
result = np.dot(A, B)  # 标准矩阵乘积
print(result)
# 输出:
# [[19 22]
#  [43 50]]

高维数组 → 张量收缩​

A = np.arange(24).reshape(2, 3, 4)  # 形状 (2, 3, 4)
B = np.arange(24).reshape(2, 4, 3)  # 形状 (2, 4, 3)
result = np.dot(A, B)               # 形状 (2, 3, 2, 3)

# 计算逻辑:对 A 的最后一个轴(4)和 B 的倒数第二个轴(4)进行点积求和
# 输出维度为 (A.shape[0], A.shape[1], B.shape[0], B.shape[-1])
print(result.shape)  # 输出: (2, 3, 2, 3)

与 @ 运算符和 np.matmul 的区别​

函数/运算符​​ ​​行为​​
np.dot 支持广义点积(包括标量、矩阵、张量收缩)
@ 或 np.matmul 专为矩阵乘法设计,对高维数组执行广播矩阵乘法,不支持标量或某些维度收缩
 

# 高维数组使用 @ 运算符
A = np.arange(24).reshape(2, 3, 4)
B = np.arange(24).reshape(2, 4, 3)
result = A @ B  # 形状 (2, 3, 3),执行批量矩阵乘法
print(result.shape)  # 输出: (2, 3, 3)

总结​​

  • ​​np.dot 的通用性​​:
    不仅支持二维矩阵乘法,还兼容一维向量内积和高维张量运算。
  • ​​适用场景​​:
    • 需要明确控制点积轴时(如自定义张量收缩)。
    • 处理混合维度(如一维与二维数组相乘)。
  • ​​替代方案​​:
    • 若需纯矩阵乘法或广播行为,优先使用 @ 或 np.matmul

数组的索引与切片

多维数组的索引

数组的切片

 

布尔类型索引

利用布尔类型的数组进行数据索引,最终返回的结果是对应索引数组中数据为True位置的值。

students=np.array(["张飞","关羽","刘备","曹操","孙权"])
scores=np.array([[ 70,  82, 101,  60,  72,  77],
       [107,  96,  97,  61,  98,  60],
       [ 69, 117,  97,  62,  76,  72],
       [ 95,  86,  66,  66, 105, 111],
       [111,  60,  81,  68,  70, 100]])
classs=np.array(["语文","数学","英语","物理","化学","生物"])
#获取刘备的成绩
liubei_score=scores[students=="刘备"]
print("刘备的成绩:\n",liubei_score)
print(liubei_score.reshape(-1))#去掉外面一层“[]”相当于降维[ 69 117  97  62  76  72]
#获取刘备的数学成绩
liubei_math_score=scores[students=="刘备"].reshape(-1)[classs=="数学"]
print("刘备的数学成绩:\n",liubei_math_score)
#获取刘备和曹操的成绩
liubei_caocao_score=scores[(students=="刘备")|(students=="曹操")]
print("刘备和曹操的成绩:\n",liubei_caocao_score)
#除了刘备和曹操之外的其他人的成绩
not_liubei_caocao_score=scores[(students!="刘备")&(students!="曹操")]
print("除了刘备和曹操之外的其他人的成绩:\n",not_liubei_caocao_score)

输出结果:

刘备的成绩:
 [[ 69 117  97  62  76  72]]
[ 69 117  97  62  76  72]
刘备的数学成绩:
 [117]
刘备和曹操的成绩:
 [[ 69 117  97  62  76  72]
 [ 95  86  66  66 105 111]]
除了刘备和曹操之外的其他人的成绩:
 [[ 70  82 101  60  72  77]
 [107  96  97  61  98  60]
 [111  60  81  68  70 100]]

花式索引

 花式索引(Fancy indexing)指的是利用整数数组进行索引的方式

arr=np.arange(32).reshape(8,4)
print(arr)
print("-"*50)
#!!!注意arr[3,2]是获取第四行的第三个数据等价于arr[3][2]
print(arr[3,2])
print("-"*50)
#获取第四行和第三行
print(arr[[3,2]])
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]
 [24 25 26 27]
 [28 29 30 31]]
--------------------------------------------------
14
--------------------------------------------------
[[12 13 14 15]
 [ 8  9 10 11]]
# 获取位置为(0,0)(4,3)(5,2)的数据
print(arr[[0,4,5],[0,3,2]])
print("-"*50)
# 获取第1,5,6行第1,4,3列的数据
print(arr[[0,4,5]].T[[0,3,2]].T)
print("-"*50)
# 获取第1,5,6行第1,4,3列的数据:使用ix_函数产生的索引器
print(arr[np.ix_([0,4,5],[0,3,2])])
print("-"*50)
[ 0 19 22]
--------------------------------------------------
[[ 0  3  2]
 [16 19 18]
 [20 23 22]]
--------------------------------------------------
[[ 0  3  2]
 [16 19 18]
 [20 23 22]]

数组的转置与轴对换

数组转置是指将shape进行重置操作,并将其值重置为原始shape元组的倒置,比如原始的shape值为:(2,3,4),那么转置后的新元组的shape的值为: (4,3,2)f

 对于二维数组而言(矩阵)数组的转置其实就是矩阵的转置;

 可以通过调用数组的transpose函数或者T属性进行数组转置操作

特性​​ ​​.T 属性​​ ​​transpose() 方法​​  
​​功能范围​​ 仅支持​​完全转置​​(反转所有轴)例如三维数组形状从 (a, b, c) 变为 (c, b, a) 支持​​任意轴顺序的排列​​  
​​参数灵活性​​ 无需参数 需指定 axes 参数(可选),通过 axes 参数自由指定轴的顺序  
​​适用维度​​ 适合二维数组快速转置 适合高维数组自定义轴顺序  
​​代码简洁性​​ 更简洁(如 arr.T 稍复杂(如 arr.transpose(1,0,2)  
示例
arr_3d = np.arange(24).reshape(2, 3, 4)  # 形状 (2, 3, 4)
print(arr_3d.T.shape)  # 输出 (4, 3, 2)
# 将轴顺序从 (0,1,2) 调整为 (1,0,2)
transposed = arr_3d.transpose(1, 0, 2)  # 形状变为 (3, 2, 4)
 

.T和transpose对比

内存与性能​​

​​视图而非副本​​:两者均返回原数组的​​视图​​(共享内存),修改转置后的数组会影响原数据。

性能一致​​:两者时间复杂度相同(O(1)),无显著性能差异。

transpose(axes)​​应用场景

  • 调整高维数据轴顺序(如深度学习中的张量格式转换):
    # 将图像数据从 (batch, height, width, channels) 转换为 (batch, channels, height, width)在深度学习和批量数据处理中,图像数据的维度通常会被扩展为​​四维数组​​
    images = np.random.rand(32, 224, 224, 3)
    reshaped = images.transpose(0, 3, 1, 2)  # 形状 (32, 3, 224, 224)
    #了解内容:
    
    当处理​​批量图像数据​​时,四维数组的维度含义为:
    (batch_size, height, width, channels)
    ​​batch_size​​:一次性处理的图像数量(如32张图)。
    ​​height/width​​:单张图像的分辨率(如224x224)。
    ​​channels​​:颜色通道数(如RGB图像的3通道)。

通用函数

ufunc:numpy模块中对ndarray中数据进行快速元素级运算的函数,也可以看做是简单的函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。

 主要包括一元函数和二元函数;

一元函数

一元ufunc

描述

调用方式

abs, fabs

计算整数、浮点数或者复数的绝对值,对于非复数,可以使用更快的fabs

np.abs(arr)
np.fabs(arr)

sqrt

计算各个元素的平方根,相当于arr ** 0.5, 要求arr的每个元素必须是非负数

np.sqrt(arr)

square

计算各个元素的平方,相当于arr ** 2

np.square(arr)

exp

计算各个元素的指数e的x次方

np.exp(arr)

log、log10、log2、log1p

分别计算自然对数、底数为10的log、底数为2的log以及log(1+x);

要求arr中的每个元素必须为正数

np.log(arr)
np.log10(arr)np.log2(arr)
np.log1p(arr)

sign

计算各个元素的正负号: 1 正数,0:零,-1:负数

np.sign(arr)

ceil

计算各个元素的ceiling值,即大于等于该值的最小整数

np.ceil(arr)

floor

计算各个元素的floor值,即小于等于该值的最大整数

np.floor(arr)

rint

将各个元素值四舍五入到最接近的整数,保留dtype的类型

np.rint(arr)

modf

将数组中元素的小数位和整数位以两部分独立数组的形式返回

np.modf(arr)

isnan

返回一个表示“那些值是NaN(不是一个数字)”的布尔类型数组

np.isnan(arr)

isfinite、isinf

分别一个表示”那些元素是有穷的(非inf、非NaN)”或者“那些元素是无穷的”的布尔型数组

np.isfinite(arr)
np.isinf(arr)

cos、cosh、sin、sinh、tan、tanh

普通以及双曲型三角函数

np.cos(arr)
np.sin(arr)
np.tan(arr)

arccos、arccosh、arcsin、arcsinh、arctan、arctanh

反三角函数

np.arccos(arr)
np.arrsin(arr)
np.arrtan(arr)

示例代码:
arr=np.array([-2.2,-5.5,-5,0,2,3,8,7,6])
print("arr=",arr)
newarr=np.fabs(arr)+1
print("np.abs(arr)=",np.abs(arr))
print("np.fabs(arr)=",np.fabs(arr))
print("np.sqrt(arr)=",np.sqrt(newarr))
print("np.square(arr)=",np.square(arr))
print("np.exp(arr)=",np.exp(arr))
print("np.log(newarr)=",np.log(newarr))
print("np.log10(newarr)=",np.log10(newarr))
print("np.sign(arr)=",np.sign(arr))
print("np.ceil(arr)=",np.ceil(arr))
print("np.floor(arr)=",np.floor(arr))
print("np.rint(arr)=",np.rint(arr))
print("np.modf(arr)=",np.modf(arr))
print("np.isnan(arr)=",np.isnan(arr))
print("np.isfinite(arr)=",np.isfinite(arr))
print("np.cos(arr)=",np.cos(arr))
print("np.arccos(newarr)=",np.arccos(np.cos(arr)))
arr= [-2.2 -5.5 -5.   0.   2.   3.   8.   7.   6. ]
np.abs(arr)= [2.2 5.5 5.  0.  2.  3.  8.  7.  6. ]
np.fabs(arr)= [2.2 5.5 5.  0.  2.  3.  8.  7.  6. ]
np.sqrt(arr)= [1.78885438 2.54950976 2.44948974 1.         1.73205081 2.
 3.         2.82842712 2.64575131]
np.square(arr)= [ 4.84 30.25 25.    0.    4.    9.   64.   49.   36.  ]
np.exp(arr)= [1.10803158e-01 4.08677144e-03 6.73794700e-03 1.00000000e+00
 7.38905610e+00 2.00855369e+01 2.98095799e+03 1.09663316e+03
 4.03428793e+02]
np.log(newarr)= [1.16315081 1.87180218 1.79175947 0.         1.09861229 1.38629436
 2.19722458 2.07944154 1.94591015]
np.log10(newarr)= [0.50514998 0.81291336 0.77815125 0.         0.47712125 0.60205999
 0.95424251 0.90308999 0.84509804]
np.sign(arr)= [-1. -1. -1.  0.  1.  1.  1.  1.  1.]
np.ceil(arr)= [-2. -5. -5.  0.  2.  3.  8.  7.  6.]
np.floor(arr)= [-3. -6. -5.  0.  2.  3.  8.  7.  6.]
np.rint(arr)= [-2. -6. -5.  0.  2.  3.  8.  7.  6.]
np.modf(arr)= (array([-0.2, -0.5, -0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ]), array([-2., -5., -5.,  0.,  2.,  3.,  8.,  7.,  6.]))
np.isnan(arr)= [False False False False False False False False False]
np.isfinite(arr)= [ True  True  True  True  True  True  True  True  True]
np.cos(arr)= [-0.58850112  0.70866977  0.28366219  1.         -0.41614684 -0.9899925
 -0.14550003  0.75390225  0.96017029]
np.arccos(newarr)= [2.2        0.78318531 1.28318531 0.         2.         3.
 1.71681469 0.71681469 0.28318531]

二元函数

二元ufunc

描述

调用方式

mod

元素级的取模(就是arr1对arr2取余)

np.mod(arr1,arr2)

dot

求两个数组的点积(所有对应的点的乘积之和)

np.dot(arr1,arr2)

greater、greater_equal、less、less_equal、equal、not_equal

执行元素级别的比较运算,最终返回一个布尔型数组

greater:(arr1>arr2)、greater_equal:(arr1>=arr2)、less:(arr1<arr2)、less_equal:(arr1<=arr2)、equal:(arr1==arr2)、not_equal

np.greater(arr1, arr2) np.less(arr1, arr2) np.equal(arr1, arr2)

logical_and、

logical_or、

logical_xor

执行元素级别的布尔逻辑运算,相当于中缀运算符&、|、^(与或非)

如:

A = np.array([True, False, True, False])
B = np.array([True, True, False, False])
logical_and(A,B)结果应该是 [True, False, False, False]

np.logical_and(arr1,arr2) np.logical_or(arr1,arr2) np.logical_xor(arr1,arr2)

power

求解对数组中的每个元素进行给定次数的指数值,类似于: arr ** 3

np.power(arr, 3)

聚合函数:统计运算

聚合函数是对一组值(eg一个数组)进行操作,返回一个单一值作为结果的函数。当然聚合函数也可以指定对某个具体的轴进行数据聚合操作;

常将的聚合操作有:平均值、最大值、最小值、标准差等等。

  • min(a,axis)
    • 返回数组的最小值或轴向的最小值。
  • max(a, axis])
    • 返回数组的最大值或轴向的最大值。
  • median(a, axis)
    • 计算指定轴上的中位数。
  • mean(a,axis, dtype)
    • 沿指定轴计算算术平均值。
  • ·std(a,axis, dtype)
    • 计算沿指定轴线的标准差。
  • ·var(a,axis,dtype)
    • 计算指定轴上的方差。
arr=np.array([[1,2,3,4],[5,6,7,8]])
arr
print("最小值",np.min(arr))
print("最小值",arr.min())
print("最大值",np.max(arr))
print("中位数",np.median(arr))
print("平均值",np.mean(arr))
print("方差",np.var(arr))
print("标准差",np.std(arr))
print("自己计算的标准差",np.sqrt(np.power(arr-np.mean(arr),2).sum()/arr.size))
最小值 1
最小值 1
最大值 8
中位数 4.5
平均值 4.5
方差 5.25
标准差 2.29128784747792
自己计算的标准差 2.29128784747792
arr=np.array([[1,2,3,4],[5,6,7,8]])
print(arr)
# 二维数组情况下axis=0表示对同列数据进行聚合
print("同列最小值:",arr.min(axis=0))
print("同列平均值",arr.mean(axis=0))
print("同列之和",arr.sum(axis=0))
# axis=1表示对同行数据进行聚合
print("同行最小值",arr.min(axis=1))
print("同行平均值",arr.mean(axis=1))
print("同行之和",arr.sum(axis=1))
[[1 2 3 4]
 [5 6 7 8]]
同列最小值: [1 2 3 4]
同列平均值 [3. 4. 5. 6.]
同列之和 [ 6  8 10 12]
同行最小值 [1 5]
同行平均值 [2.5 6.5]
同行之和 [10 26]
案例:学生成绩统计运算

# 接下来对于前四名学生,进行一些统计运算
temp = scores[:4]
# 按照列统计

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)))
前四名学生,各科成绩的最大分:[98 85 93 89 96 71]
前四名学生,各科成绩的最小分:[51 69 44 53 61 62]
前四名学生,各科成绩波动情况:[18.26198237  5.67340286 17.86756839 13.49768499 12.77693234  3.5       ]
前四名学生,各科成绩的平均分:[78.   77.25 64.5  74.75 75.5  67.5 ]
# 接下来对于前四名学生,进行一些统计运算
temp = scores[:4]
# 安照行统计

print("前四名学生,各自最高成绩:{}".format(np.max(temp, axis=1)))
print("前四名学生,各自最低成绩:{}".format(np.min(temp, axis=1)))
print("前四名学生,偏科情况:{}".format(np.std(temp, axis=1)))
print("前四名学生,各个学生的平均分:{}".format(np.mean(temp, axis=1)))
前四名学生,各自最高成绩:[74 98 96 82]
前四名学生,各自最低成绩:[51 61 44 58]
前四名学生,偏科情况:[ 8.1240384  12.80299444 17.74589154  7.36357401]
前四名学生,各个学生的平均分:[62.         80.5        77.5        71.66666667]
如果需要统计出某科最高分对应的是哪个同学?
np.argmax(temp, axis=0) — 最大元素对应的下标
np.argmin(temp, axis=0)— 最小元素对应的下标

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

条件判断

np.all()

np.any()

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

np.where函数是三元表达式x if condition else y的矢量化版本。

就是从两个数组里选出符合条件的元素,重新组成新数组并返回。(建议直接看例子)

 

x=np.array([-1,-999,-1.2,-3.3])
y=np.array([-2,-2.1,-2.2,-2.3])
condition=x<y#false
#传统写法
res1=[x if c else y for (x,y,c) in zip(x,y,condition)]
#where语句 res2
=np.where(condition,x,y) print("使用python语法计算结果为:",res1,"数据类型为:",type(res1)) print("使用where语法计算结果为:",res2,"数据类型为:",type(res2))
使用python语法计算结果为: [-2.0, -999.0, -2.2, -3.3] 数据类型为: <class 'list'>
使用where语法计算结果为: [  -2.  -999.    -2.2   -3.3] 数据类型为: <class 'numpy.ndarray'>

案例:将数组中的所有异常数字替换为0,比如将NaN替换为0

arr=np.array([[1,2,np.NaN,4],[3,5,6,7],[8,9,np.inf,np.NaN],[8,np.inf,np.inf,10]])
print(arr)
zeros=np.zeros(arr.shape)
print(zeros)
condition=(np.isinf(arr))|(np.isnan(arr))|(arr=='')
print(condition)
res1=np.where(condition,zeros,arr)
res2=np.where(condition,0,arr)
print(res1)
print(res2)
[[ 1.  2. nan  4.]
 [ 3.  5.  6.  7.]
 [ 8.  9. inf nan]
 [ 8. inf inf 10.]]
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
[[False False  True False]
 [False False False False]
 [False False  True  True]
 [False  True  True False]]
[[ 1.  2.  0.  4.]
 [ 3.  5.  6.  7.]
 [ 8.  9.  0.  0.]
 [ 8.  0.  0. 10.]]
[[ 1.  2.  0.  4.]
 [ 3.  5.  6.  7.]
 [ 8.  9.  0.  0.]
 [ 8.  0.  0. 10.]]

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

np.logical_and和np.logical_or

np.unique:数组去重

np.unique 用于查找数组中的​​唯一元素​​(去重),并返回排序后的结果。支持以下扩展功能:

  • 返回唯一元素的​​首次出现索引​​。
  • 返回从唯一数组重建原数组的​​逆索引​​。
  • 统计每个唯一元素的​​出现次数​​。
  • 支持处理​​多维数组​​(沿指定轴操作)。

参数详解​​

​​参数​​ ​​类型​​ ​​说明​​
ar array_like 输入数组。默认会展开为一维数组(除非指定 axis)。
return_index bool 若为 True,返回原数组中唯一元素的​​首次出现索引​​。默认 False
return_inverse bool 若为 True,返回​​逆索引数组​​,用于从唯一数组重建原数组。默认 False
return_counts bool 若为 True,返回每个唯一元素的​​出现次数​​。默认 False
axis int/None 指定操作的轴。默认 None(展开为一维数组)。支持多维数组处理。
equal_nan bool 若为 True,将多个 NaN 视为相同值。默认 True(自 NumPy 1.24 起)。

 返回值​​

​​返回值​​ ​​说明​​
unique 排序后的唯一元素数组。
unique_indices (可选)原数组中唯一元素的首次出现索引。需 return_index=True
unique_inverse (可选)逆索引数组,用于重建原数组。需 return_inverse=True
unique_counts (可选)每个唯一元素出现的次数。需 return_counts=True

示例解析​

(1) 基本用法:一维数组去重​

import numpy as np

arr = np.array([1, 1, 2, 2, 3, 3])
unique_values = np.unique(arr)
print(unique_values)  # 输出: [1 2 3]

二维、多维数组去重

二维数组按行去重

arr_2d = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
unique_rows = np.unique(arr_2d, axis=0)
print(unique_rows)
# 输出:
# [[1 0 0]
#  [2 3 4]]

二维数组按列去重

arr = np.array([[1, 2, 1], 
                [1, 4, 1], 
                [1, 2, 1]])
unique_subarrays = np.unique(arr, axis=1)  # 按列去重
unique_subarrays
#输出
array([[1, 2],
       [1, 4],
       [1, 2]])

应用场景​​

  • ​​数据清洗​​:去除重复值或处理缺失值(如合并多个 NaN)。
  • ​​统计分析​​:计算唯一值的频率分布。
  • ​​机器学习​​:预处理分类标签或特征编码。

np.round:四舍五入,指定保留小数位数

numpy.round 用于对数组中的数值进行​​四舍五入​​,支持以下操作:

  • 指定保留的小数位数(decimals 参数)。
  • 处理​​正/负小数位数​​(如 decimals=-1 表示舍入到十位)。
  • 支持​​复数​​(实部和虚部分别四舍五入)。
  • 提供​​中间值舍入规则​​(向最近的偶数值舍入)。

参数说明​​

​​参数​​ ​​类型​​ ​​说明​​
a array_like 输入数组(支持整数、浮点数、复数)。
decimals int 保留的小数位数。默认 0(舍入到整数);负数表示舍入到十、百、千位等。
out ndarray 可选输出数组,用于直接存储结果(需与原数组形状相同)。

基本舍入​

arr = np.array([0.37, 1.64])
print(np.round(arr))          # 输出: [0., 2.]
print(np.round(arr, decimals=1))  # 输出: [0.4 1.6]

负数小数位数

arr = np.array([1, 2, 3, 11])
print(np.round(arr, decimals=-1))  # 输出: [0, 0, 0, 10](舍入到十位)

原地修改数组​

arr = np.array([1.234, 5.678])
out = np.empty_like(arr)
np.round(arr, decimals=1, out=out)  # 结果存入 out
print(out)  # 输出: [1.2 5.7]

关键行为特性​​

中间值舍入规则​​

当数值​​恰好在两个可能的舍入结果中间​​时,numpy.round 会舍入到​​最接近的偶数值​​:采用 ​​“四舍六入五成双”​​(Round Half to Even),当末尾是 5 时向最近的偶数方向舍入。

import numpy as np

print(np.round([0.5, 1.5, 2.5, 3.5]))  # 输出: [0., 2., 2., 4.]

浮点数精度问题​​

由于 ​​IEEE 754 浮点数标准​​的限制,某些十进制小数无法精确表示为二进制浮点数,导致舍入结果可能与预期不符:

# 示例:16.055 的二进制表示存在误差
print(np.round(16.055, 2))   # 输出: 16.06(因实际存储值接近 16.0549999999999997)
print(round(16.055, 2))      # 输出: 16.05(Python 内置 round 更精确)

高精度数值的舍入误差​​

对于极大或极小的数值,numpy.round 的快速算法可能导致额外误差:

# 大数值舍入示例
print(np.round(56294995342131.5, 3))  # 输出: 56294995342131.51(误差)
print(np.format_float_positional(56294995342131.5, precision=3))  # 输出: '56294995342131.5'

性能与精度权衡​​

​​方法​​ ​​速度​​ ​​精度​​ ​​适用场景​​
numpy.round 一般 大规模数值处理
Python 内置 round 单个浮点数精确舍入
np.format_float_* 显示格式化(不修改数据)

​​总结​​

  • ​​中间值舍入规则​​:向最近的偶数值舍入,避免统计偏差。
  • ​​浮点数精度陷阱​​:理解二进制存储限制,必要时使用高精度工具。
  • ​​替代方案选择​​:根据需求在速度与精度间权衡。

通过合理使用 numpy.round 及其替代方法,可有效平衡数值处理的效率与准确性。

np.rint(x)和np.round(x, decimals=0)

对比维度 np.rint(x) np.round(x, decimals=0)
小数位数控制 不支持,只能四舍五入到整数 支持通过 decimals 参数指定任意小数位数
主要用途 专门用于取最接近的整数 灵活处理不同精度的四舍五入需求
对 0.5 的处理 四舍五入到最近的偶数(银行家舍入) 与 rint 相同(银行家舍入规则)
返回类型 始终为浮点数数组 始终为浮点数数组

补充

科学计数法

 

在 NumPy 中,np.arange(-3, 3, 0.1) 生成的数组元素末尾的 e 表示科学计数法(例如 1.5e-16 即 1.5×10⁻¹⁶)。​​这是因为浮点数精度问题导致某些数值无法精确表示,而 NumPy 默认使用科学计数法显示极小或极小的数值​​。以下是详细解释和解决方案:


为什么会出现 e(科学计数法)?

  • ​​浮点数精度限制​​
    计算机用二进制存储小数时,某些十进制数(如 0.1)无法精确表示,导致微小误差。例如:
0.1 的实际存储值 ≈ 0.10000000000000000555...

​​NumPy 的默认显示规则​​
当数值绝对值小于 1e-4 或大于 1e+16 时,NumPy 会自动使用科学计数法。例如:

x = np.array([0.0001, 10000000000000000.0])
print(x)  # 输出: [1.e-04 1.e+16]

 为什么你的 np.arange(-3, 3, 0.1) 会出现科学计数法?

    • ​​误差积累导致极小值​​
      由于 0.1 的浮点误差,某些数值的实际计算结果可能接近 0(例如 -3.0 + 0.1 * 30 = 0.0,但实际值可能是 -1.11e-16),触发科学计数法显示。

    • ​​验证示例​​
      检查数组中是否包含极小值:

import numpy as np

x = np.arange(-3, 3, 0.1)
print(x[x < 1e-10])  # 输出接近 0 的极小值,如 [-1.11e-16]

 如何强制禁用科学计数法?

通过 np.set_printoptions 调整显示规则:

方法一:禁用科学计数法(推荐)

import numpy as np

# 禁用科学计数法,并设置小数点后显示位数(例如4位)
np.set_printoptions(suppress=True, precision=4)

x = np.arange(-3, 3, 0.1)
print(x)

输出示例​​:

[-3.  -2.9 -2.8 ... 2.8 2.9]

方法二:自定义格式(高级)

# 强制所有浮点数用小数点显示,保留5位小数
np.set_printoptions(formatter={'float': '{:0.5f}'.format})

x = np.arange(-3, 3, 0.1)
print(x)

输出示例​​:

[-3.00000 -2.90000 -2.80000 ... 2.80000 2.90000]

数值本质未改变

科学计数法只是显示方式,不影响实际数值的精度。例如:

a = 1e-16
b = 0.0000000000000001
print(a == b)  # True

总结

现象 原因 解决方案
数值末尾有 e 浮点精度误差触发科学计数法 使用 np.set_printoptions 禁用
显示异常但计算正确 显示规则不同,数值未改变 无需处理,关注实际计算逻辑

调整打印选项后,所有数值将以常规小数形式显示。

 

posted @ 2025-04-02 09:29  指尖下的世界  阅读(55)  评论(0)    收藏  举报