{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 數列運算\n", "\n", "我們先載入這個章節範例程式碼中會使用到的第三方套件、模組或者其中的部分類別、函式。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 關於 NumPy\n", "\n", "NumPy 是 Numerical Python 的縮寫,是 Python 使用者用來實踐向量化(Vectorization)、科學計算與資料科學的套件,也是我們進行「運算數值」的主角,資料科學團隊使用 NumPy 所提供一種稱為 `ndarray` 的資料結構來儲存並運算數值陣列、與標準套件 `random` 相輔相成的 `numpy.random` 來處理隨機性以及 `numpy.linalg` 來處理線性代數中一些繁複的運算。透過 `ndarray` 的數值陣列幾乎能夠面對任意的資料來源,包括實驗資料、圖像(表示像素亮度的二維數值陣列)或者音訊(表示強度與時間軸的一維數值陣列。)\n", "熟悉 `ndarray` 的操作對於以 Python 應用資料科學的使用者來說是必要的前提,也扮演著理解機器學習理論的基石,如果能夠自信地操作二維數值陣列(即矩陣 Matrix)以及三維數值陣列(即張量 Tensor),將更容易理解高階機器學習框架究竟在封裝起來的函式、方法中提供了哪些功能給使用者,進而更有效率作超參數的調校。\n", "我們可以在執行 Python 的載入指令(`import`)後印出 NumPy 的版本號來確認環境中是否安裝了 NumPy 可供使用。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.18.0\n" ] } ], "source": [ "print(np.__version__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "假若得到的回應是:\n", "\n", "```\n", "Traceback (most recent call last):\n", " File \"\", line 1, in \n", "ModuleNotFoundError: No module named 'numpy'\n", "```\n", "\n", "表示目前所處的 Python 環境沒有安裝 NumPy,這時要切換回命令列安裝。\n", "\n", "```bash\n", "# 在命令列執行\n", "pip install numpy\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 為何 NumPy\n", "\n", "模組、套件的開發多半起源於某些痛點,就如同創新產品的問世一般;具體來說,Python 原生 `list` 的什麼特性讓科學計算使用者覺得有些麻煩呢?歸根究底就是 `list` 具備儲存異質資料型態的特性。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "\n", "\n", "\n" ] } ], "source": [ "heterogeneous_list = [5566, 55.66, True, False, '5566']\n", "for i in heterogeneous_list:\n", " print(type(i))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`list` 中每個元素都是一個完整的 Python 物件,具備各自的類別與數值資訊,這使得它在計算同質資料時的效能、語法付出代價,我們需要透過迭代將裡面的物件一一取出後運算。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, 9, 16, 25]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "homogeneous_list = [1, 2, 3, 4, 5]\n", "[i**2 for i in homogeneous_list]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NumPy 的 `ndarray` 與 `list` 的最大不同在於同質資料型態的特性,在計算時的效能和語法更快速與簡潔。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, 4, 9, 16, 25])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "arr = np.array([1, 2, 3, 4, 5])\n", "arr**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`ndarray` 在同質數值陣列計算的便利性相較原生 `list` 具有絕對的優勢,接著我們來暸解如何創建 `ndarray` 以及 NumPy 的標準資料型態。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 如何創建 `ndarray`\n", "\n", "創建 `ndarray` 的方法有二種:一是使用 `np.array()` 將既有的 `list` 轉換成為 `ndarray`。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "list" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "homogeneous_list = [1, 2, 3, 4, 5]\n", "type(homogeneous_list)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "[1 2 3 4 5]\n", "int64\n" ] } ], "source": [ "arr = np.array(homogeneous_list)\n", "print(type(arr))\n", "print(arr)\n", "print(arr.dtype)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`np.array()` 可以搭配 `dtype` 參數指定資料型態,可以傳入下列常見的資料型態:\n", "\n", "- int :整數型態\n", "- float :浮點數型態\n", "- bool :布林型態" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "homogeneous_list = [1, 2, 3, 4, 5]\n", "arr = np.array(homogeneous_list, dtype=int)\n", "arr.dtype" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('float64')" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "arr = np.array(homogeneous_list, dtype=float)\n", "arr.dtype" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "第二種創建 `ndarray` 的方式是利用 NumPy 的多樣化函式,同樣可以搭配 `dtype` 參數,常用的創建函式有:\n", "\n", "- `np.zeros(shape)`:創建指定外觀充滿 0 的數值陣列\n", "- `np.ones(shape)` :創建指定外觀充滿 1 的數值陣列\n", "- `np.full(shape, fill_value)`:創建指定外觀充滿 fill_value 的數值陣列" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 0, 0])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.zeros(5, dtype=int) # 外觀為 (5,)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1., 1.],\n", " [1., 1.]])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.ones((2, 2), dtype=float) # 外觀為 (2, 2)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5566, 5566],\n", " [5566, 5566]])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.full((2, 2), 5566, dtype=int) # 外觀為 (2, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "假如需要等差、均勻間隔的數列,則可以使用下列函式:\n", "\n", "- `np.arange(start, stop, step)`:創建從 `start`(包含)間隔 `step` 至 `stop`(不包含)的等差數列,使用方式同內建函式 `range()`\n", "- `np.linspace(start, stop, num)` :創建從 `start`(包含)至 `stop`(包含)的均勻切割為 `num` 個資料點的數值陣列" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 3, 5, 7, 9])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.arange(1, 10, 2)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 3, 5, 7, 9])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linspace(1, 9, 5, dtype=int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "最後,如果需要隨機數所組合成的數值陣列,則可以使用 `numpy.random` 中的函式:\n", "\n", "- `np.random.random(size)`:創建指定外觀介於 0, 1 之間、並符合均勻分佈的數值陣列\n", "- `np.random.normal(loc, scale, size)`:創建指定外觀以 `loc` 為平均數、`scale` 為標準差常態分佈的數值陣列\n", "- `np.random.randint(low, high, size)`:創建指定外觀於 `low` (包含)到 `high`(不包含)之間隨機抽樣之正整數的數值陣列" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0.07888432 0.85680097 0.59827821 ... 0.57373442 0.51850271 0.92199587]\n", "[ 1.2202809 -0.42494108 0.09156555 ... -0.40549757 1.43683747\n", " -1.45461075]\n", "[1 6 5 5 4 6]\n" ] } ], "source": [ "uniform_arr = np.random.random(10000)\n", "normal_arr = np.random.normal(0, 1, 10000)\n", "randint_arr = np.random.randint(1, 7, size=6)\n", "print(uniform_arr)\n", "print(normal_arr)\n", "print(randint_arr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "這時又可以呼應「為何視覺化」一節中我們提到的,觀察原始資料對於理解其分配特性幾乎沒有幫助,需要仰賴直方圖(histogram)才能挖掘出分配的特徵。截至目前為止我們還沒有開始認識 Matplotlib,但是為了更妥善地說明,在範例程式碼中已經先引用了 Matplotlib 所提供類別或函式,假如讀者目前對這部分感到困惑,可待讀過資料探索等本書後面的章節,再回來複習。" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig = plt.figure()\n", "ax = plt.axes()\n", "ax.hist(uniform_arr, bins=30)\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig = plt.figure()\n", "ax = plt.axes()\n", "ax.hist(normal_arr, bins=30)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 常用的 `ndarray` 屬性\n", "\n", "`ndarray` 較常被用到的屬性有:\n", "\n", "- `arr.ndim`:檢視 `arr` 有幾個維度\n", "- `arr.shape`:檢視 `arr` 的外型\n", "- `arr.size`:檢視 `arr` 的資料筆數,對一維陣列的意涵就像內建函式 `len()` 作用在 `list` 上一般\n", "- `arr.dtype` :檢視 `arr` 中同質資料的型態" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "(4,)\n", "4\n", "int64\n" ] } ], "source": [ "arr = np.array([5, 5, 6, 6])\n", "print(arr.ndim)\n", "print(arr.shape)\n", "print(arr.size)\n", "print(arr.dtype)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 純量、向量、矩陣與張量\n", "\n", "我們會依照不同需求來創建不同維度的數值陣列,而這些不同維度的數值陣列各自專屬的暱稱:\n", "\n", "- 純量:泛指沒有維度的數值\n", "- 向量:泛指具有一個維度的數值陣列,我常喜歡用「吐司條」來比喻讓它更具體些\n", "- 矩陣:泛指具有兩個維度的數值陣列,我常喜歡用「一片吐司」來比喻讓它更具體些\n", "- 張量:泛指三個維度以及超過三個維度的數值陣列,我常喜歡用「多片吐司」來比喻讓它更具體些\n", "\n", "![純量、向量、矩陣與張量](02-numpy_18_0.png)\n", "\n", "圖片來源:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5566\n", "0\n", "()\n" ] } ], "source": [ "scalar = np.array(5566)\n", "print(scalar)\n", "print(scalar.ndim)\n", "print(scalar.shape)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[5 5 6 6]\n", "1\n", "(4,)\n" ] } ], "source": [ "# 吐司條\n", "vector = np.array([5, 5, 6, 6])\n", "print(vector)\n", "print(vector.ndim)\n", "print(vector.shape)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[5 5]\n", " [6 6]]\n", "2\n", "(2, 2)\n" ] } ], "source": [ "# 一片 2x2 的吐司\n", "matrix = np.array([5, 5, 6, 6]).reshape(2, 2)\n", "print(matrix)\n", "print(matrix.ndim)\n", "print(matrix.shape)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[[5 5]\n", " [6 6]]\n", "\n", " [[5 5]\n", " [6 6]]\n", "\n", " [[5 5]\n", " [6 6]]]\n", "3\n", "(3, 2, 2)\n" ] } ], "source": [ "# 三片 2x2 的吐司\n", "tensor = np.array([5, 5, 6, 6]*3).reshape(3, 2, 2)\n", "print(tensor)\n", "print(tensor.ndim)\n", "print(tensor.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NumPy 有非常豐富的內建函式可以協助我們進行常用的向量與矩陣計算。運用 `np.eye()` 可以創建單位矩陣(Identity matrix),函式命名是取 I 的諧音。" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 0],\n", " [0, 1]])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "I = np.eye(matrix.shape[0], dtype=int)\n", "I" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "運用 `np.dot()` 可以進行向量內積與矩陣相乘。" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "122" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.dot(vector, vector)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[5 5]\n", " [6 6]]\n", "[[5 5]\n", " [6 6]]\n" ] } ], "source": [ "print(np.dot(matrix, I))\n", "print(np.dot(I, matrix))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[55, 55],\n", " [66, 66]])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.dot(matrix, matrix)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "運用 `np.transpose()` 或者 `arr.T` 可以進行轉置,將外觀 `(m, n)` 矩陣轉換為 `(n, m)`,列與欄的元素互換。" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0 1 2]\n", " [3 4 5]]\n", "[[0 3]\n", " [1 4]\n", " [2 5]]\n", "[[0 3]\n", " [1 4]\n", " [2 5]]\n" ] } ], "source": [ "matrix = np.arange(6).reshape(2, 3)\n", "print(matrix)\n", "print(np.transpose(matrix))\n", "print(matrix.T)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "運用 `np.linalg.inv()` 可以求解反矩陣,反矩陣在矩陣運算中扮演的角色就像「倒數」在四則運算中一般,例如四則運算中我們想要求解 $x$ 會在等號左右兩側都乘 $a$ 的倒數。\n", "\n", "\\begin{align}\n", "ax &= b \\\\\n", "\\frac{1}{a}ax &= \\frac{1}{a}b\\\\\n", "x &= \\frac{b}{a}\n", "\\end{align}\n", "\n", "如果在矩陣運算中想要求解 $X$,那麼就在等號左右兩側都乘上 $A$ 的反矩陣 $A^{-1}$。\n", "\n", "\\begin{align}\n", "AX &= B \\\\\n", "A^{-1}AX &= A^{-1}B \\\\\n", "X &= A^{-1}B\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-3., -4.],\n", " [ 4., 5.]])" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.array([1, 2, 3, 4]).reshape(2, 2)\n", "B = np.array([5, 6, 7, 8]).reshape(2, 2)\n", "A_inv = np.linalg.inv(A)\n", "X = np.dot(A_inv, B)\n", "X" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `ndarray` 的索引\n", "\n", "從 `ndarray` 中取出單個資料值的方式與 `list` 相同,使用 `[INDEX]` 取值,索引值同樣由左至右從 0 算起,由右至左從 -1 算起,在參數命名上稱呼最左邊為起始(start)、最右邊為終止(stop)。" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "From start to stop:\n", "55\n", "66\n", "56\n", "5566\n", "From stop to start:\n", "5566\n", "56\n", "66\n", "55\n" ] } ], "source": [ "arr = np.array([55, 66, 56, 5566])\n", "print(\"From start to stop:\")\n", "print(arr[0])\n", "print(arr[1])\n", "print(arr[2])\n", "print(arr[arr.size - 1])\n", "print(\"From stop to start:\")\n", "print(arr[-1])\n", "print(arr[-2])\n", "print(arr[-3])\n", "print(arr[-arr.size])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "面對二維以上的陣列,ndarray 支援使用 `[i, j, ...]` 的方式取出位於第 i 列(row)、第 j 欄(column)...的資料。" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[7 4 8 5]\n", " [7 3 7 8]\n", " [5 4 8 8]]\n", "3\n", "4\n" ] } ], "source": [ "np.random.seed(42)\n", "arr = np.random.randint(1, 10, size=(3, 4))\n", "print(arr)\n", "print(arr[1, 1]) # 3 located at (1, 1)\n", "print(arr[2, -3]) # 4 located at (2, -3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `ndarray` 的切割\n", "\n", "從 `ndarray` 中取出多個資料值的方式與 `list` 相同,使用 `[start:stop:step]` 取出陣列的片段,如果沒有指定 `start` 預設值 0、 `stop` 預設值 `arr.size` 意即最右邊、 `step` 預設值 1;有趣的設定是當 `step` 為 -1 時候的效果為反轉數列。" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[10 11 12 13 14 15 16 17 18 19]\n", "[10 12 14 16 18]\n", "[10 11 12 13 14]\n", "[15 16 17 18 19]\n", "[19 18 17 16 15 14 13 12 11 10]\n" ] } ], "source": [ "arr = np.arange(10, 20)\n", "print(arr[::]) # 三個參數都預設值\n", "print(arr[::2]) # step=2\n", "print(arr[:5]) # stop=5, 不包含\n", "print(arr[5:]) # start=5, 包含\n", "print(arr[::-1]) # step=-1, 反轉" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `ndarray` 特別的索引\n", "\n", "除了與 `list` 相同支援 `[INDEX]` 與 `[start:stop:step]` 的索引、切割方式,`ndarray` 額外支援兩種資料科學家熱愛的索引寫法:\n", "\n", "- 華麗索引(Fancy Indexing)\n", "- 布林索引(Boolean Indexing)\n", "\n", "華麗索引(Fancy Indexing)指的是以陣列傳入不規則的索引值選取資料值,不用遷就 `[start:stop:step]` 所生成的規則索引陣列。" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[45 48 65 68 68 10 84 22 37 88]\n", "[45 65 37]\n" ] } ], "source": [ "np.random.seed(0)\n", "arr = np.random.randint(1, 100, size=(10,))\n", "odd_indices = [0, 2, 8]\n", "print(arr)\n", "print(arr[odd_indices])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "布林索引(Boolean Indexing)指的是以外觀相同的陣列傳入布林值,將位置為 True 的資料篩選出來。" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[45 48 65 68 68 10 84 22 37 88]\n", "[45 65 37]\n" ] } ], "source": [ "is_odd = [True, False, True, False, False, False, False, False, True, False]\n", "print(arr)\n", "print(arr[is_odd])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "其中外觀相同的布林值陣列 `is_odd` 因為能透過 `ndarray` 的向量化特性搭配判斷條件輕鬆產生,因此這通常是資料科學家最喜歡使用的切割、篩選方式。" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ True False True False False False False False True False]\n", "[45 48 65 68 68 10 84 22 37 88]\n", "[45 65 37]\n" ] } ], "source": [ "is_odd = arr % 2 == 1\n", "print(is_odd)\n", "print(arr)\n", "print(arr[is_odd])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 重塑外觀\n", "\n", "並不是所有創建 `ndarray` 的函式都可以指定外觀 (m, n, ...),這時我們需要在初始化之後調整其外觀,常用方法有:\n", "\n", "- `reshape(m, n, ...)`\n", "- `ravel()`\n", "\n", "運用方法 `arr.reshape(m, n, ...)` 將數值陣列 `arr` 重塑成運算所需要的外觀。" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1 2 3 4 5 6 7 8 9]\n", "(9,)\n", "[[1 2 3]\n", " [4 5 6]\n", " [7 8 9]]\n", "(3, 3)\n" ] } ], "source": [ "arr = np.arange(1, 10)\n", "print(arr)\n", "print(arr.shape)\n", "print(arr.reshape(3, 3))\n", "print(arr.reshape(3, 3).shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "重塑時需要指定 (m, n, ...) 外觀,更便捷的寫法是將其餘 `ndim — 1` 個維度指定好,再傳最後一個維度是 -1,`ndarray` 會自行計算最後一個維度的值;例如將長度為 9 的數值陣列 `arr` 重塑成 (3, 3) 外觀時可以寫作 `arr.reshape(3, -1)` 或者 `arr.reshape(-1, 3)`。" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2 3]\n", " [4 5 6]\n", " [7 8 9]]\n", "[[1 2 3]\n", " [4 5 6]\n", " [7 8 9]]\n" ] } ], "source": [ "arr = np.arange(1, 10)\n", "print(arr.reshape(3, -1))\n", "print(arr.reshape(-1, 3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在矩陣相乘運算的應用場景中,我們常需要將一維的數值陣列重塑成二維的欄(尚未轉置的向量 $v$)或者二維的列(已轉置的向量 $v^T$),這時就可以善用 `arr.reshape(1, -1)` 或 `arr.reshape(-1, 1)`。" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1]\n", " [2]\n", " [3]]\n", "[[1 2 3]]\n" ] } ], "source": [ "arr = np.arange(1, 4)\n", "print(arr.reshape(-1, 1)) # 重塑成二維的欄\n", "print(arr.reshape(1, -1)) # 重塑成二維的列" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "運用方法 `ravel()` 可以將外觀為 (m, n, ...) 的數值陣列調整回一維。" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3, 3)\n", "2\n", "(9,)\n", "1\n" ] } ], "source": [ "arr = np.arange(1, 10).reshape(3, 3)\n", "print(arr.shape)\n", "print(arr.ndim)\n", "print(arr.ravel().shape)\n", "print(arr.ravel().ndim)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 複製陣列\n", "\n", "`ndarray` 有一個重要的預設特性為「不複製」,不論在切割或重新宣告的情境中都是創建陣列的 View,而非複製另一個陣列,這代表著對以 View 存在的子陣列(Sub-array)更新會改動到原始陣列。例如將 `arr` 的 View `mat` 之中的 5 更新為 5566,會一併更新 `arr` 的 5 為 5566。" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 1 2 3]\n", " [ 4 5566 6]\n", " [ 7 8 9]]\n", "[ 1 2 3 4 5566 6 7 8 9]\n" ] } ], "source": [ "arr = np.arange(1, 10)\n", "mat = arr.reshape(3, 3)\n", "mat[1, 1] = 5566\n", "print(mat)\n", "print(arr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我們若希望實踐陣列的複製,可以運用其 `copy()` 方法,如此一來 `mat` 之中的 5 更新為 5566,並不會影響到 `arr` 中的 5。" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 1 2 3]\n", " [ 4 5566 6]\n", " [ 7 8 9]]\n", "[1 2 3 4 5 6 7 8 9]\n" ] } ], "source": [ "arr = np.arange(1, 10)\n", "mat = arr.copy()\n", "mat = mat.reshape(3, 3)\n", "mat[1, 1] = 5566\n", "print(mat)\n", "print(arr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 合併陣列\n", "\n", "很多 NumPy 的函式都有提供 `axis` 參數能夠讓使用者在進行這些操作時能夠輕鬆地應用在列(row)或欄(column)等不同維度上,暸解 `axis` 參數可以有效幫助使用者處理陣列,我們能夠運用 `np.concatenate([arr0, arr1, ...], axis)` 將多個陣列依照 `axis` 參數的方向進行合併,當 `axis` 為預設值 0 的時候效果為垂直合併、當 `axis=1` 的時候效果為水平合併。" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2]\n", " [3 4]\n", " [5 6]\n", " [7 8]]\n", "[[1 2 5 6]\n", " [3 4 7 8]]\n" ] } ], "source": [ "arr_a = np.arange(1, 5).reshape(2, 2)\n", "arr_b = np.arange(5, 9).reshape(2, 2)\n", "print(np.concatenate([arr_a, arr_b])) # 預設 axis=0\n", "print(np.concatenate([arr_a, arr_b], axis=1)) # axis=1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 通用函式\n", "\n", "NumPy 之所以被譽為 Python 資料科學應用的基石,就是因為能透過通用函式(Universal function)實踐其他科學計算語言內建的向量化(Vectorization)功能,通用函式的作用是針對數值陣列中每個數值進行相同的運算,效率會高於使用迭代語法。我們可以在 Jupyter Notebook 中使用 `%timeit` 得知若想以迭代對一百萬筆隨機整數進行「倒數」的運算要花多少時間。" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "416 ms ± 49.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], "source": [ "long_arr = np.random.randint(1, 101, size=1000000)\n", "%timeit [1/i for i in long_arr]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "較為緩慢的原因是在每次迭代計算倒數時,Python 首先檢查物件類型再呼叫用於該類型的正確運算(在這個例子中為倒數運算)。而使用 `ndarray` 的通用函式,就像是利用已經編譯過的程式並且對固定物件類型計算,無需再檢查,運算的效率較高。" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.61 ms ± 256 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" ] } ], "source": [ "long_arr = np.random.randint(1, 101, size=1000000)\n", "%timeit np.divide(1, long_arr)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "基礎的通用函式與純量的數值運算符不謀而合,可依使用者的喜好選擇通用函式或者運算符:\n", "\n", "- `np.add()`:同 `+` 運算符\n", "- `np.subtract()`:同 `-` 運算符\n", "- `np.multiply()`:同 `*` 運算符\n", "- `np.divide()`:同 `/` 運算符\n", "- `np.power()`:同 `**` 運算符\n", "- `np.floor_divide()`:同 `//` 運算符\n", "- `np.mod()`:同 `%` 運算符" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 1 2 3 4 5 6 7 8]\n", "[ 0 1 4 9 16 25 36 49 64]\n", "[ 0 1 4 9 16 25 36 49 64]\n" ] } ], "source": [ "# 以 np.power 作為一個簡單示範\n", "arr = np.arange(9)\n", "print(arr)\n", "print(arr**2)\n", "print(np.power(arr, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "假如希望對數值陣列應用之通用函式是為自己的需求量身訂製,這時可以定義後以 `np.vectorize()` 轉換為一個通用函式,例如可以判斷一個輸入是否為質數,我們想轉換成通用函式藉此判斷數值陣列中哪些為質數。" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "是否為質數:\n", "[False True True False True False True False False False True]\n" ] } ], "source": [ "def is_prime(x):\n", " div_cnt = 0\n", " for i in range(1, x+1):\n", " if x % i == 0:\n", " div_cnt += 1\n", " if div_cnt > 2:\n", " break\n", " return div_cnt == 2\n", "\n", "is_prime_ufunc = np.vectorize(is_prime)\n", "arr = np.arange(1, 12)\n", "print(\"是否為質數:\")\n", "print(is_prime_ufunc(arr))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 聚合函式\n", "\n", "面對一筆陌生的數值資料,資料科學家會採取描述性統計探索這筆資料,例如平均值、標準差、總和、中位數、最小值、最大值、眾數或分位數等,能夠將長度超過 1 的數值陣列匯總為一個或少數幾個摘要統計指標,被泛稱為聚合函式(Aggregate functions)。通用與聚合函式最大的差異就在於輸入與輸出的數值陣列長度,不同於通用函式,聚合函式所輸出的數值陣列多數僅有長度 1,或遠小於輸入數值陣列的長度。\n", "基礎的 NumPy 聚合函式可以應用於獲得數值陣列的各種描述性統計,我們不需要一一詳細討論如何使用,只需要注意兩個特性:\n", "\n", "1. 能沿指定維度聚合\n", "2. 多數具有可運算遺漏值的相對應函式\n", "\n", "「能沿指定維度聚合」的特性意指在面對具有多個維度的數值陣列時,可以指定應用在某一個維度,舉例來說面對有兩個維度 (m, n) 的矩陣時可以單純應用 `np.sum()` 將整個矩陣中的數值加總,將得到一個輸出;如果指定 `axis=0` 則是將矩陣中的每欄(column)分別加總,將得到 n 個輸出、指定 `axis=1` 則是將矩陣中的每列(row)分別加總,將得到 m 個輸出。" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "120\n", "[18 21 24 27 30]\n", "[15 40 65]\n" ] } ], "source": [ "mat = np.arange(1, 16).reshape(3, 5)\n", "print(np.sum(mat)) # 1 個輸出\n", "print(np.sum(mat, axis=0)) # 5 個輸出\n", "print(np.sum(mat, axis=1)) # 3 個輸出" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "「多數具有可運算遺漏值的相對應函式」的特性意指在面對具有 `np.NaN` 遺漏值的陣列時,原型聚合函式會回傳 `np.NaN`,這時可以採用相對應之可計算遺漏值函式運算,將會忽略 `np.NaN` ,傳回其餘數值的摘要。" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. nan]\n", "nan\n", "105.0\n" ] } ], "source": [ "arr = np.arange(1, 16, dtype=float)\n", "arr[-1] = np.NaN\n", "print(arr)\n", "print(np.sum(arr))\n", "print(np.nansum(arr))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "常用聚合函式與 nan 對應有:\n", "\n", "- `np.sum()` 與 `np.nansum()`\n", "- `np.prod()` 與 `np.nanprod()`\n", "- `np.mean()` 與 `np.nanmean()`\n", "- `np.median()` 與 `np.nanmedian()`\n", "- `np.std()` 與 `np.nanstd()`\n", "- `np.var()` 與 `np.nanvar()`\n", "- `np.min()` 與 `np.nanmin()`\n", "- `np.max()` 與 `np.nanmax()`\n", "- `np.argmin()` 與 `np.nanargmin()`\n", "- `np.argmax()` 與 `np.nanargmax()`\n", "\n", "我們創建一個外觀 `(10, 2)` 的矩陣來示範其中一個聚合函式 `np.argmax()` 的使用方法。" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.37454012, 0.62545988],\n", " [0.95071431, 0.04928569],\n", " [0.73199394, 0.26800606],\n", " [0.59865848, 0.40134152],\n", " [0.15601864, 0.84398136],\n", " [0.15599452, 0.84400548],\n", " [0.05808361, 0.94191639],\n", " [0.86617615, 0.13382385],\n", " [0.60111501, 0.39888499],\n", " [0.70807258, 0.29192742]])" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(42)\n", "arr_0 = np.random.random((10, 1))\n", "arr_1 = 1 - arr_0\n", "arr = np.concatenate([arr_0, arr_1], axis=1)\n", "arr" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 6])" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.argmax(arr, axis=0) # 矩陣中的每欄最大值所處的列數" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 0, 0, 0, 1, 1, 1, 0, 0, 0])" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.argmax(arr, axis=1) # 矩陣中的每列最大值所處的欄數" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 延伸閱讀\n", "\n", "1. NumPy: Learn (https://numpy.org/learn/)\n", "2. Introduction to NumPy. In: Jake VanderPlas, Python Data Science Handbook (https://jakevdp.github.io/PythonDataScienceHandbook/02.00-introduction-to-numpy.html) " ] } ], "metadata": { "kernelspec": { "display_name": "Python Machine Learning", "language": "python", "name": "pyml" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.8" } }, "nbformat": 4, "nbformat_minor": 4 }