NumPy

NumPy

Yiuhang Chan

NumPy简介

NumPy(Numerical Python)是一个开源的Python库,它提供了高性能的多维数组对象和一系列用于处理这些数组的函数。这使得NumPy成为执行许多科学计算和数据分析任务的基础库之一。NumPy的核心功能是围绕着一个强大的N维数组对象ndarray展开的,这个对象允许进行高效的数组计算。此外,NumPy还包括:

  • 广播功能函数,它支持不同大小数组之间的运算。
  • 整合C/C++/Fortran代码的工具,这些底层算法的设计初衷就是要提供优异的性能,使NumPy在执行数值计算时比纯Python代码高效得多。
  • 线性代数、傅里叶变换、随机数生成等功能,这些是执行科学计算的常用工具。

NumPy的主要对象是同种元素的多维数组。这是一个表格,所有元素类型相同,每个元素都通过一个正整数元组进行索引。在NumPy中,数组的维度称为“轴”,轴的数量称为“秩”。

更专业地补充,NumPy的设计哲学强调数组的重要性,它通过以下特点体现了这一点:

  • 内存效率:NumPy的数组对象直接映射到内存中,减少了Python解释器的开销,提高了数据处理的效率。
  • 广播机制:这是一种强大的功能,允许NumPy在执行数组运算时自动扩展维度,简化了代码的复杂度,提高了开发效率。
  • 矢量化操作:NumPy允许用户对数组进行批量操作而无需编写循环语句,这不仅使代码更简洁,也利用了底层优化,提升了性能。
  • 底层语言接口:NumPy提供了API,使得在C、C++或Fortran编写的代码可以轻松集成进Python程序中,进一步提升了执行效率。
  • 科学计算工具集成:NumPy是许多高级科学计算库(如SciPy、Pandas、Matplotlib)的基石,为复杂的数据处理和分析提供了强大的支持。

以上特性使NumPy成为科学计算、数据分析、机器学习等领域不可或缺的工具,它的高效、灵活和易于使用的特性,为Python在科学计算领域的流行提供了坚实的基础。

导入NumPy库

import numpy as np

读取文件

NumPy库提供了多种方法来读取数据,其中np.loadtxt是一种非常高效且简便的方式,特别适合于读取CSV(逗号分隔值)文件。这个函数允许用户定制化地读取数据,通过指定函数参数来适应不同格式的数据文件。下面是对np.loadtxt方法参数的进一步解释和扩展,以提供更全面的理解:

  • path: 这是一个必需的参数,用于指定要读取的文件的路径。这个路径可以是相对路径或绝对路径。相对路径是相对于当前工作目录的路径,而绝对路径是从根目录开始的完整路径。例如,'./data/myfile.csv'是一个相对路径,它指向当前目录下的data文件夹中的myfile.csv文件。

  • delimiter: 用于指定文件中用于分隔数据列的字符。在CSV文件中,最常见的分隔符是逗号(','),但有时也可能使用制表符('\t')、空格或其他字符。正确指定分隔符是确保数据正确读取的关键。

  • skiprows: 该参数允许用户指定在开始读取数据之前应跳过的行数。这对于忽略文件头部的标题行或注释行非常有用。例如,skiprows=1会跳过文件的第一行。

  • dtype: 默认情况下,np.loadtxt尝试将读取的数据转换为浮点数。但是,如果数据集中包含非数值数据(如字符串或日期),则需要通过dtype参数指定数据类型。例如,dtype='str'会将所有数据读取为字符串类型。

  • encoding: 当处理非英文字符的文件时,指定正确的文件编码是必要的,以防止编码错误。encoding='utf-8'是一个常见的选项,适用于包含UTF-8编码字符的文件。

信息

进阶用法和注意事项:

  • 处理非数值数据: 除了dtype参数,NumPy提供了converters参数,允许对特定列应用转换函数,以处理文件中的非标准数据,如将日期字符串转换为日期对象。

  • 读取部分列: 使用usecols参数可以指定一个列索引列表,只读取感兴趣的列,提高读取效率。

  • 内存管理: 对于大型文件,np.loadtxt可能会消耗大量内存。在这种情况下,考虑使用np.genfromtxt,它提供了更灵活的数据加载机制,包括处理缺失值。

  • Excel文件: 正如笔记中提到的,NumPy不能直接读取Excel(.xlsx)文件。需要先将Excel文件转换为CSV格式或使用专门的库(如pandas)来读取Excel文件。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

# path = './工作/8.Advertising.csv'

# numpy读入
df = np.loadtxt('./LC.csv', delimiter=',',
skiprows=1,dtype='str',encoding='utf-8')

