深入学习NumPy:掌握数组操作与应用的终极指南

1. NumPy简介

NumPy是Python中一个重要的库,用于科学计算和数据分析。它提供了一个强大的多维数组对象(ndarray)和用于操作这些数组的函数。NumPy是许多其他Python科学计算库的基础,如Pandas、SciPy和Matplotlib。

NumPy的背景和目标

NumPy最初由Travis Olliphant于2005年创建,并且它是Numeric和Numarray两个库的继承者。NumPy的目标是为Python提供一种高效的数组处理方法,使得科学计算更加便捷和高效。

NumPy的主要特征和应用场景

NumPy的主要特征包括:

  • 强大的多维数组对象:NumPy的核心是ndarray对象,它可以存储任意维度的同类型数据。这种数组对象非常适合进行数值计算和数据处理。
  • 快速的数值运算:NumPy通过对数组的操作使用高效的C语言代码实现,因此在数值运算方面比纯Python代码更快速。
  • 广播机制:NumPy引入了广播机制,使得不同形状的数组可以进行运算,这样可以简化代码并提高运算效率。
  • 丰富的数学函数库:NumPy提供了大量的数学函数和线性代数操作,如三角函数、指数函数、统计函数、矩阵分解等,满足科学计算的需求。
  • 与其他科学计算库的兼容性:NumPy与其他常用的科学计算库(如SciPy、Pandas、Matplotlib)紧密结合,可以方便地与这些库一起使用。

NumPy在以下应用场景中得到广泛应用:

  • 数据分析和处理:NumPy的数组对象提供了高效的数据结构和操作,使得数据分析和处理变得更加便捷。它可以处理大量数据并进行快速的数值计算。
  • 科学计算:NumPy提供了丰富的数学函数和线性代数操作,适用于科学计算中的各种任务,如信号处理、图像处理、统计分析等。
  • 机器学习和人工智能:NumPy是许多机器学习和深度学习框架的基础。它提供了高效的数组操作和数学函数,可以加速算法的实现和运行。

NumPy与Python的关系

NumPy是用Python编写的,是Python科学计算生态系统中的核心组件之一。NumPy提供了高性能的数组对象和数组操作函数,使得Python在科学计算领域有了更强大的能力。

NumPy通过底层的C语言代码实现了数组操作的高效性,并通过Python的接口与用户进行交互。这使得NumPy既具有高性能的计算能力,又保持了Python的简洁和易用性。

NumPy还与Python的标准库和其他科学计算库紧密结合,使得用户可以方便地进行数据的读写、可视化和分析。通过NumPy,Python成为了一种强大的科学计算语言,受到了广泛的应用和开发者的喜爱。

2. NumPy数组

NumPy的核心是多维数组对象ndarray,它提供了一种高效存储和操作大量数据的方式。在本节中,我们将学习如何创建NumPy数组,了解数组的属性和方法,并掌握基本的数组操作。

创建NumPy数组

NumPy数组可以通过多种方式创建,如以下示例所示:

通过Python列表创建数组

import numpy as np

# 创建一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1)
# [1 2 3 4 5]

# 创建二维数组
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# [[1 2 3] 
#  [4 5 6]]

使用NumPy函数创建特殊数组

# 创建全零数组
zeros = np.zeros((2, 3))
print(zeros)
# [[0. 0. 0.] 
#  [0. 0. 0.]]

# 创建全一数组
ones = np.ones((3, 2))
print(ones)
# [[1. 1.]   
#  [1. 1.]   
#  [1. 1.]] 

# 创建指定范围的等间隔数组
range_arr = np.arange(0, 10, 2)
print(range_arr)
# [0 2 4 6 8]

# 创建指定大小的随机数组
random_arr = np.random.rand(2, 3)
print(random_arr)
# [[0.98409252 0.27752055 0.89984634]
#  [0.00264179 0.62695002 0.77690454]]

数组的属性和方法

NumPy数组具有许多有用的属性和方法,可以帮助我们了解和操作数组。以下是一些常用的属性和方法:

数组的形状和维度

arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.shape)   # 输出数组的形状
# (2, 3)
print(arr.ndim)    # 输出数组的维度
# 2

数组的数据类型

