最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

NumPy 數(shù)組花式索引(Fancy Indexing)的實(shí)現(xiàn)

 更新時(shí)間:2025年11月04日 10:00:39   作者:Humbunklung  
NumPy的花式索引是一種強(qiáng)大的數(shù)組索引方式,允許通過整數(shù)數(shù)組或列表一次性訪問或修改多個(gè)數(shù)組元素,本文主要介紹了NumPy 數(shù)組花式索引(Fancy Indexing)的實(shí)現(xiàn),感興趣的可以了解一下

前面的博客分享了我對(duì)于NumPy數(shù)組索引的學(xué)習(xí)心得——如何使用簡(jiǎn)單索引(例如 arr[0])、切片(例如 arr[:5])和布爾掩碼(例如 arr[arr > 0])來訪問和修改數(shù)組的部分內(nèi)容。
這里,我們將介紹另一種數(shù)組索引方式,稱為花式或矢量化索引,其中我們用索引數(shù)組代替單個(gè)標(biāo)量。
這種方式可以讓我們非??焖俚卦L問和修改數(shù)組中復(fù)雜子集的值。

探索花式索引

花式索引在概念上很簡(jiǎn)單:它指的是通過傳遞一個(gè)索引數(shù)組來一次性訪問多個(gè)數(shù)組元素。
以下面的數(shù)組為例:

import numpy as np
rng = np.random.default_rng(seed=1024)

x = rng.integers(100, size=10)
print(x)
[55 32 87 25  8 37 74 88 38 62]

假設(shè)我們想要訪問三個(gè)不同的元素。我們可以這樣做:

[x[3], x[9], x[8]]
[np.int64(25), np.int64(62), np.int64(38)]

另外,我們也可以傳遞一個(gè)索引列表或數(shù)組來獲得相同的結(jié)果:

ind = [3, 9, 8]
x[ind]
array([25, 62, 38])

當(dāng)使用索引數(shù)組時(shí),結(jié)果的形狀反映的是索引數(shù)組的形狀,而不是被索引數(shù)組的形狀:

ind = np.array([[3, 7],
                [4, 5]])
x[ind]
array([[25, 88],
       [ 8, 37]])

花式索引同樣適用于多維數(shù)組。請(qǐng)看下面的數(shù)組:

X = np.arange(30).reshape((5, 6))
X
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23],
       [24, 25, 26, 27, 28, 29]])

像標(biāo)準(zhǔn)索引一樣,第一個(gè)索引表示行,第二個(gè)索引表示列:

row = np.array([0, 1, 2])
col = np.array([2, 1, 3])
X[row, col]
array([ 2,  7, 15])

注意,結(jié)果中的第一個(gè)值是 X[0, 2],第二個(gè)是 X[1, 1],第三個(gè)是 X[2, 3]。
在花式索引中,索引的配對(duì)遵循了數(shù)組的廣播機(jī)制中提到的所有廣播規(guī)則。
因此,例如,如果我們?cè)谒饕薪Y(jié)合使用列向量和行向量,就會(huì)得到一個(gè)二維的結(jié)果:

X[row[:, np.newaxis], col]
array([[ 2,  1,  3],
       [ 8,  7,  9],
       [14, 13, 15]])

這里,每個(gè)行索引值都會(huì)與每個(gè)列向量進(jìn)行匹配,這與我們?cè)谒阈g(shù)運(yùn)算廣播中看到的方式完全一致。
例如:

row[:, np.newaxis] * col
array([[0, 0, 0],
       [2, 1, 3],
       [4, 2, 6]])

在使用花式索引時(shí),務(wù)必要記住:返回值的形狀反映的是索引的廣播后形狀,而不是被索引數(shù)組的形狀。

組合索引

為了實(shí)現(xiàn)更強(qiáng)大的操作,花式索引可以與我們之前見過的其他索引方式結(jié)合使用。例如,給定數(shù)組 X

