NumPy - 使用数组进行性能优化



使用数组进行性能优化

使用数组进行性能优化包括提高数组操作的效率,例如减少计算时间和内存使用。

我们应该出于以下原因优化性能:

  • 速度: 更快的计算速度带来更快的结果和更灵敏的应用程序。
  • 可扩展性: 优化的代码可以有效地处理更大的数据集和更复杂的操作。
  • 资源效率: 减少内存使用和计算开销。

使用向量化运算

向量化运算指的是能够一步执行整个数组或矩阵上的运算,而无需使用显式循环。

这是通过广播和内部优化实现的,使这些运算更快、更高效。

示例

在以下示例中,我们使用 NumPy 的数组运算对两个大型数组“a”和“b”执行向量化加法。此运算计算数组的元素级和并将结果存储在新数组“c”中:

import numpy as np

# Create two large arrays
a = np.random.rand(1000000)
b = np.random.rand(1000000)

# Vectorized addition
c = a + b
print (c)

以下是获得的输出:

[0.91662816 0.65486861 1.60409272 ... 0.95122935 1.12795861 0.15812103]

利用高效的数据类型

为数组选择合适的数据类型对于优化 NumPy 中的性能和内存使用非常重要。

例如,使用np.float32而不是np.float64可以显着影响内存使用和性能,尤其是在处理大型数据集时。

在 NumPy 中,数据类型(或 dtype)定义了数组保存的元素类型以及存储每个元素所需的存储空间。

示例

在此示例中,我们演示了通过创建具有双精度(64 位)浮点数的数组,然后使用 astype() 方法将其转换为单精度(32 位)来使用精度更改:

import numpy as np

# Create an array with double precision (64-bit)
arr_double = np.array([1.0, 2.0, 3.0], dtype=np.float64)

# Print the original double precision array
print("Original double precision array:")
print(arr_double)
print("Data type:", arr_double.dtype)

# Convert to single precision (32-bit)
arr_single = arr_double.astype(np.float32)

# Print the converted single precision array
print("\nConverted single precision array:")
print(arr_single)
print("Data type:", arr_single.dtype)

这将产生以下结果:

Original double precision array:
[1. 2. 3.]
Data type: float64

Converted single precision array:
[1. 2. 3.]
Data type: float32

避免使用 NumPy 函数的循环

在 NumPy 中,主要优势之一是可以避免使用内置函数和数组运算显式循环。这种方法通常称为向量化。

通过使用 NumPy 函数,您可以一次对整个数组执行运算,这与使用循环相比更加简洁。

示例

在下面的示例中,我们使用 np.mean() 函数计算数组元素的平均值,而无需使用任何显式循环:

import numpy as np

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

# Calculate the mean of array elements
mean = np.mean(arr)
print("mean:",mean)  

以下是上述代码的输出:

mean: 3.0

使用广播进行向量化

广播指的是能够对形状不同的数组执行元素级运算。它遵循一组规则来确定如何对形状不同的数组进行对齐以进行运算:

  • 相同维度: 如果数组具有不同的维度,则较小数组的形状在左侧填充 1,直到两个形状具有相同的长度。
  • 维度兼容性: 当两个维度相等或其中一个为 1 时,它们是兼容的。对于每个维度,如果大小不同,并且如果它们都不为 1,则广播失败。
  • 拉伸: 尺寸为 1 的数组沿该尺寸拉伸以匹配另一个数组尺寸的大小。

示例

在以下示例中,我们广播“array_1d”以匹配“array_2d”的形状,从而允许元素级加法:

import numpy as np

# Create a 2D array and a 1D array
array_2d = np.array([[1, 2, 3], [4, 5, 6]])
array_1d = np.array([10, 20, 30])

# Add the 1D array to each row of the 2D array
result = array_2d + array_1d
print(result)

获得的输出如下所示:

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

就地运算用于向量化

NumPy 中的就地运算指的是直接修改数组的数据,而无需创建新数组来存储结果,从而节省内存并提高性能。

这是通过使用修改原始数组内容的运算符和函数来实现的。这些运算通常使用带就地后缀的运算符(例如,+=、-=、*=、/=)或支持就地修改的函数。

示例:使用就地运算符

在此示例中,我们直接对数组应用算术运算“+=”而无需创建新的数组:

import numpy as np

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

# Add 10 to each element in-place
arr += 10
print(arr)  

执行上述代码后,我们得到以下输出:

[11 12 13 14 15]

示例:使用就地函数

在这里,我们使用 NumPy exp() 函数就地计算数组中每个元素的指数值:

import numpy as np

# Create an array with a floating-point data type
arr = np.array([1, 2, 3, 4, 5], dtype=np.float64)

# Compute the exponential of each element in-place
np.exp(arr, out=arr)
print(arr)

执行上述代码后,我们得到以下输出:

[  2.71828183   7.3890561   20.08553692  54.59815003 148.4131591 ]

使用内存视图进行向量化

内存视图指的是访问或查看数组中相同底层数据的不同方式,而无需复制它。此概念允许您创建数组的不同“视图”或“切片”,这些视图可以以各种方式操作相同的数据:

  • 切片: 当您切片数组时,NumPy 会创建原始数组的视图,而不是副本。此视图共享相同的内存缓冲区,因此对视图的更改会影响原始数组,反之亦然。
  • 重塑: 重塑数组会创建具有不同形状的相同数据的新的视图。这不会更改底层数据,但会更改其解释方式。

示例:切片

在下面的示例中,我们创建了一个二维 NumPy 数组和原始数组的视图(切片)。修改视图也会影响原始数组:

import numpy as np

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

# Create a view (slice) of the original array
view = arr[:, 1:]

# Modify the view
view[0, 0] = 99

print(arr)

我们得到如下所示的输出:

[[ 1 99  3]
 [ 4  5  6]]

示例:重塑

在这里,我们使用 arange() 函数创建一个一维 NumPy 数组,然后将其重塑为一个具有 3 行 4 列的二维数组,更改其结构同时保留原始数据:

import numpy as np

# Create a 1D array
arr = np.arange(12)

# Reshape to a 2D array
reshaped = arr.reshape((3, 4))

print(reshaped)

我们得到如下所示的输出:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

使用步长进行向量化

步长是一个元组,指示遍历数组时每个维度要步进的字节数。它们确定如何在内存中访问数组元素,从而深入了解数据的布局和访问方式。

步长为您提供每个维度的内存偏移量。例如,在二维数组中,第二个维度的步长告诉您在内存中移动多少字节才能访问该行中的下一个元素。

示例

在以下示例中,我们创建了一个二维 NumPy 数组,并使用strides属性检索遍历数组时每个维度要步进的字节数:

import numpy as np

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

# Print the strides of the array
print(arr.strides)

我们得到如下所示的输出:

(24, 8)
广告

© . All rights reserved.