Python库NumPy 10 个不为人知的技巧

Python库NumPy 10 个不为人知的技巧

Python库NumPy 10 个不为人知的技巧

如果你常常和 NumPy 打交道,那必定对它的数组索引能力有所了解。你可能会用切片(slicing)来获取一部分数据,或者选择某一行、某一列。但许多人不知道的是,NumPy 的索引功能远不止于此。它隐藏着一些超级巧妙的“黑科技”,能够让你的代码更精炼、运行速度更快,甚至能替代那些你觉得无法避免的循环。

作为数据科学和机器学习的基石,NumPy 的性能优化至关重大。掌握这些索引技巧,就像是为你的数据处理工作流注入了加速剂。它不仅仅是语法上的便捷,更是一种“向量化”思维的体现,能够将复杂操作转化为底层的 C 语言级别运算,从而实现效率上的飞跃。

接下来,我们将深入探讨 10 种强劲的 NumPy 索引技巧。这些方法或许你从未用过,但一旦掌握,将会彻底改变你的工作方式。


一、布尔掩码:像筛选器一样选择数据

一般,当你需要根据某个条件筛选数组元素时,可能会想到写一个for循环和if语句。但在 NumPy 中,有更简单、更快速的方法——使用布尔数组(boolean array)作为索引。

布尔数组中的每一个元素,都对应原始数组中一样位置的元素。当布尔值为True时,对应位置的元素被选中;当布尔值为False时,则被忽略。

例如,我们有一个数组arr,想要筛选出所有大于 10 的元素:

import numpy as np

arr = np.array([3, 8, 12, 5, 18, 7])
mask = arr > 10
print(arr[mask])   # 输出: [12 18]

这里的arr > 10会生成一个布尔数组[False False True False True False],然后 NumPy 会根据这个布尔掩码,直接返回arr中对应True位置的元素,即[12 18]

但布尔掩码不仅仅用于筛选,它还可以进行赋值操作。

列如,我们想把所有大于 10 的元素都设置为 99:

arr[arr > 10] = 99
print(arr)  # 输出: [ 3  8 99  5 99  7]

这是一种超级简洁、高效的条件赋值方式,省去了编写循环的麻烦,并且性能更优。


二、花式索引:用列表或数组随心所欲地选择元素

切片索引只能选择连续的元素,但如果你想根据不连续的、任意指定的索引来选择元素,该怎么办?这就是**花式索引(Fancy Indexing)**的用武之地。

你可以直接传入一个包含索引值的列表数组,来指定你想要选择的元素。这些索引值可以重复,也可以是乱序的。

arr = np.array([10, 20, 30, 40, 50])
print(arr[[4, 2, 0]])   # 输出: [50 30 10]

这个例子中,我们传递了[4, 2, 0]作为索引,NumPy 返回了第 4、第 2 和第 0 个元素,并且顺序也和索引列表一致。

花式索引同样支持赋值操作,这对于批量修改非连续位置的元素超级方便。

arr[[1, 3]] = [200, 400]
print(arr)  # 输出: [ 10 200  30 400  50]

这个技巧在数据抽样等场景中超级实用,无需编写复杂的循环。


三、负数索引:轻松获取数组末尾的元素

负数索引是 Python 中的一个经典技巧,在 NumPy 中同样适用。它提供了一种从数组末尾开始计数的便捷方式。

  • -1代表倒数第一个元素。
  • -2代表倒数第二个元素,以此类推。
  • arr[-3:]则表明从倒数第三个元素开始,到数组末尾的所有元素。
arr = np.array([5, 10, 15, 20, 25])
print(arr[-1])   # 输出: 25
print(arr[-3:])  # 输出: [15 20 25]

这个简单的技巧在机器学习中超级有用,列如当你只关心最近 N 个数据点时。


四、步长切片:跳跃式选择和轻松反转数组

切片操作一般格式为arr[start:end:step],但许多人在使用中会忽略step参数。通过设置步长,你可以实现跳跃式地选择元素。

arr = np.arange(10)
print(arr[::2])   # 输出: [0 2 4 6 8]

::2表明从头到尾,每隔一个元素选择一个。

更神奇的是,将步长设置为-1,可以轻松实现数组反转

print(arr[::-1])  # 输出: [9 8 7 6 5 4 3 2 1 0]