print(X)
[[ 0  1  2  3  4  5]
 [ 6  7  8  9 10 11]
 [12 13 14 15 16 17]
 [18 19 20 21 22 23]
 [24 25 26 27 28 29]]

我們可以將花式索引與簡(jiǎn)單索引結(jié)合使用:

X[2, [2, 0, 1]]
array([14, 12, 13])

我們還可以將花式索引與切片結(jié)合使用:

X[1:, [2, 0, 1]]
array([[ 8,  6,  7],
       [14, 12, 13],
       [20, 18, 19],
       [26, 24, 25]])

我們還可以將花式索引與掩碼(布爾索引)結(jié)合使用:

mask = np.array([True, False, True, False, False, False])
X[row[:, np.newaxis], mask]
array([[ 0,  2],
       [ 6,  8],
       [12, 14]])

所有這些索引方式的組合,使我們能夠非常靈活、高效地訪問和修改數(shù)組的值。

示例:選擇隨機(jī)點(diǎn)

花式索引的一個(gè)常見用途是從矩陣中選擇部分行的子集。
例如,我們可能有一個(gè) N × D N \times D N×D 的矩陣,表示 N N N 個(gè) D D D 維空間中的點(diǎn),比如下面這些從二維正態(tài)分布中抽取的點(diǎn):

mean = [0, 0]
cov = [[1, 2],
       [2, 5]]
X = rng.multivariate_normal(mean, cov, 100)
X.shape
(100, 2)

使用Matplotlib,我們可以將這些點(diǎn)可視化為散點(diǎn)圖(見下圖):

%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('seaborn-v0_8-whitegrid')

plt.scatter(X[:, 0], X[:, 1]);

讓我們用花式索引選擇 20 個(gè)隨機(jī)點(diǎn)。我們將首先隨機(jī)選擇 20 個(gè)不重復(fù)的索引,然后用這些索引來選取原數(shù)組中的一部分:

indices = np.random.choice(X.shape[0], 20, replace=False)
indices
array([87, 77, 55, 54, 34, 76, 30, 12, 61, 90, 29, 94,  8, 91, 81, 97, 74,
        5, 99, 20], dtype=int32)
selection = X[indices]  # 使用花式索引選擇點(diǎn)
selection.shape
(20, 2)

現(xiàn)在,為了查看哪些點(diǎn)被選中了,我們將在被選中的點(diǎn)的位置上疊加大圓圈(見下圖):

plt.scatter(X[:, 0], X[:, 1], alpha=0.3)
plt.scatter(selection[:, 0], selection[:, 1],
            facecolor='none', edgecolor='black', s=200);

這種策略常用于快速劃分?jǐn)?shù)據(jù)集,例如在統(tǒng)計(jì)模型驗(yàn)證(如超參數(shù)與模型驗(yàn)證)中進(jìn)行訓(xùn)練/測(cè)試集分割,以及在采樣方法中用于解答統(tǒng)計(jì)問題。

用花式索引修改值

正如花式索引可以用來訪問數(shù)組的部分內(nèi)容,它也可以用來修改數(shù)組的部分內(nèi)容。
例如,假設(shè)我們有一個(gè)索引數(shù)組,并希望將數(shù)組中對(duì)應(yīng)的元素設(shè)置為某個(gè)值:

x = np.arange(10)
i = np.array([2, 1, 8, 4])
x[i] = 99
print(x)
[ 0 99 99  3 99  5  6  7 99  9]

我們可以對(duì)其使用任何賦值類型的運(yùn)算符。例如:

x[i] -= 10
print(x)
[ 0 89 89  3 89  5  6  7 89  9]

請(qǐng)注意,對(duì)于這些操作,如果索引中有重復(fù)項(xiàng),可能會(huì)導(dǎo)致一些意想不到的結(jié)果。請(qǐng)看下面的例子:

x = np.zeros(10)
x[[0, 0]] = [4, 6]
print(x)
[6. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

4 到哪去了?這個(gè)操作首先執(zhí)行 x[0] = 4,然后執(zhí)行 x[0] = 6。
結(jié)果當(dāng)然是 x[0] 的值為 6。

這很合理,但請(qǐng)考慮下面這個(gè)操作:

i = [2, 3, 3, 4, 4, 4]
x[i] += 1
x
array([6., 0., 1., 1., 1., 0., 0., 0., 0., 0.])

你可能會(huì)期望 x[3] 的值為 2,x[4] 的值為 3,因?yàn)槊總€(gè)索引重復(fù)的次數(shù)就是它們應(yīng)該增加的次數(shù)。為什么實(shí)際不是這樣呢?
從概念上講,這是因?yàn)?x[i] += 1 實(shí)際上等價(jià)于 x[i] = x[i] + 1。x[i] + 1 會(huì)先被整體計(jì)算出來,然后再把結(jié)果賦值回 x 的這些索引位置。
這樣一來,實(shí)際上是賦值操作被多次執(zhí)行,而不是累加操作被多次執(zhí)行,這就導(dǎo)致了看起來不太直觀的結(jié)果。

那么如果你想要每個(gè)索引都累加多次該怎么辦?這時(shí)可以使用 ufunc 的 at 方法,如下所示:

x = np.zeros(10)
np.add.at(x, i, 1)
print(x)
[0. 0. 1. 2. 3. 0. 0. 0. 0. 0.]

at 方法會(huì)在指定的索引(這里是 i)處對(duì)給定的操作符(這里是 1)進(jìn)行原地應(yīng)用。
另一個(gè)在原理上類似的方法是 ufunc 的 reduceat 方法,你可以在 NumPy 官方文檔 中了解更多信息。

示例:數(shù)據(jù)分箱(Binning Data)

你可以利用這些思想高效地對(duì)數(shù)據(jù)進(jìn)行自定義分箱計(jì)算。
例如,假設(shè)我們有 100 個(gè)數(shù)值,并希望快速判斷它們分別落在一組分箱(bins)中的哪個(gè)區(qū)間。
我們可以像下面這樣用 ufunc.at 來實(shí)現(xiàn):

rng = np.random.default_rng(seed=1024)
x = rng.normal(size=100)

# 手工計(jì)算直方圖
bins = np.linspace(-5, 5, 20)
counts = np.zeros_like(bins)

# 為每個(gè) x 找到合適的分箱
i = np.searchsorted(bins, x)

# 將這些索引對(duì)應(yīng)的分箱加一
np.add.at(counts, i, 1)

現(xiàn)在,counts 反映了每個(gè)分箱中的點(diǎn)的數(shù)量——換句話說,就是一個(gè)直方圖(見下圖):

# 繪制直方圖
plt.plot(bins, counts, drawstyle='steps');

當(dāng)然,如果每次想要繪制直方圖都要手動(dòng)實(shí)現(xiàn)上述步驟會(huì)很不方便。
這也是為什么 Matplotlib 提供了 plt.hist 例程,它可以用一行代碼完成相同的操作:

plt.hist(x, bins, histtype='step');

這個(gè)函數(shù)會(huì)生成一個(gè)與剛才幾乎相同的圖像。
在計(jì)算分箱時(shí),Matplotlib 實(shí)際上調(diào)用了 np.histogram 函數(shù),其實(shí)現(xiàn)方式與我們手動(dòng)實(shí)現(xiàn)的非常類似。我們可以在這里對(duì)比兩者:

print(f"NumPy 直方圖 ({len(x)} 點(diǎn)):")
%timeit counts, edges = np.histogram(x, bins)

print(f"自定義直方圖 ({len(x)} 點(diǎn)):")
%timeit np.add.at(counts, np.searchsorted(bins, x), 1)
NumPy 直方圖 (100 點(diǎn)):
6.43 μs ± 68.1 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
自定義直方圖 (100 點(diǎn)):
5.53 μs ± 86.7 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

