20個不常見但卻非常有用的Numpy函數

2021-12-25 DeepHub IMBA

點擊上方「Deephub Imba」,關注公眾號,好文章不錯過 !


Numpy是每個數據科學家都應該掌握的Python包,它提供了許多創建和操作數字數組的方法。它構成了許多與數據科學相關的廣泛使用的Python庫的基礎,比如panda和Matplotlib。

以下這些函數並不常見,甚至你可能都沒聽說過,但是在有些時候它們真的很有用。

np.full_like

我敢打賭,你肯定使用過像ones_like 或 zeros_like 這樣的常見 NumPy 函數。full_like 和這兩個完全一樣,除了你可以創建一個與另一個矩陣具有相同形狀的矩陣但是這些矩陣是使用自定義值填充的。

array = np.array([[1, 4, 6, 8], [9, 4, 4, 4], [2, 7, 2, 3]])

array_w_inf = np.full_like(array, fill_value=np.pi, dtype=np.float32)

>>> array_w_inf

array([[3.1415927, 3.1415927, 3.1415927, 3.1415927],
      [3.1415927, 3.1415927, 3.1415927, 3.1415927],
      [3.1415927, 3.1415927, 3.1415927, 3.1415927]], dtype=float32)

在這裡,我們正在創建一個數組值都是pi 矩陣。

np.logspace

我相信你經常使用linspace。它可以在一個區間內創建自定義的線性間隔數據點數量。它的同類logspace在這方面做得更深入一些。它可以在對數尺度上生成均勻間隔的自定義點數。你可以選擇任何一個數作為基數,只要它是非零的:

log_array = np.logspace(start=1, stop=100, num=15, base=np.e)

>>> log_array
array([2.71828183e+00, 3.20167238e+03, 3.77102401e+06, 4.44162312e+09,
      5.23147450e+12, 6.16178472e+15, 7.25753148e+18, 8.54813429e+21,
      1.00682443e+25, 1.18586746e+28, 1.39674961e+31, 1.64513282e+34,
      1.93768588e+37, 2.28226349e+40, 2.68811714e+43])

np.meshgrid

這是只有在文檔中才能看到的函數之一。因為大部分人難理解它。可以使用meshgrid從給定的X和Y數組創建每個可能的坐標對。這裡有一個簡單的例子:

x = [1, 2, 3, 4]
y = [3, 5, 6, 8]

xx, yy = np.meshgrid(x, y)

>>> xx
array([[1, 2, 3, 4],
      [1, 2, 3, 4],
      [1, 2, 3, 4],
      [1, 2, 3, 4]])

>>> yy
array([[3, 3, 3, 3],
      [5, 5, 5, 5],
      [6, 6, 6, 6],
      [8, 8, 8, 8]])

得到 16 個唯一坐標對,結果數組中的每個索引到索引元素對對應一個。可視化一下就很好理解了

>>> plt.plot(xx, yy, linestyle="none", marker="o", color="red");

meshgrid通常用於使用循環需要很長時間的複雜任務。如繪製三維正弦函數等高線圖就是一個例子:

def sinus2d(x, y):
  return np.sin(x) + np.sin(y)

xx, yy = np.meshgrid(np.linspace(0, 2 * np.pi, 100), np.linspace(0, 2 * np.pi, 100))
z = sinus2d(xx, yy) # Create the image on this grid

import matplotlib.pyplot as plt

plt.imshow(z, origin="lower", interpolation="none")
plt.show()

np.triu / np.tril

與ones_like或zeros_like類似,這兩個函數在矩陣的某個對角線上方或下方返回0。例如,我們可以使用triu函數在主對角線上創建一個值為True的布爾掩碼,並在繪製相關熱圖時使用這個掩碼。

import seaborn as sns

diamonds = sns.load_dataset("diamonds")

matrix = diamonds.corr()
mask = np.triu(np.ones_like(matrix, dtype=bool))

sns.heatmap(matrix, square=True, mask=mask, annot=True, fmt=".2f", center=0);

如你所見,用triu創建的掩碼可以用在相關矩陣上,去掉不必要的上三角形和對角線。這使得熱圖更加緊湊,可讀性更強。

np.ravel / np.flatten

