引言
当涉足数据分析世界,无论是初学者还是经验丰富的分析师,都会发现Python是一个强大而灵活的工具。而在Python的数据分析生态系统中,NumPy(Numerical Python)是一个不可或缺的库,它提供了广泛的数学和统计函数,以及多维数组操作的能力。
在本博客中,将探讨NumPy库的核心功能,从创建和操作多维数组,到使用广播机制进行数组计算,再到了解结构化数组和矩阵计算。NumPy不仅提供了强大的数学工具,还是数据分析、机器学习和科学计算的基础。
无论你是希望提高数据分析技能,还是需要深入了解NumPy的各种用途,这篇博客都将为你提供宝贵的知识。让我们一起开始探索NumPy,掌握这个强大的数据分析工具!
结构体系
基于 NumPy 的知识体系梳理:
一、NumPy 基础
数组创建与操作:ndarray对象、数组切片、数组形状修改、数组数据类型、数组转换与复制
数组索引与切片:一维、二维和多维数组的索引与切片操作。
数组运算与广播:NumPy 数组支持元素级别的运算,也可以使用数学函数和线性代数函数对数组进行操作。
数组统计与排序:计算数组的最大值、最小值、平均数、中位数、方差、标准差等统计量;以及对数组进行排序。
随机数生成:使用 NumPy 可以生成各种类型的随机数,例如服从正态分布、均匀分布等的随机数。
二、NumPy 进阶
- 数组高级操作:布尔索引、花式索引、where 函数、迭代器等。
- 数组合并和拆分:vstack、hstack、concatenate 等函数可以将多个数组合并成一个数组,split、hsplit 和 vsplit 函数则可以将一个数组拆分成多个部分。
- 广义表与轴:广义表是由嵌套列表或数组组成的数据结构,可以用来表示树状、分层结构的数据。轴是表示数组维度的对象,通过指定轴可以对数组进行各种操作。
- 数组的内存布局:了解 NumPy 数组内存布局对于数组的性能优化非常重要,包括数组的 C 风格和 Fortran 风格的存储方式,以及如何使用 strides 属性来自定义数组内存布局。
- 结构化数组:结构化数组可以看作是表格或数据库中的行,每个元素包含多个字段,可以使用字段名或下标访问。
- 矩阵计算:NumPy 提供了专门的矩阵类 matrix,以及针对矩阵的一些特定运算函数,例如矩阵乘法、求逆矩阵等。
1.数组创建与操作
1.1 ndarray对象
ndarray
(N-dimensional array)是NumPy中的核心数据结构,用于表示多维数组。它提供了高效的数值计算和广泛的数学函数支持。
创建ndarray对象
在NumPy中,使用np.array()
函数来创建ndarray
对象。例如,创建一个简单的一维数组:
import numpy as np
# 创建一维数组
arr = np.array([1, 2, 3, 4, 5])
print(arr)
#输出
[1 2 3 4 5]
数组的属性
ndarray
对象有一些重要的属性:
- 形状(shape):数组的维度和大小。
- 数据类型(dtype):数组中元素的数据类型。
- 维度(ndim):数组的维度数量。
- 大小(size):数组中元素的总数。
# 数组的属性
print("形状:", arr.shape)
print("数据类型:", arr.dtype)
print("维度:", arr.ndim)
print("大小:", arr.size)
# 输出
形状: (5,)
数据类型: int32
维度: 1
大小: 5
多维数组
除了一维数组,NumPy还支持多维数组的创建。例如,创建一个二维数组:
# 创建二维数组
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(matrix)
# 输出
[[1 2 3]
[4 5 6]
[7 8 9]]
数组索引与切片
你可以使用索引和切片来访问数组中的元素。例如,访问一维数组的第三个元素:
# 访问一维数组的元素
print(arr[2]) # 输出 3
或者访问二维数组的特定元素:
# 访问二维数组的元素
print(matrix[1, 2]) # 输出 6
1.2 数组切片
切片一维数组
在一维数组中,使用切片来获取数组的子集。例如,创建一个包含一些整数的一维数组:
import numpy as np
arr = np.array([10, 20, 30, 40, 50])
# 使用切片获取数组的子集
subset = arr[1:4] # 从索引1到3(不包含4)
print(subset)
这将输出 [20 30 40]
,即从索引1到3的元素。
切片多维数组
在多维数组中,你可以使用切片来获取行、列或子数组。例如,创建一个二维数组:
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 使用切片获取行
row = matrix[1, :] # 获取第二行
print(row)
这将输出 [4 5 6]
,表示我们获取了第二行的所有元素。
# 使用切片获取列
column = matrix[:, 2] # 获取第三列
print(column)
这将输出 [3 6 9]
,表示我们获取了第三列的所有元素。
高级切片
NumPy允许使用更高级的切片操作,如跳跃切片、布尔值切片等,以满足更复杂的需求。例如,通过布尔值切片,可以根据某个条件选择数组中的元素。
# 布尔值切片示例
values = np.array([1, 2, 3, 4, 5])
condition = values > 2
result = values[condition]
print(result)
这将输出 [3 4 5]
,表示我们选择了大于2的元素。
继续我们的探讨,下一个要点是数组形状修改。在NumPy中,你可以轻松地改变数组的形状以满足你的需求。
1.3 数组形状修改
使用reshape()
方法
reshape()
方法允许改变数组的形状,而不改变其数据。例如,创建一个一维数组,然后将其形状改变为二维数组:
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
# 使用reshape()方法改变数组形状
reshaped_arr = arr.reshape(2, 3) # 将一维数组转换为二维数组
print(reshaped_arr)
这将输出一个二维数组:
[[1 2 3]
[4 5 6]]
使用ravel()
和flatten()
方法
需要将多维数组转换为一维数组,这时可以使用 ravel()
或 flatten()
方法。
# 使用ravel()方法将多维数组转换为一维数组
raveled_arr = reshaped_arr.ravel()
print(raveled_arr)
# 使用flatten()方法同样将多维数组转换为一维数组
flattened_arr = reshaped_arr.flatten()
print(flattened_arr)
这两种方法都会将多维数组转换为一维数组,不过
ravel()
有时会返回视图(视图与原数组共享数据),而flatten()
总是返回数组的副本(新数组)。
注意事项
在使用 reshape()
进行形状修改时,需要确保新形状兼容原数组的大小。否则,将会收到 ValueError
错误。例如,尝试将一个包含6个元素的一维数组改为3x3的形状将导致错误。
# 错误的形状修改示例
arr = np.array([1, 2, 3, 4, 5, 6])
reshaped_arr = arr.reshape(3, 3) # 这将引发错误
1.4 数组数据类型
默认数据类型
当创建一个数组时,NumPy会根据输入的数据自动选择一个合适的数据类型。例如,创建一个包含整数的数组,NumPy通常会使用整数数据类型。
import numpy as np
arr = np.array([1, 2, 3, 4])
print("默认数据类型:", arr.dtype)
显示指定数据类型
需要显式指定数组的数据类型,以确保计算的精度或满足特定需求。可以使用dtype
参数来指定数据类型:
# 显式指定数据类型
arr_float = np.array([1, 2, 3, 4], dtype=float)
print("指定数据类型为浮点数:", arr_float.dtype)
数据类型的影响
不同的数据类型会影响数组的内存占用和数值精度。例如,浮点数通常会占用更多内存,但具有更高的数值精度,而整数占用较少内存,但精度有限。
# 不同数据类型的影响
arr_int = np.array([1, 2, 3, 4], dtype=int)
arr_float = np.array([1, 2, 3, 4], dtype=float)
print("整数数组内存占用:", arr_int.itemsize)
print("浮点数数组内存占用:", arr_float.itemsize)
数据类型转换
使用astype()
方法来将数组的数据类型转换为其他类型:
# 数据类型转换
arr_int = np.array([1, 2, 3, 4], dtype=int)
arr_float = arr_int.astype(float)
print("转换后的数据类型:", arr_float.dtype)
1.5 数组转换与复制
数组的数据类型转换
在前面的部分,已经讨论了如何使用 astype()
方法将数组的数据类型转换为其他类型。这对于将数组从一个数据类型转换为另一个数据类型非常有用。例如:
import numpy as np
arr_int = np.array([1, 2, 3, 4], dtype=int)
arr_float = arr_int.astype(float) # 将整数数组转换为浮点数数组
复制和视图
当你对数组执行操作时,有时会创建原始数组的副本,有时会创建一个视图,它与原始数组共享数据。这可能会导致一些不同的行为。
- 副本:如果你明确要创建一个新的独立副本,可以使用
copy()
方法。
original_arr = np.array([1, 2, 3])
copied_arr = original_arr.copy() # 创建副本
- 视图:在某些情况下,切片和形状修改操作会返回一个视图,而不是副本。这表示数据在原始数组和视图之间共享。
original_arr = np.array([1, 2, 3, 4, 5])
sliced_arr = original_arr[1:4] # 创建一个切片(视图)
引用和复制的区别
理解视图和副本的区别非常重要。修改视图可能会影响原始数组,而修改副本则不会。例如:
original_arr = np.array([1, 2, 3, 4, 5])
sliced_arr = original_arr[1:4] # 创建一个切片(视图)
sliced_arr[0] = 99 # 修改切片,会影响原始数组
print(original_arr)
在上面的例子中,原始数组中的值也被修改为 [1, 99, 3, 4, 5]
,因为切片是一个视图,与原始数组共享数据。
original_arr = np.array([1, 2, 3, 4, 5])
copied_arr = original_arr.copy() # 创建副本
copied_arr[0] = 99 # 修改副本,不会影响原始数组
print(original_arr)
在这个例子中,原始数组保持不变,因为我们在副本上进行了修改
2. 数组索引与切片
2.1 一维数组的索引与切片
在NumPy中,一维数组的索引和切片操作非常类似于Python中的列表。示例:
import numpy as np
arr = np.array([10, 20, 30, 40, 50])
# 获取单个元素
element = arr[2] # 获取索引为2的元素,值为30
# 使用切片获取子数组
subset = arr[1:4] # 获取索引1到3的元素,子数组为 [20, 30, 40]
# 使用负数索引从尾部开始计数
tail_element = arr[-1] # 获取最后一个元素,值为50
# 使用步长切片
every_other = arr[::2] # 每隔一个元素,得到 [10, 30, 50]
# 修改元素的值
arr[3] = 42 # 将索引为3的元素修改为42
# 反转数组
reversed_arr = arr[::-1] # 得到 [50, 42, 30, 20, 10]
这些是一维数组索引和切片的基本操作。通过索引获取单个元素,通过切片获取子数组,使用负数索引从尾部开始计数,以及使用步长切片来创建新的数组。
2.2 二维和多维数组的索引与切片
在二维和多维数组中,索引和切片操作稍微复杂一些,因为需要考虑多个维度。示例:
import numpy as np
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 获取单个元素
element = matrix[1, 2] # 获取第二行第三列的元素,值为6
# 使用切片获取行或列
row = matrix[0, :] # 获取第一行,数组为 [1, 2, 3]
column = matrix[:, 1] # 获取第二列,数组为 [2, 5, 8]
# 获取子矩阵
submatrix = matrix[0:2, 1:3] # 获取第一行到第二行、第二列到第三列的子矩阵
在二维和多维数组中,使用逗号分隔的索引来访问不同维度的元素。还可以使用切片来获取行、列或子矩阵。
非常好,让我们继续探讨数组运算与广播,这是NumPy中非常重要的概念,允许你进行元素级别的运算以及应用数学函数和线性代数函数。
3. 数组运算与广播
3.1 元素级别的运算
NumPy数组支持元素级别的运算,可以对数组的每个元素进行操作,而不必编写循环。
import numpy as np
# 创建两个数组
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([5, 6, 7, 8])
# 加法
result_add = arr1 + arr2 # 对应元素相加
# 减法
result_subtract = arr1 - arr2 # 对应元素相减
# 乘法
result_multiply = arr1 * arr2 # 对应元素相乘
# 除法
result_divide = arr1 / arr2 # 对应元素相除
# 幂运算
result_power = arr1 ** 2 # 对每个元素进行平方运算
# 开方
result_sqrt = np.sqrt(arr1) # 对每个元素进行平方根运算
这些是一些常见的元素级别运算示例,但NumPy支持更多运算,包括三角函数、指数函数等。
3.2 广播
广播是NumPy中强大的特性之一,它允许对不同形状的数组进行运算。当你对不同形状的数组进行运算时,NumPy会自动扩展较小的数组以匹配较大的数组,使它们具有相同的形状,然后执行运算。
例如,将一个标量(如单个数字)与数组相乘:
import numpy as np
arr = np.array([1, 2, 3, 4])
result = arr * 2 # 数组中的每个元素都乘以2
在这个示例中,标量2被广播到数组arr
的每个元素上,以执行元素级别的乘法运算。
广播也适用于不同形状的数组,只要它们在某些维度上具有相同的大小。例如,将一个一维数组与一个二维数组相加:
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([[10, 20, 30], [40, 50, 60]])
result = arr1 + arr2
在这个示例中,一维数组arr1
被广播到与二维数组arr2
相同的形状,以执行元素级别的加法运算。
广播是NumPy中强大的功能,能够执行各种不同形状的数组之间的运算,而无需显式编写循环。
这是数组运算与广播的基础知识,NumPy提供了丰富的数学函数和线性代数函数,可以应用于数组,以进行更复杂的数学和统计分析。
继续我们的学习,下一个要点是数组统计与排序,在NumPy中,你可以轻松地执行各种统计操作和数组排序。
4. 数组统计与排序
4.1 数组的统计操作
NumPy提供了一系列用于计算数组统计量的函数,如最大值、最小值、平均数、中位数、方差、标准差等。
下面是一些常见的统计函数示例:
import numpy as np
arr = np.array([12, 45, 7, 23, 56, 19, 5])
# 计算数组的最大值
max_value = np.max(arr) # 输出 56
# 计算数组的最小值
min_value = np.min(arr) # 输出 5
# 计算数组的平均值
mean_value = np.mean(arr) # 输出 23.285714285714285
# 计算数组的中位数
median_value = np.median(arr) # 输出 19.0
# 计算数组的方差
variance = np.var(arr) # 输出 301.38775510204084
# 计算数组的标准差
std_deviation = np.std(arr) # 输出 17.387426361556492
这些函数可用于一维数组,也可以用于多维数组,并通过指定axis
参数来沿指定轴进行计算。
首先,创建一个二维数组,以进行示例:
import numpy as np
matrix = np.array([[12, 45, 7, 23],
[56, 19, 5, 8],
[33, 22, 11, 43]])
沿行进行统计
沿行计算统计量,例如每行的平均值,可以使用axis
参数指定轴0(行轴):
# 计算每行的平均值
row_means = np.mean(matrix, axis=0)
这将计算每一列的平均值,结果为 [33.66666667 28.66666667 7.66666667 24.66666667]
。
沿列进行统计
沿列计算统计量,例如每列的最大值,可以使用axis
参数指定轴1(列轴):
# 计算每列的最大值
column_max = np.max(matrix, axis=1)
这将计算每一行的最大值,结果为 [45 56 43]
。
这是一个简单的案例,说明如何使用axis
参数来沿指定轴执行统计操作。可以使用相同的方法来计算其他统计量,如最小值、中位数、方差等。
4.2 数组的排序
NumPy还提供了用于数组排序的函数,按升序或降序对数组进行排序。下面是一些示例:
import numpy as np
arr = np.array([12, 45, 7, 23, 56, 19, 5])
# 对数组升序排序 sort默认为升序
sorted_arr_ascending = np.sort(arr) # 输出 [ 5 7 12 19 23 45 56]
# 对数组降序排序
sorted_arr_descending = np.sort(arr)[::-1] # 输出 [56 45 23 19 12 7 5]
使用argsort()
函数来获取排序后的索引,以便对其他数组进行相同的排序操作。
# 获取排序后的索引
sorted_indices = np.argsort(arr)
继续我们的学习,下一个要点是随机数生成,在NumPy中,你可以使用随机数生成函数生成各种类型的随机数,包括服从正态分布、均匀分布等的随机数。
5. 随机数生成
5.1 生成均匀分布的随机数
使用np.random.rand()
函数生成服从均匀分布的随机数。这将生成在0到1之间均匀分布的随机数。以下是一些示例:
import numpy as np
# 生成一个均匀分布的随机数
uniform_random = np.random.rand() # 生成一个[0, 1)范围内的随机数
# 生成一个一维数组,包含多个均匀分布的随机数
uniform_array = np.random.rand(5) # 生成包含5个随机数的一维数组
# 生成一个二维数组,包含多个均匀分布的随机数
uniform_matrix = np.random.rand(3, 2) # 生成3x2的二维数组,包含均匀分布的随机数
5.2 生成正态分布的随机数
使用np.random.randn()
函数生成服从标准正态分布(均值为0,标准差为1)的随机数。以下是一些示例:
import numpy as np
# 生成一个标准正态分布的随机数
normal_random = np.random.randn() # 生成一个标准正态分布的随机数
# 生成一个一维数组,包含多个标准正态分布的随机数
normal_array = np.random.randn(5) # 生成包含5个标准正态分布的随机数的一维数组
# 生成一个二维数组,包含多个标准正态分布的随机数
normal_matrix = np.random.randn(3, 2) # 生成3x2的二维数组,包含标准正态分布的随机数
5.3 生成指定分布的随机数
NumPy还提供了其他随机数生成函数,可以生成不同分布的随机数,如二项分布、泊松分布等。使用np.random
模块来探索这些函数。
# 生成二项分布的随机数
binomial_random = np.random.binomial(10, 0.5, 5) # 生成5个符合二项分布的随机数
# 生成泊松分布的随机数
poisson_random = np.random.poisson(3, 5) # 生成5个符合泊松分布的随机数
这些函数非常有用,可以生成符合不同分布的随机数,以用于模拟、实验或统计分析。
非常好,我们将继续学习数组高级操作,包括布尔索引、花式索引、where
函数、迭代器等。
6. 数组高级操作
6.1 布尔索引
布尔索引是一种强大的工具,用于根据某些条件从数组中选择元素。创建一个布尔数组,其中每个元素表示与条件相对应的真值,然后使用该布尔数组来选择满足条件的元素。以下是一个示例:
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
# 创建一个布尔数组,满足条件的元素为True
condition = arr > 3
# 使用布尔索引选择满足条件的元素
selected = arr[condition] # 选择大于3的元素
print(selected) # 输出 [4 5 6]
6.2 花式索引
花式索引是一种通过指定索引数组来获取数组元素的方式。使用整数数组来选择元素,从而创建一个新的数组。以下是一个示例:
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
# 创建一个索引数组,用于选择元素
indices = np.array([1, 3, 5])
# 使用花式索引选择元素
selected = arr[indices]
print(selected) # 输出 [2 4 6]
6.3 where
函数
np.where()
函数是一个强大的工具,用于根据条件从数组中选择元素。它可以返回满足条件的元素的索引或实际的元素值。以下是一些示例:
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
# 使用np.where()选择满足条件的元素的索引
indices = np.where(arr > 3)
# 使用np.where()选择满足条件的元素的值 np.where(condition, x, y)
values = np.where(arr > 3, arr, 0)
print(indices) # 输出 (array([3, 4, 5]),)
print(values) # 输出 [0 0 0 4 5 6]
np.where()
函数是一个非常灵活的工具,它可以在不同的条件下返回不同的结果。
6.4 迭代器
NumPy数组可以使用Python标准的for
循环进行迭代,但更常见的做法是使用NumPy提供的迭代器。以下是一个示例:
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
# 使用NumPy迭代器遍历数组
for element in np.nditer(arr):
print(element)
继续我们的学习,下一个要点是数组合并和拆分,在NumPy中,你可以使用各种函数来将多个数组合并成一个数组,或将一个数组拆分成多个部分。
7. 数组合并和拆分
7.1 数组合并
在NumPy中,可以用不同的函数来合并多个数组,如 vstack
、hstack
、concatenate
等。以下是一些示例:
vstack
函数用于垂直堆叠(按行堆叠)多个数组:
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
# 使用vstack垂直堆叠数组
result = np.vstack((arr1, arr2))
print(result)
#输出
[[1 2 3]
[4 5 6]]
结果是一个新的数组,其中包含两个输入数组按行堆叠在一起。
hstack
函数用于水平堆叠(按列堆叠)多个数组:
import numpy as np
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
# 使用hstack水平堆叠数组
result = np.hstack((arr1, arr2))
print(result) #输出[1 2 3 4 5 6]
结果是一个新的数组,其中包含两个输入数组按列堆叠在一起。
concatenate
函数允许你指定要沿着哪个轴合并数组:
import numpy as np
# 定义两个一维数组
arr1 = np.array([1, 2, 3])
arr2 = np.array([4, 5, 6])
# 使用 concatenate 函数按行合并数组
result_row = np.concatenate((arr1, arr2), axis=0)
# 将一维数组转换为二维数组
arr1_2d = arr1.reshape(1, -1)
arr2_2d = arr2.reshape(1, -1)
# 使用 concatenate 函数按列合并二维数组
result_column = np.concatenate((arr1_2d, arr2_2d), axis=1)
print(result_row) # 输出 [1 2 3 4 5 6]
print(result_column) # 输出 [[1 4]
# [2 5]
# [3 6]]
7.2 数组拆分
NumPy也提供了函数来拆分数组,如 split
、hsplit
和 vsplit
。以下是一些示例:
split
函数可以将数组沿指定轴拆分成多个部分:
np.split(arr, indices_or_sections, axis=0)
import numpy as np
# 定义一个一维数组
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# 将数组分割成两个等份
result1 = np.split(arr, 2)
print(result1) # 输出 [array([1, 2, 3, 4, 5]), array([6, 7, 8, 9, 10])]
# 在索引 3 和 7 处进行分割
result2 = np.split(arr, [3, 7])
print(result2) # 输出 [array([1, 2, 3]), array([4, 5, 6]), array([7, 8, 9, 10])]
# 沿着列方向分割二维数组
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
result3 = np.split(arr_2d, 3, axis=1)
print(result3) # 输出 [array([[1], [4], [7]]), array([[2], [5], [8]]), array([[3], [6], [9]])]
结果是一个包含拆分后的数组部分的列表。
hsplit
函数用于水平拆分数组,vsplit
用于垂直拆分数组:
import numpy as np
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 使用hsplit函数水平拆分数组
hsplit_parts = np.hsplit(matrix, 3)
# 使用vsplit函数垂直拆分数组
vsplit_parts = np.vsplit(matrix, 3)
print(hsplit_parts)
print(vsplit_parts)
# 输出
[array([[1],
[4],
[7]]), array([[2],
[5],
[8]]), array([[3],
[6],
[9]])]
[array([[1, 2, 3]]), array([[4, 5, 6]]), array([[7, 8, 9]])]
这些函数允许你按照指定轴将数组拆分为多个部分。
8. 广义表与轴
8.1 广义表
广义表是一种由嵌套列表或多维数组组成的数据结构,它可以表示树状、分层结构的数据。在NumPy中,多维数组通常被视为广义表,其中每个维度对应于数据的一个层次结构。
例如,考虑一个包含学生成绩的二维数组,其中第一个维度表示不同的课程,第二个维度表示不同的学生。这可以被视为广义表,其中每个课程有一组学生成绩。以下是一个示例:
import numpy as np
# 创建包含学生成绩的二维数组
grades = np.array([[85, 92, 78],
[90, 88, 80],
[79, 85, 88]])
# grades 是一个广义表,其中每行表示一个课程的成绩
8.2 轴
在NumPy中,轴是表示数组的维度的对象。对于二维数组,通常有两个轴:0轴表示行,1轴表示列。对于三维数组,会有三个轴,以此类推。
轴在NumPy中非常重要,因为它们允许你在多维数组中执行各种操作。例如,可以使用轴来计算每一行或每一列的总和、平均值等。
以下是一些示例:
import numpy as np
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 计算每一行的总和(沿0轴)
row_sum = np.sum(matrix, axis=0)
# 计算每一列的总和(沿1轴)
column_sum = np.sum(matrix, axis=1)
print(row_sum) # 输出 [12 15 18]
print(column_sum) # 输出 [ 6 15 24]
理解NumPy数组的内存布局对于数组的性能优化至关重要。NumPy支持两种主要的内存布局方式:C风格和Fortran风格,并提供strides
属性来自定义数组的内存布局。让我们详细了解这些概念。
9. 数组的内存布局
9.1 C风格内存布局
C风格内存布局是NumPy中的默认方式,它与大多数编程语言的内存布局一致,数据以行为主存储。在C风格内存布局中,数组的元素在内存中是按行存储的,即相邻元素在相邻内存位置中。
下面是一个示例:
import numpy as np
arr = np.array([[1, 2, 3],
[4, 5, 6]])
print(arr.flags['C_CONTIGUOUS']) # 输出 True,表示C风格内存布局
9.2 Fortran风格内存布局
Fortran风格内存布局与C风格相反,它以列为主存储数据。在Fortran风格内存布局中,数组的元素在内存中是按列存储的,即相邻元素在相邻内存位置中。
下面是一个示例:
import numpy as np
arr = np.array([[1, 2, 3],
[4, 5, 6]])
print(arr.flags['F_CONTIGUOUS']) # 输出 False,表示不是Fortran风格内存布局
9.3 自定义数组内存布局
NumPy还允许自定义数组的内存布局,通过使用strides
属性。strides
是一个元组,指定了在每个维度上跨越数组元素所需的字节数。可以使用strides
属性来创建一个不连续的数组,以满足特定的需求。
下面是一个示例,创建一个具有自定义内存布局的数组:
import numpy as np
# 创建一个自定义内存布局的数组
arr = np.array([[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16, 17, 18]], dtype=np.int32)
# 设置自定义strides,使数组在每行之间有空隙
arr.strides = (24, 4) # 每行跨越24字节,每个元素之间跨越4字节
print(arr)
通过自定义strides
属性,可以创建特定内存布局的数组,以满足性能和数据处理需求。
10结构化数据
结构化数组是NumPy中的一个重要概念,它允许创建类似表格或数据库中的数据结构,每个元素都包含多个字段,可以使用字段名或下标来访问和操作数据。结构化数组通常用于处理异构数据,例如,每个元素包含不同类型的数据,如字符串、整数、浮点数等。
以下是如何创建和使用结构化数组的基本示例:
import numpy as np
# 创建一个结构化数组,定义字段名和数据类型
data_type = [('name', 'U10'), ('age', int), ('weight', float)]
# 创建一个空的结构化数组
people = np.array([], dtype=data_type)
# 添加数据到结构化数组
person1 = ('Alice', 25, 55.5)
person2 = ('Bob', 30, 68.2)
person3 = ('Charlie', 22, 61.8)
people = np.append(people, np.array([person1, person2, person3], dtype=data_type))
# 访问结构化数组中的数据
print(people['name']) # 输出 ['Alice' 'Bob' 'Charlie']
print(people['age']) # 输出 [25 30 22]
print(people['weight']) # 输出 [55.5 68.2 61.8]
在这个示例中,我们首先定义了结构化数组的数据类型,其中包含三个字段:’name’(字符串类型,最大长度为10),’age’(整数类型),’weight’(浮点数类型)。然后,我们创建了一个空的结构化数组,并向其中添加数据,最后通过字段名访问数组中的数据。
11矩阵计算
NumPy提供了专门的矩阵类matrix
,以及针对矩阵的一些特定运算函数,使矩阵计算更方便。虽然NumPy中的多维数组(ndarray
)可以执行矩阵计算,但matrix
类提供了更直观的语法和一些额外的矩阵运算功能。
以下是一些关于矩阵计算的基本示例:
import numpy as np
# 创建矩阵
matrix1 = np.matrix([[1, 2], [3, 4]])
matrix2 = np.matrix([[5, 6], [7, 8]])
# 矩阵乘法
result = matrix1 * matrix2
print(result)
# 求逆矩阵
inverse_matrix = matrix1.I
print(inverse_matrix)
# 矩阵转置
transpose_matrix = matrix1.T
print(transpose_matrix)
# 输出
[[19 22]
[43 50]]
[[-2. 1. ]
[ 1.5 -0.5]]
[[1 3]
[2 4]]
在上述示例中,首先创建了两个矩阵matrix1
和matrix2
,然后进行了矩阵乘法、求逆矩阵和矩阵转置的操作。matrix
类提供了更直观的运算符重载,使矩阵计算更容易理解。
虽然matrix
类在某些情况下很有用,但在实际应用中,NumPy多维数组(ndarray
)仍然是更通用和灵活的数据结构,因为它们支持更多的数学和科学计算操作,并且在生态系统中更为广泛使用。