NumPy `ndarray`:多维数组对象深度解析

NumPy ndarray:多维数组对象深度解析 – 告别循环,拥抱飞速!

各位程序猿、攻城狮、代码艺术家们,大家好!欢迎来到今天的“NumPy ndarray:多维数组对象深度解析”讲座!我是你们的老朋友,一位在代码丛林中摸爬滚打多年的“老司机”。今天,咱们不讲晦涩难懂的理论,不搞高深莫测的公式,就用最通俗易懂的语言,带大家深入了解 NumPy 的核心灵魂——ndarray,让你的数据处理速度像火箭一样嗖嗖嗖!🚀

一、 故事的开始:循环的苦恼

话说,在很久很久以前(其实也没多久),我们处理大量数据的时候,最常用的方法就是…循环!for 循环、while 循环,一层套一层,仿佛永无止境。数据量小的时候,还能勉强应付,但一旦数据量大了,程序跑起来就像蜗牛爬,慢到让你怀疑人生。🐌

想象一下,你要计算一个包含 100 万个数字的列表中每个数字的平方。用 Python 原生的循环,代码可能是这样的:

numbers = list(range(1000000))
squares = []
for number in numbers:
    squares.append(number ** 2)

这段代码虽然简单易懂,但效率实在是不敢恭维。运行起来,感觉时间都静止了… ⏱️

二、 NumPy ndarray:救星降临!

就在我们被循环折磨得死去活来的时候,NumPy 带着它的 ndarray 闪亮登场!它就像一位身披金色战甲的骑士,骑着一匹名叫“向量化”的骏马,拯救我们于水火之中。

ndarray,全称 N-dimensional array object,也就是 N 维数组对象。它是一种高效、灵活的数据结构,专门用于存储同类型的数据。最关键的是,NumPy 针对 ndarray 进行了大量的优化,使得我们可以用更简洁、更快速的方式处理数据。

三、 ndarray 的魅力:高效、便捷、强大!

ndarray 的魅力体现在以下几个方面:

  • 高效的存储: ndarray 存储的是同类型的数据,这意味着它可以更紧凑地存储数据,减少内存占用。
  • 向量化操作: 这是 ndarray 最核心的优势!它可以对整个数组进行操作,而不需要显式地使用循环。这极大地提高了代码的执行效率。
  • 广播机制: 当对不同形状的 ndarray 进行运算时,NumPy 会自动进行广播,使得运算能够顺利进行。这让我们的代码更加简洁灵活。
  • 丰富的函数库: NumPy 提供了大量的函数,用于对 ndarray 进行各种各样的操作,包括数学运算、统计分析、线性代数等等。

四、 ndarray 的构造:像搭积木一样简单!

创建 ndarray 的方法有很多,就像搭积木一样,你可以根据自己的需要选择不同的方式。

  • 从 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 内置函数创建: NumPy 提供了很多内置函数,用于创建特定类型的 ndarray

    • np.zeros():创建全零数组。
    • np.ones():创建全一数组。
    • np.empty():创建未初始化的数组。
    • np.arange():创建等差数组。
    • np.linspace():创建等间距数组。
    • np.random.rand():创建随机数组。

    举几个例子:

    # 创建一个 3x4 的全零数组
    zeros_arr = np.zeros((3, 4))
    print(zeros_arr)
    # 输出:
    # [[0. 0. 0. 0.]
    #  [0. 0. 0. 0.]
    #  [0. 0. 0. 0.]]
    
    # 创建一个 1 到 10 的等差数组
    arange_arr = np.arange(1, 11)
    print(arange_arr)  # 输出: [ 1  2  3  4  5  6  7  8  9 10]
    
    # 创建一个 0 到 1 之间,包含 5 个元素的等间距数组
    linspace_arr = np.linspace(0, 1, 5)
    print(linspace_arr)  # 输出: [0.   0.25 0.5  0.75 1.  ]
  • 从文件中读取数据: 可以使用 np.loadtxt()np.genfromtxt() 从文本文件中读取数据,并将其转换为 ndarray