NumPy是關於高維矩陣和ndarrays的。但是有時候你只是想把這些數組壓縮成一維。這就是你使用ravel或flatten的地方:

array = np.random.randint(0, 10, size=(4, 5))

>>> array
array([[6, 4, 8, 9, 6],
      [5, 0, 4, 8, 5],
      [1, 3, 1, 0, 3],
      [2, 3, 3, 6, 5]])

>>> array.ravel()
array([6, 4, 8, 9, 6, 5, 0, 4, 8, 5, 1, 3, 1, 0, 3, 2, 3, 3, 6, 5])

>>> array.flatten()
array([6, 4, 8, 9, 6, 5, 0, 4, 8, 5, 1, 3, 1, 0, 3, 2, 3, 3, 6, 5])

它們看起來一樣嗎?不完全是。flatten總是返回一個1D副本,而ravel則試圖生成原始數組的1D視圖。也就是說如果修改從ravel返回的數組可能會改變原來的數組。

np.vstack / np.hstack

在Kaggle上這兩個函數經常被使用。通常人們從不同的模型對測試集有多個預測,他們希望以某種方式集成這些預測。為了使它們易於處理,必須將它們組合成一個矩陣。

array1 = np.arange(1, 11).reshape(-1, 1)
array2 = np.random.randint(1, 10, size=10).reshape(-1, 1)

hstacked = np.hstack((array1, array2))

>>> hstacked
array([[ 1, 2],
      [ 2, 6],
      [ 3, 6],
      [ 4, 7],
      [ 5, 4],
      [ 6, 6],
      [ 7, 6],
      [ 8, 8],
      [ 9, 2],
      [10, 8]])

array1 = np.arange(20, 31).reshape(1, -1)
array2 = np.random.randint(20, 31, size=11).reshape(1, -1)

vstacked = np.vstack((array1, array2))