print(df)
# 那我们是不是可以直接把括号里面的path直接换成路径呢?
# 其次注意numpy不能直接读取excel文件,就是xlsx后缀的文件,得先将文件存储为csv文件

创建数组

NumPy的array()函数是创建数组的基础工具,它允许从多种数据结构(如列表、元组、数组、生成器等)创建数组。此功能的强大之处在于它提供了一种高效的方式来存储和操作大量数据,这是NumPy库核心功能之一。

NumPy数组可以包含多种数据类型(dtype),如int(整数)、float(浮点数)、str(字符串)等。在创建数组时,可以通过dtype参数显式指定数组的数据类型,这对于优化内存使用和提高计算效率至关重要。

创建一维数组

  • 直接从列表或元组创建:通过传递一个列表或元组给np.array()函数,可以创建一个一维数组。数组的数据类型会根据列表或元组中的元素自动确定,但也可以通过dtype参数显式指定。array()函数创建,括号内可以是列表、元祖、数组、生成器等

    1
    2
    3
    import numpy as np
    a = np.array([1, 2, 3])
    print(a)

    输出的数组a是一个一维数组,包含了三个整数元素。

  • 使用数组属性

    • a.ndim:显示数组的维数。对于一维数组,此属性值为1。
    • a.shape:返回一个表示数组形状的元组,对于一维数组,形状会显示为(n,),其中n是数组中元素的数量。
    • a.size:表示数组中元素的总数。
    • a.dtype:显示数组元素的数据类型,例如int64
    • a.itemsize:数组中每个元素的字节大小。
    • a.data:指向数组数据的内存缓冲区。

创建数组的其他方式

  • 直接传入列表的方式

    1
    2
    3
    a= np.array([1,2,3,4])
    print(a)
    print(type(a))
  • 通过range生成序列

    1
    2
    b = np.array(range(5))
    print(b)

    这会创建一个包含0到4的整数的一维数组。

  • 使用np.linspace()生成等间距数值

    np.linspace()函数用于在指定的区间内生成等间隔的数值。它接受起始值、终止值和数值数量作为输入,并生成一个一维数组。如果endpoint参数设置为True(默认值),则包含终止值;如果为False,则不包含。

    参数解释:

    • start:序列的起始值。
    • stop:序列的结束值。
    • num:生成的样本数,默认为 50。
    • endpoint:如果为 True(默认),则包括停止值;如果为 False,则不包括停止值。
    • retstep:如果为 True,则返回
    • (samples, step),其中
    • step 是样本之间的间距。
    • dtype:输出数组的数据类型。
    • axis:生成数组的轴。
    1
    2
    3
    numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
    arr = np.linspace(0, 10, 5)
    print(arr)

    这将在0到10之间均匀分布生成5个数值的数组。

信息

高级创建数组方法

除了上述方法,NumPy还提供了其他函数来创建特殊数组:

  • **np.zeros()**:创建一个所有元素都为0的数组。
  • **np.ones()**:创建一个所有元素都为1的数组。
  • **np.empty()**:创建一个未初始化的数组,其元素值不确定。
  • **np.arange()**:类似于Python的range,但返回的是数组。
  • **np.full()**:创建一个所有元素都设定为指定值的数组。

创建二维数组

列表嵌套转换为ndarray

使用numpy库可以方便地将嵌套列表转换为二维数组。这是一种常见的创建二维数组的方法,适用于已经有一个列表数据结构,希望将其转换为numpy的ndarray格式进行更高效的数值计算。

1
2
3
4
5
6
7
8
9
10
import numpy as np

# 创建列表
lst = [[1, 2], [3, 4], [5, 6]]

# 转换为numpy的二维数组
num1 = np.array(lst)

# 打印二维数组
print(num1)

使用元组创建浮点型二维数组

通过将元组传递给np.array()函数,可以直接创建二维数组。如果元组中包含浮点数,则数组的数据类型会自动适应为浮点型,这对于需要进行浮点数计算的场景非常有用。

1
2
3
4
5
# 使用元组创建,并指定为浮点型
num2 = np.array([(1.5, 2, 3), (4, 5, 6)])

# 打印二维数组
print(num2)

各种类型的数组创建

