{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b5684dc2",
   "metadata": {},
   "source": [
    "# 第 8 讲｜NumPy 数组计算：数组操作与向量化\n",
    "\n",
    "**参考教材：** `da_4f_numpy.pdf`  \n",
    "**适用基础：** 已学过变量、列表、条件判断、循环与函数调用。  \n",
    "**本讲目标：** 用 NumPy 数组批量完成数值计算、筛选与统计，并能读懂常见的数据分析代码。\n",
    "\n",
    "NumPy 的核心对象叫 **ndarray（多维数组）**。它与 Python 列表看起来相似，但更擅长对整批同类型数据进行计算。后续学习 pandas 时，很多底层计算思路仍然来自 NumPy。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f562a29b",
   "metadata": {},
   "source": [
    "## 训练路线\n",
    "\n",
    "1. 创建数组，认识维度、形状和数据类型；\n",
    "2. 使用向量化运算替代逐个元素计算；\n",
    "3. 掌握一维、二维索引与切片；\n",
    "4. 使用布尔条件筛选和替换数据；\n",
    "5. 按整体或指定轴完成统计；\n",
    "6. 完成排序、去重、转置与可复现随机抽样。\n",
    "\n",
    "每个知识点均按“示例代码 → 对应解读 → 动手练习”展开。动手练习保留了空白，补全后再运行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c51286a8",
   "metadata": {},
   "source": [
    "## 教材内容取舍\n",
    "\n",
    "本课件保留竞赛数据处理和后续 pandas 学习中最常用的内容：数组创建、属性、类型转换、向量化、索引切片、布尔筛选、条件替换、统计、排序去重、转置和随机数。\n",
    "\n",
    "花式索引、复杂广播、数组二进制文件、完整线性代数和随机游走放在末尾“阅读拓展”，不作为本次必须掌握的主线。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69090f9d",
   "metadata": {},
   "source": [
    "## 1. 导入 NumPy，并用 np.array 创建数组\n",
    "\n",
    "`import numpy as np` 导入 NumPy，并把模块简称为 `np`。这是最常见的标准写法。\n",
    "\n",
    "`np.array(已有序列)` 把列表等序列转换为 NumPy 数组。数组支持对所有元素一次性计算，这叫**向量化运算**。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b64abda",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "87bc15bc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.746198Z",
     "iopub.status.busy": "2026-07-30T07:26:33.745938Z",
     "iopub.status.idle": "2026-07-30T07:26:33.844450Z",
     "shell.execute_reply": "2026-07-30T07:26:33.843940Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Python 列表乘 2： [72, 85, 90, 72, 85, 90]\n",
      "NumPy 数组乘 2： [144 170 180]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "scores_list = [72, 85, 90]\n",
    "scores_array = np.array(scores_list)\n",
    "\n",
    "print(\"Python 列表乘 2：\", scores_list * 2)\n",
    "print(\"NumPy 数组乘 2：\", scores_array * 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cacac89c",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `scores_list * 2` 会把列表内容重复两次，并不会计算每个成绩的两倍。\n",
    "- `scores_array * 2` 会让数组中的每个元素都乘以 2。\n",
    "- 后续代码都沿用简称 `np`，例如 `np.array(...)`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98ae9f55",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "32cde52e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.846173Z",
     "iopub.status.busy": "2026-07-30T07:26:33.846030Z",
     "iopub.status.idle": "2026-07-30T07:26:33.847803Z",
     "shell.execute_reply": "2026-07-30T07:26:33.847364Z"
    }
   },
   "outputs": [],
   "source": [
    "# 把 [68, 75, 92] 转换为 NumPy 数组，再让每个元素加 5\n",
    "# scores = __________\n",
    "# new_scores = __________\n",
    "# print(new_scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4827642e",
   "metadata": {},
   "source": [
    "## 2. 数组属性：ndim、shape 与 dtype\n",
    "\n",
    "NumPy 数组有三个常用属性：\n",
    "\n",
    "- `.ndim`：维度数量；\n",
    "- `.shape`：每个维度的长度，结果用元组表示；\n",
    "- `.dtype`：数组元素的数据类型。\n",
    "\n",
    "它们是属性，末尾不加括号。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e246491c",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "166fa3d5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.849190Z",
     "iopub.status.busy": "2026-07-30T07:26:33.849090Z",
     "iopub.status.idle": "2026-07-30T07:26:33.851423Z",
     "shell.execute_reply": "2026-07-30T07:26:33.850906Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数组：\n",
      "[[120 135 150]\n",
      " [ 98 110 125]]\n",
      "维度数量： 2\n",
      "形状： (2, 3)\n",
      "元素类型： int64\n"
     ]
    }
   ],
   "source": [
    "sales = np.array([[120, 135, 150],\n",
    "                  [98, 110, 125]])\n",
    "\n",
    "print(\"数组：\")\n",
    "print(sales)\n",
    "print(\"维度数量：\", sales.ndim)\n",
    "print(\"形状：\", sales.shape)\n",
    "print(\"元素类型：\", sales.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "370395a6",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- 两层方括号构成二维数组，所以 `sales.ndim` 是 `2`。\n",
    "- `sales.shape` 是 `(2, 3)`：2 行、3 列。\n",
    "- 所有元素都是整数，所以 `dtype` 显示为当前系统采用的整数类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b65ad0c",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "61fbb36b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.852735Z",
     "iopub.status.busy": "2026-07-30T07:26:33.852631Z",
     "iopub.status.idle": "2026-07-30T07:26:33.854249Z",
     "shell.execute_reply": "2026-07-30T07:26:33.853875Z"
    }
   },
   "outputs": [],
   "source": [
    "# 创建一个 3 行 2 列的数组，分别输出 ndim、shape 和 dtype\n",
    "# data = np.array([__________])\n",
    "# print(__________)\n",
    "# print(__________)\n",
    "# print(__________)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6763b327",
   "metadata": {},
   "source": [
    "## 3. 快速创建数组：zeros、ones 与 arange\n",
    "\n",
    "三个常用创建函数：\n",
    "\n",
    "- `np.zeros(形状)`：创建全 0 数组；\n",
    "- `np.ones(形状)`：创建全 1 数组；\n",
    "- `np.arange(起点, 终点, 步长)`：按步长生成数值，**不包含终点**。\n",
    "\n",
    "一维数组的形状可写成整数；二维数组的形状写成 `(行数, 列数)`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a43439e6",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "acbd0d00",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.855472Z",
     "iopub.status.busy": "2026-07-30T07:26:33.855391Z",
     "iopub.status.idle": "2026-07-30T07:26:33.858382Z",
     "shell.execute_reply": "2026-07-30T07:26:33.857885Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4 个 0： [0. 0. 0. 0.]\n",
      "2 行 3 列的 1：\n",
      "[[1. 1. 1.]\n",
      " [1. 1. 1.]]\n",
      "2 到 10 的偶数： [ 2  4  6  8 10]\n"
     ]
    }
   ],
   "source": [
    "zeros_array = np.zeros(4)\n",
    "ones_matrix = np.ones((2, 3))\n",
    "even_numbers = np.arange(2, 11, 2)\n",
    "\n",
    "print(\"4 个 0：\", zeros_array)\n",
    "print(\"2 行 3 列的 1：\")\n",
    "print(ones_matrix)\n",
    "print(\"2 到 10 的偶数：\", even_numbers)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cf0ea49",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `np.zeros(4)` 得到长度为 4 的一维数组。\n",
    "- `np.ones((2, 3))` 得到 2 行 3 列的二维数组。\n",
    "- `np.arange(2, 11, 2)` 从 2 开始，每次加 2；终点写 11 才能包含 10。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aafe862e",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ffa9217f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.859888Z",
     "iopub.status.busy": "2026-07-30T07:26:33.859789Z",
     "iopub.status.idle": "2026-07-30T07:26:33.861445Z",
     "shell.execute_reply": "2026-07-30T07:26:33.861052Z"
    }
   },
   "outputs": [],
   "source": [
    "# 创建一个 3 行 2 列的全 0 数组\n",
    "# zero_table = __________\n",
    "# 创建 5、10、15、20 组成的一维数组\n",
    "# multiples = __________\n",
    "# print(zero_table)\n",
    "# print(multiples)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ce874f6",
   "metadata": {},
   "source": [
    "## 4. 用 astype 转换数组的数据类型\n",
    "\n",
    "`.astype(目标类型)` 返回一个转换类型后的新数组。常用目标类型包括 `int`、`float` 和 `str`。\n",
    "\n",
    "从小数转换为整数时，小数部分会被直接去掉，不会自动四舍五入。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "429f8950",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "5fe840b4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.862736Z",
     "iopub.status.busy": "2026-07-30T07:26:33.862655Z",
     "iopub.status.idle": "2026-07-30T07:26:33.865254Z",
     "shell.execute_reply": "2026-07-30T07:26:33.864768Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原数组： [12.8 19.5 25.2]\n",
      "转换后的数组： [12 19 25]\n",
      "原数组类型： float64\n",
      "转换后类型： int64\n"
     ]
    }
   ],
   "source": [
    "prices = np.array([12.8, 19.5, 25.2])\n",
    "integer_prices = prices.astype(int)\n",
    "\n",
    "print(\"原数组：\", prices)\n",
    "print(\"转换后的数组：\", integer_prices)\n",
    "print(\"原数组类型：\", prices.dtype)\n",
    "print(\"转换后类型：\", integer_prices.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7da1aaf2",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `prices.astype(int)` 创建整数数组 `integer_prices`。\n",
    "- `12.8`、`19.5`、`25.2` 分别变为 `12`、`19`、`25`。\n",
    "- `prices` 本身没有被修改，因为 `astype` 返回的是新数组。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1d1d1ba",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "21b2de47",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.866640Z",
     "iopub.status.busy": "2026-07-30T07:26:33.866537Z",
     "iopub.status.idle": "2026-07-30T07:26:33.868225Z",
     "shell.execute_reply": "2026-07-30T07:26:33.867842Z"
    }
   },
   "outputs": [],
   "source": [
    "# 把字符串数组转换为浮点数数组\n",
    "# text_numbers = np.array([\"3.5\", \"4.0\", \"6.2\"])\n",
    "# numbers = __________\n",
    "# print(numbers)\n",
    "# print(numbers.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4d2e3b2",
   "metadata": {},
   "source": [
    "## 5. 向量化算术与逐元素比较\n",
    "\n",
    "数组与一个数做 `+`、`-`、`*`、`/` 运算时，这个运算会应用到每个元素。两个形状相同的数组也可以逐元素运算。\n",
    "\n",
    "比较运算 `>`、`>=`、`<`、`<=`、`==`、`!=` 会逐元素返回 `True` 或 `False`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8fd281b",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "21818e12",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.869685Z",
     "iopub.status.busy": "2026-07-30T07:26:33.869591Z",
     "iopub.status.idle": "2026-07-30T07:26:33.872148Z",
     "shell.execute_reply": "2026-07-30T07:26:33.871683Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调整后成绩： [65 75 87 90]\n",
      "是否及格： [ True  True  True  True]\n",
      "统一提高 10%： [71.5 82.5 95.7 99. ]\n"
     ]
    }
   ],
   "source": [
    "original = np.array([60, 72, 85, 90])\n",
    "bonus = np.array([5, 3, 2, 0])\n",
    "\n",
    "adjusted = original + bonus\n",
    "passed = adjusted >= 60\n",
    "\n",
    "print(\"调整后成绩：\", adjusted)\n",
    "print(\"是否及格：\", passed)\n",
    "print(\"统一提高 10%：\", adjusted * 1.1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2575715c",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `original + bonus` 把两个数组对应位置的元素相加。\n",
    "- `adjusted >= 60` 对每个成绩分别比较，得到布尔数组。\n",
    "- `adjusted * 1.1` 一次完成所有成绩的比例调整，不需要写循环。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "458542b3",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "c8075ed6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.873526Z",
     "iopub.status.busy": "2026-07-30T07:26:33.873418Z",
     "iopub.status.idle": "2026-07-30T07:26:33.875390Z",
     "shell.execute_reply": "2026-07-30T07:26:33.874862Z"
    }
   },
   "outputs": [],
   "source": [
    "# 已知单价和数量，逐元素计算每种商品的金额\n",
    "# prices = np.array([8, 12, 15])\n",
    "# quantities = np.array([3, 2, 4])\n",
    "# amounts = __________\n",
    "# print(amounts)\n",
    "# 判断每种商品的金额是否至少为 30\n",
    "# print(__________)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6dde27c2",
   "metadata": {},
   "source": [
    "## 6. 逐元素函数：sqrt 与 maximum\n",
    "\n",
    "NumPy 中很多函数会逐元素处理数组：\n",
    "\n",
    "- `np.sqrt(数组)`：计算每个元素的平方根；\n",
    "- `np.maximum(数组1, 数组2)`：比较对应位置，保留较大值。\n",
    "\n",
    "这类快速处理数组元素的函数也称为通用函数（ufunc）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "76d3d168",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "95ed4680",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.876865Z",
     "iopub.status.busy": "2026-07-30T07:26:33.876754Z",
     "iopub.status.idle": "2026-07-30T07:26:33.879404Z",
     "shell.execute_reply": "2026-07-30T07:26:33.878904Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "平方根： [1. 2. 3. 4.]\n",
      "每个位置的较大值： [85 95 78]\n"
     ]
    }
   ],
   "source": [
    "squares = np.array([1, 4, 9, 16])\n",
    "plan_a = np.array([80, 95, 70])\n",
    "plan_b = np.array([85, 90, 78])\n",
    "\n",
    "print(\"平方根：\", np.sqrt(squares))\n",
    "print(\"每个位置的较大值：\", np.maximum(plan_a, plan_b))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da00f328",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `np.sqrt(squares)` 依次得到 `1、2、3、4`。\n",
    "- `np.maximum(plan_a, plan_b)` 不是只返回一个最大值，而是逐位置比较两个数组。\n",
    "- 示例结果的三个元素分别来自两套方案中对应位置的较大值。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a19db92b",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "833a0167",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.880788Z",
     "iopub.status.busy": "2026-07-30T07:26:33.880681Z",
     "iopub.status.idle": "2026-07-30T07:26:33.882388Z",
     "shell.execute_reply": "2026-07-30T07:26:33.881995Z"
    }
   },
   "outputs": [],
   "source": [
    "# 计算 [25, 36, 49] 中每个元素的平方根\n",
    "# values = np.array([25, 36, 49])\n",
    "# roots = __________\n",
    "# print(roots)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "919f526b",
   "metadata": {},
   "source": [
    "## 7. 一维数组的索引与切片\n",
    "\n",
    "一维数组的索引和列表相似：第一个位置是 `0`，最后一个位置可写 `-1`。\n",
    "\n",
    "切片写作 `[起点:终点]`，包含起点、不包含终点；省略起点或终点分别表示从开头或取到末尾。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "033798fd",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "8e70fc24",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.884146Z",
     "iopub.status.busy": "2026-07-30T07:26:33.884013Z",
     "iopub.status.idle": "2026-07-30T07:26:33.886658Z",
     "shell.execute_reply": "2026-07-30T07:26:33.886252Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一个元素： 55\n",
      "最后一个元素： 96\n",
      "索引 1 到 3： [68 72 85]\n",
      "前三个元素： [55 68 72]\n",
      "索引 3 到末尾： [85 91 96]\n"
     ]
    }
   ],
   "source": [
    "scores = np.array([55, 68, 72, 85, 91, 96])\n",
    "\n",
    "print(\"第一个元素：\", scores[0])\n",
    "print(\"最后一个元素：\", scores[-1])\n",
    "print(\"索引 1 到 3：\", scores[1:4])\n",
    "print(\"前三个元素：\", scores[:3])\n",
    "print(\"索引 3 到末尾：\", scores[3:])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03e637a1",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `scores[0]` 和 `scores[-1]` 各取一个元素。\n",
    "- `scores[1:4]` 取得索引 `1、2、3` 的元素，不包含索引 4。\n",
    "- `scores[:3]` 与 `scores[0:3]` 等价；`scores[3:]` 一直取到末尾。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e06d5fbd",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "42a77474",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.887983Z",
     "iopub.status.busy": "2026-07-30T07:26:33.887889Z",
     "iopub.status.idle": "2026-07-30T07:26:33.889658Z",
     "shell.execute_reply": "2026-07-30T07:26:33.889218Z"
    }
   },
   "outputs": [],
   "source": [
    "# 从下列数组中取出 20、30、40\n",
    "# values = np.array([10, 20, 30, 40, 50])\n",
    "# selected = __________\n",
    "# print(selected)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67d1fbed",
   "metadata": {},
   "source": [
    "## 8. 切片是视图：需要独立数据时使用 copy\n",
    "\n",
    "NumPy 的普通切片通常是原数组的**视图**：修改切片，也会修改原数组对应位置。\n",
    "\n",
    "如果需要一份互不影响的数据，使用 `.copy()` 创建副本。这是 NumPy 中非常重要的易错点。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4042b19",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "8be0a721",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.891049Z",
     "iopub.status.busy": "2026-07-30T07:26:33.890949Z",
     "iopub.status.idle": "2026-07-30T07:26:33.893450Z",
     "shell.execute_reply": "2026-07-30T07:26:33.893008Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "修改视图后的原数组： [ 10 999  30  40  50]\n",
      "修改副本后的原数组： [ 10 999  30  40  50]\n",
      "独立副本： [-1 30 40]\n"
     ]
    }
   ],
   "source": [
    "values = np.array([10, 20, 30, 40, 50])\n",
    "\n",
    "view_part = values[1:4]\n",
    "view_part[0] = 999\n",
    "print(\"修改视图后的原数组：\", values)\n",
    "\n",
    "copy_part = values[1:4].copy()\n",
    "copy_part[0] = -1\n",
    "print(\"修改副本后的原数组：\", values)\n",
    "print(\"独立副本：\", copy_part)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0add9bb",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `view_part` 指向 `values` 的一部分，因此 `view_part[0] = 999` 让原数组索引 1 也变成 999。\n",
    "- `values[1:4].copy()` 创建独立副本。\n",
    "- 修改 `copy_part` 后，原数组不再跟着改变。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5347dfd",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "a70e5133",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.894745Z",
     "iopub.status.busy": "2026-07-30T07:26:33.894655Z",
     "iopub.status.idle": "2026-07-30T07:26:33.896347Z",
     "shell.execute_reply": "2026-07-30T07:26:33.895957Z"
    }
   },
   "outputs": [],
   "source": [
    "# 取出中间三个数作为独立副本，再把副本第一个元素改为 0\n",
    "# data = np.array([5, 10, 15, 20, 25])\n",
    "# middle = __________\n",
    "# middle[0] = 0\n",
    "# print(data)    # 原数组应保持不变\n",
    "# print(middle)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db827750",
   "metadata": {},
   "source": [
    "## 9. 二维数组索引、切片与轴\n",
    "\n",
    "二维数组使用 `[行索引, 列索引]` 定位一个元素。冒号 `:` 表示该方向全部保留。\n",
    "\n",
    "在统计中，`axis=0` 表示沿行方向汇总，结果对应各列；`axis=1` 表示沿列方向汇总，结果对应各行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "769c5d1c",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "1057e6ec",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.897535Z",
     "iopub.status.busy": "2026-07-30T07:26:33.897460Z",
     "iopub.status.idle": "2026-07-30T07:26:33.899952Z",
     "shell.execute_reply": "2026-07-30T07:26:33.899583Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第 2 行第 3 列： 125\n",
      "第 1 行： [120 135 150]\n",
      "第 2 列： [135 110 138]\n",
      "后两列：\n",
      "[[135 150]\n",
      " [110 125]\n",
      " [138 160]]\n"
     ]
    }
   ],
   "source": [
    "sales = np.array([[120, 135, 150],\n",
    "                  [98, 110, 125],\n",
    "                  [140, 138, 160]])\n",
    "\n",
    "print(\"第 2 行第 3 列：\", sales[1, 2])\n",
    "print(\"第 1 行：\", sales[0, :])\n",
    "print(\"第 2 列：\", sales[:, 1])\n",
    "print(\"后两列：\")\n",
    "print(sales[:, 1:])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46aec646",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `sales[1, 2]` 使用从 0 开始的索引，取得第 2 行第 3 列的 `125`。\n",
    "- `sales[0, :]` 保留第 1 行的所有列。\n",
    "- `sales[:, 1]` 保留所有行，只取第 2 列。\n",
    "- `sales[:, 1:]` 取得所有行的后两列。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f3eb021",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "f375e9e6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.901249Z",
     "iopub.status.busy": "2026-07-30T07:26:33.901166Z",
     "iopub.status.idle": "2026-07-30T07:26:33.902947Z",
     "shell.execute_reply": "2026-07-30T07:26:33.902549Z"
    }
   },
   "outputs": [],
   "source": [
    "# 取出下列数组的前两行、后两列\n",
    "# table = np.array([[1, 2, 3],\n",
    "#                   [4, 5, 6],\n",
    "#                   [7, 8, 9]])\n",
    "# result = __________\n",
    "# print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3441470",
   "metadata": {},
   "source": [
    "## 10. 布尔数组与条件筛选\n",
    "\n",
    "数组与条件比较后会得到布尔数组，也叫**布尔掩码**。把掩码放进方括号中，可以取出条件为 `True` 的元素。\n",
    "\n",
    "布尔筛选返回的是符合条件的数据，而不仅是 `True` 和 `False`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2cd49ec7",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "bbdbc85c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.904228Z",
     "iopub.status.busy": "2026-07-30T07:26:33.904144Z",
     "iopub.status.idle": "2026-07-30T07:26:33.906534Z",
     "shell.execute_reply": "2026-07-30T07:26:33.906091Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "布尔掩码： [False  True  True  True  True  True]\n",
      "及格成绩： [68 72 85 91 96]\n",
      "90 分以上： [91 96]\n"
     ]
    }
   ],
   "source": [
    "scores = np.array([55, 68, 72, 85, 91, 96])\n",
    "passed_mask = scores >= 60\n",
    "passed_scores = scores[passed_mask]\n",
    "\n",
    "print(\"布尔掩码：\", passed_mask)\n",
    "print(\"及格成绩：\", passed_scores)\n",
    "print(\"90 分以上：\", scores[scores >= 90])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "661f764e",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `scores >= 60` 为每个成绩生成一个 `True` 或 `False`。\n",
    "- `scores[passed_mask]` 只保留掩码中 `True` 对应的成绩。\n",
    "- 条件也可以直接写进方括号，如 `scores[scores >= 90]`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a675af8",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "35328d9a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.907840Z",
     "iopub.status.busy": "2026-07-30T07:26:33.907743Z",
     "iopub.status.idle": "2026-07-30T07:26:33.909603Z",
     "shell.execute_reply": "2026-07-30T07:26:33.909167Z"
    }
   },
   "outputs": [],
   "source": [
    "# 筛选出所有大于 100 的销售额\n",
    "# sales = np.array([80, 120, 95, 150, 110])\n",
    "# mask = __________\n",
    "# selected = __________\n",
    "# print(selected)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01b094c7",
   "metadata": {},
   "source": [
    "## 11. 组合多个条件：&、| 与 ~\n",
    "\n",
    "NumPy 数组的多个条件要使用：\n",
    "\n",
    "- `&`：并且；\n",
    "- `|`：或者；\n",
    "- `~`：取反。\n",
    "\n",
    "每个比较条件都要放在括号中。不要用 Python 的 `and`、`or` 直接连接数组条件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a055e8d6",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "478ff575",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.910917Z",
     "iopub.status.busy": "2026-07-30T07:26:33.910831Z",
     "iopub.status.idle": "2026-07-30T07:26:33.913389Z",
     "shell.execute_reply": "2026-07-30T07:26:33.912964Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "70 到 89 分： [72 85]\n",
      "低于 60 或至少 90： [55 91 96]\n",
      "不及格成绩： [55]\n"
     ]
    }
   ],
   "source": [
    "scores = np.array([55, 68, 72, 85, 91, 96])\n",
    "\n",
    "middle_mask = (scores >= 70) & (scores < 90)\n",
    "edge_mask = (scores < 60) | (scores >= 90)\n",
    "not_passed_mask = ~(scores >= 60)\n",
    "\n",
    "print(\"70 到 89 分：\", scores[middle_mask])\n",
    "print(\"低于 60 或至少 90：\", scores[edge_mask])\n",
    "print(\"不及格成绩：\", scores[not_passed_mask])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a06f5dca",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `(scores >= 70) & (scores < 90)` 要求两个条件同时成立。\n",
    "- `(scores < 60) | (scores >= 90)` 只要求至少一个条件成立。\n",
    "- `~(scores >= 60)` 把布尔结果反转，得到“不及格”的条件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e27e42d",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "68571fd6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.914791Z",
     "iopub.status.busy": "2026-07-30T07:26:33.914698Z",
     "iopub.status.idle": "2026-07-30T07:26:33.916573Z",
     "shell.execute_reply": "2026-07-30T07:26:33.916027Z"
    }
   },
   "outputs": [],
   "source": [
    "# 筛选 100 到 200（含两端）之间的销售额\n",
    "# sales = np.array([80, 100, 135, 200, 240])\n",
    "# mask = __________\n",
    "# result = __________\n",
    "# print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6dca903",
   "metadata": {},
   "source": [
    "## 12. 用 np.where 按条件选择或替换\n",
    "\n",
    "`np.where(条件, 条件为真时的值, 条件为假时的值)` 会逐元素判断并生成一个新数组。\n",
    "\n",
    "它适合分类标记、缺失值替代和上下限处理；原数组不会被自动修改。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d4e5ad4",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "98b2b611",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.917859Z",
     "iopub.status.busy": "2026-07-30T07:26:33.917754Z",
     "iopub.status.idle": "2026-07-30T07:26:33.920222Z",
     "shell.execute_reply": "2026-07-30T07:26:33.919863Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "分类标签： ['不及格' '及格' '及格' '及格' '及格']\n",
      "最高按 90 计： [55 68 72 85 90]\n",
      "原成绩： [55 68 72 85 91]\n"
     ]
    }
   ],
   "source": [
    "scores = np.array([55, 68, 72, 85, 91])\n",
    "labels = np.where(scores >= 60, \"及格\", \"不及格\")\n",
    "capped_scores = np.where(scores > 90, 90, scores)\n",
    "\n",
    "print(\"分类标签：\", labels)\n",
    "print(\"最高按 90 计：\", capped_scores)\n",
    "print(\"原成绩：\", scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a8eb113",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- 第一个 `np.where` 对每个成绩生成“及格”或“不及格”标签。\n",
    "- 第二个 `np.where` 把超过 90 的值改为 90，其余位置保留原值。\n",
    "- 两次操作都返回新数组，所以最后打印的 `scores` 保持不变。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55a5b5d2",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "d4239819",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.921516Z",
     "iopub.status.busy": "2026-07-30T07:26:33.921421Z",
     "iopub.status.idle": "2026-07-30T07:26:33.923073Z",
     "shell.execute_reply": "2026-07-30T07:26:33.922706Z"
    }
   },
   "outputs": [],
   "source": [
    "# 把负数替换为 0，其他值保持不变\n",
    "# changes = np.array([3, -2, 5, -1, 0])\n",
    "# cleaned = __________\n",
    "# print(cleaned)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ee52209",
   "metadata": {},
   "source": [
    "## 13. 统计汇总：sum、mean、min、max 与 argmax\n",
    "\n",
    "数组常用统计方法：`.sum()` 求和、`.mean()` 求平均值、`.min()` 求最小值、`.max()` 求最大值、`.argmax()` 返回最大值第一次出现的位置索引。\n",
    "\n",
    "二维数组可传入 `axis=0` 或 `axis=1`，控制按列还是按行得到结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c672ce21",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "50aa9e95",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.924267Z",
     "iopub.status.busy": "2026-07-30T07:26:33.924189Z",
     "iopub.status.idle": "2026-07-30T07:26:33.926981Z",
     "shell.execute_reply": "2026-07-30T07:26:33.926633Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "全部元素总和： 1176\n",
      "全部元素平均值： 130.66666666666666\n",
      "各列平均值： [119.33333333 127.66666667 145.        ]\n",
      "各行总和： [405 333 438]\n",
      "最大值： 160\n",
      "最大值的一维位置索引： 8\n"
     ]
    }
   ],
   "source": [
    "sales = np.array([[120, 135, 150],\n",
    "                  [98, 110, 125],\n",
    "                  [140, 138, 160]])\n",
    "\n",
    "print(\"全部元素总和：\", sales.sum())\n",
    "print(\"全部元素平均值：\", sales.mean())\n",
    "print(\"各列平均值：\", sales.mean(axis=0))\n",
    "print(\"各行总和：\", sales.sum(axis=1))\n",
    "print(\"最大值：\", sales.max())\n",
    "print(\"最大值的一维位置索引：\", sales.argmax())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0dc732c",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- 不写 `axis` 时，`sum` 和 `mean` 汇总所有元素。\n",
    "- `sales.mean(axis=0)` 沿行方向汇总，得到每一列的平均值。\n",
    "- `sales.sum(axis=1)` 沿列方向汇总，得到每一行的总和。\n",
    "- `sales.argmax()` 先按一维顺序看数组，返回最大值 `160` 的位置索引。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec0dd656",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "d053a2ba",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.928327Z",
     "iopub.status.busy": "2026-07-30T07:26:33.928237Z",
     "iopub.status.idle": "2026-07-30T07:26:33.930015Z",
     "shell.execute_reply": "2026-07-30T07:26:33.929606Z"
    }
   },
   "outputs": [],
   "source": [
    "# 计算每位学生（三行）的平均成绩，并找出全表最低分\n",
    "# scores = np.array([[80, 85, 90],\n",
    "#                    [70, 75, 78],\n",
    "#                    [92, 88, 95]])\n",
    "# student_means = __________\n",
    "# lowest = __________\n",
    "# print(student_means)\n",
    "# print(lowest)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b22e0954",
   "metadata": {},
   "source": [
    "## 14. 布尔统计：sum、any 与 all\n",
    "\n",
    "在布尔数组中，`True` 可按 1 计算，`False` 可按 0 计算：\n",
    "\n",
    "- `.sum()`：统计满足条件的元素个数；\n",
    "- `.any()`：是否至少有一个 `True`；\n",
    "- `.all()`：是否全部为 `True`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ffa3944f",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "a3c9cc81",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.931404Z",
     "iopub.status.busy": "2026-07-30T07:26:33.931302Z",
     "iopub.status.idle": "2026-07-30T07:26:33.934072Z",
     "shell.execute_reply": "2026-07-30T07:26:33.933602Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "及格人数： 4\n",
      "是否有人超过 90 分： True\n",
      "是否全部及格： False\n"
     ]
    }
   ],
   "source": [
    "scores = np.array([55, 68, 72, 85, 91])\n",
    "passed = scores >= 60\n",
    "\n",
    "print(\"及格人数：\", passed.sum())\n",
    "print(\"是否有人超过 90 分：\", (scores > 90).any())\n",
    "print(\"是否全部及格：\", passed.all())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3217635",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `passed.sum()` 把四个 `True` 计为 4，得到及格人数。\n",
    "- `(scores > 90).any()` 检查是否至少有一个成绩超过 90。\n",
    "- `passed.all()` 检查是否每个成绩都及格；因为有 55，所以结果为 `False`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83081263",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "34d18436",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.935392Z",
     "iopub.status.busy": "2026-07-30T07:26:33.935298Z",
     "iopub.status.idle": "2026-07-30T07:26:33.937007Z",
     "shell.execute_reply": "2026-07-30T07:26:33.936596Z"
    }
   },
   "outputs": [],
   "source": [
    "# 统计大于等于 100 的销售额有几个，并判断是否全部为正数\n",
    "# sales = np.array([80, 120, 95, 150, 110])\n",
    "# count = __________\n",
    "# all_positive = __________\n",
    "# print(count)\n",
    "# print(all_positive)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7af4ac7b",
   "metadata": {},
   "source": [
    "## 15. 排序与去重：np.sort 和 np.unique\n",
    "\n",
    "`np.sort(数组)` 返回升序排列后的新数组，原数组保持不变。\n",
    "\n",
    "`np.unique(数组)` 返回已经排序的唯一值，可用于查看分类值或去掉重复数字。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4026309b",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "1b5ba0f5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.938262Z",
     "iopub.status.busy": "2026-07-30T07:26:33.938186Z",
     "iopub.status.idle": "2026-07-30T07:26:33.947863Z",
     "shell.execute_reply": "2026-07-30T07:26:33.947326Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "排序结果： [1 1 2 2 3 3 5]\n",
      "唯一值： [1 2 3 5]\n",
      "原数组： [3 1 3 2 5 2 1]\n"
     ]
    }
   ],
   "source": [
    "values = np.array([3, 1, 3, 2, 5, 2, 1])\n",
    "sorted_values = np.sort(values)\n",
    "unique_values = np.unique(values)\n",
    "\n",
    "print(\"排序结果：\", sorted_values)\n",
    "print(\"唯一值：\", unique_values)\n",
    "print(\"原数组：\", values)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9cce890",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `np.sort(values)` 得到升序的新数组 `sorted_values`。\n",
    "- `np.unique(values)` 删除重复值，并按升序返回结果。\n",
    "- 最后打印 `values` 可以确认两个函数都没有修改原数组。\n",
    "- 数组方法 `values.sort()` 会直接修改原数组，本讲优先使用更容易保留原数据的 `np.sort`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ac1143c",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "3c7c8d82",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.949307Z",
     "iopub.status.busy": "2026-07-30T07:26:33.949196Z",
     "iopub.status.idle": "2026-07-30T07:26:33.951065Z",
     "shell.execute_reply": "2026-07-30T07:26:33.950592Z"
    }
   },
   "outputs": [],
   "source": [
    "# 对下列成绩升序排列，并找出不重复的成绩\n",
    "# scores = np.array([80, 90, 80, 75, 90, 88])\n",
    "# sorted_scores = __________\n",
    "# unique_scores = __________\n",
    "# print(sorted_scores)\n",
    "# print(unique_scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "65ca4b6a",
   "metadata": {},
   "source": [
    "## 16. 二维数组转置：T\n",
    "\n",
    "二维数组的 `.T` 属性用于转置，即把行和列交换。原来形状为 `(行数, 列数)`，转置后形状变为 `(列数, 行数)`。\n",
    "\n",
    "`.T` 是属性，末尾不加括号。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ff01a7a",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "01ee4691",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.952376Z",
     "iopub.status.busy": "2026-07-30T07:26:33.952268Z",
     "iopub.status.idle": "2026-07-30T07:26:33.954592Z",
     "shell.execute_reply": "2026-07-30T07:26:33.954170Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原数组：\n",
      "[[1 2 3]\n",
      " [4 5 6]]\n",
      "原形状： (2, 3)\n",
      "转置后：\n",
      "[[1 4]\n",
      " [2 5]\n",
      " [3 6]]\n",
      "转置后形状： (3, 2)\n"
     ]
    }
   ],
   "source": [
    "table = np.array([[1, 2, 3],\n",
    "                  [4, 5, 6]])\n",
    "transposed = table.T\n",
    "\n",
    "print(\"原数组：\")\n",
    "print(table)\n",
    "print(\"原形状：\", table.shape)\n",
    "print(\"转置后：\")\n",
    "print(transposed)\n",
    "print(\"转置后形状：\", transposed.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae7564fc",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `table` 是 2 行 3 列，形状为 `(2, 3)`。\n",
    "- `table.T` 把每一列变成一行，结果是 3 行 2 列。\n",
    "- 转置常用于调整数据方向，也为后续矩阵计算做准备。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0baf9ed",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "d300fa16",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.956007Z",
     "iopub.status.busy": "2026-07-30T07:26:33.955902Z",
     "iopub.status.idle": "2026-07-30T07:26:33.957597Z",
     "shell.execute_reply": "2026-07-30T07:26:33.957179Z"
    }
   },
   "outputs": [],
   "source": [
    "# 把一个 3 行 2 列数组转置为 2 行 3 列\n",
    "# data = np.array([[10, 20],\n",
    "#                  [30, 40],\n",
    "#                  [50, 60]])\n",
    "# result = __________\n",
    "# print(result)\n",
    "# print(result.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dc087b3",
   "metadata": {},
   "source": [
    "## 17. 可复现随机数：default_rng 与 integers\n",
    "\n",
    "`np.random.default_rng(种子)` 创建随机数生成器。使用相同种子重新运行时，会得到相同的随机结果，便于检查代码和复现实验。\n",
    "\n",
    "生成器的 `.integers(下限, 上限, size=数量)` 生成随机整数，包含下限、不包含上限。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c1fd2a9",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "663dd14f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:33.958842Z",
     "iopub.status.busy": "2026-07-30T07:26:33.958763Z",
     "iopub.status.idle": "2026-07-30T07:26:34.080905Z",
     "shell.execute_reply": "2026-07-30T07:26:34.080320Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "模拟投掷 10 次骰子："
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " [6 2 1 4 3 3 1 3 4 3]\n",
      "平均点数： 3.0\n",
      "出现 6 的次数： 1\n"
     ]
    }
   ],
   "source": [
    "rng = np.random.default_rng(2026)\n",
    "sample = rng.integers(1, 7, size=10)\n",
    "\n",
    "print(\"模拟投掷 10 次骰子：\", sample)\n",
    "print(\"平均点数：\", sample.mean())\n",
    "print(\"出现 6 的次数：\", (sample == 6).sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a5bd746",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `default_rng(2026)` 中的 `2026` 是随机种子，用来固定本例结果。\n",
    "- `integers(1, 7, size=10)` 生成 10 个从 1 到 6 的整数；上限 7 不会出现。\n",
    "- 后两行复用了已经学过的 `mean`、比较运算和布尔数组 `sum`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "225fbaf7",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "c1afa526",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T07:26:34.082812Z",
     "iopub.status.busy": "2026-07-30T07:26:34.082650Z",
     "iopub.status.idle": "2026-07-30T07:26:34.084852Z",
     "shell.execute_reply": "2026-07-30T07:26:34.084242Z"
    }
   },
   "outputs": [],
   "source": [
    "# 使用种子 100，模拟生成 8 个 50 到 100（含 100）的整数成绩\n",
    "# rng = __________\n",
    "# scores = __________\n",
    "# print(scores)\n",
    "# print(scores.mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a6b811b",
   "metadata": {},
   "source": [
    "## 教材中的阅读拓展\n",
    "\n",
    "以下内容在 `da_4f_numpy.pdf` 中有介绍，但不列入本次必须掌握范围：\n",
    "\n",
    "- **花式索引**：用整数数组一次选择多个指定位置；\n",
    "- **复杂广播**：让不同形状的数组在满足规则时参与逐元素运算；\n",
    "- **矩阵乘法和线性代数**：`@`、`np.dot`、逆矩阵、特征值等；\n",
    "- **NumPy 专用文件**：`np.save`、`np.load`、`np.savez`；\n",
    "- **随机游走与复杂概率分布**：需要更多统计背景后再学习。\n",
    "\n",
    "当前阶段先确保能正确创建、筛选和汇总数组。遇到不同形状的数组运算时，应先检查 `.shape`，不要依赖猜测。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d876c085",
   "metadata": {},
   "source": [
    "## 离场检验\n",
    "\n",
    "1. `np.arange(1, 6)` 是否包含 6？\n",
    "2. 一个数组的 `.shape` 是 `(4, 3)`，它有几行几列？\n",
    "3. 为什么修改普通切片有时会影响原数组？怎样避免？\n",
    "4. NumPy 中两个数组条件为什么要用 `&`，而不是 `and`？\n",
    "5. 对二维数组执行 `sum(axis=1)`，得到的是每行还是每列的和？\n",
    "6. `np.sort(arr)` 与 `arr.sort()` 对原数组的影响有什么不同？\n",
    "\n",
    "<details>\n",
    "<summary>点击查看参考答案</summary>\n",
    "\n",
    "1. 不包含；`arange` 不包含终点。\n",
    "2. 4 行 3 列。\n",
    "3. 普通切片通常是原数组的视图；需要独立数据时使用 `.copy()`。\n",
    "4. `&` 会逐元素组合布尔数组；`and` 不能直接完成这种逐元素判断。每个比较条件都要加括号。\n",
    "5. 每行的和。\n",
    "6. `np.sort(arr)` 返回排序后的新数组；`arr.sort()` 直接修改原数组。\n",
    "\n",
    "</details>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (skills)",
   "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.14.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