arr = np.array([1, 2, 3])
print(arr.dtype)   # 输出数组的数据类型
# int32

数组的大小和元素个数

arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr.size)    # 输出数组的大小,即元素的总个数
# 6
print(arr.itemsize)    # 输出数组中每个元素的字节大小
# 4

操作数组的基本操作

数组的索引和切片

arr = np.array([1, 2, 3, 4, 5])
print(arr[0])     # 输出第一个元素
# 1
print(arr[2:4])   # 输出索引为2和3的元素
# [3 4]

arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr[1, 2])  # 输出第二行第三列的元素
# 6
print(arr[:, 1])  # 输出所有行的第二列元素
# [2 5]

数组的变形

arr = np.array([1, 2, 3, 4, 5, 6])
# [1 2 3 4 5 6]
reshaped_arr = arr.reshape(2, 3)   # 将一维数组变形为二维数组
print(reshaped_arr)
# [[1 2 3]
#  [4 5 6]]

arr = np.array([[1, 2], [3, 4]])
# [[1 2]
#  [3 4]]
flattened_arr = arr.flatten()   # 将二维数组展平为一维数组
print(flattened_arr)
# [1 2 3 4]

数组的拼接

arr1 = np.array([1, 2, 3])
# [1 2 3]
arr2 = np.array([4, 5, 6])
# [4 5 6]
concatenated_arr = np.concatenate((arr1, arr2))   # 拼接两个数组
print(concatenated_arr)
# [1 2 3 4 5 6]

arr1 = np.array([[1, 2], [3, 4]])
# [[1 2]
#  [3 4]]
arr2 = np.array([[5, 6]])
# [[5 6]]
concatenated_arr = np.concatenate((arr1, arr2), axis=0)   # 沿行方向拼接
print(concatenated_arr)
# [[1 2]
#  [3 4]
#  [5 6]]

3. NumPy数据类型

NumPy提供了多种数据类型来存储和处理数组中的元素。在本节中,我们将学习常见的数据类型、数据类型转换以及如何自定义数据类型。

常见的数据类型

NumPy支持以下常见的数据类型:

  • bool:布尔值(True或False)
  • int:整数类型,根据平台可能是int32或int64
  • float:浮点数类型,根据平台可能是float32或float64
  • complex:复数类型,由两个浮点数表示实部和虚部
  • str:字符串类型
  • object:Python对象类型
  • datetime:日期时间类型
  • timedelta:时间间隔类型
  • np.void:Void类型,可以存储任意类型的数据

数据类型转换

在NumPy中,可以使用astype()函数进行数据类型转换。以下是一些常见的数据类型转换示例:

arr = np.array([1, 2, 3])
# [1 2 3]

# 转换为浮点数类型
float_arr = arr.astype(float)
print(float_arr)
# [1. 2. 3.]

# 转换为字符串类型
str_arr = arr.astype(str)
print(str_arr)
# ['1' '2' '3']

# 转换为布尔类型
bool_arr = arr.astype(bool)
print(bool_arr)
# [ True  True  True]

自定义数据类型

NumPy还允许用户自定义数据类型,以满足特定的需求。可以使用np.dtype()函数来定义自定义数据类型,并指定字段的名称和数据类型。以下是一个自定义数据类型的示例:

  • 对于定义的自定义数据类型,其中的字符串字段需要指定字符串的最大长度
  • 未指定字符串字段的最大长度,则默认为长度为0的空字符串,导致相关字段为空
  • 'U10'表示最大长度为10的Unicode字符串
# 定义自定义数据类型
person_dtype = np.dtype([('name', 'U10'), ('age', 'int'), ('height', 'float')])

# 创建一个数组并使用自定义数据类型
person_arr = np.array([('John', 25, 180.5), ('Alice', 30, 165.2)], dtype=person_dtype)
print(person_arr)
# [('John', 25, 180.5) ('Alice', 30, 165.2)]
print(person_arr[0]['name'])
# John

4. 数组计算

NumPy提供了丰富的数组计算方法、广播机制以及数学函数和线性代数操作,使得处理和操作数组变得更加便捷和高效。

数组的计算方法