Numpy提供了灵活的方式来创建数组,这些方法可以满足不同场景的需求。

  1. 使用np.array()创建一维数组

    • range()创建数组
    • 直接传递单个数值创建零维数组(标量)
    • 传递列表创建一维数组
    • 传递嵌套列表创建二维数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import numpy as np

    ar1 = np.array(range(10))
    ar2 = np.array(10)
    ar3 = np.array([1, 2, 3, 4, 5])
    ar4 = np.array([[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]])

    print(ar1)
    print(ar2)
    print(ar3)
    print(ar4)
  2. 使用np.arange()创建数组

    np.arange()是一个非常有用的函数,它在指定的间隔内返回等间隔的值。它类似于Python内置的range()函数,但np.arange()可以生成浮点数序列,并且可以直接用于创建numpy数组。

    1
    2
    3
    4
    5
    print(np.arange(10))  # 整型,0到9
    print(np.arange(10.0)) # 浮点型,0.0到9.0
    print(np.arange(5, 15)) # 5到14
    print(np.arange(5.0, 12.0, 2)) # 5.0到11.0,步长为2
    print(np.arange(100000000)) # 大数组,numpy自动优化显示

数组的数据类型

在创建数组时,numpy会根据提供的数据自动确定数组的数据类型,但也可以通过dtype参数显式指定数据类型。这对于需要控制数据存储和计算精度的应用场景非常重要。

1
2
3
4
5
6
7
8
# 创建整型数组
arr_int = np.array([1, 2, 3], dtype=np.int32)

# 创建浮点型数组
arr_float = np.array([1, 2, 3], dtype=np.float64)

print(arr_int.dtype)
print(arr_float.dtype)

三维数组在NumPy中是一种非常有用的数据结构,它可以被视为一个由多个二维数组(矩阵)组成的数据立方体。这种结构非常适合表示多维数据集,比如时间序列数据、空间数据或多通道图像数据(例如彩色图像的RGB通道)。

创建三维数组

三维数组的创建可以通过直接传递一个三层嵌套的列表(或元组)给np.array()函数,也可以使用特定的NumPy函数如np.zeros(), np.ones(), np.empty(), 或np.random.rand()等,来创建具有特定形状的三维数组。

通过列表创建三维数组

1
2
3
4
5
6
7
8
import numpy as np

# 创建一个三维数组
# 这里可以看成是由3个2x2的矩阵组成
lst_3d = [[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]]
arr_3d = np.array(lst_3d)

print(arr_3d)

使用NumPy函数创建三维数组

1
2
3
4
5
6
7
8
9
10
11
12
# 使用np.zeros创建一个3x2x2的三维数组,所有元素初始化为0
arr_zeros = np.zeros((3, 2, 2))

# 使用np.ones创建一个3x3x3的三维数组,所有元素初始化为1
arr_ones = np.ones((3, 3, 3))

# 使用np.random.rand创建一个3x4x2的三维数组,所有元素为随机数
arr_random = np.random.rand(3, 4, 2)

print("Zeros:\n", arr_zeros)
print("Ones:\n", arr_ones)
print("Random:\n", arr_random)

访问三维数组元素

访问三维数组的元素类似于访问二维数组,但需要指定三个索引:第一个索引表示哪一个二维数组(矩阵),第二和第三个索引分别表示该二维数组中的行和列。

1
2
3
# 访问第一个二维数组的第二行第一列的元素
element = arr_3d[0, 1, 0]
print(element)

三维数组的应用

三维数组在科学计算、数据分析、图像处理等领域有广泛的应用。例如,在图像处理中,彩色图像可以表示为一个三维数组,其中两个维度对应于图像的宽度和高度,第三个维度用于表示颜色通道(如RGB)。

数学函数和统计函数

NumPy数组操作实例

首先,这是创建和打印NumPy数组的基础代码:

1
2
3
import numpy as np
arr1 = np.array([1, 7, 2, 19, 23, 0, 88, 11, 6, 11])
print(arr1)

基本统计操作

  • 最大值和最小值

    • arr1.max()返回数组中的最大值。
    • arr1.min()返回数组中的最小值。
  • 平均值和中位数

    • arr1.mean()计算数组所有元素的平均值。
    • np.median(arr1)计算数组中的中位数,注意是使用np.median()而不是arr1.median()
  • 索引操作

    • arr1.argmax()返回数组中最大值的索引。
    • arr1.argmin()返回数组中最小值的索引。
    • np.argwhere(arr1 > 20)返回数组中所有大于20的元素的索引。

聚合操作

  • 总和和累积总和

    • np.sum(arr1)计算数组元素的总和。
    • np.cumsum(arr1)计算数组元素的累积总和,即每个元素是原数组到该位置的所有元素的总和。

分散度测量

  • 标准差和方差

    • np.std(arr1)计算数组的标准差,衡量数据的分散程度。
    • np.var(arr1)计算数组的方差,方差越大,数据的分散程度越高。

数学函数

  • 指数和对数

    • np.exp(arr1)计算数组中所有元素的指数。
    • np.log(arr1)计算数组中所有元素的自然对数。注意,对数运算要求数组中的所有元素都必须大于0。