>>> vstacked
array([[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
      [21, 23, 23, 26, 29, 26, 27, 27, 28, 25, 25]])

在將每個數組與這些數組堆疊之前,要對數組進行重塑,因為默認情況下它們需要2D數組。這就是我們使用重塑函數的原因。這裡,reshape(-1,1)表示將數組轉換為具有儘可能多行的單列。

類似地,reshape(1,-1)將數組轉換為具有儘可能多列的單行向量。

np.r_ / np.c_

如果你像我一樣懶惰,不想對所有數組調用重塑,那麼有一個更優雅的解決方案。np.r_ / np.c_操作符(不是函數!)允許將數組分別堆疊為行和列。

下面,我們模擬一個有100個可能性的預測數組。為了將它們堆疊在一起,我們調用np.r_用括號表示(如pandas.DataFrame.loc)。

preds1 = np.random.rand(100)
preds2 = np.random.rand(100)

as_rows = np.r_[preds1, preds2]
as_cols = np.c_[preds1, preds2]

>>> as_rows.shape
(200,)

>>> as_cols.shape
(100, 2)

類似地,np.c_將數組堆疊在一起創建一個矩陣。其實它們的功能並不局限於簡單的水平和垂直堆棧。要了解更多的功能,我建議你閱讀文檔。

np.info

NumPy的函數非常的多。你可能沒有時間和耐心學習每個函數和類。如果你面對一個未知的函數呢?你不用去看文檔了因為有更好的選擇。

info函數可以列印NumPy API中任何名稱的docstring。這裡是info使用的信息:

>>> np.info(np.info)

info(object=None, maxwidth=76,
    output=<ipykernel.iostream.OutStream object at 0x0000021B875A8820>,
    toplevel='numpy')

Get help information for a function, class, or module.

Parameters

object : object or str, optional
  Input object or name to get information about. If `object` is a
  numpy object, its docstring is given. If it is a string, available
  modules are searched for matching objects. If None, information
  about `info` itself is returned.
maxwidth : int, optional
  Printing width.

還記得我們在vscode的文章中說過lint要求強制編寫docstring嗎,這就是原因了。

np.where

顧名思義,這個函數返回一個條件為True的數組的所有下標:

probs = np.random.rand(100)

idx = np.where(probs > 0.8)

>>> probs[idx]
array([0.80444302, 0.80623093, 0.98833642, 0.96856382, 0.89329919,
      0.88664223, 0.90515148, 0.96363973, 0.81847588, 0.88250337,
      0.98737432, 0.92104315])

它在搜索稀疏數組中的非零元素時特別有用,甚至可以在Pandas DataFrames上使用它來基於條件進行更快的索引檢索。

np.all / np.any

當與assert語句一起使用時,這兩個函數將在數據清理期間非常方便。np.all僅當數組中的所有元素都符合特定條件時返回True:

array1 = np.random.rand(100)
array2 = np.random.rand(100)

>>> np.all(array1 == array2)
False

因為我們創建了兩個隨機數的數組,所以不可能每個元素都相等。然而,如果這些數字是整數,那麼它們中至少有兩個相等的可能性要大得多:

a1 = np.random.randint(1, 100, size=100)
a2 = np.random.randint(1, 100, size=100)

>>> np.any(a1 == a2)
True

any返回True是因為數組中至少有一個元素滿足特定條件,

np.allclose

如果想要檢查兩個長度相等的數組是否互為副本,簡單的==操作符不會將其截斷。但是你可能想要比較浮點數數組,但是它們的小數點長度使得比較困難。在這種情況下可以使用allclose,如果一個數組的所有元素彼此之間距離很近,給定一定的容忍度,它將返回True。

a1 = np.arange(1, 10, step=0.5)
a2 = np.arange(0.8, 9.8, step=0.5)

>>> np.all(a1 == a2)
False

>>> a1
array([1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5, 6. , 6.5, 7. ,
      7.5, 8. , 8.5, 9. , 9.5])

>>> a2
array([0.8, 1.3, 1.8, 2.3, 2.8, 3.3, 3.8, 4.3, 4.8, 5.3, 5.8, 6.3, 6.8,
      7.3, 7.8, 8.3, 8.8, 9.3])

>>> np.allclose(a1, a2, rtol=0.2)
False

>>> np.allclose(a1, a2, rtol=0.3)
True

只有當差異(<)小於rtol時,函數才返回True,而不是<=!

np.argsort

np.sort返回一個已排序的數組副本。有時需要對數組進行排序的索引,以便為不同的目的多次使用相同的索引。這就是 argsort 派上用場的地方:

random_ints = np.random.randint(1, 100, size=20)
idx = np.argsort(random_ints)

>>> random_ints[idx]
array([ 6, 19, 22, 23, 35, 36, 37, 45, 46, 57, 61, 62, 64, 66, 66, 68, 72,
      74, 87, 89])

它來自以 arg 開頭的一系列函數,這些函數總是從某個函數的結果返回一個或多個索引。例如,argmax 查找數組中的最大值並返回其索引(分類的TOP N就可以用這種方法)。

np.isneginf / np.isposinf

這兩個布爾函數檢查數組中的元素是負無窮大還是正無窮大。但是計算機和 NumPy 不理解無窮大的概念(好吧,我也不知道是為什麼)。它們只能將無窮大表示為一個非常大或非常小的數字,這樣才可以放入一個變量中(我希望我說得對)。

這就是為什麼當你列印 np.inf 的類型時,它返回浮點數:

>>> type(np.inf)  # type of the infinity
float

>>> type(-np.inf)
float

這意味著無窮大值可以很容易地被當作數組的正常值。所以你需要一個特殊的功能來找到這些異常的值:

a = np.array([-9999, 99999, 97897, -79897, -np.inf])

>>> np.all(a.dtype == "float64")
True

>>> np.any(np.isneginf(a))
True

np.polyfit

如果要執行傳統的線性回歸,則不一定需要 Sklearn。NumPy 也可以的:

X = diamonds["carat"].values.flatten()
y = diamonds["price"].values.flatten()

slope, intercept = np.polyfit(X, y, deg=1)

>>> slope, intercept
(7756.425617968436, -2256.3605800454034)

polyfit 獲取兩個向量,對它們應用線性回歸併返回斜率和截距。你只需要使用 deg 指定次數,因為此函數可用於逼近任何次數多項式的根。

檢查發現用 polyfit 找到的斜率和截距與 Sklearn 的 LinearRegression 模型相同:

from sklearn.linear_model import LinearRegression

lr = LinearRegression().fit(X.reshape(-1, 1), y)

>>> lr.coef_, lr.intercept_
(array([7756.42561797]), -2256.360580045441)

概率分布

NumPy 的 random 模塊有多種偽隨機數生成器可供選擇。除了我最喜歡的樣本和選擇之外,還有模擬偽完美概率分布的函數。

例如,二項式、伽馬、正態和 tweedie 函數從它們各自的分布中繪製自定義數量的數據點。

當你必須近似數據中特徵的分布時,你可能會發現它們非常有用。例如,下面我們檢查鑽石價格是否服從正態分布。

fig, ax = plt.subplots(figsize=(6, 8))

price_mean = diamonds["price"].mean()
price_std = diamonds["price"].std()

# Draw from a perfect normal distribution
perfect_norm = np.random.normal(price_mean, price_std, size=1000000)

sns.kdeplot(diamonds["price"], ax=ax)
sns.kdeplot(perfect_norm, ax=ax)

plt.legend(["Price", "Perfect Normal Distribution"]);

這可以通過在完美正態分布之上繪製鑽石價格的 KDE 來實現,以使差異可見。

np.rint

如果你想將數組的每個元素四捨五入到最接近的整數, rint 是一個漂亮的小函數。當你想將類概率轉換為二進位分類中的類標籤時,可以不必調用模型的 predict 方法改成直接使用它:

preds = np.random.rand(100)

>>> np.rint(preds[:50])
array([1., 1., 0., 1., 0., 1., 1., 0., 0., 0., 0., 1., 0., 1., 0., 1., 0.,
      1., 0., 1., 1., 1., 1., 1., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,
      0., 0., 0., 0., 0., 0., 1., 0., 1., 0., 1., 1., 0., 0., 1., 0.])

np.nanmean / np.nan*

是否知道如果至少有一個元素是 NaN,則純 NumPy 數組上的算術運算會失敗?

a = np.array([12, 45, np.nan, 9, np.nan, 22])

>>> np.mean(a)
nan

要在不修改原始數組的情況下解決此問題,你可以使用一系列 nan 函數:

>>> np.nanmean(a)
22.0

以上是忽略缺失值的算術平均函數的示例。許多其他函數以同樣的方式工作:

>>> [func for func in dir(np) if func.startswith("nan")]

['nan',
'nan_to_num',
'nanargmax',
'nanargmin',
'nancumprod',
'nancumsum',
'nanmax',
'nanmean',
'nanmedian',
'nanmin',
'nanpercentile',
'nanprod',
'nanquantile',
'nanstd',
'nansum',
'nanvar']

但是,如果只使用 Pandas DataFrames 或 Series,可能會有些不同,因為它們默認會忽略 NaN。

np.clip

當想對數組的值施加嚴格限制時,clip 很有用。下面,我們將裁剪任何超出 10 和 70 硬限制的值:

ages = np.random.randint(1, 110, size=100)

limited_ages = np.clip(ages, 10, 70)

>>> limited_ages
array([13, 70, 10, 70, 70, 10, 63, 70, 70, 69, 45, 70, 70, 56, 60, 70, 70,
      10, 52, 70, 32, 62, 21, 70, 13, 13, 10, 50, 38, 32, 70, 20, 27, 64,
      34, 10, 70, 70, 53, 70, 53, 54, 26, 70, 57, 70, 46, 70, 17, 48, 70,
      15, 49, 70, 10, 70, 19, 23, 70, 70, 70, 45, 47, 70, 70, 34, 25, 70,
      10, 70, 42, 62, 70, 10, 70, 23, 25, 49, 70, 70, 62, 70, 70, 11, 10,
      70, 30, 44, 70, 49, 10, 35, 52, 21, 70, 70, 25, 10, 55, 59])

np.count_nonzero

使用稀疏數組是很常見的。通常,它們是對具有高基數(High-Cardinality)或只有許多二進位列的分類特徵進行獨熱編碼的結果。

你可以使用count_nonzero來檢查任意數組中非零元素的數量:

a = np.random.randint(-50, 50, size=100000)

>>> np.count_nonzero(a)
98993

100k隨機整數中,~1000個為零。

np.array_split

它可以用來將ndarray或dataframe分成N個bucket。此外,當你想要將數組分割成大小不相等的塊(如vsplit)時,它不會引發錯誤:

import datatable as dt

df = dt.fread("data/train.csv").to_pandas()

splitted_dfs = np.array_split(df, 100)

>>> len(splitted_dfs)
100


作者:Bex.T



相關焦點

  • 第89天:NumPy 排序和篩選函數
    NumPy 也提供了多種排序和篩選函數,本文就來介紹一下 NumPy 常見的排序和篩選函數。排序函數NumPy 中提供了排序相關的函數。排序函數已經幫助我們實現了不同的排序算法,我們只需要拿來直接使用就行。每個排序算法的執行速度,時間複雜度,空間複雜度和算法的穩定性都不相同,我們來看看常見的幾種排序算法的比較。
  • NumPy ufunc通用函數
    ufunc 是 universal function 的縮寫,意思是「通用函數」,它是一種能對數組的每個元素進行操作的函數。許多 ufunc 函數都是用C語言級別實現的,因此它們的計算速度非常快。此外,ufun 比 math 模塊中的函數更靈活。
  • 【入門基礎】Numpy基礎20問
    ,這裡不多說。'''輸出:(2, 3)元素長度為2代表二維,元素2代表0軸有兩個元素,元素3代表1軸有3個元素。10、如何查看數組的類型?numpy一維數組的索引和切片操作類似python列表,這裡不多講。比如說取一維數組前三個元素。
  • 學習Python必知的Numpy函數
    大量的擴展包是 Python 的一大特色,包括各種適用於數據分析的例程和函數。有了這些擴展包,數據科學家們可以不用重複造輪子,節省了很多時間。其中一個重要的數學擴展包就是 NumPy。NumPy 是一個基礎數學包,以其高效的多維數組函數而聞名,適用於線性代數,傅立葉變換,邏輯運算等。
  • 為什麼你用不好Numpy的random函數?
    在python數據分析的學習和應用過程中,經常需要用到numpy的隨機函數,由於隨機函數random的功能比較多,經常會混淆或記不住
  • 這3個函數,是你學習Numpy的基礎!
    你要的算法+案例分析+代碼統統有!點擊上圖,查看教學大綱1. 本文介紹前面我們已經為大家詳細介紹了ndarray數組的6種常用屬性,以及numpy數組中的9種常數。說白了,就是講述3個函數。當然,這只是numpy函數中的冰山一角。這裡只是介紹在學習numpy過程中,黃同學最先遇到的幾個函數。
  • Python數據科學Numpy基礎20問
    numpy中可以使用array函數創建數組:import numpy as npnp.array([1,2,3])# 輸出:array([1, 2, 3])4、如何區分一維、二維、多維?判斷一個數組是幾維,主要是看它有幾個軸(axis)。
  • Python中的Numpy基礎20問
    import numpy as np# 創建二維數組x2 = np.array([[1,2,3],[4,5,6]])# 將x2轉換為三維數組,並且自定義每個軸的元素數量x2.resize((1,2,3))x2'''輸出:array([[[1, 2, 3],[4, 5, 6]]])'''如何對數組進行索引和切片操作?numpy一維數組的索引和切片操作類似python列表,這裡不多講。
  • 總結numpy中的ndarray,非常齊全
    shape屬性表示ndarray的形狀,是一個元組,表示數組有幾維,每個維度有多少個數據。ndim屬性表示數組的維度。size屬性表示數組中的元素個數,size可以通過shape中的值相乘得到。dtype屬性表示數組中保存的數據類型。從Python解釋器的角度看,ndarray屬於numpy.ndarray對象。
  • Numpy中常用的10個矩陣操作示例
    為了得到點積,第一個矩陣的列數應該等於第二個矩陣的行數。有兩種方法可以在numpy中創建矩陣。最常見的一種是使用numpy ndarray類。這裡我們創建了二維numpy數組(ndarray對象)。另一種方法是使用numpy矩陣類。
  • Python擴展庫numpy中where()函數的三種用法
    第二種用法:給where()函數傳遞一個包含True/False值的數組,返回該數組中True值的下標,結合numpy數組的關係運算,可以返回數組中符合特定條件的元素的下標。第三種用法:給where()函數傳遞一個條件數組和兩個值或數組,對於條件數組中等價於True的位置,從第一個值或數組中取值進行替換,否則從第二個值或數組中取值進行替換。
  • NumPy 1.20.0 版本發布,支持Python 3.7+
    新版本支持 Python 3.7-3.9,但不支持 Python 3.6。主要亮點如下:NumPy 函數注釋;擴大 SIMD 使用範圍,提升 ufuncs(Universal Functions)的執行速度;更改數據類型和強制轉換實現的準備工作,以便為擴展數據類型提供更簡單的途徑;文檔改進,包括大約 185 個 PR 合併;關於移除 Python 2.7 的進一步清理(cleanups), 這樣可以提高代碼的可讀性並消除技術負擔
  • Numpy中的通用函數
    NumPy數組的計算:通用函數緩慢的循環通用函數介紹探索Numpy的通用函數高級通用函數的特性聚合:最小值、 最大值和其他值數組值求和最大值和最小值其他聚合函數《Python數據科學手冊》讀書筆記NumPy數組的計算:通用函數NumPy 數組的計算有時非常快, 有時也非常慢。
  • 5個高效&簡潔的Numpy函數
    1 個,那麼將會報錯。Argpartition:在數組中找到最大的 N 個元素。Numpy 的 argpartion 函數可以高效地找到 N 個最大值的索引並返回 N 個值。在給出索引後,我們可以根據需要進行值排序。
  • 數據分析之Numpy入門
    判斷一個數組是幾維,主要是看它有幾個軸axis一個軸表示一維數組,兩個軸表示二維數組,以此類推。每個軸都代表一個一維數組。比如說,二維數組第一個軸裡的每個元素都是一個一維數組,也就是第二個軸。,[4,5,6]])# 查看元素總數reduce(lambda x,y:x*y , x2.shape)'''輸出:6shape形狀:(2,3)'''7、數組元素數據類型numpy支持的數據類型非常多,所以很適合做數值計算,常見的數據類型如下來源:https://www.runoob.com
  • 圖解NumPy:常用函數的內在機制
    因此,常見的做法是要麼先使用 Python 列表,準備好之後再將其轉換為 NumPy 數組,要麼是使用 np.zeros 或 np.empty 預先留下必要的空間:通常我們有必要創建在形狀和元素類型上與已有數組匹配的空數組。
  • Numpy入門指南
    存在以下屬性:•ndarray.ndim:數組的軸(維度)的個數。•ndarray.shape:數組的維度。一個整數元組,表示每個維度中數組的大小。對於有n行和m列的矩陣,shape將是(n,m),即shape元組長度就是rank或者維度的個數ndim。• ndarray.size:數組元素的總數。•ndarray.dtype:一個描述數組中元素類型的對象 。
  • Numpy高級操作大全!!!
    [[ 0 5 10 15] [20 25 30 35] [40 45 50 55]][1 2 3 4]0 15 210 315 420 125 230 335 440 145 250 355 4二.數組形狀修改函數1.ndarray.reshape函數在不改變數據的條件下修改形狀
  • 每個數據科學家都應該知道的20個NumPy操作
    在這篇文章中,我將介紹20種常用的對NumPy數組的操作。這些操作可分為4個主要類別:首先就是需要引入numpy的包 import numpy as np創建數組我們創建了一個有100個浮點數的數組。矩陣的逆矩陣是與原矩陣相乘得到單位矩陣的矩陣。不是每個矩陣都有逆矩陣。如果矩陣A有一個逆矩陣,則稱為可逆或非奇異。
  • Numpy 中不得不知的4個重要函數
    今天分享幾個數據分析中經常需要用到的重要函數。掌握這些函數可以幫助我們保持代碼整潔並且避免重複造輪子。準備工作導入numpy示例數據本文以二分類任務為例,通常我們的model會輸出預測的概率,得到概率後需要進行後續的處理,比如:•根據閾值,將概率大於某個閾值的label設置為1,小於閾值的設置為0•在模型診斷過程中,找出滿足某些條件的樣本本文使用的示例數據如下:predict_prob = np.array