{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Numpy - 多维数据数组软件库" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NumPy是Python中科学计算的基本软件包。它是一个Python库,提供多维数组对象、各种派生类(如掩码数组和矩阵)和各种例程。\n", "* 用于对数组进行快速操作,包括数学、逻辑、形状操作、排序、选择、I/O、离散傅立叶变换、基本线性代数、基本统计操作、随机模拟等等。\n", "* Numpy作为Python数据计算的基础广泛应用到数据处理、信号处理、机器学习等领域。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![cover image](images/numpy.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. 简介" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`numpy`包(模块)用在几乎所有使用Python的数值计算中,为Python提供高性能向量,矩阵和高维数据结构的模块。它是用C和Fortran语言实现的,因此当计算向量化数据(用向量和矩阵表示)时,性能非常的好。\n", "\n", "为了使用`numpy`模块,你先要像下面的例子一样导入这个模块:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# 这一行的作用会在Matplotlib中介绍\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# 不建议用这种方式导入库\n", "from numpy import *" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# 建议使用这种方式\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**建议大家使用第二种导入方法** `import numpy as np`\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. 创建`numpy`数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "有很多种方法去初始化新的numpy数组, 例如从\n", "\n", "* Python列表或元组\n", "* 使用专门用来创建numpy arrays的函数,例如 `arange`, `linspace`等\n", "* 从文件中读取数据" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 从列表中" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "例如,为了从Python列表创建新的向量和矩阵我们可以用`numpy.array`函数。\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 4]\n" ] }, { "data": { "text/plain": [ "array([1, 2, 3, 4])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "a = [1, 2, 3, 4]\n", "print(a)\n", "\n", "# a vector: the argument to the array function is a Python list\n", "v = np.array(a)\n", "\n", "v" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2]\n", " [3 4]\n", " [5 6]]\n", "\n", "(3, 2)\n" ] } ], "source": [ "# 矩阵:数组函数的参数是一个嵌套的Python列表\n", "M = np.array([[1, 2], [3, 4], [5, 6]])\n", "\n", "print(M)\n", "print()\n", "print(M.shape)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[[ 1 2]\n", " [ 3 4]\n", " [ 5 6]]\n", "\n", " [[ 3 4]\n", " [ 5 6]\n", " [ 7 8]]\n", "\n", " [[ 5 6]\n", " [ 7 8]\n", " [ 9 10]]\n", "\n", " [[ 7 8]\n", " [ 9 10]\n", " [11 12]]]\n", "\n", "(4, 3, 2)\n" ] } ], "source": [ "M = np.array([[[1,2], [3,4], [5,6]], \\\n", " [[3,4], [5,6], [7,8]], \\\n", " [[5,6], [7,8], [9,10]], \\\n", " [[7,8], [9,10], [11,12]]])\n", "print(M)\n", "print()\n", "print(M.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`v`和`M`两个都是属于`numpy`模块提供的`ndarray`类型。" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(numpy.ndarray, numpy.ndarray)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(v), type(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`v`和`M`之间的区别仅在于他们的形状。我们可以用属性函数`ndarray.shape`得到数组形状的信息。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4,)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v.shape" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4, 3, 2)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通过属性函数`ndarray.size`我们可以得到数组中元素的个数" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "24" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.size" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "同样,我们可以用函数`numpy.shape`和`numpy.size`" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4, 3, 2)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.shape(M)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "24" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.size(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "到目前为止`numpy.ndarray`看起来非常像Python列表(或嵌套列表)。为什么不简单地使用Python列表来进行计算,而不是创建一个新的数组类型?\n", "\n", "下面有几个原因:\n", "\n", "* Python列表非常普遍。它们可以包含任何类型的对象。它们是动态类型的。它们不支持矩阵和点乘等数学函数。由于动态类型的关系,为Python列表实现这类函数的效率不是很高。\n", "* Numpy数组是**静态类型的**和**同构的**。元素的类型是在创建数组时确定的。\n", "* Numpy数组是内存高效的。\n", "* 由于是静态类型,数学函数的快速实现,比如“numpy”数组的乘法和加法可以用编译语言实现(使用C和Fortran).\n", "\n", "利用`ndarray`的属性函数`dtype`(数据类型),我们可以看出数组的数据是那种类型。\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.dtype" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们试图给一个numpy数组中的元素赋一个错误类型的值,我们会得到一个错误:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "invalid literal for int() with base 10: 'hello'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mM\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"hello\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'hello'" ] } ], "source": [ "M[0,0,0] = \"hello\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们想的话,我们可以利用`dtype`关键字参数显式地定义我们创建的数组数据类型:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1.+0.j, 2.+0.j],\n", " [3.+0.j, 4.+0.j]])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = np.array([[1, 2], [3, 4]], dtype=complex)\n", "\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "常规可以伴随`dtype`使用的数据类型是:`int`, `float`, `complex`, `bool`, `object`等\n", "\n", "我们也可以显式地定义数据类型的大小,例如:`int64`, `int16`, `float128`, `complex128`。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2 使用数组生成函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对于较大的数组,使用显式的Python列表人为地初始化数据是不切实际的。除此之外我们可以用`numpy`的很多函数得到不同类型的数组。有一些常用的分别是:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### arange" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 1 2 3 4 5 6 7 8 9]\n", "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" ] } ], "source": [ "# 创建一个范围\n", "\n", "x = np.arange(0, 10, 1) # 参数:start, stop, step: \n", "y = range(0, 10, 1)\n", "print(x)\n", "print(list(y))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([-1.00000000e+00, -9.00000000e-01, -8.00000000e-01, -7.00000000e-01,\n", " -6.00000000e-01, -5.00000000e-01, -4.00000000e-01, -3.00000000e-01,\n", " -2.00000000e-01, -1.00000000e-01, -2.22044605e-16, 1.00000000e-01,\n", " 2.00000000e-01, 3.00000000e-01, 4.00000000e-01, 5.00000000e-01,\n", " 6.00000000e-01, 7.00000000e-01, 8.00000000e-01, 9.00000000e-01])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = np.arange(-1, 1, 0.1)\n", "\n", "x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### linspace and logspace" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0. , 2.5, 5. , 7.5, 10. ])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 使用linspace两边的端点也被包含进去\n", "np.linspace(0, 10, 5)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1.00000000e+00, 3.03773178e+00, 9.22781435e+00, 2.80316249e+01,\n", " 8.51525577e+01, 2.58670631e+02, 7.85771994e+02, 2.38696456e+03,\n", " 7.25095809e+03, 2.20264658e+04])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.logspace(0, 10, 10, base=np.e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### mgrid" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "y, x = np.mgrid[0:5, 0:5] # 和MATLAB中的meshgrid类似" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4],\n", " [0, 1, 2, 3, 4]])" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 0, 0, 0, 0],\n", " [1, 1, 1, 1, 1],\n", " [2, 2, 2, 2, 2],\n", " [3, 3, 3, 3, 3],\n", " [4, 4, 4, 4, 4]])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### random data" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "from numpy import random" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[[0.57397454, 0.12434228],\n", " [0.74835474, 0.01034541],\n", " [0.91383579, 0.02807574],\n", " [0.14217509, 0.64698341]],\n", "\n", " [[0.65606545, 0.84787378],\n", " [0.31064031, 0.70205451],\n", " [0.30486756, 0.34702889],\n", " [0.47537986, 0.91154076]],\n", "\n", " [[0.32192343, 0.77700745],\n", " [0.80485914, 0.85919158],\n", " [0.29751565, 0.27228179],\n", " [0.57796668, 0.18255467]],\n", "\n", " [[0.50020698, 0.58134695],\n", " [0.14200095, 0.97556272],\n", " [0.32948647, 0.35170435],\n", " [0.27768833, 0.75059373]],\n", "\n", " [[0.23972627, 0.08461662],\n", " [0.1929383 , 0.80565903],\n", " [0.2627892 , 0.73361884],\n", " [0.18415944, 0.44976198]]])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 均匀随机数在[0,1)区间\n", "random.rand(5,4,2)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-1.74300737, 1.94689131, 0.18922227, -0.20440928],\n", " [ 1.31664152, -0.01176745, -0.43956951, 0.53571291],\n", " [ 0.02140654, -0.09635041, -1.84205831, 0.64951045],\n", " [ 0.35682903, 0.96657395, -0.50099255, -0.80044681]])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 标准正态分布随机数\n", "random.randn(4,4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### diag" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 0, 0],\n", " [0, 2, 0],\n", " [0, 0, 3]])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 一个对角矩阵\n", "np.diag([1,2,3])" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 0, 0, 0],\n", " [1, 0, 0, 0],\n", " [0, 2, 0, 0],\n", " [0, 0, 3, 0]])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 从主对角线偏移的对角线\n", "np.diag([1,2,3], k=-1) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### zeros and ones" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0., 0., 0.],\n", " [0., 0., 0.],\n", " [0., 0., 0.]])" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.zeros((3,3))" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1., 1., 1.],\n", " [1., 1., 1.],\n", " [1., 1., 1.]])" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.ones((3,3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. 文件 I/O" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.1 逗号分隔值 (CSV)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对于数据文件来说一种非常常见的文件格式是逗号分割值(CSV),或者有关的格式例如TSV(制表符分隔的值)。为了从这些文件中读取数据到Numpy数组中,我们可以用`numpy.genfromtxt`函数。例如:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1800 1 1 -6.1 -6.1 -6.1 1\r\n", "1800 1 2 -15.4 -15.4 -15.4 1\r\n", "1800 1 3 -15.0 -15.0 -15.0 1\r\n", "1800 1 4 -19.3 -19.3 -19.3 1\r\n", "1800 1 5 -16.8 -16.8 -16.8 1\r\n", "1800 1 6 -11.4 -11.4 -11.4 1\r\n", "1800 1 7 -7.6 -7.6 -7.6 1\r\n", "1800 1 8 -7.1 -7.1 -7.1 1\r\n", "1800 1 9 -10.1 -10.1 -10.1 1\r\n", "1800 1 10 -9.5 -9.5 -9.5 1\r\n" ] } ], "source": [ "!head stockholm_td_adj.dat" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "data = np.genfromtxt('stockholm_td_adj.dat')" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(77431, 7)" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data.shape" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "fig, ax = plt.subplots(figsize=(14,4))\n", "ax.plot(data[:,0]+data[:,1]/12.0+data[:,2]/365, data[:,5])\n", "ax.axis('tight')\n", "ax.set_title('tempeatures in Stockholm')\n", "ax.set_xlabel('year')\n", "ax.set_ylabel('temperature (C)');" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使用`numpy.savetxt`我们可以将一个Numpy数组以CSV格式存入:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.34743109, 0.34666094, 0.67796236],\n", " [0.37775535, 0.7452935 , 0.44639271],\n", " [0.7097024 , 0.54721637, 0.96400871]])" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = np.random.rand(3,3)\n", "\n", "M" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "np.savetxt(\"random-matrix.csv\", M)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.474310879390657414e-01 3.466609365910759966e-01 6.779623624489031775e-01\r\n", "3.777553531256817587e-01 7.452935047749419395e-01 4.463927097637667707e-01\r\n", "7.097023968559375007e-01 5.472163711854115542e-01 9.640087120207403437e-01\r\n" ] } ], "source": [ "!cat random-matrix.csv" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.34743 0.34666 0.67796\r\n", "0.37776 0.74529 0.44639\r\n", "0.70970 0.54722 0.96401\r\n" ] } ], "source": [ "np.savetxt(\"random-matrix.csv\", M, fmt='%.5f') # fmt 确定格式\n", "\n", "!cat random-matrix.csv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.2 numpy 的本地文件格式" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当存储和读取numpy数组时非常有用。利用函数`numpy.save`和`numpy.load`:" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "random-matrix.npy: NumPy array, version 1.0, header length 118\r\n" ] } ], "source": [ "np.save(\"random-matrix.npy\", M)\n", "\n", "!file random-matrix.npy" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.34743109, 0.34666094, 0.67796236],\n", " [0.37775535, 0.7452935 , 0.44639271],\n", " [0.7097024 , 0.54721637, 0.96400871]])" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.load(\"random-matrix.npy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. 更多Numpy数组的性质" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "int64\n", "8\n" ] } ], "source": [ "M = np.array([[1, 2], [3, 4], [5, 6]])\n", "\n", "print(M.dtype)\n", "print(M.itemsize) # 每个元素的字节数\n" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "48" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.nbytes # 字节数" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.ndim # 维度" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. 操作数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5.1 索引" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以用方括号和下标索引元素:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v = np.array([1, 2, 3, 4, 5])\n", "\n", "# v 是一个向量,仅仅只有一维,取一个索引\n", "v[0]" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4\n", "4\n", "[3 4]\n" ] } ], "source": [ "# M 是一个矩阵或者是一个二维的数组,取两个索引 \n", "print(M[1,1])\n", "print(M[1][1])\n", "print(M[1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们省略了一个多维数组的索引,它将会返回整行(或者,总的来说,一个 N-1 维的数组)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3, 4])" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "相同的事情可以利用`:`而不是索引来实现:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3, 4])" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[1,:] # 行 1" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 4, 6])" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[:,1] # 列 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以用索引赋新的值给数组中的元素:" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "M[0,0] = 1" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "# 对行和列也同样有用\n", "M[1,:] = 0\n", "M[:,1] = -1" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1, -1],\n", " [ 0, -1],\n", " [ 5, -1]])" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5.2 切片索引" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "切片索引是语法 `M[lower:upper:step]` 的技术名称,用于提取数组的一部分:" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4, 5])" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.array([1,2,3,4,5])\n", "A" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 3])" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[1:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "切片索引到的数据是 *可变的* : 如果它们被分配了一个新值,那么从其中提取切片的原始数组将被修改:" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3, 4, 5])" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[1:3] = [-2,-3] # auto convert type\n", "A[1:3] = np.array([-2, -3]) \n", "\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以省略 `M[lower:upper:step]` 中任意的三个值" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3, 4, 5])" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[::] # lower, upper, step 都取默认值" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3, 4, 5])" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[:]" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -3, 5])" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[::2] # step is 2, lower and upper 代表数组的开始和结束" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3])" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[:3] # 前3个元素" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([4, 5])" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[3:] # 从索引3开始的元素" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "负索引计数从数组的结束(正索引从开始):" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [], "source": [ "A = np.array([1,2,3,4,5])" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[-1] # 数组中最后一个元素" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3, 4, 5])" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[-3:] # 最后三个元素" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "索引切片的工作方式与多维数组完全相同:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 1, 2, 3, 4],\n", " [10, 11, 12, 13, 14],\n", " [20, 21, 22, 23, 24],\n", " [30, 31, 32, 33, 34],\n", " [40, 41, 42, 43, 44]])" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.array([[n+m*10 for n in range(5)] for m in range(5)])\n", "\n", "A" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[11, 12, 13],\n", " [21, 22, 23],\n", " [31, 32, 33]])" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 原始数组中的一个块\n", "A[1:4, 1:4]" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 2, 4],\n", " [20, 22, 24],\n", " [40, 42, 44]])" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 步长\n", "A[::2, ::2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5.3 花式索引" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fancy索引是一个名称时,一个数组或列表被使用在一个索引:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[10 11 12 13 14]\n", " [30 31 32 33 34]\n", " [20 21 22 23 24]]\n", "[[ 0 1 2 3 4]\n", " [10 11 12 13 14]\n", " [20 21 22 23 24]\n", " [30 31 32 33 34]\n", " [40 41 42 43 44]]\n" ] } ], "source": [ "A = np.array([[n+m*10 for n in range(5)] for m in range(5)])\n", "\n", "row_indices = [1, 3, 2]\n", "print(A[row_indices])\n", "print(A)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([11, 31, 24])" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "col_indices = [1, 1, -1] # 索引-1 代表最后一个元素\n", "A[row_indices, col_indices]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们也可以使用索引掩码:如果索引掩码是一个数据类型`bool`的Numpy数组,那么一个元素被选择(True)或不(False)取决于索引掩码在每个元素位置的值:" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4])" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = np.array([n for n in range(5)])\n", "B" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 2])" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row_mask = np.array([True, False, True, False, False])\n", "B[row_mask]" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 2])" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 相同的事情\n", "row_mask = np.array([1,0,1,0,0], dtype=bool)\n", "B[row_mask]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这个特性对于有条件地从数组中选择元素非常有用,例如使用比较运算符:" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5, 5. , 5.5, 6. ,\n", " 6.5, 7. , 7.5, 8. , 8.5, 9. , 9.5])" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = np.arange(0, 10, 0.5)\n", "x" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([False, False, False, False, False, False, False, False, False,\n", " False, False, True, True, True, True, False, False, False,\n", " False, False])" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mask = (5 < x) * (x < 7.5)\n", "\n", "mask" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([5.5, 6. , 6.5, 7. ])" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[mask]" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3.5, 4. , 4.5, 5. , 5.5])" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[(3\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mA\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mv1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (2,3) (2,) " ] } ], "source": [ "A*v1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7.4 矩阵代数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "矩阵的乘法有两种方法,第一种方法是点乘函数,它对两个参数应用矩阵-矩阵、矩阵-向量或内向量乘法" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[2.59833251, 1.8189686 , 1.32946437, 2.15441681, 1.55219543],\n", " [1.4561364 , 1.26875236, 0.97855704, 1.35013248, 1.05524471],\n", " [2.38061437, 1.70445667, 1.16297305, 2.27888345, 1.66499116],\n", " [1.08602725, 0.76015292, 0.46415646, 1.38753125, 1.00011024],\n", " [1.82122991, 1.34175794, 0.92375387, 1.74770416, 1.27559765]])" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.random.rand(5, 5)\n", "v1 = np.random.rand(5, 1)\n", "\n", "np.dot(A, A)" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[2.0139906 ],\n", " [1.41657535],\n", " [2.09784627],\n", " [1.2752073 ],\n", " [1.6253844 ]])" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.dot(A, v1)\n" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[2.08466462]])" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.dot(v1.T, v1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "另外,我们可以将数组对象投到`matrix`类型上。这将改变标准算术运算符`+, -, *` 的行为,以使用矩阵代数。" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "M = np.matrix(A)\n", "v = np.matrix(v1).T # make it a column vector" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0.45282687, 0.64874757, 0.70028245, 0.91412865, 0.36429705]])" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[2.59833251, 1.8189686 , 1.32946437, 2.15441681, 1.55219543],\n", " [1.4561364 , 1.26875236, 0.97855704, 1.35013248, 1.05524471],\n", " [2.38061437, 1.70445667, 1.16297305, 2.27888345, 1.66499116],\n", " [1.08602725, 0.76015292, 0.46415646, 1.38753125, 1.00011024],\n", " [1.82122991, 1.34175794, 0.92375387, 1.74770416, 1.27559765]])" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M * M" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[2.0139906 ],\n", " [1.41657535],\n", " [2.09784627],\n", " [1.2752073 ],\n", " [1.6253844 ]])" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M * v.T" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[2.08466462]])" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 內积\n", "v * v.T" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们尝试用不相配的矩阵形状加,减或者乘我们会得到错误:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [], "source": [ "v = np.matrix([1,2,3,4,5,6]).T" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "((5, 5), (6, 1))" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.shape(M), np.shape(v)" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "shapes (5,5) and (6,1) not aligned: 5 (dim 1) != 6 (dim 0)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mM\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/numpy/matrixlib/defmatrix.py\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 218\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mN\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 219\u001b[0m \u001b[0;31m# This promotes 1-D vectors to row vectors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 220\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mN\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0masmatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 221\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misscalar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'__rmul__'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mN\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m<__array_function__ internals>\u001b[0m in \u001b[0;36mdot\u001b[0;34m(*args, **kwargs)\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: shapes (5,5) and (6,1) not aligned: 5 (dim 1) != 6 (dim 0)" ] } ], "source": [ "M * v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7.5 矩阵计算与数据处理" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 求逆" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-2. , 1. ],\n", " [ 1.5, -0.5]])" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C = np.array([[1, 2], [3, 4]])\n", "np.linalg.inv(C) # equivalent to C.I " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 行列式" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-2.0000000000000004" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linalg.det(C)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 数据统计\n", "通常将数据集存储在Numpy数组中是非常有用的。Numpy提供了许多函数用于计算数组中数据集的统计。\n", "\n", "例如,让我们从上面使用的斯德哥尔摩温度数据集计算一些属性。" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(77431, 7)" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "data = np.genfromtxt('stockholm_td_adj.dat')\n", "\n", "# 提醒一下,温度数据集存储在数据变量中:\n", "np.shape(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### mean" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(77431, 7)\n" ] }, { "data": { "text/plain": [ "6.197109684751585" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 温度数据在第三列中\n", "print(data.shape)\n", "np.mean(data[:,3])" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.4931528475182218" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.random.rand(4, 3)\n", "np.mean(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在过去的200年里,斯德哥尔摩每天的平均气温大约是6.2 C。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 标准差和方差" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(8.282271621340573, 68.59602320966341)" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.std(data[:,3]), np.var(data[:,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 最小值和最大值" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-25.8" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 最低日平均温度\n", "data[:,3].min()" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "28.3" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 最高日平均温度\n", "data[:,3].max()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### sum, prod, and trace" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d = np.arange(0, 10)\n", "d" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "45" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 将所有的元素相加\n", "np.sum(d)" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3628800" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 全元素积分\n", "np.prod(d+1)" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45])" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 累计求和\n", "np.cumsum(d)" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, 2, 6, 24, 120, 720, 5040,\n", " 40320, 362880, 3628800])" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 累计乘积\n", "np.cumprod(d+1)" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.4446600641166332" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 计算对角线元素的和,和diag(A).sum()一样\n", "np.trace(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7.6 数组子集的计算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以使用索引、花式索引和从数组中提取数据的其他方法(如上所述)来计算数组中的数据子集。\n", "\n", "例如,让我们回到温度数据集:" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1800 1 1 -6.1 -6.1 -6.1 1\r\n", "1800 1 2 -15.4 -15.4 -15.4 1\r\n", "1800 1 3 -15.0 -15.0 -15.0 1\r\n" ] } ], "source": [ "!head -n 3 stockholm_td_adj.dat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "数据集的格式是:年,月,日,日平均气温,低,高,位置。\n", "\n", "如果我们对某个特定月份的平均温度感兴趣,比如二月,然后我们可以创建一个索引掩码,使用它来选择当月的数据:" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.])" ] }, "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.unique(data[:,1]) # 列的值从1到12" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[False False False ... False False False]\n" ] } ], "source": [ "mask_feb = data[:,1] == 2\n", "print(mask_feb)" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-3.212109570736596\n", "5.090390768766271\n" ] } ], "source": [ "# 温度数据实在第三行\n", "print(np.mean(data[mask_feb,3]))\n", "print(np.std(data[mask_feb,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "有了这些工具,我们就有了非常强大的数据处理能力。例如,提取每年每个月的平均气温只需要几行代码:" ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "months = np.unique(data[:,1])\n", "monthly_mean = [np.mean(data[data[:,1] == month, 3]) for month in months]\n", "\n", "fig, ax = plt.subplots()\n", "ax.bar(months, monthly_mean)\n", "ax.set_xlabel(\"Month\")\n", "ax.set_ylabel(\"Monthly avg. temp.\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 7.7 高维数据的计算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当例如`min`, `max`等函数应用在高维数组上时,有时将计算应用于整个数组是有用的,而且很多时候有时只基于行或列。用`axis`参数我们可以决定这个函数应该怎样表现:" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.85882078, 0.0838741 , 0.4529751 ],\n", " [0.32355282, 0.23641565, 0.37693805],\n", " [0.06769945, 0.30438005, 0.9780961 ],\n", " [0.46162058, 0.42681981, 0.71106984]])" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "m = np.random.rand(4,3)\n", "m" ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.978096099540799" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# global max\n", "m.max()" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.85882078, 0.42681981, 0.9780961 ])" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# max in each column\n", "m.max(axis=0)" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.85882078, 0.37693805, 0.9780961 , 0.71106984])" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# max in each row\n", "m.max(axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "许多其他的在`array` 和`matrix`类中的函数和方法接受同样(可选的)的关键字参数`axis`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 8. 阵列的重塑、调整大小和堆叠" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numpy数组的形状可以被确定而无需复制底层数据,这使得即使对于大型数组也能有较快的操作。" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.58458652 0.95489874 0.76873658]\n", " [0.79144906 0.35559767 0.96031963]\n", " [0.55942317 0.78723157 0.3650356 ]\n", " [0.04685468 0.43444695 0.33839966]]\n" ] } ], "source": [ "import numpy as np\n", "\n", "A = np.random.rand(4, 3)\n", "print(A)" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4 3\n" ] } ], "source": [ "n, m = A.shape\n", "print(n, m)" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.58458652, 0.95489874, 0.76873658, 0.79144906, 0.35559767,\n", " 0.96031963, 0.55942317, 0.78723157, 0.3650356 , 0.04685468,\n", " 0.43444695, 0.33839966]])" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = A.reshape((1,n*m))\n", "B" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.58458652]\n", " [0.95489874]\n", " [0.76873658]\n", " [0.79144906]\n", " [0.35559767]\n", " [0.96031963]\n", " [0.55942317]\n", " [0.78723157]\n", " [0.3650356 ]\n", " [0.04685468]\n", " [0.43444695]\n", " [0.33839966]]\n", "(12, 1)\n" ] } ], "source": [ "B2 = A.reshape((n*m, 1))\n", "print(B2)\n", "print(B2.shape)" ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5. , 5. , 5. , 5. , 5. ,\n", " 0.96031963, 0.55942317, 0.78723157, 0.3650356 , 0.04685468,\n", " 0.43444695, 0.33839966]])" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B[0,0:5] = 5 # modify the array\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5. , 5. , 5. ],\n", " [5. , 5. , 0.96031963],\n", " [0.55942317, 0.78723157, 0.3650356 ],\n", " [0.04685468, 0.43444695, 0.33839966]])" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A # and the original variable is also changed. B is only a different view of the same data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use the function `flatten` to make a higher-dimensional array into a vector. But this function create a copy of the data." ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([5. , 5. , 5. , 5. , 5. ,\n", " 0.96031963, 0.55942317, 0.78723157, 0.3650356 , 0.04685468,\n", " 0.43444695, 0.33839966])" ] }, "execution_count": 130, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = A.flatten()\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(12,)\n" ] } ], "source": [ "print(B.shape)" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0.88616566 0.11474399 0.49426839 0.86496944 0.44553257 0.01731081\n", " 0.26391484 0.81714822 0.9077824 0.45350327 0.34418481 0.30680307\n", " 0.22397584 0.96490185 0.25766897 0.1628303 0.35022665 0.87266285\n", " 0.14436895 0.2987234 0.04567582 0.62524215 0.03006832 0.15222984\n", " 0.86554462 0.30036796 0.66637188 0.51245662 0.46296801 0.53384373\n", " 0.90012971 0.00319531 0.48428543 0.24703543 0.53384405 0.48024175\n", " 0.17175873 0.1834814 0.43739033 0.64565657 0.49266811 0.72123815\n", " 0.57728476 0.76663343 0.68360823 0.34881945 0.64329004 0.79011718\n", " 0.7055079 0.32594224 0.48795517 0.43684614 0.32047664 0.63067622\n", " 0.24496431 0.25019593 0.57181523 0.38889906 0.53574819 0.02653888]\n" ] } ], "source": [ "T = np.random.rand(3, 4, 5)\n", "T2 = T.flatten()\n", "print(T2)" ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([10. , 10. , 10. , 10. , 10. ,\n", " 0.96031963, 0.55942317, 0.78723157, 0.3650356 , 0.04685468,\n", " 0.43444695, 0.33839966])" ] }, "execution_count": 133, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B[0:5] = 10\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5. , 5. , 5. ],\n", " [5. , 5. , 0.96031963],\n", " [0.55942317, 0.78723157, 0.3650356 ],\n", " [0.04685468, 0.43444695, 0.33839966]])" ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A # 现在A并没有改变,因为B的数值是A的复制,并不指向同样的值。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9. 添加、删除维度:newaxis、squeeze" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当矩阵乘法的时候,需要两个矩阵的对应的纬度保持一致才可以正确执行,有了`newaxis`,我们可以在数组中插入新的维度,例如将一个向量转换为列或行矩阵:" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [], "source": [ "v = np.array([1,2,3])" ] }, { "cell_type": "code", "execution_count": 136, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3,)\n", "[1 2 3]\n" ] } ], "source": [ "print(np.shape(v))\n", "print(v)" ] }, { "cell_type": "code", "execution_count": 137, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3, 1)\n", "[[1]\n", " [2]\n", " [3]]\n" ] } ], "source": [ "v2 = v.reshape(3, 1)\n", "print(v2.shape)\n", "print(v2)" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3,)\n", "(3, 1)\n" ] } ], "source": [ "# 做一个向量v的列矩阵\n", "v2 = v[:, np.newaxis]\n", "print(v.shape)\n", "print(v2.shape)\n" ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3, 1)" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 列矩阵\n", "v[:,np.newaxis].shape" ] }, { "cell_type": "code", "execution_count": 140, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 3)" ] }, "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 行矩阵\n", "v[np.newaxis,:].shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "也可以通过 `np.expand_dims` 来实现类似的操作" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3, 1)\n", "[[1]\n", " [2]\n", " [3]]\n" ] } ], "source": [ "v = np.array([1,2,3])\n", "v3 = np.expand_dims(v, 1)\n", "print(v3.shape)\n", "print(v3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在某些情况,需要将纬度为1的那个纬度删除掉,可以使用`np.squeeze`实现" ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 2, 3)\n", "[[[1 2 3]\n", " [2 3 4]]]\n" ] } ], "source": [ "arr = np.array([[[1, 2, 3], [2, 3, 4]]])\n", "print(arr.shape)\n", "print(arr)" ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(2, 3)\n", "[[1 2 3]\n", " [2 3 4]]\n" ] } ], "source": [ "# 实际上第一个纬度为`1`,我们不需要\n", "arr2 = np.squeeze(arr, 0)\n", "print(arr2.shape)\n", "print(arr2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "需要注意:只有数组长度在该纬度上为1,那么该纬度才可以被删除;否则会报错。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 10. 叠加和重复数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "利用函数`repeat`, `tile`, `vstack`, `hstack`, 和`concatenate` 可以用较小的向量和矩阵来创建更大的向量和矩阵。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 10.1 tile and repeat" ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2]\n", " [3 4]]\n" ] } ], "source": [ "a = np.array([[1, 2], [3, 4]])\n", "print(a)" ] }, { "cell_type": "code", "execution_count": 145, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])" ] }, "execution_count": 145, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 重复每一个元素三次\n", "np.repeat(a, 3)" ] }, { "cell_type": "code", "execution_count": 146, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 1, 2, 1, 2],\n", " [3, 4, 3, 4, 3, 4]])" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# tile the matrix 3 times \n", "np.tile(a, 3)" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 1, 2, 1, 2],\n", " [3, 4, 3, 4, 3, 4]])" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 更好的方案\n", "np.tile(a, (1, 3))" ] }, { "cell_type": "code", "execution_count": 148, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [1, 2],\n", " [3, 4],\n", " [1, 2],\n", " [3, 4]])" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.tile(a, (3, 1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 10.2 concatenate" ] }, { "cell_type": "code", "execution_count": 149, "metadata": {}, "outputs": [], "source": [ "b = np.array([[5, 6]])" ] }, { "cell_type": "code", "execution_count": 150, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.concatenate((a, b), axis=0)" ] }, { "cell_type": "code", "execution_count": 151, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.concatenate((a, b.T), axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 10.3 hstack and vstack" ] }, { "cell_type": "code", "execution_count": 152, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 152, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.vstack((a,b))" ] }, { "cell_type": "code", "execution_count": 153, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] }, "execution_count": 153, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.hstack((a,b.T))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 11. 复制和“深度复制”" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "为了获得高性能,Python中的赋值通常不复制底层对象。例如,在函数之间传递对象时,通过引用传递从而避免不必要的大量内存复制。" ] }, { "cell_type": "code", "execution_count": 154, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4]])" ] }, "execution_count": 154, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.array([[1, 2], [3, 4]])\n", "\n", "A" ] }, { "cell_type": "code", "execution_count": 155, "metadata": {}, "outputs": [], "source": [ "# 现在B和A指的是同一个数组数据\n", "B = A " ] }, { "cell_type": "code", "execution_count": 156, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 156, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 改变B影响A\n", "B[0,0] = 10\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 157, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们想避免这种引用赋值的行为,那么当我们从 `A` 复制一个新的完全独立的对象 `B` 时,我们需要使用函数 `copy` 来做一个所谓的“深度复制”:" ] }, { "cell_type": "code", "execution_count": 158, "metadata": {}, "outputs": [], "source": [ "B = np.copy(A)" ] }, { "cell_type": "code", "execution_count": 159, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-5, 2],\n", " [ 3, 4]])" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 现在如果我们改变B,A不受影响\n", "B[0,0] = -5\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 160, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 160, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 12. 遍历数组元素" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通常,我们希望尽可能避免遍历数组元素(不惜一切代价)。原因是在像Python(或MATLAB)这样的解释语言中,迭代与向量化操作相比真的很慢。\n", "\n", "然而,有时迭代是不可避免的。对于这种情况,Python的For循环是最方便的遍历数组的方法:" ] }, { "cell_type": "code", "execution_count": 161, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n", "4\n" ] } ], "source": [ "v = np.array([1,2,3,4])\n", "\n", "for element in v:\n", " print(element)" ] }, { "cell_type": "code", "execution_count": 162, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "row [1 2]\n", "1\n", "2\n", "row [3 4]\n", "3\n", "4\n" ] } ], "source": [ "M = np.array([[1,2], [3,4]])\n", "\n", "for row in M:\n", " print(\"row\", row)\n", " \n", " for element in row:\n", " print(element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当我们需要去\n", "当我们需要遍历一个数组的每个元素并修改它的元素时,使用`enumerate`函数可以方便地在`for`循环中获得元素及其索引:" ] }, { "cell_type": "code", "execution_count": 163, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "row_idx 0 row [1 2]\n", "col_idx 0 element 1\n", "col_idx 1 element 2\n", "row_idx 1 row [3 4]\n", "col_idx 0 element 3\n", "col_idx 1 element 4\n" ] } ], "source": [ "for row_idx, row in enumerate(M):\n", " print(\"row_idx\", row_idx, \"row\", row)\n", " \n", " for col_idx, element in enumerate(row):\n", " print(\"col_idx\", col_idx, \"element\", element)\n", " \n", " # 更新矩阵:对每个元素求平方\n", " M[row_idx, col_idx] = element ** 2" ] }, { "cell_type": "code", "execution_count": 164, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1, 4],\n", " [ 9, 16]])" ] }, "execution_count": 164, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 现在矩阵里的每一个元素都已经求得平方\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 13. 向量化功能" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "正如前面多次提到的,为了获得良好的性能,我们应该尽量避免对向量和矩阵中的元素进行循环,而应该使用向量化算法。将标量算法转换为向量化算法的第一步是确保我们编写的函数使用向量输入。" ] }, { "cell_type": "code", "execution_count": 165, "metadata": {}, "outputs": [], "source": [ "def Theta(x):\n", " \"\"\"\n", " 阶跃函数的普遍版本\n", " \"\"\"\n", " if x >= 0:\n", " return 1\n", " else:\n", " return 0" ] }, { "cell_type": "code", "execution_count": 166, "metadata": { "scrolled": true }, "outputs": [ { "ename": "ValueError", "evalue": "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mTheta\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mTheta\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0m阶跃函数的普遍版本\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \"\"\"\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()" ] } ], "source": [ "Theta(np.array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这个操作并不可行,因为所实现的 `Theta` 函数不能接收向量输入。\n", "\n", "为了得到向量化的版本,我们可以使用Numpy函数 `vectorize` 。在许多情况下,它可以自动向量化一个函数:" ] }, { "cell_type": "code", "execution_count": 167, "metadata": {}, "outputs": [], "source": [ "Theta_vec = np.vectorize(Theta)" ] }, { "cell_type": "code", "execution_count": 168, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 1, 1])" ] }, "execution_count": 168, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Theta_vec(np.array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们也可以实现从一开始就接受矢量输入的函数(需要更多的计算,但可能会有更好的性能):" ] }, { "cell_type": "code", "execution_count": 169, "metadata": {}, "outputs": [], "source": [ "def Theta(x):\n", " \"\"\"\n", " Heaviside阶跃函数的矢量感知实现。\n", " \"\"\"\n", " return 1 * (x >= 0)" ] }, { "cell_type": "code", "execution_count": 170, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 1, 1])" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Theta(np.array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "code", "execution_count": 171, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[False False False True True True True]\n" ] }, { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 1, 1])" ] }, "execution_count": 171, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = np.array([-3,-2,-1,0,1,2,3])\n", "b = a>=0\n", "print(b)\n", "b*1" ] }, { "cell_type": "code", "execution_count": 172, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0, 1)" ] }, "execution_count": 172, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 同样适用于标量\n", "Theta(-1.2), Theta(2.6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 14. 在条件中使用数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当在条件中使用数组时,例如`if`语句和其他布尔表达,一个需要用`any`或者`all`,这让数组任何或者所有元素都等于`True`。" ] }, { "cell_type": "code", "execution_count": 173, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4]])" ] }, "execution_count": 173, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = np.array([[1, 2], [3, 4]])\n", "M" ] }, { "cell_type": "code", "execution_count": 174, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(M > 2).any()" ] }, { "cell_type": "code", "execution_count": 175, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "at least one element in M is larger than 2\n" ] } ], "source": [ "if (M > 2).any():\n", " print(\"at least one element in M is larger than 2\")\n", "else:\n", " print(\"no element in M is larger than 2\")" ] }, { "cell_type": "code", "execution_count": 176, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "all elements in M are not larger than 5\n" ] } ], "source": [ "if (M > 5).all():\n", " print(\"all elements in M are larger than 5\")\n", "else:\n", " print(\"all elements in M are not larger than 5\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 15. 类型转换" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "因为Numpy数组是*静态类型*,数组的类型一旦创建就不会改变。但是我们可以用`astype`函数(参见类似的“asarray”函数)显式地转换一个数组的类型到其他的类型,这总是创建一个新类型的新数组。" ] }, { "cell_type": "code", "execution_count": 177, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.dtype\n" ] }, { "cell_type": "code", "execution_count": 178, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1., 2.],\n", " [3., 4.]])" ] }, "execution_count": 178, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2 = M.astype(float)\n", "\n", "M2" ] }, { "cell_type": "code", "execution_count": 179, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('float64')" ] }, "execution_count": 179, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2.dtype" ] }, { "cell_type": "code", "execution_count": 180, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ True, True],\n", " [ True, True]])" ] }, "execution_count": 180, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M3 = M.astype(bool)\n", "\n", "M3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 16. 进一步学习" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* [NumPy 简易教程](https://www.runoob.com/numpy/numpy-tutorial.html)\n", "* [NumPy 官方用户指南](https://www.numpy.org.cn/user/)\n", "* [NumPy 官方参考手册](https://www.numpy.org.cn/reference/)\n", "* [一个针对MATLAB使用者的Numpy教程](https://numpy.org/doc/stable/user/numpy-for-matlab-users.html)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.8.3" } }, "nbformat": 4, "nbformat_minor": 1 }