信息

扩展信息

  • 数据归一化

    在数据处理中,经常需要对数据进行归一化处理,以便将数据限制在特定的范围内。使用NumPy可以轻松实现这一点,例如通过将数组的每个元素减去数组的最小值然后除以数组的范围(最大值-最小值)来实现归一化。

  • 条件选择

    NumPy允许使用条件表达式选择数组中的元素,这对于数据分析和预处理非常有用。例如,arr1[arr1 > 10]将返回arr1中所有大于10的元素组成的数组。

  • 多维数组操作

    虽然这里主要讨论的是一维数组的操作,但NumPy的真正强大之处在于它可以无缝地处理多维数组(如二维数组或三维数组)。许多上述操作(如求和、最大值、最小值等)都可以沿指定轴在多维数组上执行。

随机数生成

整数随机数

NumPy的np.random.randint()函数用于生成指定范围内的随机整数。生成一个4行5列的随机整数数组,其中整数范围是从0(包含)到10(不包含)。

1
2
3
import numpy as np
arr2 = np.random.randint(0, 10, size=(4, 5))
print(arr2)
浮点数随机数

与此同时,np.random.uniform()函数用于生成一个给定范围内的浮点数随机数组。展示了如何创建一个4行5列的数组,并且如何使用np.around()函数来保留结果至指定的小数位数。

1
2
3
4
arr2 = np.random.uniform(0, 10, size=(4, 5))
print(arr2)
arr2_rounded = np.around(arr2, decimals=2)
print(arr2_rounded)

计算平均值

计算数组的平均值是数据分析中的基本操作,axis参数允许指定沿哪个轴计算平均值。

  • 列平均值 (axis=0): 计算每列的平均值。
  • 行平均值 (axis=1): 计算每行的平均值。
1
2
3
4
# 列平均值
print(arr2.mean(axis=0))
# 行平均值
print(arr2.mean(axis=1))

数组连接与拆分

连接数组

numpy.concatenate()函数是连接两个或多个数组的工具。提供了列对齐和行对齐连接的示例,这非常有助于理解如何根据实际需求选择合适的axis参数。

1
2
3
4
5
6
7
8
9
10
11
# 列对齐连接
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6]])
result = np.concatenate((arr1, arr2), axis=0)
print(result)

# 行对齐连接
arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [8, 9]])
result = np.concatenate((arr1, arr2), axis=1)
print(result)
拆分数组

numpy.split()函数提供了一种灵活的方式来拆分数组。它可以根据指定的段数均匀拆分数组,也可以根据提供的索引数组来拆分。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 均匀拆分一维数组
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
result = np.split(arr, 3)
print(result)

# 按照索引拆分
result = np.split(arr, [2, 5])
print(result)

# 二维数组的拆分
arr1 = np.array([[1, 2], [3, 4]])
result = np.split(arr1, 2, axis=0)
print(result)

信息

扩展信息

  • 多维数组的连接与拆分numpy.concatenate()numpy.split()不仅限于处理一维或二维数组,它们也可以用于更高维度的数组。当处理多维数组时,选择正确的axis参数变得尤为重要。

  • 其他数组操作函数:NumPy提供了其他函数如np.vstack()(垂直栈)、np.hstack()(水平栈)以及np.array_split()(允许不等量的分割),这些函数提供了更多

灵活性在数组的操作上。

  • 随机数种子:为了使随机数生成的结果可复现,可以通过np.random.seed()函数设置随机数生成的种子。

NumPy数组的shape属性

  • 获取数组形状shape属性是NumPy数组的内置属性,用于返回数组的维度信息。这是理解和操作多维数组的基础。
    • 对于一维数组,shape返回的是一个元组,其中只有一个元素,表示数组中的元素数量。
    • 对于二维数组,shape返回的是两个元素的元组,分别表示数组的行数和列数。
    • 对于高维数组,shape将以此类推,返回每个维度的大小。
1
2
3
# 示例代码
print(arr1.shape) # 打印arr1的形状
print(arr2.shape) # 打印arr2的形状

数组类型的转换

  • 使用astype方法astype方法用于数组的数据类型转换。这个方法返回数组的一个新副本,其中的元素已转换为指定的类型。
  • 更新的数据类型表示:在新版本的NumPy中,建议使用Python标准的数据类型名称,如float代替np.float,以避免弃用警告或错误。
1
2
3
4
# 示例代码
arr3 = arr1.astype(float) # 将arr1的数据类型转换为float
print(arr3)
print(arr3.dtype) # 查看转换后的数据类型
  • 数据类型和显示方式:转换为浮点数后,即使是0值也会显示为0.0,这是浮点数表示法的一部分,并不影响值的实际大小或精度。