这个技巧在处理时间序列或图像数据时超级实用,可以快速翻转数据而无需使用额外的函数。


五、多维数组的花式索引:精准选取复杂数据点

当数组是多维的(列如矩阵)时,花式索引的能力会更加强劲。你可以使用多个列表来同时指定行和列的索引,从而精准地选择你想要的数据点。

arr = np.arange(1, 13).reshape(3, 4)
# arr 目前是:
# [[ 1  2  3  4]
#  [ 5  6  7  8]
#  [ 9 10 11 12]]

print(arr[[0, 2], [1, 3]])   # 输出: [ 2 12 ]

这里[0, 2]是行索引列表,[1, 3]是列索引列表。NumPy 会根据这两个列表,找到对应的arr[0, 1]arr[2, 3]两个元素,并将它们作为新数组返回。这个操作在一行代码中就完成了,省去了编写循环的繁琐。


六、使用 np.ix_ 构造子矩阵

有时,你希望通过组合多个行索引和列索引,来提取一个子矩阵(submatrix),而不是单个元素。直接使用多维花式索引可能会得到一维数组,这时就需要np.ix_出场了。

np.ix_ 函数可以接受多个索引数组,并返回一个元组,这个元组可以用来进行**笛卡尔积(Cartesian product)**式的索引,从而得到一个规整的子矩阵。

arr = np.arange(1, 13).reshape(3, 4)
rows = [0, 2]
cols = [1, 3]

submatrix = arr[np.ix_(rows, cols)]
print(submatrix)
# 输出:
# [[ 2  4]
#  [10 12]]

这个操作会选取第 0 行和第 2 行,以及第 1 列和第 3 列,然后返回由这些行列交叉点构成的子矩阵。如果没有np.ix_,你可能需要编写更复杂的代码来处理索引的组合。


七、np.where:条件选择与赋值的瑞士军刀

np.where是一个超级强劲的函数,它结合了条件判断、选择和赋值的功能。它的基本语法是np.where(condition, x, y)

  • 如果condition为真,np.where会返回x对应位置的值。
  • 如果condition为假,np.where则返回y对应位置的值。

这个函数最常见的用法是根据条件进行值的替换。

arr = np.array([2, 7, 12, 5, 18])
result = np.where(arr > 10, "big", "small")
print(result)   # 输出: ['small' 'small' 'big' 'small' 'big']

这里,np.where根据arr > 10的条件,将结果数组中对应位置的值设置为“big”“small”

它也可以直接用于修改数组。

arr = np.where(arr > 10, 0, arr)
print(arr)  # 输出: [2 7 0 5 0]

这个例子中,所有大于 10 的元素都被替换为 0,而其他元素保持不变。


八、None索引:广播式创建新维度

这个技巧看起来有点像魔法,它利用了None关键字来在数组中插入一个新的维度(new axis)。这个新维度一般用于触发 NumPy 的**广播(broadcasting)**机制,从而实现一些超级高效的运算,列如外积(outer product)。

arr = np.array([1, 2, 3])
print(arr[:, None] * arr[None, :])

输出结果是:

[[1 2 3]
 [2 4 6]
 [3 6 9]]

arr[:, None]将一个一维数组变成了[[1], [2], [3]],而arr[None, :]则变成了[[1, 2, 3]]。当这两个数组相乘时,NumPy 的广播机制会自动将它们扩展成一个 3×3 的矩阵进行运算,从而得到了外积,这个过程没有使用任何循环。


九、省略号…:简化多维数组索引

当你的数组维度超级多时(列如 4D、5D 甚至更高),手动编写每一个维度的索引会变得超级繁琐。这时,**省略号**就派上用场了。它代表了“所有中间的维度”。

arr = np.random.rand(4, 5, 6, 7)
print(arr[0, ..., -1].shape)   # 输出: (5, 6)

在这个例子中,arr[0, …, -1]表明选择第 0 个维度上的第 0 个元素,以及最后一个维度上的最后一个元素,而则代表了中间的所有维度,即第 1 和第 2 维度。这种方式可以让你的代码更短、更易读。


十、组合技巧:将复杂操作压缩为一行代码

