{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基础知识\n",
    "\n",
    "NumPy 主要的数据对象是多维数组。它是一个由元素（通常是数字）组成的表，其中所有的元素必须是相同的类型。通过`tuple`可以对表中的元素进行索引（即可进行访问或取值等操作）。在NumPy中，每一维也称为一轴。\n",
    "\n",
    "举个粟子，一个表示三维座标的点 `[1, 2, 1]` 只有一个轴。这个轴上有3个元素，所以它的长度为3。下面的例子有2个轴，第一个轴长度为2，第二个轴长度为3。\n",
    "\n",
    "```\n",
    "[[ 1, 0, 0],\n",
    " [ 0, 1, 2]]\n",
    "```\n",
    "\n",
    "NumPy 中的数组类为 `ndarray`, 别名为 `array`。 注意`numpy.array`跟Python标准库里只能处理一维数据的`array.array`完全不是一回事。`ndarray`中比较重要的属性如下：\n",
    "\n",
    "* `ndarray.ndim` : 维度\n",
    "* `ndarray.shape` : 多维数组的形状，由一个`tuple`表示，它的长度即是多维数组的维度，其中每个元素代表它对应轴的长度。比如一个`n`行`m`列的矩阵，它的`shape`就是`(n, m)`。\n",
    "* `ndarray.size` : 多维数组的元素数量。等于 `shape` 中各个元素的连乘。\n",
    "* `ndarray.dtype` : 多数数组的元素类型。除了 NumPy 提供的基本类型如 `numpy.int32`、`numpy.int16`、`numpy.float64`之外，你也可以创建自定义类型或 Python 的类型。\n",
    "* `ndarray.itemsize` : 每个元素占用的字节数，主要由 `ndarray.dtype` 决定\n",
    "* `ndarray.data` : 实际存储所有元素的缓冲区。一般情况不会用到这个属性，而是通过索引机制来访问数组内部的元素。\n",
    "\n",
    "## 基本操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3,  4],\n",
       "       [ 5,  6,  7,  8,  9],\n",
       "       [10, 11, 12, 13, 14]])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(15).reshape(3, 5)\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3, 5)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.ndim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int64')"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.itemsize"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "numpy.ndarray"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([6, 7, 8])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = np.array([6, 7 , 8])\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "numpy.ndarray"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多维数组的创建\n",
    "\n",
    "创建多维数组的方式有多种。最简单的是通过将Python的数列(一组有序的数字序列，如`list`或`tuple`)传入`numpy.array`函数来创建。其元素类型将会自动推导成为NumPy的类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 2, 3])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 3])\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int64')"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.2, 2.3, 3.4])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = np.array([1.2, 2.3, 3.4])\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b.dtype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要特别注意传入的参数是__一个__ 数列，而不是__多个__参数。比如 `np.array(1, 2, 3)` 这样的调用方式是错误的，会引发异常。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`array` 根据传入数列的深度自动生成对应维度的多维数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [3, 4, 6]])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([\n",
    "    (1, 2, 3),\n",
    "    (3, 4, 6)\n",
    "])\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[1, 2, 3],\n",
       "        [2, 3, 4]],\n",
       "\n",
       "       [[3, 4, 5],\n",
       "        [4, 5, 6]]])"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = np.array([\n",
    "    [\n",
    "        [1, 2, 3],\n",
    "        [2, 3, 4],\n",
    "    ],\n",
    "    [\n",
    "        [3, 4, 5],\n",
    "        [4, 5, 6]\n",
    "    ]\n",
    "])\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 2, 3)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意，多维数组的每个轴上的每个元素的`shape`必须是一致的。如以下的Python `list` 是合法的，但不能转换成NumPy的多维数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "<ipython-input-26-ab70420eb76c>:11: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray\n",
      "  ndl = np.array(l)\n"
     ]
    }
   ],
   "source": [
    "l = [\n",
    "    [\n",
    "        [1, 2, 3],\n",
    "        [2, 3, 4],\n",
    "    ],\n",
    "    [\n",
    "        [3, 4, 5]\n",
    "    ]\n",
    "]\n",
    "\n",
    "ndl = np.array(l) # won't success"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一般而言，我们不能预先知道一个多维数组元素取值，但它的`shape`往往是已知的，就像是写一个函数，虽然实参会是什么值不知道，但有多少个形数是确定的。因此，NumPy提供了若干个函数用于初始化多维数组。这样就能避免增量分配带来的时间损耗。\n",
    "\n",
    "`zeros`函数用于初始化一个填满`0`的多维数组，`ones`函数则初始化一个填满`1`的多维数组，`empty`函数则只分配内存空间，其元素值是随机的，取决于所分配内存块当时的状态而定。若不指定`dtype`则默认使用`float64`类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0.]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.zeros((3, 4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[1, 1, 1, 1],\n",
       "        [1, 1, 1, 1],\n",
       "        [1, 1, 1, 1]],\n",
       "\n",
       "       [[1, 1, 1, 1],\n",
       "        [1, 1, 1, 1],\n",
       "        [1, 1, 1, 1]]], dtype=int16)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.ones((2, 3, 4), dtype=np.int16)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[6.93551934e-310, 6.93551934e-310, 0.00000000e+000],\n",
       "       [0.00000000e+000, 0.00000000e+000, 0.00000000e+000]])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.empty((2, 3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了方便创建等差数列，NumPy提供了`arange`函数，类似于Python内置的`range`函数，但返回不是迭代器，而是一个多维数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([10, 15, 20, 25])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.arange(10, 30, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.arange(0, 2, 0.3)         # it accepts float arguments"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "给`arange`传入浮点时，受精度影响很难准确地对元素的个数进行预测。因此，针对浮点步进而又需要确定元素个数的场景，建议采用`linspace`函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.linspace(0, 2, 9)    # 9 numbers from 0 to 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((100,), (100,), 3.173325912716963, -0.03172793349806786)"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.linspace(0, 2*np.pi, 100)\n",
    "y = np.sin(x)\n",
    "x.shape, y.shape, x[50], y[50]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多维数组的打印\n",
    "\n",
    "多维数组的打印遵循以下几个原则：\n",
    "\n",
    "* 最后一个轴的元素从左到右打印\n",
    "* 倒数第二个轴从上往下打印\n",
    "* 其它轴同样从上往下打印，但元素之间以空行隔开"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a= [0 1 2 3 4 5]\n",
      "b= [[ 0  1  2  3]\n",
      " [ 4  5  6  7]\n",
      " [ 8  9 10 11]]\n",
      "c= [[[ 0  1  2  3]\n",
      "  [ 4  5  6  7]\n",
      "  [ 8  9 10 11]]\n",
      "\n",
      " [[12 13 14 15]\n",
      "  [16 17 18 19]\n",
      "  [20 21 22 23]]]\n"
     ]
    }
   ],
   "source": [
    "a = np.arange(6)\n",
    "print('a=', a)\n",
    "\n",
    "b = np.arange(12).reshape(3, 4)\n",
    "print('b=', b)\n",
    "\n",
    "c = np.arange(24).reshape(2, 3, 4)\n",
    "print('c=', c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基本操作\n",
    "\n",
    "一般的算术操作符在数组上__按元素__为单位进行操作，同时返回一个新的数组实例。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a= [2 3 4 5]\n",
      "b= [0 1 2 3]\n",
      "c=a-b= [2 2 2 2]\n",
      "b**2= [0 1 4 9]\n",
      "10*np.sin(a)= [ 9.09297427  1.41120008 -7.56802495 -9.58924275]\n",
      "a<35= [ True  True  True  True]\n"
     ]
    }
   ],
   "source": [
    "a = np.array([2, 3, 4, 5])\n",
    "print('a=', a)\n",
    "b = np.arange(4)\n",
    "print('b=', b)\n",
    "c = a - b\n",
    "print('c=a-b=', c)\n",
    "print('b**2=', b**2)\n",
    "print('10*np.sin(a)=', 10*np.sin(a))\n",
    "print('a<35=', a<35)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不同于许多矩阵语言(如MATLAB)，乘法操作符`*`指代矩阵乘法，在NumPy中则是按元素操作。矩阵乘法通过`@`操作符或者`dot`函数实现。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A * B =  [[2 0]\n",
      " [0 4]]\n",
      "A @ B =  [[5 4]\n",
      " [3 4]]\n",
      "A.dot(B) =  [[5 4]\n",
      " [3 4]]\n"
     ]
    }
   ],
   "source": [
    "A = np.array([\n",
    "    [1, 1],\n",
    "    [0, 1],\n",
    "])\n",
    "B = np.array([\n",
    "    [2, 0],\n",
    "    [3, 4]\n",
    "])\n",
    "print('A * B = ', A * B)\n",
    "print('A @ B = ', A @ B)\n",
    "print('A.dot(B) = ', A.dot(B))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一些操作符，如`+=`和`*=`等，则会修改已有数组本身，而不会产生新的数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([3, 5, 7])"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([1, 2, 3])\n",
    "b = np.array([2, 3, 4])\n",
    "a += b\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在拥有不同元素类型的矩阵间进行运算操作时，NumPy会自动进行向上类型转换。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('int16')"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([1, 2, 3], dtype=np.int8)\n",
    "b = np.array([2, 3, 4], dtype=np.int16)\n",
    "c = a + b\n",
    "c.dtype"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dtype('float64')"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([1, 2, 3], dtype=np.int32)\n",
    "b = np.array([1, 2, 3], dtype=np.float64)\n",
    "c = a * b\n",
    "c.dtype"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`ndarray` 上实现了许多一元操作符，如求总操作等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a =  [[ 0  1  2  3]\n",
      " [ 4  5  6  7]\n",
      " [ 8  9 10 11]]\n",
      "a.sum() 66\n",
      "a.min() 0\n",
      "a.max() 11\n"
     ]
    }
   ],
   "source": [
    "a = np.arange(12).reshape((3, 4))\n",
    "print('a = ', a)\n",
    "print('a.sum()', a.sum())\n",
    "print('a.min()', a.min())\n",
    "print('a.max()', a.max())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "若不传入任何参数，这些操作在数组上的表现就跟操作一维数组一样，即不会考虑`ndarray`的`shape`。通过给这些函数传入`axis`参数，即可达到对多维数组上特定轴进行操作的目的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 6, 22, 38])"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(12).reshape((3, 4))\n",
    "a.sum(axis=1)  # 被指定的维将会被统计变成一个标量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3],\n",
       "       [ 4,  6,  8, 10],\n",
       "       [12, 15, 18, 21]])"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.cumsum(axis=0) # 累计求和"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 通用函数\n",
    "\n",
    "NumPy 提供一些常用的数学函数，如 `sin`, `cos`, `exp` 等。这些函数亦称通用函数`ufunc`，它们__按元素__为单元进行运算， 产生一个新的数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2])"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(3)\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.        , 2.71828183, 7.3890561 ])"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.exp(a) # 自然对数为底"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.        , 1.        , 1.41421356])"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sqrt(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 2, 3])"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = np.arange(1, 4)\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 3, 5])"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.add(a, b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 索引、分片及迭代\n",
    "\n",
    "只有一个轴的数组其索引、分片和迭代的形式与一般的 Python 列表一致"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729])"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(10) ** 3\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0,  8, 64])"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[:6:2]  # 前6个数中其索引与2整除的元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([  0,   1,  -1,  -1,  -1, 125, 216, 343, 512, 729])"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[2:5] = -1 # 更新分片\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([729, 512, 343, 216, 125,  -1,  -1,  -1,   1,   0])"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[::-1] # 逆序"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "拥有多个轴的数组每个轴都能被索引，索引之间使用逗号`,`分隔"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3],\n",
       "       [ 4,  5,  6,  7],\n",
       "       [ 8,  9, 10, 11]])"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(12).reshape((3, 4))\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "11"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[2, 3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4, 5],\n",
       "       [8, 9]])"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[1:3, :2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "若传入的索引数量少于数组的轴数，则余下的轴会取其全部。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 4,  5,  6,  7],\n",
       "       [ 8,  9, 10, 11]])"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[1:3]  # 相当于 a[1:3, :]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NumPy 还支持使用省略符`...`，如上面表达式也可以写成 `a[1:3, ...]`。省略符的好处是可以提高可读性，读代码的人马上知道这是一个多维的数组，而不仅仅是一维的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对一个多维的数组进行迭代是按它的第一个轴进行的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3]\n",
      "[4 5 6 7]\n",
      "[ 8  9 10 11]\n"
     ]
    }
   ],
   "source": [
    "a = np.arange(12).reshape((3, 4))\n",
    "\n",
    "for b in a:\n",
    "    print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "假如你想按元素为单位对数组进行操作，则可以利用`flat`属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n",
      "6\n",
      "7\n",
      "8\n",
      "9\n",
      "10\n",
      "11\n"
     ]
    }
   ],
   "source": [
    "for b in a.flat:\n",
    "    print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 形状操纵\n",
    "\n",
    "### 数组变形\n",
    "\n",
    "每个数组都有形状`shape`，它由每个轴上元素的个数决定。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 4)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[ 17.,  -5., -15., -15.],\n",
       "       [ -2., -12.,  -7.,  -4.],\n",
       "       [-15.,  11.,  -3.,  -4.]])"
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.floor(10 * np.random.randn(3, 4))\n",
    "print(a.shape)\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数组的形状可以通过一些命令来改变。注意下面几个命令都返回一个新的数组实例。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 17.,  -5., -15., -15.,  -2., -12.,  -7.,  -4., -15.,  11.,  -3.,\n",
       "        -4.])"
      ]
     },
     "execution_count": 100,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.ravel()   # 返回一个打平的数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 17.,  -5., -15., -15.,  -2., -12.,  -7.,  -4., -15.,  11.,  -3.,\n",
       "        -4.])"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = a.reshape(12)\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  0.,  -5., -15., -15.],\n",
       "       [ -2., -12.,  -7.,  -4.],\n",
       "       [-15.,  11.,  -3.,  -4.]])"
      ]
     },
     "execution_count": 106,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b[0] = 0    # 两个数组虽然shape不同，但内部元素依然使用同一块内存空间\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  0.,  -2., -15.],\n",
       "       [ -5., -12.,  11.],\n",
       "       [-15.,  -7.,  -3.],\n",
       "       [-15.,  -4.,  -4.]])"
      ]
     },
     "execution_count": 107,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.T         # 返回转置的数组"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "正常情况下， `ravel`返回的数组元素存储顺序遵循C语言风格，即遵守最右边的索引“变化最快”原则，因此，`a[0, 0]` 的下一个元素是 `a[0, 1]`。当对数组实施变形时，该数组也将会被认为是C语言风格的。一般而言，NumPy创建的数组皆遵守这个原则，所以 `ravel()` 通常都不需要拷贝元素数据，但是，如果数组是由分片或其它一些特殊的方法创建时，可能不需要进行数据拷贝。\n",
    "另外，`ravel` 和 `reshape` 函数也可以指定使用 Fortran风格的数组，即最左边的索引“变化最快”原则来存储元素数据。\n",
    "\n",
    "`reshape`函数返回的新数组实例，`resize`函数则直接修改数组本身的形状。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  0.,  -5., -15.],\n",
       "       [-15.,  -2., -12.],\n",
       "       [ -7.,  -4., -15.],\n",
       "       [ 11.,  -3.,  -4.]])"
      ]
     },
     "execution_count": 108,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.resize(4, 3)\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果在变形操作中给轴长度参数传入`-1`，则其轴长度会自动计算。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[  0.,  -5., -15., -15.,  -2., -12.],\n",
       "       [ -7.,  -4., -15.,  11.,  -3.,  -4.]])"
      ]
     },
     "execution_count": 109,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.reshape(2, -1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数组间的堆叠\n",
    "\n",
    "若干个数据组可以沿着指定方向进行堆叠"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1],\n",
       "       [2, 3]])"
      ]
     },
     "execution_count": 115,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(4).reshape(2, 2)\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[4, 5],\n",
       "       [6, 7]])"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b = np.arange(4, 8).reshape(2, 2)\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1],\n",
       "       [2, 3],\n",
       "       [4, 5],\n",
       "       [6, 7]])"
      ]
     },
     "execution_count": 118,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.vstack((a, b))  # 注意接受的参数是一个tuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1, 4, 5],\n",
       "       [2, 3, 6, 7]])"
      ]
     },
     "execution_count": 119,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hstack((a, b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "按列堆叠函数`column_stack`用于将一系列一维数组当成列来组合。当传入的数组都是2维时，其效果相当于`hstack`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 1, 4, 5],\n",
       "       [2, 3, 6, 7]])"
      ]
     },
     "execution_count": 120,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.column_stack((a, b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 5],\n",
       "       [2, 6],\n",
       "       [3, 7],\n",
       "       [4, 8]])"
      ]
     },
     "execution_count": 121,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = np.array([1, 2, 3, 4])\n",
    "d = np.array([5, 6, 7, 8])\n",
    "np.column_stack((c, d))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "既然有`column_stack`，相对地就有`row_stack`， 但其实它只是`vstack`的别名。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 122,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.column_stack is np.hstack"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 123,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.row_stack is np.vstack"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "总的来讲，对于维度大于2的数组，`hstack`沿着第二个轴进行堆叠，`vstack`则沿着第一个轴进行堆叠，而 `concatenate`函数则允许传入可选的参数来指定沿着哪个轴进行堆叠。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数组的拆分\n",
    "\n",
    "通过`hsplit`，可以将数组水平方向拆分，传入一个数字来得到若干个相等大小的数组，也可传入一个`tuple`来达到根据列下标进行拆分的目的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11],\n",
       "       [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]])"
      ]
     },
     "execution_count": 126,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(24).reshape(2, -1)\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[array([[ 0,  1,  2,  3],\n",
       "        [12, 13, 14, 15]]),\n",
       " array([[ 4,  5,  6,  7],\n",
       "        [16, 17, 18, 19]]),\n",
       " array([[ 8,  9, 10, 11],\n",
       "        [20, 21, 22, 23]])]"
      ]
     },
     "execution_count": 128,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hsplit(a, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[array([[ 0,  1,  2],\n",
       "        [12, 13, 14]]),\n",
       " array([[ 3,  4,  5,  6,  7],\n",
       "        [15, 16, 17, 18, 19]]),\n",
       " array([[ 8,  9],\n",
       "        [20, 21]]),\n",
       " array([[10, 11],\n",
       "        [22, 23]])]"
      ]
     },
     "execution_count": 135,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.hsplit(a, (3, 8, 10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对应地，`vsplit`沿垂直方向进行拆，`array_split`则允许指定按某一轴进行拆分。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 复制与视图\n",
    "\n",
    "当你在操作数组时，有时会导致数组的数据被复制到新的数组，有时则不会。这种状态容易导致新手感到困扰。其实，简单来讲，大致上有三种情形："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 完全不复制\n",
    "\n",
    "简单地赋值操作不会引发数据复制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 137,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 137,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([1, 2, 3, 4])\n",
    "b = a       # 不会创建新的对象\n",
    "b is a      # a 和 b  是同一对象的两个名称"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python 对于可变对象使用传引用的方式进行变量传递，所以函数调用也不会引发数据复制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 140,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id of a 140373490450640\n",
      "id of x 140373490450640\n"
     ]
    }
   ],
   "source": [
    "def f(x):\n",
    "    print('id of x', id(x))\n",
    "    \n",
    "print('id of a', id(a))\n",
    "f(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 视图或浅拷贝\n",
    "\n",
    "不同的数组间可共享相同的数据（即使用同一内存空间）。视图方法`view`创建的数组对象即属于这种情形"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 141,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 141,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = a.view()\n",
    "c is a           # c 是一个新的对象，它不是 a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 142,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 142,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.base is a      #　表示 c 是基于 a 的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 143,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.flags.owndata  # c 没有属于自己的数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 144,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(4,)"
      ]
     },
     "execution_count": 144,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = c.reshape((2, 2))  # a 的 shape 不因 c 的 shape 改变而改变\n",
    "a.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([100,   2,   3,   4])"
      ]
     },
     "execution_count": 146,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c[0, 0] = 100     # 修改 c 的数据即是修改 a 的数据\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数组的分片其实就是返回一个 `view` 对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 3])"
      ]
     },
     "execution_count": 148,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = a[1:3]\n",
    "s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 149,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([100, 200, 200,   4])"
      ]
     },
     "execution_count": 149,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s[:] = 200\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 深拷贝\n",
    "\n",
    "`copy`方法在创建一个新数组的同时，也会对原数据的数据进行一个完全的拷贝操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 150,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 150,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = d.copy()\n",
    "d is a              # d 不是 a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 151,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 151,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d.base is a         # d 也不基于 a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([100, 200, 200,   4])"
      ]
     },
     "execution_count": 153,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d[:] = 0            # 修改 d 的数据完全不影响 a\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在分片之后，若原来的数组以后不会再被使用，则应调用 `copy` 方法对数据进行拷贝，然后释放原数组。特别是当原数组是一个巨大无比的中间变量时，可以节省大量的内存。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.random.randn(100)\n",
    "b = a[20:30].copy()  # 若不调用 copy , 则返回的是视图，它保持着对 a 的引用，只有 b 被回收后 a 才能被回收\n",
    "del a                # copy 后的 b 与 a 没有引用关系，可以被回收 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 中级知识\n",
    "\n",
    "\n",
    "## 广播法则\n",
    "\n",
    "在对多个数组进行运算时，这些数组的形状常常并不是一致的，需要一种有意义的法则来统一处理不同形状数组间的运算问题，因此有了广播法则。\n",
    "\n",
    "广播的第一条规则即是当输入的多个数组的维度不一致时，将以最高维度的数组的轴数为准，向低维的数组插入若干个长度为1的新轴直至所有数组的轴数相等。如 `(2, 3, 4), (5), (2, 3)` 将被调整为 `(2, 3, 4), (1, 1, 5), (1, 2, 3)`\n",
    "\n",
    "第二条规则是数组中长度为1的轴，将以所有数组中同维的最长数为准进行扩展，扩展元素的值被认为等于扩展前同一轴上的值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(3, 3)\n",
      "(3,)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[0, 0, 0],\n",
       "       [3, 3, 3],\n",
       "       [6, 6, 6]])"
      ]
     },
     "execution_count": 156,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([\n",
    "    [1, 2, 3],\n",
    "    [4, 5, 6],\n",
    "    [7, 8, 9],\n",
    "])\n",
    "print(a.shape)\n",
    "b = np.array([-1, -2, -3])\n",
    "print(b.shape)\n",
    "a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 157,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 3)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[0, 0, 0],\n",
       "       [3, 3, 3],\n",
       "       [6, 6, 6]])"
      ]
     },
     "execution_count": 157,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = np.array([\n",
    "    [-1, -2, -3]\n",
    "])\n",
    "print(c.shape)\n",
    "a + c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 1)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[0, 1, 2],\n",
       "       [3, 4, 5],\n",
       "       [6, 7, 8]])"
      ]
     },
     "execution_count": 158,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d = np.array([\n",
    "    [-1]\n",
    "])\n",
    "print(d.shape)\n",
    "a + d"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 高级索引及索引技巧\n",
    "\n",
    "除了使用之前提到过的标准 Python 索引方式之外，NumPy 还提供了额外的索引方式。比如，支持通过整数数组或布尔数组的方式进行索引。 \n",
    "\n",
    "### 整数索引数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a= [ 0  2  4  6  8 10 12 14 16 18 20 22]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([ 2,  2,  6, 16, 10])"
      ]
     },
     "execution_count": 163,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(12) * 2\n",
    "print('a=', a)\n",
    "i = np.array([1, 1, 3, 8 , 5])\n",
    "a[i]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 6,  8],\n",
       "       [18, 14]])"
      ]
     },
     "execution_count": 164,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "j = np.array([[3, 4], [9, 7]])\n",
    "a[j]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当被索引的数组 `a` 是一个多维数组时，传入一维索引数组则代表对`a`的第一个轴进行引用。下面的例子演示如何使用色板将__索引图像__转换成__彩色图像__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[  0,   0,   0],\n",
       "        [255,   0,   0],\n",
       "        [  0, 255,   0],\n",
       "        [  0,   0,   0]],\n",
       "\n",
       "       [[  0,   0,   0],\n",
       "        [  0,   0, 255],\n",
       "        [255, 255, 255],\n",
       "        [  0,   0,   0]]])"
      ]
     },
     "execution_count": 165,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "palette = np.array([             # 色板\n",
    "    [  0,   0,   0],             # 黑色\n",
    "    [255,   0,   0],             # 红色\n",
    "    [  0, 255,   0],             # 绿色\n",
    "    [  0,   0, 255],             # 蓝色\n",
    "    [255, 255, 255],             # 白色\n",
    "])\n",
    "\n",
    "image = np.array([               # 索引图像\n",
    "    [0, 1, 2, 0],                # 每个值为其颜色在色板上的索引值\n",
    "    [0, 3, 4, 0],\n",
    "])\n",
    "\n",
    "palette[image]                   # 将 image 作为索引数组对色板进行索引即可得到彩色图像"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此外，也可对多维数组使用多个索引数组，传入的索引数组间必须拥有相同的形状。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3],\n",
       "       [ 4,  5,  6,  7],\n",
       "       [ 8,  9, 10, 11]])"
      ]
     },
     "execution_count": 179,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(12).reshape(3, 4)\n",
    "i = np.array([[0, 1], [1, 2]])\n",
    "j = np.array([[2, 1], [3, 3]])\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 178,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[ 0,  1,  2,  3],\n",
       "        [ 4,  5,  6,  7]],\n",
       "\n",
       "       [[ 4,  5,  6,  7],\n",
       "        [ 8,  9, 10, 11]]])"
      ]
     },
     "execution_count": 178,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[i]             # 取i行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 180,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 2,  6],\n",
       "       [ 6, 10]])"
      ]
     },
     "execution_count": 180,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[i, 2]         # 取i行，再取第2列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 181,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 2,  5],\n",
       "       [ 7, 11]])"
      ]
     },
     "execution_count": 181,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[i, j]         # 取i行，再取j列，i 和 j 的 shape 必须相同。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Python 语言里，`a[i, j]` 和 `a[(i, j)]` 在语法上等交，因此，可以把 `i` 和 `j` 放到 `tuple` 里面再利用它来进行索引。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 182,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 2,  5],\n",
       "       [ 7, 11]])"
      ]
     },
     "execution_count": 182,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l = (i, j)\n",
    "a[l]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然而，将它们放到数组则是不行的。因为它就变成了索引数组，所有元素都将指向被索引数组的第一个轴。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 186,
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "index 3 is out of bounds for axis 0 with size 3",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mIndexError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-186-688953b2ce97>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0ms\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[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mj\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[0;32m----> 2\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m    \u001b[0;31m# a 的第一个轴长度只有3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mIndexError\u001b[0m: index 3 is out of bounds for axis 0 with size 3"
     ]
    }
   ],
   "source": [
    "s = np.array([i, j])\n",
    "a[s]    # a 的第一个轴长度只有3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "索引数组还有一个常见的用途：在时间相关的序列里面查找最大值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 188,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 20.  ,  51.25,  82.5 , 113.75, 145.  ])"
      ]
     },
     "execution_count": 188,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time = np.linspace(20, 145, 5)\n",
    "time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 190,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0.        ,  0.84147098,  0.90929743,  0.14112001],\n",
       "       [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],\n",
       "       [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],\n",
       "       [-0.53657292,  0.42016704,  0.99060736,  0.65028784],\n",
       "       [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])"
      ]
     },
     "execution_count": 190,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = np.sin(np.arange(20)).reshape(5, 4)\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 209,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 0, 3, 1])"
      ]
     },
     "execution_count": 209,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ind = data.argmax(axis=0)\n",
    "ind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 212,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 82.5 ,  20.  , 113.75,  51.25])"
      ]
     },
     "execution_count": 212,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "time_max = time[ind]\n",
    "time_max"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 214,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])"
      ]
     },
     "execution_count": 214,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data_max = data[ind, range(data.shape[1])]\n",
    "data_max"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此外，还可以通过多个索引向元素进行赋值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 217,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 0, 2, 0, 0])"
      ]
     },
     "execution_count": 217,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(5)\n",
    "a[[1, 3, 4]] = 0\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然而，若索引数组中存在重复索引时，赋值操作将会依次进行，该元素的值为最后赋值的值。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 218,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 1, 3, 3, 4])"
      ]
     },
     "execution_count": 218,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(5)\n",
    "a[[0, 0, 2]] = [1, 2, 3]\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但要注意，如果使用了 `+=` 操作符，则累加操作只会进行一次。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 219,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 1, 3, 3, 4])"
      ]
     },
     "execution_count": 219,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(5)\n",
    "a[[0, 0, 2]] += 1\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "虽然0在索引数组中出现多次，但是第0个元素只会被累加一次。因为 Python 要求表达式 `a += 1` 的语义与 `a = a + 1`一致。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 布尔索引数组\n",
    "\n",
    "当我们使用整数索引数组进行索引时，提供的是所需元素的索引值。布尔数组则不然，需要显式地说明哪些元素需要，哪些不需要。\n",
    "\n",
    "用人话讲，就是布尔索引数组采用一个与被索引数组形状大小一致的布尔数组来进行元素过滤。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 220,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[False, False, False, False],\n",
       "       [False,  True,  True,  True],\n",
       "       [ True,  True,  True,  True]])"
      ]
     },
     "execution_count": 220,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(12).reshape(3, 4)\n",
    "b = a > 4\n",
    "b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 221,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 5,  6,  7,  8,  9, 10, 11])"
      ]
     },
     "execution_count": 221,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[b]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "布尔索引数组的这种特性，尤其适合赋值操作的场景。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 224,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1j"
      ]
     },
     "execution_count": 224,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[a > 4] = 0\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面，我们来看一下如何利用布尔索引数组生成 Mandelbrot 集的图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 231,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0 0 0 ... 0 0 0]\n",
      " [0 0 0 ... 0 0 0]\n",
      " [0 0 0 ... 0 0 0]\n",
      " ...\n",
      " [0 0 0 ... 0 0 0]\n",
      " [0 0 0 ... 0 0 0]\n",
      " [0 0 0 ... 0 0 0]]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7fab3ad51d30>"
      ]
     },
     "execution_count": 231,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 36528 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "def mandelbrot(h, w, maxit=20):\n",
    "    \"\"\"返回一个指定大小(h,w)的 Mandelbrot 分形图像\"\"\"\n",
    "    y, x = np.ogrid[-1.4 : 1.4 : h*1j, -2 : 0.8 : w*1j]\n",
    "    c = x + y * 1j\n",
    "    z = c\n",
    "    divtime = maxit + np.zeros(z.shape, dtype=int)\n",
    "    for i in range(maxit):\n",
    "        z = z ** 2 + c\n",
    "        diverge = z * np.conj(z) > 2 ** 2\n",
    "        div_now = diverge & (divtime == maxit)\n",
    "        divtime[div_now] = i\n",
    "        z[diverge] = 2\n",
    "    return divtime\n",
    "\n",
    "\n",
    "plt.imshow(mandelbrot(400, 400))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另一种使用方式比较接近于整数索引数组，对应于多维数组的每一个轴，可以分别给出一个一维的数组来指示所需要的分片："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 236,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([[ 0,  1,  2,  3],\n",
       "        [ 4,  5,  6,  7],\n",
       "        [ 8,  9, 10, 11]]),\n",
       " array([False,  True,  True]),\n",
       " array([ True, False,  True, False]))"
      ]
     },
     "execution_count": 236,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(12).reshape(3, 4)\n",
    "b1 = np.array([False, True, True])\n",
    "b2 = np.array([True, False, True, False])\n",
    "a, b1, b2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 237,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 4,  5,  6,  7],\n",
       "       [ 8,  9, 10, 11]])"
      ]
     },
     "execution_count": 237,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[b1, :]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 238,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  2],\n",
       "       [ 4,  6],\n",
       "       [ 8, 10]])"
      ]
     },
     "execution_count": 238,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[:, b2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 239,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 4, 10])"
      ]
     },
     "execution_count": 239,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a[b1, b2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意每个一维布尔索引数组的长度必须与对应被索引数组的轴长度相同。如上所示，`b1`的长度为3与`a`的第一个轴长度相同，`b2`的长度为4与`a`的第二个轴长度相同"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ix_() 函数\n",
    "\n",
    "`ix_` 函数可用于对多个向量进行组合维度扩展。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 244,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((4, 1, 1), (1, 3, 1), (1, 1, 5))"
      ]
     },
     "execution_count": 244,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([2, 3, 4, 5])\n",
    "b = np.array([8, 5, 4])\n",
    "c = np.array([5, 4, 6, 8, 3])\n",
    "ax, bx, cx = np.ix_(a, b, c)\n",
    "ax.shape, bx.shape, cx.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这样我们就可以很方便地实现一些向量运算，如 `a+b*c` ，其中`a`、`b`、`c`都是向量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 245,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[42, 34, 50, 66, 26],\n",
       "        [27, 22, 32, 42, 17],\n",
       "        [22, 18, 26, 34, 14]],\n",
       "\n",
       "       [[43, 35, 51, 67, 27],\n",
       "        [28, 23, 33, 43, 18],\n",
       "        [23, 19, 27, 35, 15]],\n",
       "\n",
       "       [[44, 36, 52, 68, 28],\n",
       "        [29, 24, 34, 44, 19],\n",
       "        [24, 20, 28, 36, 16]],\n",
       "\n",
       "       [[45, 37, 53, 69, 29],\n",
       "        [30, 25, 35, 45, 20],\n",
       "        [25, 21, 29, 37, 17]]])"
      ]
     },
     "execution_count": 245,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ax + bx * cx"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 字符串索引\n",
    "\n",
    "详见 [Structured arrays](https://numpy.org/devdocs/user/basics.rec.html#structured-arrays)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 线性代数\n",
    "\n",
    "## 基本操作\n",
    "\n",
    "更多信息请查阅 numpy 文件夹中的 `linalg.py` 文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 246,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 2.],\n",
       "       [3., 4.]])"
      ]
     },
     "execution_count": 246,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.array([[1.0, 2.0], [3.0, 4.0]])\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 247,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 3.],\n",
       "       [2., 4.]])"
      ]
     },
     "execution_count": 247,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.transpose()    # 转置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 249,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2. ,  1. ],\n",
       "       [ 1.5, -0.5]])"
      ]
     },
     "execution_count": 249,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.linalg.inv(a)  # 逆矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 250,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 0.],\n",
       "       [0., 1.]])"
      ]
     },
     "execution_count": 250,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.eye(2)         # 返回 2x2 单位阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 251,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 7., 10.],\n",
       "       [15., 22.]])"
      ]
     },
     "execution_count": 251,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a @ a             # 矩阵乘法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 252,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5.0"
      ]
     },
     "execution_count": 252,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.trace(a)       # 求主对角线元素的和，即矩阵特征值之和"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 253,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-3.],\n",
       "       [ 4.]])"
      ]
     },
     "execution_count": 253,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = np.array([[5.], [7.]])\n",
    "np.linalg.solve(a, y)     # 解线性方程组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 255,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([-0.37228132,  5.37228132]),\n",
       " array([[-0.82456484, -0.41597356],\n",
       "        [ 0.56576746, -0.90937671]]))"
      ]
     },
     "execution_count": 255,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.linalg.eig(a)          # 求特征值和特征向量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 技巧\n",
    "\n",
    "## 自动变形\n",
    "\n",
    "在对数组进行维度调整时，可以省略其中某一轴的长度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 256,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 5, 3)"
      ]
     },
     "execution_count": 256,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.arange(30)\n",
    "b = a.reshape((2, -1, 3))   # 其中 -1 将会被自动计算\n",
    "b.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  向量堆叠\n",
    "\n",
    "如何将一组长度相同的行向量堆叠成一个矩阵？在 MATLAB 这样的操作比较简单，假如 `x` 和 `y` 是两个长度一样的向量，只需用 `m = [x;y]` 即可产生一个矩阵。在 NumPy 中，类似的功能需要通过函数实现 `column_stack`, `dstack`, `hstack` 和 '`vstack`。具体采用哪个函数取决于要沿着哪一个轴堆叠。例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 257,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0, 2, 4, 6, 8],\n",
       "       [0, 1, 2, 3, 4]])"
      ]
     },
     "execution_count": 257,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.arange(0, 10, 2)\n",
    "y = np.arange(0, 5, 1)\n",
    "m = np.vstack([x, y])\n",
    "m"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 258,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4])"
      ]
     },
     "execution_count": 258,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "xy = np.hstack([x, y])\n",
    "xy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 直方图\n",
    "\n",
    "NumPy 的 `histogram` 函数接收一个数组，返回一对向量：数组的直方图、组边向量。注意：`matplotlib` 也有个创建直方图的函数(同MATLAB中的`hist`)，它与 NumPy 的 `histogram` 是不一样的。主要区别是 `pylab.hist` 会自动地画出直方图，而`numpy.histogram`只是返回数据。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 263,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([2.1727921 , 2.41080907, 2.16521854, ..., 2.15062613, 1.61436007,\n",
       "        2.09274213]),\n",
       " (10000,),\n",
       " array([0.00128706, 0.00257412, 0.00257412, 0.00257412, 0.00772237,\n",
       "        0.00900943, 0.01415767, 0.03861183, 0.04247301, 0.03989889,\n",
       "        0.05663068, 0.08623308, 0.13900258, 0.16088261, 0.24454157,\n",
       "        0.30117225, 0.34750645, 0.41700774, 0.51997261, 0.60234451,\n",
       "        0.68214229, 0.69501289, 0.78768128, 0.73619884, 0.79540365,\n",
       "        0.78896834, 0.72976354, 0.7014482 , 0.69501289, 0.60749275,\n",
       "        0.57660329, 0.4478972 , 0.39126652, 0.28186634, 0.24196745,\n",
       "        0.20592975, 0.13385434, 0.10425193, 0.07851072, 0.04762125,\n",
       "        0.0296024 , 0.03217652, 0.01029649, 0.00900943, 0.0064353 ,\n",
       "        0.00772237, 0.00514824, 0.00128706, 0.00257412, 0.00128706]),\n",
       " (50,),\n",
       " array([0.08106893, 0.15876533, 0.23646173, 0.31415813, 0.39185453,\n",
       "        0.46955093, 0.54724733, 0.62494373, 0.70264013, 0.78033653,\n",
       "        0.85803293, 0.93572933, 1.01342573, 1.09112213, 1.16881853,\n",
       "        1.24651493, 1.32421133, 1.40190773, 1.47960413, 1.55730053,\n",
       "        1.63499693, 1.71269333, 1.79038973, 1.86808613, 1.94578253,\n",
       "        2.02347893, 2.10117533, 2.17887173, 2.25656814, 2.33426454,\n",
       "        2.41196094, 2.48965734, 2.56735374, 2.64505014, 2.72274654,\n",
       "        2.80044294, 2.87813934, 2.95583574, 3.03353214, 3.11122854,\n",
       "        3.18892494, 3.26662134, 3.34431774, 3.42201414, 3.49971054,\n",
       "        3.57740694, 3.65510334, 3.73279974, 3.81049614, 3.88819254,\n",
       "        3.96588894]),\n",
       " (51,))"
      ]
     },
     "execution_count": 263,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rg = np.random.default_rng(1)\n",
    "mu, sigma = 2, 0.5\n",
    "v = rg.normal(mu,sigma,10000)        # \n",
    "plt.hist(v, bins=50, density=1)\n",
    "n, bins = np.histogram(v, bins=50, density=True)\n",
    "v, v.shape, n, n.shape, bins, bins.shape"
   ]
  }
 ],
 "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.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