NumPy数据类型概览

  • 布尔型bool,表示真值TrueFalse,使用一个字节存储。
  • 整型:包括int8, int16, int32, int64及其无符号版本uint8, uint16, uint32, uint64,分别对应不同的大小范围。
  • 浮点型float16, float32, float64(或简写为float),用于表示小数,具有不同的精度和表示范围。
  • 复数型complex64, complex128(或简写为complex),用于表示复数,其中实部和虚部分别由浮点数表示。

创建特殊数组

全零数组

  • np.zeros()函数非常适用于初始化固定大小的数组。全零数组在算法初期的数据准备阶段特别有用,可以作为数据容器的起点。
1
2
3
4
5
6
7
# 一维全零数组
zeros_array = np.zeros(5)
print(zeros_array)

# 二维全零数组
zeros_array = np.zeros((2, 3))
print(zeros_array)

全一数组

  • np.ones()函数创建的是全一数组,这在需要初始化为特定值的数组时非常有用,例如在某些算法中初始化权重。
1
2
3
4
5
6
7
# 一维全一数组
ones_array = np.ones(5)
print(ones_array)

# 二维全一数组
ones_array = np.ones((2, 3))
print(ones_array)

未初始化数组

  • np.empty()函数用于创建未初始化的数组。这种数组的内容是内存中的随机值,因此np.empty()np.zeros()np.ones()更快,但使用时需要注意其值是不确定的。
1
2
3
4
5
6
7
# 一维未初始化数组
empty_array = np.empty(5)
print(empty_array)

# 二维未初始化数组
empty_array = np.empty((2, 3))
print(empty_array)

数组结构的变化

获取数组形状

  • 使用.shape属性而不是.shape()方法来获取数组的形状。这是一个常见的误区。
1
2
ar1 = np.random.randint(0, 10, size=(4, 5))
print(ar1.shape)

转置操作

  • .T属性用于数组的转置。对于二维数组,它会交换数组的行和列。对于一维数组,.T不会改变数组。
1
2
3
ar2 = np.random.randint(0, 10, size=(4, 5))
print(ar2)
print(ar2.T)

改变数组形状

  • np.reshape()函数用于在不改变数据的前提下改变数组的形状。重塑后的数组元素总数必须与原始数组相同。
1
2
3
4
5
6
7
8
9
10
11
arr1 = np.array([1, 7, 2, 19, 23, 0, 88, 11, 6, 11])
reshaped_arr = np.reshape(arr1, (2, 5))
print(reshaped_arr)

# 把二维变一维
reshaped_arr = np.reshape(arr1, (1, 10))
print(reshaped_arr)

# 使用arange生成的一维数组变成二维
arr2 = np.reshape(np.arange(20), (4, 5))
print(arr2)

信息

进阶提示

  • 数组重塑的灵活性np.reshape()可以接受一个参数为-1,这意味着该维度的大小将自动计算。这在将多维数组平坦化为一维数组时非常有用,或者在不知道某个维度具体大小但知道总元素数量时使用。

  • 内存顺序order参数在np.zeros(), np.ones(), np.empty()函数中指定了数组在内存中的布局。'C'代表C语言风格,即行优先;'F'代表Fortran风格,即列优先。了解这一点对于性能优化尤其重要。

NumPy数组的操作与迭代

数组的操作

数组的切片和索引

一维数组的索引

一维数组的索引相对简单直观。通过指定下标,可以访问数组中的特定元素或子数组。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

ar = np.arange(20)
print(ar) # 打印数组中的所有元素

# 索引下标取值
print(ar[4]) # 获取下标为4的元素
print(ar[3:6]) # 获取下标为3到5的元素,注意这里是左闭右开区间

# 使用步长进行索引
print(ar[2:8:2]) # 从下标2开始到下标7结束,步长为2

负值索引: 负数索引允许从数组的末尾开始计数,例如,-1代表最后一个元素,-2代表倒数第二个元素,依此类推。

1
2
3
print(ar[-1])  # 获取最后一个元素
print(ar[-1:-8:-1]) # 获取最后一个到倒数第八个元素,步长为-1
print(ar[9:0:-1]) # 从下标9开始到1(不包括0),步长为-1

注意,当使用负数步长时,起始索引应该大于结束索引。

完整数组索引:

1
2
3
4
print(ar[:])  # 获取数组的所有元素
print(ar[:9]) # 获取从开始到下标8的所有元素
print(ar[9:]) # 获取从下标9到数组结束的所有元素
print(ar[::-1]) # 获取数组的所有元素,反向

二维数组索引