NumPy 索引的真正威力在于可以将上述多种技巧组合使用,从而将复杂的逻辑压缩到一行代码中。这需要一些练习,但一旦掌握,你就能写出极为高效且精炼的代码。

列如,我们想从一个多维数组中,先选取最后两行,然后在这些行中,再筛选出值大于 12 的元素。

arr = np.arange(20).reshape(4, 5)

# 先选取最后两行,然后在这两行中,选择列索引从2开始,并且值大于12的元素
rows = [-2, -1]
mask = arr[rows, 2:] > 12
result = arr[rows, 2:][mask]

print(result) # 输出: [13 14 18 19]

虽然这个表达式可能不像上面那些例子那样一目了然,但它将原本需要 6 到 7 行嵌套循环才能完成的任务,浓缩成了一行。这正是 NumPy“向量化”思维的精髓所在。


NumPy 索引的底层逻辑:高效的执行策略

你可能会好奇,为什么这些索引操作会如此高效。

从底层来看,NumPy 的索引并不仅仅是简单地“挑选元素”。它是一个复杂的调度机制,能够识别不同类型的索引输入——列如切片、布尔掩码、整数或列表。然后,NumPy 会根据这些输入,制定一个高效的执行策略,将你的索引表达式转化为底层的、快速的 C 语言级别操作。

简单来说,当你在使用这些高级索引时,NumPy 并没有在后台运行 Python 循环。它将整个操作打包成一个向量化操作,然后交给底层的 C 语言核心去处理,这正是其速度远超 Python 原生循环的缘由。

掌握索引技巧的真正价值

学习这些 NumPy 索引技巧不仅仅是为了让代码看起来更酷,它在实际工作中具有超级重大的意义。

  • 性能优化: 在机器学习中,高效地选择特征可以节省大量的训练时间;在金融领域,快速切片处理大型时间序列数据可以避免性能瓶颈;在图像处理中,使用掩码进行像素筛选比使用嵌套循环快得多。
  • 代码精炼: 减少冗余代码,提高代码可读性和可维护性。
  • 思维转变: 强制你采用“向量化”的思维方式,将问题抽象成整体的数组操作,而不是单个元素的迭代。

NumPy 已经存在几十年了,但它的索引系统依旧隐藏着许多宝藏,等待着开发者去发掘。下次当你本能地想用for循环来处理数组时,不妨停下来思考一下:“NumPy 的索引能帮我解决这个问题吗?”。

答案很有可能就是:能。

#pgc-card .pgc-card-href { text-decoration: none; outline: none; display: block; width: 100%; height: 100%; } #pgc-card .pgc-card-href:hover { text-decoration: none; } /*pc 样式*/ .pgc-card { box-sizing: border-box; height: 164px; border: 1px solid #e8e8e8; position: relative; padding: 20px 94px 12px 180px; overflow: hidden; } .pgc-card::after { content: ” “; display: block; border-left: 1px solid #e8e8e8; height: 120px; position: absolute; right: 76px; top: 20px; } .pgc-cover { position: absolute; width: 162px; height: 162px; top: 0; left: 0; background-size: cover; } .pgc-content { overflow: hidden; position: relative; top: 50%; -webkit-transform: translateY(-50%); transform: translateY(-50%); } .pgc-content-title { font-size: 18px; color: #222; line-height: 1; font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pgc-content-desc { font-size: 14px; color: #444; overflow: hidden; text-overflow: ellipsis; padding-top: 9px; overflow: hidden; line-height: 1.2em; display: -webkit-inline-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .pgc-content-price { font-size: 22px; color: #f85959; padding-top: 18px; line-height: 1em; } .pgc-card-buy { width: 75px; position: absolute; right: 0; top: 50px; color: #406599; font-size: 14px; text-align: center; } .pgc-buy-text { padding-top: 10px; } .pgc-icon-buy { height: 23px; width: 20px; display: inline-block; background: url(https://lf6-cdn-tos.bytescm.com/obj/cdn-static-resource/pgc/v2/pgc_tpl/static/image/commodity_buy_f2b4d1a.png); }

NumPy 数据处理详解

¥64

购买

<script src=”//mp.toutiao.com/mp/agw/mass_profit/pc_product_promotions_js?item_id=7543078425293029928″></script>

© 版权声明

相关文章

暂无评论

none
暂无评论...