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

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

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

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

探索花式索引

花式索引在概念上很簡單:它指的是通過傳遞一個索引數(shù)組來一次性訪問多個數(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è)我們想要訪問三個不同的元素。我們可以這樣做:

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

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

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

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

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

花式索引同樣適用于多維數(shù)組。請看下面的數(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)索引一樣,第一個索引表示行,第二個索引表示列:

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

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

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

這里,每個行索引值都會與每個列向量進行匹配,這與我們在算術(shù)運算廣播中看到的方式完全一致。
例如:

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

在使用花式索引時,務(wù)必要記?。悍祷刂档男螤罘从车氖撬饕膹V播后形狀,而不是被索引數(shù)組的形狀。

組合索引

為了實現(xiàn)更強大的操作,花式索引可以與我們之前見過的其他索引方式結(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é)合使用:

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ù)組的值。

示例:選擇隨機點

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

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

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

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

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

讓我們用花式索引選擇 20 個隨機點。我們將首先隨機選擇 20 個不重復(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]  # 使用花式索引選擇點
selection.shape
(20, 2)

現(xià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)計模型驗證(如超參數(shù)與模型驗證)中進行訓(xùn)練/測試集分割,以及在采樣方法中用于解答統(tǒng)計問題。

用花式索引修改值

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

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]

我們可以對其使用任何賦值類型的運算符。例如:

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

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

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

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

這很合理,但請考慮下面這個操作:

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

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

那么如果你想要每個索引都累加多次該怎么辦?這時可以使用 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 方法會在指定的索引(這里是 i)處對給定的操作符(這里是 1)進行原地應(yīng)用。
另一個在原理上類似的方法是 ufunc 的 reduceat 方法,你可以在 NumPy 官方文檔 中了解更多信息。

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

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

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

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

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

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

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

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

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

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

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

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

print(f"自定義直方圖 ({len(x)} 點):")
%timeit np.add.at(counts, np.searchsorted(bins, x), 1)
NumPy 直方圖 (100 點):
6.43 μs ± 68.1 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
自定義直方圖 (100 點):
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?? 即可),你會發(fā)現(xiàn)它比我們簡單的“查找并計數(shù)”要復(fù)雜得多;這是因為 NumPy 的算法更加靈活,尤其是在數(shù)據(jù)點數(shù)量很大時,專門針對更好的性能進行了設(shè)計:

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

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

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

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

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

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

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

相關(guān)文章

最新評論

佛坪县| 秦皇岛市| 宁化县| 五原县| 新兴县| 连平县| 西林县| 美姑县| 千阳县| 张家口市| 西藏| 香河县| 海淀区| 巴楚县| 大化| 越西县| 浦县| 通城县| 洞头县| 乐清市| 巴林右旗| 永定县| 道孚县| 吉水县| 黄浦区| 都安| 比如县| 大方县| 南召县| 滦南县| 博罗县| 邯郸县| 永兴县| 固阳县| 嘉禾县| 唐海县| 宜春市| 镇康县| 天长市| 景洪市| 武邑县|