二维数组的索引更加复杂,涉及行和列的概念。NumPy数组的索引遵循[行, 列]的格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ar = np.arange(16).reshape(4,4)
print(ar) # 打印二维数组

# 行取值
print(ar[2:3]) # 获取第3行的数据

# 列取值
print(ar[:,2:3]) # 获取第3列的数据

# 复杂索引示例
# 如果想取出1,2,5,6,9,10,13,14这些值
print(ar[0:4, 1:3])

# 如果想取出10,11,14,15这些值
print(ar[2:, 2:])

注意,在二维数组中,:用于表示选取该维度的所有数据。

三维数组索引

三维数组的索引引入了更多层次,通常表示为[深度, 行, 列]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ar = np.arange(27).reshape(3,3,3)  # 创建一个三维数组
print(ar)

# 获取第一个二维数组
print(ar[0,:,:])

# 获取所有二维数组中的第一行
print(ar[:,0,:])

# 获取所有二维数组中的第一列
print(ar[:,:,0])

# 更复杂的索引
# 获取第二个二维数组中,第二行及之后的数据
print(ar[1, 1:,:])

在三维数组中,可以通过指定每个维度的索引来访问特定的元素、行、列或子数组。这些示例展示了如何通过多维索引来操作和访问数组中的数据。

数据的排序

排序是数据处理中的一个基本操作,它按照一定的顺序重新排列数据元素。NumPy提供了numpy.sort()函数来执行数组的排序。

NumPy排序函数

numpy.sort()是NumPy中用于排序的函数,它的参数如下:

  • a: 要排序的数组。
  • axis: 指定沿着哪个轴进行排序。默认值为-1,意味着沿最后一个轴排序。
  • kind: 选择排序算法。可选值包括'quicksort''mergesort''heapsort'。默认为'quicksort'
  • order: 当数组是结构化数组时,这个参数可以指定根据哪个字段进行排序。

示例代码:

1
2
3
4
5
6
7
8
import numpy as np

arr = np.array([3, 1, 2, 4])
sorted_arr = np.sort(arr) # 默认使用快速排序
print(sorted_arr) # 输出:[1 2 3 4]

sorted_arr = np.sort(arr, kind='mergesort') # 使用归并排序
print(sorted_arr) # 输出:[1 2 3 4]

归并排序算法

归并排序是一种分治策略的算法,通过递归地将数组分割成更小的数组,直到每个子数组只有一个元素,然后将这些数组合并成一个有序数组。

归并排序的步骤:

  1. 分割(Divide): 将数组不断二分,直到每个子数组只包含一个元素。
  2. 合并(Merge): 将分割后的子数组合并,过程中将它们排序。

Python实现示例:

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
32
33
34
35
36
37
def merge_sort(arr):
if len(arr) <= 1:
return arr

# 分割数组
mid = len(arr) // 2
left_arr = arr[:mid]
right_arr = arr[mid:]

# 递归排序左右两部分
left_arr = merge_sort(left_arr)
right_arr = merge_sort(right_arr)

# 合并排序后的两部分
return merge(left_arr, right_arr)

def merge(left_arr, right_arr):
result = []
i = j = 0
while i < len(left_arr) and j < len(right_arr):
if left_arr[i] < right_arr[j]:
result.append(left_arr[i])
i += 1
else:
result.append(right_arr[j])
j += 1

# 将剩余元素添加到结果数组中
result.extend(left_arr[i:])
result.extend(right_arr[j:])

return result

# 测试归并排序
arr = [38, 27, 43, 3, 9, 82, 10]
sorted_arr = merge_sort(arr)
print("排序后的数组:", sorted_arr)

这份笔记概述了在NumPy中进行二维数组排序、使用结构化数据以及特殊函数numpy.argsort()的基本应用。我将对这些主题进行扩展和细化,以提供更加全面和专业的视角。


二维数组排序

在NumPy中,对二维数组进行排序可以根据指定轴进行。这意味着可以按行或按列对数组中的元素进行排序。

沿着指定轴排序

通过设置axis参数,numpy.sort()允许指定沿数组的哪个轴进行排序:

  • axis=0: 沿着第一个轴(垂直方向,即列)进行排序,即对每一列的元素进行排序。
  • axis=1: 沿着第二个轴(水平方向,即行)进行排序,即对每一行的元素进行排序。

示例代码:

1
2
3
4
5
6
7
8
import numpy as np

arr = np.array([[3, 1], [2, 4]])
sorted_arr_axis0 = np.sort(arr, axis=0) # 沿着第一个轴(行)排序
sorted_arr_axis1 = np.sort(arr, axis=1) # 沿着第二个轴(列)排序