我們自己的一行算法居然比 NumPy 中的優(yōu)化算法快!這是怎么回事?如果你深入查看 np.histogram 的源碼(在 IPython 中輸入 np.histogram?? 即可),你會(huì)發(fā)現(xiàn)它比我們簡(jiǎn)單的“查找并計(jì)數(shù)”要復(fù)雜得多;這是因?yàn)?NumPy 的算法更加靈活,尤其是在數(shù)據(jù)點(diǎn)數(shù)量很大時(shí),專門針對(duì)更好的性能進(jìn)行了設(shè)計(jì):

x = rng.normal(size=1000000)
print(f"NumPy 直方圖 ({len(x)} 點(diǎn)):")
%timeit counts, edges = np.histogram(x, bins)

print(f"自定義直方圖 ({len(x)} 點(diǎn)):")
%timeit np.add.at(counts, np.searchsorted(bins, x), 1)
NumPy 直方圖 (1000000 點(diǎn)):
5.75 ms ± 107 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
自定義直方圖 (1000000 點(diǎn)):
48.7 ms ± 1.19 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

這個(gè)對(duì)比說明,算法效率幾乎從來不是一個(gè)簡(jiǎn)單的問題。適用于大數(shù)據(jù)集的高效算法,并不總是小數(shù)據(jù)集下的最佳選擇,反之亦然。
但自己編寫算法的優(yōu)勢(shì)在于:只要理解了這些基礎(chǔ)方法,你就擁有了無限的可能性——不再受限于內(nèi)置例程,可以創(chuàng)造屬于自己的數(shù)據(jù)探索方式。
高效使用 Python 進(jìn)行數(shù)據(jù)密集型應(yīng)用的關(guān)鍵,不僅在于了解像 np.histogram 這樣的通用便捷函數(shù)及其適用場(chǎng)景,還在于當(dāng)你需要更靈活的行為時(shí),能夠利用底層功能實(shí)現(xiàn)自定義操作。

內(nèi)容總結(jié)

本章介紹了 NumPy 的花式索引(Fancy Indexing)及其強(qiáng)大用法。主要內(nèi)容包括:

  • 花式索引允許通過整數(shù)數(shù)組或列表一次性訪問或修改多個(gè)數(shù)組元素,支持一維和多維數(shù)組,并遵循廣播機(jī)制。
  • 花式索引可以與切片、布爾索引等其他索引方式靈活組合,實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)選取和操作。
  • 通過實(shí)際案例,展示了如何用花式索引高效地選擇、可視化和修改數(shù)據(jù)子集。
  • 講解了花式索引賦值時(shí)的“非累加”特性,以及如何用 np.add.at 實(shí)現(xiàn)真正的逐元素累加。
  • 以自定義直方圖為例,說明了花式索引和 ufunc 的結(jié)合在數(shù)據(jù)分箱等統(tǒng)計(jì)計(jì)算中的高效應(yīng)用。
  • 最后強(qiáng)調(diào),理解底層索引和廣播機(jī)制,有助于靈活高效地處理大規(guī)模數(shù)據(jù),突破內(nèi)置函數(shù)的限制,提升數(shù)據(jù)分析能力。

到此這篇關(guān)于NumPy 數(shù)組花式索引(Fancy Indexing)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)NumPy 數(shù)組花式索引內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

安岳县| 翁牛特旗| 诸城市| 铁岭市| 湖北省| 广平县| 玛纳斯县| 和政县| 中卫市| 巫溪县| 涿州市| 平陆县| 二手房| 上犹县| 洞口县| 三明市| 霞浦县| 双柏县| 大荔县| 怀安县| 嘉善县| 红安县| 京山县| 玉山县| 屯昌县| 潮安县| 长泰县| 宁波市| 都兰县| 孟连| 博客| 河源市| 汶川县| 西和县| 高雄县| 弥渡县| 疏勒县| 福清市| 庐江县| 库伦旗| 巫山县|