使用NumPy,你可以对数组进行各种数学运算和统计计算。以下是一些常用的数组计算方法示例:

  • np.sum(arr):计算数组所有元素的总和。
  • np.mean(arr):计算数组所有元素的平均值。
  • np.std(arr):计算数组所有元素的标准差。
  • np.var(arr):计算数组所有元素的方差。
  • np.min(arr):找到数组中的最小值。
  • np.max(arr):找到数组中的最大值。
  • np.argmin(arr):找到数组中最小值的索引。
  • np.argmax(arr):找到数组中最大值的索引。
arr1 = np.array([1, 2, 3, 4, 5])
arr2 = np.array([6, 7, 8, 9, 10])

# 计算数组总和
sum_result = np.sum(arr1)
print("数组总和:", sum_result)
# 数组总和: 15

# 计算数组平均值
mean_result = np.mean(arr1)
print("数组平均值:", mean_result)
# 数组平均值: 3.0

# 计算数组标准差
std_result = np.std(arr1)
print("数组标准差:", std_result)
# 数组标准差: 1.4142135623730951

# 计算数组最大值
max_result = np.max(arr1)
print("数组最大值:", max_result)
# 数组最大值: 5

广播机制

广播机制(Broadcasting)是NumPy中一种强大且灵活的功能,它允许不同形状的数组在进行元素级别的运算时自动调整形状,以满足运算的要求。广播机制可以简化代码并提高计算的效率。

广播机制的核心原则是在进行运算时,NumPy会自动调整数组的形状,使其能够逐元素地进行计算。具体来说,广播机制会按照以下规则进行数组的形状调整:

  1. 如果两个数组的维度数不同,那么在较小的维度上添加长度为1的维度,直到两个数组的维度数相同。
    a. 维度数(number of dimensions)是指数组的维度或轴的数量。在NumPy中,数组的维度数可以通过ndim属性获取
    b. 长度为1的维度指的是在数组的形状中,某个维度的长度(即元素个数)为1
# 创建示例数组
arr1 = np.array([1, 2, 3])  # 形状为 (3,)
# [1 2 3]
arr2 = np.array([[4], [5], [6]])  # 形状为 (3, 1)
# [[4]
#  [5]
#  [6]]

# 在较小维度上添加长度为1的维度,使维度数相同
broadcasted_arr1 = arr1[:, np.newaxis]  # 添加维度后的形状为 (3, 1)

print("调整后的数组形状:")
print(broadcasted_arr1)
# [[1]
#  [2]
#  [3]]
print(arr2)
# [[4]
#  [5]
#  [6]]
  1. 如果两个数组的维度长度相同,或者其中一个数组在某个维度上的长度为1,那么这两个数组在该维度上是兼容的。
    a. 二维数组中,第一维度:行数;第二维度:列数;
# 创建示例数组
arr1 = np.array([1, 2, 3])  # 形状为 (3,)
# [1 2 3]
arr2 = np.array([[4, 5, 6]])  # 形状为 (1, 3)
# [[4 5 6]]

# 在维度长度相同的情况下,数组在该维度上是兼容的
result = arr1 + arr2

print("运算结果:")
print(result)
# [[5 7 9]]
  1. 如果两个数组在所有维度上的长度都不相等,且其中没有一个数组的长度为1,则无法进行广播,会引发错误。
# 创建示例数组
arr1 = np.array([1, 2, 3])  # 形状为 (3,)
# [1 2 3]
arr2 = np.array([4, 5, 6, 7])  # 形状为 (4,)
# [4 5 6 7]

# 无法进行广播,会引发错误
result = arr1 + arr2

# 运行到此处时会抛出 ValueError: operands could not be broadcast together with shapes (3,) (4,) 错误

广播机制的应用场景包括:

  • 对不同形状的数组进行元素级别的运算,如加法、乘法等。
# 广播示例1: 不同形状的数组相加
arr1 = np.array([1, 2, 3]) # 形状 (3, ) 维度 1
arr2 = np.array([[4, 5, 6], [7, 8, 9]]) # 形状 (2, 3) 维度 2
result2 = arr1 + arr2

# 为了使它们的维度数相同,arr1会在其形状中添加一个长度为1的维度,变为 (1, 3)
# [[1, 2, 3]]

