{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Numpy - 多维数据的数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "J.R. Johansson (jrjohansson at gmail.com)\n", "\n", "最新的[IPython notebook](http://ipython.org/notebook.html)课程可以在[http://github.com/jrjohansson/scientific-python-lectures](http://github.com/jrjohansson/scientific-python-lectures) 找到.\n", "\n", "其他有关这个课程的参考书在这里标注出[http://jrjohansson.github.io](http://jrjohansson.github.io).\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# 这一行的作用会在课程4中回答\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 简介" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这个`numpy`包(模块)用在几乎所有使用Python的数值计算中。他是一个为Python提供高性能向量,矩阵和高维数据结构的模块。它是用C和Fortran语言实现的,因此当计算被向量化(用向量和矩阵表示)时,性能非常的好。\n", "\n", "为了使用`numpy`模块,你先要向下面的例子一样导入这个模块:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from numpy import *\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在`numpy`模块中,用于向量,矩阵和高维数据集的术语是*数组*。\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 创建`numpy`数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "有很多种方法去初始化新的numpy数组, 例如从\n", "\n", "* Python列表或元组\n", "* 使用专门用来创建numpy arrays的函数,例如 `arange`, `linspace`等\n", "* 从文件中读取数据" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 从列表中" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "例如,为了从Python列表创建新的向量和矩阵我们可以用`numpy.array`函数。\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4])" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "# a vector: the argument to the array function is a Python list\n", "v = np.array([1,2,3,4])\n", "\n", "v" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2]\n", " [3 4]\n", " [5 6]]\n", "(3, 2)\n" ] } ], "source": [ "# 矩阵:数组函数的参数是一个嵌套的Python列表\n", "M = array([[1, 2], [3, 4], [5, 6]])\n", "\n", "print(M)\n", "print(M.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`v`和`M`两个都是属于`numpy`模块提供的`ndarray`类型。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(numpy.ndarray, numpy.ndarray)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(v), type(M)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`v`和`M`之间的区别仅在于他们的形状。我们可以用属性函数`ndarray.shape`得到数组形状的信息。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4,)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v.shape" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3, 2)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通过属性函数`ndarray.size`我们可以得到数组中元素的个数" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.size" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "同样,我们可以用函数`numpy.shape`和`numpy.size`" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3, 2)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.shape(M)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 10, "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": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.dtype" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们试图给一个numpy数组中的元素赋一个错误类型的值,我们会得到一个错误:" ] }, { "cell_type": "code", "execution_count": 12, "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;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] = \"hello\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们想的话,我们可以利用`dtype`关键字参数显式地定义我们创建的数组数据类型:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1.+0.j, 2.+0.j],\n", " [3.+0.j, 4.+0.j]])" ] }, "execution_count": 13, "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": [ "### 使用数组生成函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对于较大的数组,使用显式的Python列表人为地初始化数据是不切实际的。除此之外我们可以用`numpy`的很多函数得到不同类型的数组。有一些常用的分别是:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### arange" ] }, { "cell_type": "code", "execution_count": 14, "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": 15, "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": 15, "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": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0. , 0.41666667, 0.83333333, 1.25 , 1.66666667,\n", " 2.08333333, 2.5 , 2.91666667, 3.33333333, 3.75 ,\n", " 4.16666667, 4.58333333, 5. , 5.41666667, 5.83333333,\n", " 6.25 , 6.66666667, 7.08333333, 7.5 , 7.91666667,\n", " 8.33333333, 8.75 , 9.16666667, 9.58333333, 10. ])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 使用linspace两边的端点也被包含进去\n", "np.linspace(0, 10, 25)" ] }, { "cell_type": "code", "execution_count": 17, "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": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.logspace(0, 10, 10, base=e)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### mgrid" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "x, y = np.mgrid[0:5, 0:5] # 和MATLAB中的meshgrid类似" ] }, { "cell_type": "code", "execution_count": 19, "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": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "code", "execution_count": 20, "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": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### random data" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "from numpy import random" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.31850549, 0.64755869, 0.93737096, 0.06141188, 0.17055487],\n", " [0.95771684, 0.88466718, 0.81119863, 0.95268744, 0.73734857],\n", " [0.51036326, 0.8779331 , 0.41560197, 0.300393 , 0.42244209],\n", " [0.50866631, 0.84322931, 0.34459543, 0.47379641, 0.03312725],\n", " [0.96519922, 0.20557788, 0.38343937, 0.21493144, 0.27541461]])" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 均匀随机数在[0,1)区间\n", "random.rand(5,5)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1.12204579, 2.90667688, -1.06379302, 1.52801804, 1.34553205],\n", " [ 2.22610261, -0.18597008, 1.12948162, -1.44339033, 0.14366645],\n", " [ 0.12767746, -0.04534549, 0.1536468 , 0.7333602 , 0.96510913],\n", " [ 0.30848743, -2.31710677, 0.37803085, -0.52433003, 1.39883453],\n", " [-0.52307504, 0.40612781, 0.48341866, -1.96277249, 1.1671546 ]])" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 标准正态分布随机数\n", "random.randn(5,5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### diag" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 0, 0],\n", " [0, 2, 0],\n", " [0, 0, 3]])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 一个对角矩阵\n", "np.diag([1,2,3])" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 0, 0],\n", " [0, 0, 2, 0],\n", " [0, 0, 0, 3],\n", " [0, 0, 0, 0]])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 从主对角线偏移的对角线\n", "diag([1,2,3], k=1) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### zeros and ones" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0., 0., 0.],\n", " [0., 0., 0.],\n", " [0., 0., 0.]])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.zeros((3,3))" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1., 1., 1.],\n", " [1., 1., 1.],\n", " [1., 1., 1.]])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.ones((3,3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 文件 I/O" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 逗号分隔值 (CSV)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对于数据文件来说一种非常常见的文件格式是逗号分割值(CSV),或者有关的格式例如TSV(制表符分隔的值)。为了从这些文件中读取数据到Numpy数组中,我们可以用`numpy.genfromtxt`函数。例如:" ] }, { "cell_type": "code", "execution_count": 28, "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": 29, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "data = np.genfromtxt('stockholm_td_adj.dat')" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(77431, 7)" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data.shape" ] }, { "cell_type": "code", "execution_count": 31, "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": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.73171836, 0.46544202, 0.72372739],\n", " [0.32390603, 0.09679475, 0.95467059],\n", " [0.36051701, 0.78361037, 0.00716923]])" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = np.random.rand(3,3)\n", "\n", "M" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "np.savetxt(\"random-matrix.csv\", M)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7.317183558113176112e-01 4.654420244898096470e-01 7.237273924754552556e-01\r\n", "3.239060308567449642e-01 9.679474636543183852e-02 9.546705930168928322e-01\r\n", "3.605170063363589694e-01 7.836103655978251536e-01 7.169228636445423852e-03\r\n" ] } ], "source": [ "!cat random-matrix.csv" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.73172 0.46544 0.72373\r\n", "0.32391 0.09679 0.95467\r\n", "0.36052 0.78361 0.00717\r\n" ] } ], "source": [ "np.savetxt(\"random-matrix.csv\", M, fmt='%.5f') # fmt 确定格式\n", "\n", "!cat random-matrix.csv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Numpy 的本地文件格式" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当存储和读取numpy数组时非常有用。利用函数`numpy.save`和`numpy.load`:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "random-matrix.npy: data\r\n" ] } ], "source": [ "np.save(\"random-matrix.npy\", M)\n", "\n", "!file random-matrix.npy" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.73171836, 0.46544202, 0.72372739],\n", " [0.32390603, 0.09679475, 0.95467059],\n", " [0.36051701, 0.78361037, 0.00716923]])" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.load(\"random-matrix.npy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 更多Numpy数组的性质" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "float64\n", "8\n" ] } ], "source": [ "print(M.dtype)\n", "print(M.itemsize) # 每个元素的字节数\n" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "72" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.nbytes # 字节数" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.ndim # 维度" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 操作数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 索引" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以用方括号和下标索引元素:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v = np.array([1, 2, 3, 4, 5])\n", "# v 是一个向量,仅仅只有一维,取一个索引\n", "v[0]" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.09679474636543184\n", "0.09679474636543184\n", "[0.32390603 0.09679475 0.95467059]\n" ] } ], "source": [ "\n", "# 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": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.73171836, 0.46544202, 0.72372739],\n", " [0.32390603, 0.09679475, 0.95467059],\n", " [0.36051701, 0.78361037, 0.00716923]])" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.32390603, 0.09679475, 0.95467059])" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "相同的事情可以利用`:`而不是索引来实现:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.32390603, 0.09679475, 0.95467059])" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[1,:] # 行 1" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.46544202, 0.09679475, 0.78361037])" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M[:,1] # 列 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以用索引赋新的值给数组中的元素:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "M[0,0] = 1" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1. , 0.46544202, 0.72372739],\n", " [0.32390603, 0.09679475, 0.95467059],\n", " [0.36051701, 0.78361037, 0.00716923]])" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [], "source": [ "# 对行和列也同样有用\n", "M[1,:] = 0\n", "M[:,2] = -1" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1. , 0.46544202, -1. ],\n", " [ 0. , 0. , -1. ],\n", " [ 0.36051701, 0.78361037, -1. ]])" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 切片索引" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "切片索引是语法`M[lower:upper:step]`的技术名称,用于提取数组的一部分:" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4, 5])" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.array([1,2,3,4,5])\n", "A" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 3])" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[1:3]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "切片索引是*可变的*: 如果它们被分配了一个新值,那么从其中提取切片的原始数组将被修改:\n" ] }, { "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[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]`中任意的三个值\n", "We can omit any of the three parameters in `M[lower:upper:step]`:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3, 4, 5])" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[::] # lower, upper, step 都取默认值" ] }, { "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[:]" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -3, 5])" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[::2] # step is 2, lower and upper 代表数组的开始和结束" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, -2, -3])" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[:3] # 前3个元素" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([4, 5])" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[3:] # 从索引3开始的元素" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "负索引计数从数组的结束(正索引从开始):" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [], "source": [ "A = np.array([1,2,3,4,5])" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[-1] # 数组中最后一个元素" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3, 4, 5])" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A[-3:] # 最后三个元素" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "索引切片的工作方式与多维数组完全相同:" ] }, { "cell_type": "code", "execution_count": 62, "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": 62, "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": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[11, 12, 13],\n", " [21, 22, 23],\n", " [31, 32, 33]])" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 原始数组中的一个块\n", "A[1:4, 1:4]" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0, 2, 4],\n", " [20, 22, 24],\n", " [40, 42, 44]])" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 步长\n", "A[::2, ::2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 花式索引" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Fancy索引是一个名称时,一个数组或列表被使用在一个索引:" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[10 11 12 13 14]\n", " [20 21 22 23 24]\n", " [30 31 32 33 34]]\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": [ "row_indices = [1, 2, 3]\n", "print(A[row_indices])\n", "print(A)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([11, 22, 34])" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "col_indices = [1, 2, -1] # 索引-1 代表最后一个元素\n", "A[row_indices, col_indices]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们也可以使用索引掩码:如果索引掩码是一个数据类型`bool`的Numpy数组,那么一个元素被选择(True)或不(False)取决于索引掩码在每个元素位置的值:" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4])" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = array([n for n in range(5)])\n", "B" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 2])" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row_mask = array([True, False, True, False, False])\n", "B[row_mask]" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 2])" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 相同的事情\n", "row_mask = array([1,0,1,0,0], dtype=bool)\n", "B[row_mask]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这个特性对于有条件地从数组中选择元素非常有用,例如使用比较运算符:" ] }, { "cell_type": "code", "execution_count": 70, "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": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = np.arange(0, 10, 0.5)\n", "x" ] }, { "cell_type": "code", "execution_count": 71, "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": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mask = (5 < x) * (x < 7.5)\n", "\n", "mask" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([5.5, 6. , 6.5, 7. ])" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x[mask]" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3.5, 4. , 4.5, 5. , 5.5])" ] }, "execution_count": 73, "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[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (2,3) (5,) " ] } ], "source": [ "A * v1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 矩阵代数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "那么矩阵的乘法呢?有两种方法。我们可以使用点函数,它对两个参数应用矩阵-矩阵、矩阵-向量或内向量乘法" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.3767892 , 1.47079714, 0.31117826, 1.29726746, 0.51486767],\n", " [0.25604237, 0.97247777, 0.34479677, 0.93969314, 0.3976715 ],\n", " [0.81557228, 1.22841789, 0.86636095, 0.93499185, 0.28560187],\n", " [0.52515694, 1.56792282, 1.1443364 , 1.84965072, 0.74141231],\n", " [0.78004097, 1.51298694, 1.22023006, 1.42991218, 0.71648303]])" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.random.rand(5, 5)\n", "v = np.random.rand(5, 1)\n", "\n", "np.dot(A, A)" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([3.03824466, 2.65209134, 2.94637897, 6.50153897, 5.54270391])" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.dot(A, v1)" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.dot(v1, v1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "另外,我们可以将数组对象投到`matrix`类型上。这将改变标准算术运算符`+, -, *` 的行为,以使用矩阵代数。" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [], "source": [ "M = np.matrix(A)\n", "v = np.matrix(v1).T # make it a column vector" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0],\n", " [1],\n", " [2],\n", " [3],\n", " [4]])" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0.3767892 , 1.47079714, 0.31117826, 1.29726746, 0.51486767],\n", " [0.25604237, 0.97247777, 0.34479677, 0.93969314, 0.3976715 ],\n", " [0.81557228, 1.22841789, 0.86636095, 0.93499185, 0.28560187],\n", " [0.52515694, 1.56792282, 1.1443364 , 1.84965072, 0.74141231],\n", " [0.78004097, 1.51298694, 1.22023006, 1.42991218, 0.71648303]])" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M * M" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[3.03824466],\n", " [2.65209134],\n", " [2.94637897],\n", " [6.50153897],\n", " [5.54270391]])" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M * v" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[30]])" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 內积\n", "v.T * v" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[3.03824466],\n", " [3.65209134],\n", " [4.94637897],\n", " [9.50153897],\n", " [9.54270391]])" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 对于矩阵对象,适用标准的矩阵代数\n", "v + M*v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们尝试用不相配的矩阵形状加,减或者乘我们会得到错误:" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [], "source": [ "v = np.matrix([1,2,3,4,5,6]).T" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "((5, 5), (5, 1))" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.shape(M), np.shape(v)" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[5.06458489],\n", " [4.08471675],\n", " [4.990684 ],\n", " [9.17423165],\n", " [8.08502244]])" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M * v" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "同样了解相关的函数:`inner`, `outer`, `cross`, `kron`, `tensordot`。例如用`help(kron)`。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 数组/矩阵转换" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "同样我们也用`.T`对矩阵目标`v`进行转置。我们也可以利用`transpose`函数去实现同样的事情。\n", "\n", "变换矩阵对象的其他数学函数有:" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.04208911 0.65828119 0.21987187 0.10069326]\n", " [0.61960112 0.52726045 0.35884175 0.51931613]\n", " [0.66708619 0.76886997 0.06792093 0.6548313 ]]\n", "[[0.04208911 0.61960112 0.66708619]\n", " [0.65828119 0.52726045 0.76886997]\n", " [0.21987187 0.35884175 0.06792093]\n", " [0.10069326 0.51931613 0.6548313 ]]\n" ] } ], "source": [ "A = np.random.rand(3,4)\n", "print(A)\n", "print(A.T)" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0.+1.j, 0.+2.j],\n", " [0.+3.j, 0.+4.j]])" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C = np.matrix([[1j, 2j], [3j, 4j]])\n", "C" ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0.-1.j, 0.-2.j],\n", " [0.-3.j, 0.-4.j]])" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "conjugate(C)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "厄米共轭:转置+共轭" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0.-1.j, 0.-3.j],\n", " [0.-2.j, 0.-4.j]])" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C.H" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以将复数数组的实部和虚部提取出来并用`real`和`imag`来表示:" ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0., 0.],\n", " [0., 0.]])" ] }, "execution_count": 130, "metadata": {}, "output_type": "execute_result" } ], "source": [ "real(C) # same as: C.real" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[1., 2.],\n", " [3., 4.]])" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "imag(C) # same as: C.imag" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "或者说复数和绝对值" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0.78539816, 1.10714872],\n", " [ 1.24904577, 1.32581766]])" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "angle(C+1) # heads up MATLAB Users, angle is used instead of arg" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[ 1., 2.],\n", " [ 3., 4.]])" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "abs(C)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 矩阵计算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 求逆" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[0.+2.j , 0.-1.j ],\n", " [0.-1.5j, 0.+0.5j]])" ] }, "execution_count": 132, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linalg.inv(C) # equivalent to C.I " ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "matrix([[1.00000000e+00+0.j, 0.00000000e+00+0.j],\n", " [2.22044605e-16+0.j, 1.00000000e+00+0.j]])" ] }, "execution_count": 133, "metadata": {}, "output_type": "execute_result" } ], "source": [ "C.I * C" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 行列式" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2.0000000000000004+0j)" ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.linalg.det(C)" ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0.49999999999999967+0j)" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "linalg.det(C.I)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 数据处理" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通常将数据集存储在Numpy数组中是非常有用的。Numpy提供了许多函数用于计算数组中数据集的统计。\n", "\n", "例如,让我们从上面使用的斯德哥尔摩温度数据集计算一些属性。" ] }, { "cell_type": "code", "execution_count": 136, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(77431, 7)" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 提醒一下,温度数据集存储在数据变量中:\n", "np.shape(data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### mean" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(77431, 7)\n" ] }, { "data": { "text/plain": [ "6.197109684751585" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 温度数据在第三列中\n", "print(data.shape)\n", "np.mean(data[:,3])" ] }, { "cell_type": "code", "execution_count": 137, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.4764047026464162" ] }, "execution_count": 137, "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": 138, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(8.282271621340573, 68.59602320966341)" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.std(data[:,3]), np.var(data[:,3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 最小值和最大值" ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-25.8" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 最低日平均温度\n", "data[:,3].min()" ] }, { "cell_type": "code", "execution_count": 140, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "28.3" ] }, "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 最高日平均温度\n", "data[:,3].max()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### sum, prod, and trace" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d = np.arange(0, 10)\n", "d" ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "45" ] }, "execution_count": 142, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 将所有的元素相加\n", "np.sum(d)" ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3628800" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 全元素积分\n", "np.prod(d+1)" ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 0, 1, 3, 6, 10, 15, 21, 28, 36, 45])" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 累计求和\n", "np.cumsum(d)" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1, 2, 6, 24, 120, 720, 5040,\n", " 40320, 362880, 3628800])" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 累计成绩\n", "np.cumprod(d+1)" ] }, { "cell_type": "code", "execution_count": 148, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.04879166276667" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 计算对角线元素的和,和diag(A).sum()一样\n", "np.trace(A)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 数组子集的计算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我们可以使用索引、花式索引和从数组中提取数据的其他方法(如上所述)来计算数组中的数据子集。\n", "\n", "例如,让我们回到温度数据集:" ] }, { "cell_type": "code", "execution_count": 149, "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": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.])" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.unique(data[:,1]) # 列的值从1到12" ] }, { "cell_type": "code", "execution_count": 150, "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": 151, "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": 153, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "months = np.arange(1,13)\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": [ "### 高维数据的计算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "当例如`min`, `max`等函数应用在高维数组上时,有时将计算应用于整个数组是有用的,而且很多时候有时只基于行或列。用`axis`参数我们可以决定这个函数应该怎样表现:" ] }, { "cell_type": "code", "execution_count": 157, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.99782852, 0.15992805, 0.31262638],\n", " [0.51702607, 0.45658172, 0.66789036],\n", " [0.77771351, 0.42574723, 0.14011317]])" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "m = np.random.rand(3,3)\n", "m" ] }, { "cell_type": "code", "execution_count": 158, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.997828517861979" ] }, "execution_count": 158, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# global max\n", "m.max()" ] }, { "cell_type": "code", "execution_count": 159, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.99782852, 0.45658172, 0.66789036])" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# max in each column\n", "m.max(axis=0)" ] }, { "cell_type": "code", "execution_count": 160, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.99782852, 0.66789036, 0.77771351])" ] }, "execution_count": 160, "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": [ "## 阵列的重塑、调整大小和堆叠" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numpy数组的形状可以被确定而无需复制底层数据,这使得即使对于大型数组也能有较快的操作。" ] }, { "cell_type": "code", "execution_count": 162, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.97579482 0.78668761 0.61373444]\n", " [0.58850244 0.9784108 0.08465447]\n", " [0.57262123 0.44795615 0.75564229]\n", " [0.36770219 0.34095592 0.16259103]]\n" ] } ], "source": [ "import numpy as np\n", "\n", "A = np.random.rand(4, 3)\n", "print(A)" ] }, { "cell_type": "code", "execution_count": 163, "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": 166, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.97579482, 0.78668761, 0.61373444, 0.58850244, 0.9784108 ,\n", " 0.08465447, 0.57262123, 0.44795615, 0.75564229, 0.36770219,\n", " 0.34095592, 0.16259103]])" ] }, "execution_count": 166, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = A.reshape((1,n*m))\n", "B" ] }, { "cell_type": "code", "execution_count": 167, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.97579482]\n", " [0.78668761]\n", " [0.61373444]\n", " [0.58850244]\n", " [0.9784108 ]\n", " [0.08465447]\n", " [0.57262123]\n", " [0.44795615]\n", " [0.75564229]\n", " [0.36770219]\n", " [0.34095592]\n", " [0.16259103]]\n" ] } ], "source": [ "B2 = A.reshape((n*m, 1))\n", "print(B2)" ] }, { "cell_type": "code", "execution_count": 168, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5. , 5. , 5. , 5. , 5. ,\n", " 0.08465447, 0.57262123, 0.44795615, 0.75564229, 0.36770219,\n", " 0.34095592, 0.16259103]])" ] }, "execution_count": 168, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B[0,0:5] = 5 # modify the array\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 169, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5. , 5. , 5. ],\n", " [5. , 5. , 0.08465447],\n", " [0.57262123, 0.44795615, 0.75564229],\n", " [0.36770219, 0.34095592, 0.16259103]])" ] }, "execution_count": 169, "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": 170, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([5. , 5. , 5. , 5. , 5. ,\n", " 0.08465447, 0.57262123, 0.44795615, 0.75564229, 0.36770219,\n", " 0.34095592, 0.16259103])" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B = A.flatten()\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 171, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(12,)\n" ] } ], "source": [ "print(B.shape)" ] }, { "cell_type": "code", "execution_count": 172, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0.0643267 0.02070895 0.01127191 0.36318507 0.26309744 0.8332378\n", " 0.79477743 0.52745619 0.35675021 0.55907373 0.18993756 0.15919449\n", " 0.54789401 0.23186893 0.02898541 0.43545343 0.80684175 0.44014057\n", " 0.05129167 0.95111801 0.40743132 0.57197596 0.6692788 0.80824496\n", " 0.40301441 0.84369196 0.95294593 0.14876807 0.58005171 0.30849079\n", " 0.27846197 0.01062528 0.62870079 0.6416306 0.76945123 0.39443503\n", " 0.76619764 0.42833327 0.60720341 0.16246792 0.76067082 0.27134944\n", " 0.36268568 0.78501742 0.36935191 0.43410334 0.10594888 0.12941728\n", " 0.51760718 0.57260509 0.09756568 0.13216908 0.32918105 0.9338644\n", " 0.71681907 0.58218819 0.58798528 0.81665138 0.73604797 0.91730101]\n" ] } ], "source": [ "T = np.random.rand(3, 4, 5)\n", "T2 = T.flatten()\n", "print(T2)" ] }, { "cell_type": "code", "execution_count": 176, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([10. , 10. , 10. , 10. , 10. ,\n", " 0.08465447, 0.57262123, 0.44795615, 0.75564229, 0.36770219,\n", " 0.34095592, 0.16259103])" ] }, "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ "B[0:5] = 10\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 177, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5. , 5. , 5. ],\n", " [5. , 5. , 0.08465447],\n", " [0.57262123, 0.44795615, 0.75564229],\n", " [0.36770219, 0.34095592, 0.16259103]])" ] }, "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A # 现在A并没有改变,因为B的数值是A的复制,并不指向同样的值。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 添加新的维度:newaxis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "有了`newaxis`,我们可以在数组中插入新的维度,例如将一个向量转换为列或行矩阵:" ] }, { "cell_type": "code", "execution_count": 178, "metadata": {}, "outputs": [], "source": [ "v = np.array([1,2,3])" ] }, { "cell_type": "code", "execution_count": 179, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3,)" ] }, "execution_count": 179, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.shape(v)" ] }, { "cell_type": "code", "execution_count": 180, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1 2 3]\n" ] } ], "source": [ "print(v)" ] }, { "cell_type": "code", "execution_count": 182, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3, 1)\n" ] } ], "source": [ "v2 = v.reshape(3, 1)\n", "print(v2.shape)" ] }, { "cell_type": "code", "execution_count": 190, "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": 191, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(3, 1)" ] }, "execution_count": 191, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 列矩阵\n", "v[:,newaxis].shape" ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, 3)" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 行矩阵\n", "v[newaxis,:].shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 叠加和重复数组" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "利用函数`repeat`, `tile`, `vstack`, `hstack`, 和`concatenate` 我们可以用较小的向量和矩阵来创建更大的向量和矩阵:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### tile and repeat" ] }, { "cell_type": "code", "execution_count": 192, "metadata": {}, "outputs": [], "source": [ "a = np.array([[1, 2], [3, 4]])" ] }, { "cell_type": "code", "execution_count": 194, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1 2]\n", " [3 4]]\n" ] }, { "data": { "text/plain": [ "array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4])" ] }, "execution_count": 194, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(a)\n", "\n", "# 重复每一个元素三次\n", "np.repeat(a, 3)" ] }, { "cell_type": "code", "execution_count": 195, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 1, 2, 1, 2],\n", " [3, 4, 3, 4, 3, 4]])" ] }, "execution_count": 195, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# tile the matrix 3 times \n", "np.tile(a, 3)" ] }, { "cell_type": "code", "execution_count": 196, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 1, 2, 1, 2],\n", " [3, 4, 3, 4, 3, 4]])" ] }, "execution_count": 196, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 更好的方案\n", "np.tile(a, (1, 3))" ] }, { "cell_type": "code", "execution_count": 34, "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": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.tile(a, (3, 1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### concatenate" ] }, { "cell_type": "code", "execution_count": 197, "metadata": {}, "outputs": [], "source": [ "b = np.array([[5, 6]])" ] }, { "cell_type": "code", "execution_count": 198, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 198, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.concatenate((a, b), axis=0)" ] }, { "cell_type": "code", "execution_count": 200, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] }, "execution_count": 200, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.concatenate((a, b.T), axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### hstack and vstack" ] }, { "cell_type": "code", "execution_count": 201, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4],\n", " [5, 6]])" ] }, "execution_count": 201, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.vstack((a,b))" ] }, { "cell_type": "code", "execution_count": 202, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 5],\n", " [3, 4, 6]])" ] }, "execution_count": 202, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.hstack((a,b.T))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 复制和“深度复制”" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "为了获得高性能,Python中的赋值通常不复制底层对象。例如,在函数之间传递对象时,这一点非常重要,以避免不必要时大量的内存复制(技术术语:通过引用传递)。" ] }, { "cell_type": "code", "execution_count": 203, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4]])" ] }, "execution_count": 203, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = np.array([[1, 2], [3, 4]])\n", "\n", "A" ] }, { "cell_type": "code", "execution_count": 204, "metadata": {}, "outputs": [], "source": [ "# 现在B和A指的是同一个数组数据\n", "B = A " ] }, { "cell_type": "code", "execution_count": 205, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 205, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 改变B影响A\n", "B[0,0] = 10\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 206, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 206, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果我们想避免这种行为,那么当我们从`A`中复制一个新的完全独立的对象`B`时,我们需要使用函数`copy`来做一个所谓的“深度复制”:" ] }, { "cell_type": "code", "execution_count": 207, "metadata": {}, "outputs": [], "source": [ "B = np.copy(A)" ] }, { "cell_type": "code", "execution_count": 208, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-5, 2],\n", " [ 3, 4]])" ] }, "execution_count": 208, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 现在如果我们改变B,A不受影响\n", "B[0,0] = -5\n", "\n", "B" ] }, { "cell_type": "code", "execution_count": 209, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[10, 2],\n", " [ 3, 4]])" ] }, "execution_count": 209, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 遍历数组元素" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通常,我们希望尽可能避免遍历数组元素(不惜一切代价)。原因是在像Python(或MATLAB)这样的解释语言中,迭代与向量化操作相比真的很慢。\n", "\n", "然而,有时迭代是不可避免的。对于这种情况,Python的For循环是最方便的遍历数组的方法:" ] }, { "cell_type": "code", "execution_count": 210, "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": 211, "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": 162, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('row_idx', 0, 'row', array([1, 2]))\n", "('col_idx', 0, 'element', 1)\n", "('col_idx', 1, 'element', 2)\n", "('row_idx', 1, 'row', array([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", " # update the matrix M: square each element\n", " M[row_idx, col_idx] = element ** 2" ] }, { "cell_type": "code", "execution_count": 163, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 1, 4],\n", " [ 9, 16]])" ] }, "execution_count": 163, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# each element in M is now squared\n", "M" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vectorizing functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As mentioned several times by now, to get good performance we should try to avoid looping over elements in our vectors and matrices, and instead use vectorized algorithms. The first step in converting a scalar algorithm to a vectorized algorithm is to make sure that the functions we write work with vector inputs." ] }, { "cell_type": "code", "execution_count": 213, "metadata": {}, "outputs": [], "source": [ "def Theta(x):\n", " \"\"\"\n", " Scalar implemenation of the Heaviside step function.\n", " \"\"\"\n", " if x >= 0:\n", " return 1\n", " else:\n", " return 0" ] }, { "cell_type": "code", "execution_count": 214, "metadata": {}, "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[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[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[0mScalar\u001b[0m \u001b[0mimplemenation\u001b[0m \u001b[0mof\u001b[0m \u001b[0mthe\u001b[0m \u001b[0mHeaviside\u001b[0m \u001b[0mstep\u001b[0m \u001b[0mfunction\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[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[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32melse\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(array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OK, that didn't work because we didn't write the `Theta` function so that it can handle a vector input... \n", "\n", "To get a vectorized version of Theta we can use the Numpy function `vectorize`. In many cases it can automatically vectorize a function:" ] }, { "cell_type": "code", "execution_count": 215, "metadata": {}, "outputs": [], "source": [ "Theta_vec = np.vectorize(Theta)" ] }, { "cell_type": "code", "execution_count": 216, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 1, 1])" ] }, "execution_count": 216, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Theta_vec(np.array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also implement the function to accept a vector input from the beginning (requires more effort but might give better performance):" ] }, { "cell_type": "code", "execution_count": 217, "metadata": {}, "outputs": [], "source": [ "def Theta(x):\n", " \"\"\"\n", " Vector-aware implemenation of the Heaviside step function.\n", " \"\"\"\n", " return 1 * (x >= 0)" ] }, { "cell_type": "code", "execution_count": 219, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 1, 1])" ] }, "execution_count": 219, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Theta(np.array([-3,-2,-1,0,1,2,3]))" ] }, { "cell_type": "code", "execution_count": 221, "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": 221, "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": 222, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0, 1)" ] }, "execution_count": 222, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# still works for scalars as well\n", "Theta(-1.2), Theta(2.6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using arrays in conditions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using arrays in conditions,for example `if` statements and other boolean expressions, one needs to use `any` or `all`, which requires that any or all elements in the array evalutes to `True`:" ] }, { "cell_type": "code", "execution_count": 223, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2],\n", " [3, 4]])" ] }, "execution_count": 223, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = np.array([[1, 2], [3, 4]])\n", "M" ] }, { "cell_type": "code", "execution_count": 224, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 224, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(M > 2).any()" ] }, { "cell_type": "code", "execution_count": 225, "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": 226, "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": [ "## Type casting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since Numpy arrays are *statically typed*, the type of an array does not change once created. But we can explicitly cast an array of some type to another using the `astype` functions (see also the similar `asarray` function). This always create a new array of new type:" ] }, { "cell_type": "code", "execution_count": 227, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('int64')" ] }, "execution_count": 227, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M.dtype" ] }, { "cell_type": "code", "execution_count": 228, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1., 2.],\n", " [3., 4.]])" ] }, "execution_count": 228, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2 = M.astype(float)\n", "\n", "M2" ] }, { "cell_type": "code", "execution_count": 229, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dtype('float64')" ] }, "execution_count": 229, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M2.dtype" ] }, { "cell_type": "code", "execution_count": 230, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ True, True],\n", " [ True, True]])" ] }, "execution_count": 230, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M3 = M.astype(bool)\n", "\n", "M3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Further reading" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* http://numpy.scipy.org\n", "* http://scipy.org/Tentative_NumPy_Tutorial\n", "* http://scipy.org/NumPy_for_Matlab_Users - A Numpy guide for MATLAB users." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Versions" ] }, { "cell_type": "code", "execution_count": 178, "metadata": {}, "outputs": [ { "data": { "application/json": { "Software versions": [ { "module": "Python", "version": "2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)]" }, { "module": "IPython", "version": "3.2.1" }, { "module": "OS", "version": "Darwin 14.1.0 x86_64 i386 64bit" }, { "module": "numpy", "version": "1.9.2" } ] }, "text/html": [ "
SoftwareVersion
Python2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)]
IPython3.2.1
OSDarwin 14.1.0 x86_64 i386 64bit
numpy1.9.2
Sat Aug 15 11:02:09 2015 JST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", "Python & 2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)] \\\\ \\hline\n", "IPython & 3.2.1 \\\\ \\hline\n", "OS & Darwin 14.1.0 x86\\_64 i386 64bit \\\\ \\hline\n", "numpy & 1.9.2 \\\\ \\hline\n", "\\hline \\multicolumn{2}{|l|}{Sat Aug 15 11:02:09 2015 JST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ "Software versions\n", "Python 2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)]\n", "IPython 3.2.1\n", "OS Darwin 14.1.0 x86_64 i386 64bit\n", "numpy 1.9.2\n", "Sat Aug 15 11:02:09 2015 JST" ] }, "execution_count": 178, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%reload_ext version_information\n", "\n", "%version_information numpy" ] } ], "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.6.8" } }, "nbformat": 4, "nbformat_minor": 1 }