print(sorted_arr_axis0) # 输出:[[2 1] [3 4]]
print(sorted_arr_axis1) # 输出:[[1 3] [2 4]]

数组结构优化:结构化数据

NumPy支持创建结构化数组,这种数组可以包含不同类型的数据,类似于数据库表或Excel电子表格中的数据。

结构化数组的排序

使用结构化数组时,numpy.sort()函数允许通过order参数指定根据哪个字段进行排序。

示例代码:

1
2
3
4
5
6
7
8
import numpy as np

dtype = [('name', 'S10'), ('age', int)]
values = [('Tom', 25), ('Kate', 22), ('John', 30)]
arr = np.array(values, dtype=dtype)
sorted_arr = np.sort(arr, order='age') # 按照 'age' 字段排序

print(sorted_arr) # 输出按照年龄升序排列的结构化数组

访问结构化数组中的数据

可以通过字段名访问结构化数组中的数据,这使得操作结构化数据变得非常方便。

1
2
3
4
5
6
7
# 输出所有人的姓名和年龄
print("所有人的姓名:", arr['name'])
print("所有人的年龄:", arr['age'])

# 访问特定位置的姓名和年龄
print("第二个人的姓名:", arr[1]['name'])
print("第三个人的年龄:", arr[2]['age'])

这份笔记已经概述了NumPy中一些特殊函数的使用,包括numpy.argsort(), numpy.lexsort(), numpy.insert(), numpy.delete(), 和numpy.unique()等函数,以及数组堆叠的基本概念。下面我将对这些主题进行扩展和细化,以提供更加全面和专业的视角。

特殊函数

numpy.argsort()

  • 功能: 返回数组排序后的索引。
  • 语法: numpy.argsort(a, axis=-1, kind='quicksort', order=None)
    • a: 要排序的数组。
    • axis: 沿着哪个轴排序,默认为 -1,表示沿最后一个轴排序。
    • kind: 排序算法,可选'quicksort''mergesort''heapsort',默认为 'quicksort'
    • order: 结构化数组排序时指定字段。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

# 一维数组示例
arr = np.array([3, 1, 2, 4])
sorted_indices = np.argsort(arr)
print(sorted_indices) # 输出:[1 2 0 3]

# 二维数组示例
ar = np.reshape(np.arange(16), (4,4))
sorted_indices = np.argsort(ar)
print(sorted_indices) # 输出每行元素排序后的索引

numpy.lexsort()

  • 功能: 对多个序列进行间接排序。
  • 语法: numpy.lexsort(keys, axis=-1)
    • keys: 排序的键值对,多个序列的元组或数组。
    • axis: 排序轴,默认为 -1

示例代码:

1
2
3
4
names = np.array(['Tom', 'Kate', 'John'])
ages = np.array([25, 22, 30])
sorted_indices = np.lexsort((names, ages)) # 先按ages排序,若相同则按names排序
print(sorted_indices) # 输出:[1 0 2]

数据的添加

在你的笔记中,你提到了使用np.append()函数在NumPy数组中添加元素的不同情况,包括未指定轴时的行为以及如何沿特定轴添加元素。下面,我将对这部分内容进行更专业和详细的整理,同时纠正一些错误用法。

numpy.append()

np.append() 函数用于在数组的末尾添加元素。这个函数可以处理不同维度的数组,但其行为会根据是否指定axis参数而改变。

未指定轴(默认行为)

当未指定axis参数时,np.append()会先将输入数组扁平化为一维数组,然后再添加元素。

1
2
3
4
5
6
import numpy as np

arr0 = np.array([[1, 2, 3], [4, 5, 6]])
# 未指定轴,结果为一维数组
result = np.append(arr0, [[7, 8, 9]])
print(result) # 输出: [1 2 3 4 5 6 7 8 9]

这说明,如果没有明确指定axis,无论原数组的维度如何,结果都会是一维数组。