print("不同形状的数组相加结果:")
print(result2)
# [[ 5  7  9]
#  [ 8 10 12]]
  • 在数组与标量之间进行运算,将标量广播到数组的每个元素。
# 广播示例2: 数组与标量的运算

scalar = 10
arr1 = np.array([1, 2, 3])

result1 = arr1 + scalar
print("数组与标量的运算结果:")
print(result1)
# [11 12 13]
  • 在数组与更高维度的数组之间进行运算,使其形状兼容。

数学函数及线性代数操作

NumPy提供了广泛的数学函数和线性代数操作,可以对数组进行各种数学运算和矩阵操作。以下是一些常用的数学函数和线性代数操作示例:

  • np.sin(arr):计算数组中每个元素的正弦值。
# 使用数学函数
sin_result = np.sin(arr1)
print("正弦值:", sin_result)
  • np.cos(arr):计算数组中每个元素的余弦值。
  • np.exp(arr):计算数组中每个元素的指数值。
  • np.log(arr):计算数组中每个元素的自然对数。
  • np.dot(arr1, arr2):计算两个数组的点积。
# 进行矩阵乘法
matrix1 = np.array([[1, 2], [3, 4]])
# [[1 2]
#  [3 4]]
matrix2 = np.array([[5, 6], [7, 8]])
# [[5 6]
#  [7 8]]
dot_result = np.dot(matrix1, matrix2)
print("矩阵乘法结果:")
print(dot_result)
# [[19 22]
#  [43 50]]
  • np.transpose(arr):计算数组的转置。
matrix1 = np.array([[1, 2], [3, 4]])
# [[1 2]
#  [3 4]]

# 计算数组的转置
transpose_result = np.transpose(matrix1)
print("数组转置结果:")
print(transpose_result)
# [[1 3]
#  [2 4]]
  • np.linalg.inv(arr):计算数组的逆矩阵。
matrix1 = np.array([[1, 2], [3, 4]])
# [[1 2]
#  [3 4]]

# 计算数组的逆矩阵
inv_result = np.linalg.inv(matrix1)
print("逆矩阵结果:")
print(inv_result)
# [[-2.   1. ]
#  [ 1.5 -0.5]]

5. 数组的读写存储

NumPy提供了方便的函数来读写和存储数组数据。在本节中,学习如何使用NumPy读取和写入数组数据。

读写数组

写入数组数据

使用NumPy的np.save()函数。该函数将数组数据保存到二进制文件中,以便以后读取使用。

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

# 写入数组数据
np.save('data.npy', arr)

读取数组数据

使用NumPy的np.load()函数。该函数从存储的二进制文件中加载数组,并返回一个包含数组数据的NumPy数组对象

# 读取数组数据
arr = np.load('data.npy')

# 打印数组
print(arr)

存储与加载数组

除了读写数组数据,NumPy还提供了更灵活的函数来存储和加载数组数据,如np.savez()和np.load()。

使用np.savez()函数可以将多个数组保存到一个压缩文件.npz中。

# 创建两个数组
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])

# 存储多个数组数据
np.savez('data.npz', array1=arr1, array2=arr2)

使用np.load()函数加载.npz文件时,它会返回一个类似于字典的对象,其中包含了被保存的数组数据。

# 加载多个数组数据
data = np.load('data.npz')

# 访问数组数据
arr1 = data['array1']
arr2 = data['array2']

# 打印数组
print(arr1)
print(arr2)

6. 数组的统计计算

在NumPy中,我们可以对数组进行各种统计计算。这些计算可以帮助我们了解数组中的数据分布、趋势和相关性等信息。

基本统计计算

平均值(mean):计算数组的平均值。
标准差(std):计算数组的标准差,衡量数据的离散程度。
方差(var):计算数组的方差,衡量数据的离散程度。
最小值(min):找到数组中的最小值。
最大值(max):找到数组中的最大值。
总和(sum):计算数组中所有元素的总和。
最小值索引(argmin):找到数组中最小值的索引。
最大值索引(argmax):找到数组中最大值的索引。

高级统计计算

百分位数(percentiles):计算数组中给定百分比的元素值。

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

# 计算50%的百分位数【计算50%的百分位数就是返回数组中的中位数】
percentile_value = np.percentile(arr, 50)  
print(percentile_value)
# 3.0