五、 ndarray 的属性:了解你的数据!

ndarray 有很多重要的属性,可以帮助我们了解数据的结构和类型。

  • ndim:数组的维度。
  • shape:数组的形状,即每个维度的大小。
  • size:数组元素的总个数。
  • dtype:数组元素的数据类型。
  • itemsize:每个元素占用的字节数。
arr = np.array([[1, 2, 3], [4, 5, 6]])

print("维度:", arr.ndim)       # 输出: 2
print("形状:", arr.shape)      # 输出: (2, 3)
print("大小:", arr.size)       # 输出: 6
print("数据类型:", arr.dtype)    # 输出: int64 (取决于你的系统)
print("元素字节数:", arr.itemsize) # 输出: 8 (int64 占用 8 字节)

六、 ndarray 的索引和切片:精准定位,随心所欲!

ndarray 的索引和切片操作非常灵活,可以让我们精准地定位到数组中的特定元素或子数组。

  • 基本索引: 和 Python 列表类似,可以使用方括号 [] 来访问数组中的元素。

    arr = np.array([1, 2, 3, 4, 5])
    print(arr[0])  # 输出: 1
    print(arr[2])  # 输出: 3
    
    arr2 = np.array([[1, 2, 3], [4, 5, 6]])
    print(arr2[0, 1])  # 输出: 2 (第一行,第二列)
    print(arr2[1, 2])  # 输出: 6 (第二行,第三列)
  • 切片: 可以使用切片来访问数组中的子数组。

    arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    
    # 获取前 5 个元素
    print(arr[:5])   # 输出: [1 2 3 4 5]
    
    # 获取从第 3 个元素到结尾的元素
    print(arr[2:])   # 输出: [ 3  4  5  6  7  8  9 10]
    
    # 获取从第 2 个元素到第 8 个元素,步长为 2
    print(arr[1:8:2]) # 输出: [2 4 6 8]
    
    arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    
    # 获取第一行
    print(arr2[0, :]) # 输出: [1 2 3]
    
    # 获取第二列
    print(arr2[:, 1]) # 输出: [2 5 8]
    
    # 获取左上角的 2x2 子数组
    print(arr2[:2, :2])
    # 输出:
    # [[1 2]
    #  [4 5]]
  • 布尔索引: 可以使用布尔数组来选择数组中的特定元素。这是一种非常强大的索引方式。

    arr = np.array([1, 2, 3, 4, 5, 6])
    bool_arr = arr > 3  # 创建一个布尔数组,表示哪些元素大于 3
    print(bool_arr)   # 输出: [False False False  True  True  True]
    print(arr[bool_arr]) # 输出: [4 5 6] (只选择大于 3 的元素)
    
    # 更简洁的写法
    print(arr[arr > 3]) # 输出: [4 5 6]
  • 花式索引: 可以使用整数数组来选择数组中的特定元素。

    arr = np.array([1, 2, 3, 4, 5, 6])
    indices = [0, 2, 4]  # 要选择的元素的索引
    print(arr[indices])  # 输出: [1 3 5]
    
    arr2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    row_indices = [0, 1]
    col_indices = [1, 2]
    print(arr2[row_indices, col_indices]) # 输出: [2 6]

七、 ndarray 的运算:告别循环,拥抱向量化!