指定轴进行添加
  • 沿着行添加(axis=0

当指定axis=0时,元素或数组将会作为新行添加到原数组中。这要求新添加的数组在非指定轴的维度上与原数组相匹配。

1
2
3
4
5
6
7
# 沿着行(axis=0)添加
result = np.append(arr0, [[7, 8, 9]], axis=0)
print(result)
# 输出:
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
  • 沿着列添加(axis=1

沿列添加要求新添加的行数与原数组匹配。下面是一个正确的例子,展示了如何沿列正确添加元素:

1
2
3
4
5
6
# 沿着列(axis=1)正确添加
result = np.append(arr0, [[7], [8]], axis=1)
print(result)
# 输出:
# [[1 2 3 7]
# [4 5 6 8]]

注意,在尝试沿着列添加时,你需要确保添加的数组在行的数量上与原数组一致。下面是一个修正的示例,正确展示了如何沿列添加多个元素:

1
2
3
4
5
6
7
8
9
# 正确的沿列添加
result = np.append(arr0, [[7, 10], [8, 11], [9, 12]], axis=0)
print(result)
# 输出:
# [[ 1 2 3]
# [ 4 5 6]
# [ 7 10]
# [ 8 11]
# [ 9 12]]

numpy.insert()

  • 功能: 在指定位置插入元素。
  • 语法: numpy.insert(arr, obj, values, axis=None)
    • arr: 输入数组。
    • obj: 插入的索引位置。
    • values: 要插入的值或数组。
    • axis: 指定的轴向。

示例代码:

1
2
3
4
5
6
7
8
9
# 一维数组示例
arr = np.array([1, 2, 3, 4, 5])
arr = np.insert(arr, 2, 10)
print("插入后的数组:", arr) # 输出:[1 2 10 3 4 5]

# 二维数组示例
arr = np.array([[1, 2, 3], [4, 5, 6]])
arr = np.insert(arr, 1, [7, 8, 9], axis=0)
print(arr) # 在第1行后插入一行[7, 8, 9]

数据的删除与去重

这份笔记已经概述了numpy.delete()函数的使用,numpy.unique()函数的多个功能,以及如何使用numpy.hstack()numpy.vstack()进行数组的堆叠。我将对这些内容进行更详细的整理和补充,以提供一个全面的视角。

numpy.delete()函数

numpy.delete()用于删除数组中的元素。

  • 语法: numpy.delete(arr, obj, axis=None)

    • arr: 输入数组。
    • obj: 表示要删除的子数组的索引。
    • axis: 指定要从哪个轴删除元素。如果不指定,输入数组将会被展平。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import numpy as np

# 创建一个数组
a = np.arange(12).reshape(3, 4)
print(a)

# 未指定轴,数组被展平后删除指定索引的元素
print(np.delete(a, 8))

# 删除第二行
print(np.delete(a, 1, axis=0))

# 删除第二列
print(np.delete(a, 1, axis=1))

# 按索引删除多列
a = np.delete(a, [0, 2], axis=1)
print(a)

# 按索引删除多行
a = np.delete(a, [0], axis=0)
print(a)

numpy.unique()函数

numpy.unique()用于找出数组中的唯一值并返回已排序的结果。

  • 语法: numpy.unique(ar, return_index=False, return_inverse=False, return_counts=False, axis=None)

    • ar: 输入数组。
    • return_index: 如果为True,返回唯一值的原始索引。
    • return_inverse: 如果为True,返回重构原数组的索引数组。
    • return_counts: 如果为True,返回每个唯一值的出现次数。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = np.array([1, 2, 2, 3, 5, 6, 6, 7, 12, 6, 8])
u = np.unique(a)
print(u) # 返回唯一值

# 返回唯一值及其原始索引
u, indices = np.unique(a, return_index=True)
print(indices)

# 返回唯一值及其出现次数
u, counts = np.unique(a, return_counts=True)
print(counts)

# 返回重构原数组的索引数组
u, inverse = np.unique(a, return_inverse=True)
print(inverse)

数组的堆叠

  • **numpy.hstack()**:水平堆叠数组。
1
2
3
4
5
6
7
8
9
10
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
ar1 = np.hstack((a, b))
print(ar1)

# 对于一维数组
a = np.arange(5)
b = np.arange(5, 9)
ar1 = np.hstack((a, b))
print(ar1)
  • **numpy.vstack()**:垂直堆叠数组。
1
2
3
4
5
6
7
8
9
10
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
ar2 = np.vstack((a, b))
print(ar2)

# 形状不同的数组也可以垂直堆叠
a = np.array([[1], [2], [3]])
b = np.array([['a'], ['b'], ['c'], ['d']])
ar2 = np.vstack((a, b))
print(ar2, ar2.shape)

信息

  • 当使用np.vstack()np.hstack()进行堆叠时,形状的兼容性是关键:对于np.hstack(),输入数组必须在除了第一个轴以外的维度上有相同的形状;对于np.vstack(),则是在第一个轴以外的其他轴必须形状相同。
  • 当形状不完全匹配时,可以通过调整数组形状或使用不同的堆叠函数(如np.concatenate())来实现堆叠。
  • 标题: NumPy
  • 作者: Yiuhang Chan
  • 创建于 : 2018-10-22 16:02:56
  • 更新于 : 2024-02-28 18:49:26
  • 链接: https://www.yiuhangblog.com/2018/10/22/20181022NumPy/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论