# 找到一个值,使得10%的元素小于或等于这个值
percentile_value = np.percentile(arr, 10)  
print(percentile_value)
# 1.4

中位数(median):计算数组的中位数。

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

median_value = np.median(arr)
print(median_value)
# 3.0

相关系数(corrcoef):计算数组的相关系数。

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

corrcoef_value = np.corrcoef(arr1, arr2)
print(corrcoef_value)
# [[ 1. -1.]
#  [-1.  1.]]

协方差(cov):计算数组的协方差矩阵。

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

cov_matrix = np.cov(arr1, arr2)
print(cov_matrix)
# [[ 2.5 -2.5]
#  [-2.5  2.5]]

直方图(histogram):计算数组的直方图。
直方图是一种用于可视化数据分布的图形表示方法。
它将数据划分为一系列称为“箱子”(或“柱”)的区间,并计算落入每个区间的数据点数量
直方图可以帮助我们了解数据的分布情况、数据的集中程度和数据的离散程度。

  • hist 是一个包含每个区间内数据点数量的数组
  • bins 是一个包含划分的区间边界值的数组
arr = np.array([1, 2, 3, 4, 5])

hist, bins = np.histogram(arr)
print(hist)
print(bins)
# [1 0 1 0 0 1 0 1 0 1]
# [1.  1.4 1.8 2.2 2.6 3.  3.4 3.8 4.2 4.6 5. ]

差分(diff):计算数组的差分值。
差分操作将计算相邻元素之间的差值

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

diff_arr = np.diff(arr)
print(diff_arr)
# [1 1 1 1]

梯度(gradient):计算数组的梯度。
数组的梯度是指数组中相邻元素之间的变化率或斜率
对于一维数组,梯度表示每个元素的变化幅度;
对于多维数组,梯度表示在每个维度上的变化幅度。

arr = np.array([1, 2, 3, 4, 5])
gradient_arr = np.gradient(arr)
print(gradient_arr)
# [1. 1. 1. 1. 1.]

7. 数组的复制和视图

在 NumPy 中,对数组进行复制和视图操作是常见的操作之一。理解复制和视图的概念及其区别对于正确处理数组非常重要。本教程将介绍深度复制、浅复制和视图的概念以及它们之间的区别。

深度复制

深度复制是指创建一个完全独立于原始数组的新数组,即在内存中生成一个完全相同的副本。对复制后的数组进行修改不会影响原始数组。

使用 copy() 方法可以进行深度复制。

arr = np.array([1, 2, 3, 4, 5])
arr_copy = arr.copy()

# 修改复制后的数组,不会影响原始数组
arr_copy[0] = 10

print("原始数组:", arr)
print("深度复制后的数组:", arr_copy)
# 原始数组: [1 2 3 4 5]
# 深度复制后的数组: [10  2  3  4  5]

浅复制

浅复制是指创建一个新的数组对象,但该对象与原始数组共享相同的数据内容。这意味着对浅复制后的数组进行修改会影响原始数组。

使用切片操作view()方法可以进行浅复制。

arr = np.array([1, 2, 3, 4, 5])
arr_shallow_copy = arr[:]

# 修改浅复制后的数组,会影响原始数组
arr_shallow_copy[0] = 10

print("原始数组:", arr)
print("浅复制后的数组:", arr_shallow_copy)
# 原始数组: [10  2  3  4  5]
# 浅复制后的数组: [10  2  3  4  5]

视图

视图是指创建一个新的数组对象,与原始数组共享相同的数据内容,但具有不同的维度大小或形状。对视图进行修改会影响原始数组,反之亦然。

使用view()方法可以创建视图。

arr = np.array([1, 2, 3, 4, 5])
arr_view = arr.view()

# 修改视图的形状和大小
arr_view.shape = (5, 1)
arr_view[1] = 10

print("原始数组:", arr)
print("视图数组:", arr_view)
# 原始数组: [ 1 10  3  4  5]
# 视图数组: [[ 1]
#  [10]
#  [ 3]
#  [ 4]
#  [ 5]]

你可能感兴趣的:(深入学习NumPy:掌握数组操作与应用的终极指南)