ndarray 最强大的地方在于它的向量化运算。我们可以直接对整个数组进行运算,而不需要显式地使用循环。这极大地提高了代码的执行效率。

  • 基本运算: ndarray 支持加、减、乘、除、幂等基本运算。

    arr1 = np.array([1, 2, 3])
    arr2 = np.array([4, 5, 6])
    
    print(arr1 + arr2)  # 输出: [5 7 9]
    print(arr1 * arr2)  # 输出: [ 4 10 18]
    print(arr1 ** 2)  # 输出: [1 4 9]
  • 广播机制: 当对不同形状的 ndarray 进行运算时,NumPy 会自动进行广播,使得运算能够顺利进行。

    arr1 = np.array([1, 2, 3])
    scalar = 2
    
    print(arr1 + scalar)  # 输出: [3 4 5] (scalar 被广播到 arr1 的每个元素)
    
    arr2 = np.array([[1, 2, 3], [4, 5, 6]])
    arr3 = np.array([10, 20, 30])
    
    print(arr2 + arr3)
    # 输出:
    # [[11 22 33]
    #  [14 25 36]] (arr3 被广播到 arr2 的每一行)
  • 常用函数: NumPy 提供了大量的函数,用于对 ndarray 进行各种各样的操作。

    • np.sum():求和。
    • np.mean():求平均值。
    • np.std():求标准差。
    • np.max():求最大值。
    • np.min():求最小值。
    • np.sort():排序。
    • np.reshape():改变数组的形状。
    • np.transpose():转置数组。
    arr = np.array([[1, 2, 3], [4, 5, 6]])
    
    print("求和:", np.sum(arr))        # 输出: 21
    print("平均值:", np.mean(arr))       # 输出: 3.5
    print("最大值:", np.max(arr))        # 输出: 6
    print("改变形状:", arr.reshape((3, 2)))
    # 输出:
    # [[1 2]
    #  [3 4]
    #  [5 6]]
    print("转置:n", arr.transpose())
    # 输出:
    # [[1 4]
    #  [2 5]
    #  [3 6]]

八、 ndarray 的应用:无处不在!

ndarray 在数据科学、机器学习、图像处理等领域都有着广泛的应用。

  • 数据分析: ndarray 可以用来存储和处理各种各样的数据,比如股票价格、用户行为数据等等。
  • 机器学习: ndarray 是机器学习算法的基础,比如神经网络的输入和输出都是 ndarray
  • 图像处理: 图像可以看作是一个多维数组,ndarray 可以用来存储和处理图像数据。

九、 提升效率的秘诀:避免不必要的拷贝!

在使用 ndarray 的过程中,需要注意避免不必要的拷贝。有些操作会创建新的 ndarray,而有些操作则会修改原来的 ndarray。了解这些操作的区别,可以帮助我们提升代码的效率。

  • 视图(View): 有些操作会创建 ndarray 的视图,而不是拷贝数据。视图是指向原始数据的指针,修改视图会影响原始数据。切片操作通常会创建视图。
  • 拷贝(Copy): 有些操作会创建 ndarray 的拷贝,拷贝是指创建原始数据的一个副本,修改拷贝不会影响原始数据。copy() 方法可以显式地创建拷贝。
arr = np.array([1, 2, 3, 4, 5])

# 切片操作创建视图
arr_view = arr[1:4]
print("原始数组:", arr)     # 输出: [1 2 3 4 5]
print("视图:", arr_view)   # 输出: [2 3 4]

arr_view[0] = 100  # 修改视图
print("原始数组(修改后):", arr) # 输出: [  1 100   3   4   5] (原始数组被修改)
print("视图(修改后):", arr_view)  # 输出: [100   3   4]

# 显式创建拷贝
arr_copy = arr.copy()
print("拷贝:", arr_copy)  # 输出: [  1 100   3   4   5]

arr_copy[0] = 200  # 修改拷贝
print("原始数组(修改后):", arr) # 输出: [  1 100   3   4   5] (原始数组没有被修改)
print("拷贝(修改后):", arr_copy)   # 输出: [200 100   3   4   5]

十、 总结:ndarray,你值得拥有!

今天,我们一起深入了解了 NumPy 的核心灵魂——ndarray。它高效、便捷、强大,是数据处理的利器。掌握 ndarray 的使用,可以让你告别循环的苦恼,拥抱向量化的飞速!

记住,ndarray 不仅仅是一个数据结构,更是一种编程思想。它鼓励我们用向量化的方式思考问题,从而写出更简洁、更高效的代码。

希望今天的讲座对大家有所帮助。在未来的数据处理之旅中,愿 ndarray 成为你最可靠的伙伴!💪

最后,送给大家一句代码界的至理名言:Life is short, use NumPy! 😉

感谢大家的聆听!下次再见!👋

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注