{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d3d109cf",
   "metadata": {},
   "source": [
    "# 第 10 讲｜pandas 数据读写：导入、导出与常见文件格式\n",
    "\n",
    "**参考教材：** `da_6f_read.pdf`  \n",
    "**主要课件：** 本 Notebook  \n",
    "**配套文件：** `示例数据` 文件夹  \n",
    "**适用基础：** 已学过文件路径、文本文件、JSON、pandas Series 和 DataFrame。  \n",
    "**本讲目标：** 使用 pandas 把 CSV、TSV、JSON、Excel 和 HTML 表格读取为 DataFrame，并能控制表头、分隔符、列、类型、日期、缺失值和输出格式。\n",
    "\n",
    "数据分析通常从“把外部数据正确读入表格”开始。读取成功并不代表读取正确；列名、数据类型、缺失值和日期是否符合预期，都需要主动检查。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cdac39b",
   "metadata": {},
   "source": [
    "## 训练路线\n",
    "\n",
    "1. 用 `read_csv` 读取最常见的表格文本；\n",
    "2. 处理无表头、不同分隔符、说明行和自定义缺失标记；\n",
    "3. 只读取需要的行列，并控制索引、类型和日期；\n",
    "4. 用 `chunksize` 分块处理较大文件；\n",
    "5. 把 DataFrame 导出为 CSV、JSON 和 Excel；\n",
    "6. 从 JSON、Excel 和 HTML 表格恢复 DataFrame。\n",
    "\n",
    "每个知识点均按“示例代码 → 对应解读 → 动手练习”展开。练习单元格保留空白，补全后再运行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "858680b9",
   "metadata": {},
   "source": [
    "## 运行方式与教材取舍\n",
    "\n",
    "本 Notebook 直接读取同一章节中 `示例数据` 文件夹里的真实文件。请从本章节文件夹打开 Notebook，并按顺序运行单元格。\n",
    "\n",
    "第一段示例会把 `\"示例数据/\"` 保存到变量 `data_dir`。后面的代码使用 `data_dir + \"文件名\"` 拼出相对路径。例如：\n",
    "\n",
    "```python\n",
    "data_dir + \"城市销量.csv\"\n",
    "```\n",
    "\n",
    "主线保留竞赛和日常数据处理中常用的 CSV、TSV、JSON、Excel、HTML 表格及重要参数。手工 `csv` 解析、XML、pickle、Parquet、HDF5、Web API 和数据库放在末尾阅读拓展，避免引入网络、数据库或额外存储服务依赖。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b44c95e",
   "metadata": {},
   "source": [
    "## 1. 用 pd.read_csv 读取 CSV 表格\n",
    "\n",
    "`pd.read_csv(文件路径)` 读取逗号分隔文本并返回 DataFrame。\n",
    "\n",
    "本节先定义 `data_dir = \"示例数据/\"`，它只是保存文件夹名称的字符串变量。`data_dir + \"城市销量.csv\"` 得到完整的相对路径。`encoding=\"utf-8\"` 指定文本编码，适合本章提供的中文 CSV 文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7d31659",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21327ddb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:17.701770Z",
     "iopub.status.busy": "2026-07-30T13:18:17.701589Z",
     "iopub.status.idle": "2026-07-30T13:18:18.692806Z",
     "shell.execute_reply": "2026-07-30T13:18:18.692127Z"
    }
   },
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "data_dir = \"示例数据/\"\n",
    "sales = pd.read_csv(\n",
    "    data_dir + \"城市销量.csv\",\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(sales)\n",
    "print(\"形状：\", sales.shape)\n",
    "print(\"各列类型：\")\n",
    "print(sales.dtypes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02a24ef5",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `data_dir + \"城市销量.csv\"` 指向配套的真实 CSV 文件。\n",
    "- `read_csv` 把第一行识别为列名，把后面四行识别为数据。\n",
    "- 逗号负责分隔字段，因此结果有“城市”“销量”“增长率”三列。\n",
    "- 读取后立即检查 `shape` 和 `dtypes`，可以发现行列数量或类型是否异常。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4e9d3e3",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d463bcf6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.703184Z",
     "iopub.status.busy": "2026-07-30T13:18:18.703024Z",
     "iopub.status.idle": "2026-07-30T13:18:18.705697Z",
     "shell.execute_reply": "2026-07-30T13:18:18.704805Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“城市销量.csv”，并查看形状和各列类型\n",
    "# practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     encoding=__________\n",
    "# )\n",
    "# print(practice)\n",
    "# print(__________)\n",
    "# print(__________)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2622fdca",
   "metadata": {},
   "source": [
    "## 2. 无表头文件：header=None 与 names\n",
    "\n",
    "文件第一行也是数据而不是列名时，使用 `header=None`，防止第一行被误当成表头。\n",
    "\n",
    "`names=[列名列表]` 为结果指定列名。列名数量应与每行字段数量一致。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eaa01ecb",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb830a4b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.707431Z",
     "iopub.status.busy": "2026-07-30T13:18:18.707278Z",
     "iopub.status.idle": "2026-07-30T13:18:18.712074Z",
     "shell.execute_reply": "2026-07-30T13:18:18.711591Z"
    }
   },
   "outputs": [],
   "source": [
    "goods = pd.read_csv(\n",
    "    data_dir + \"无表头商品.csv\",\n",
    "    header=None,\n",
    "    names=[\"商品\", \"单价\", \"数量\"],\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(goods)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a8168a7",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `无表头商品.csv` 的三行都是数据，没有单独的列名行。\n",
    "- `header=None` 明确告诉 pandas“不要把任何一行自动当作列名”。\n",
    "- `names` 按顺序为三个字段指定“商品”“单价”“数量”。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e72ea3ef",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df4f6b4d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.714202Z",
     "iopub.status.busy": "2026-07-30T13:18:18.714043Z",
     "iopub.status.idle": "2026-07-30T13:18:18.716454Z",
     "shell.execute_reply": "2026-07-30T13:18:18.715850Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“无表头商品.csv”，并指定“商品”“单价”“数量”三个列名\n",
    "# goods_practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     header=__________,\n",
    "#     names=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(goods_practice)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77e9dbb7",
   "metadata": {},
   "source": [
    "## 3. 不同分隔符：sep\n",
    "\n",
    "`sep=\"分隔符\"` 指定字段之间使用什么字符分隔。CSV 默认是逗号；制表符文件可用 `sep=\"\\t\"`，其中 `\\t` 表示制表符。\n",
    "\n",
    "常见扩展名 `.tsv` 通常表示 tab-separated values，即制表符分隔数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "88332811",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3344e9d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.718090Z",
     "iopub.status.busy": "2026-07-30T13:18:18.717973Z",
     "iopub.status.idle": "2026-07-30T13:18:18.722405Z",
     "shell.execute_reply": "2026-07-30T13:18:18.721923Z"
    }
   },
   "outputs": [],
   "source": [
    "sales_tsv = pd.read_csv(\n",
    "    data_dir + \"城市销量.tsv\",\n",
    "    sep=\"\\t\",\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(sales_tsv)\n",
    "print(\"列名：\", sales_tsv.columns)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1238675b",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `.tsv` 文件使用制表符分隔字段。\n",
    "- `sep=\"\\t\"` 让 pandas 在每个制表符位置切分字段。\n",
    "- 如果分隔符设置错误，多个字段可能会挤在同一列中，因此要检查 `columns`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cbe7807a",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4bbce7ea",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.724142Z",
     "iopub.status.busy": "2026-07-30T13:18:18.724045Z",
     "iopub.status.idle": "2026-07-30T13:18:18.726080Z",
     "shell.execute_reply": "2026-07-30T13:18:18.725488Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“城市销量.tsv”，补全制表符分隔参数\n",
    "# tsv_practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     sep=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(tsv_practice)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc7472a0",
   "metadata": {},
   "source": [
    "## 4. 选择性读取：usecols 与 nrows\n",
    "\n",
    "文件很宽或很长时，可以只读取需要的部分：\n",
    "\n",
    "- `usecols=[列名列表]`：只读取指定列，并按文件中的列顺序返回；\n",
    "- `nrows=数量`：只读取表头之后的前若干行。\n",
    "\n",
    "`usecols` 用于减少读取范围；如果要调整结果列顺序，读取后再用 DataFrame 列选择。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03b1b8d1",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fec6aa6",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.727627Z",
     "iopub.status.busy": "2026-07-30T13:18:18.727530Z",
     "iopub.status.idle": "2026-07-30T13:18:18.732144Z",
     "shell.execute_reply": "2026-07-30T13:18:18.731587Z"
    }
   },
   "outputs": [],
   "source": [
    "preview = pd.read_csv(\n",
    "    data_dir + \"城市销量.csv\",\n",
    "    usecols=[\"城市\", \"销量\"],\n",
    "    nrows=2,\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(preview)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc030146",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `usecols` 让结果只包含“城市”和“销量”，不会读入“增长率”。\n",
    "- `nrows=2` 只读取前两条数据，因此结果是杭州和宁波。\n",
    "- 这种写法适合先预览大文件的结构，也可以减少不必要的内存占用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d662e9c0",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "525fc18c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.733619Z",
     "iopub.status.busy": "2026-07-30T13:18:18.733499Z",
     "iopub.status.idle": "2026-07-30T13:18:18.735756Z",
     "shell.execute_reply": "2026-07-30T13:18:18.735247Z"
    }
   },
   "outputs": [],
   "source": [
    "# 从“城市销量.csv”中只读取“城市”和“增长率”两列，并只看前 3 行\n",
    "# preview_practice = pd.read_csv(\n",
    "#     data_dir + \"城市销量.csv\",\n",
    "#     usecols=__________,\n",
    "#     nrows=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(preview_practice)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fba03280",
   "metadata": {},
   "source": [
    "## 5. 把某一列设为行索引：index_col\n",
    "\n",
    "`index_col=\"列名\"` 在读取时把指定列设置为 DataFrame 的行索引。\n",
    "\n",
    "适合索引的列通常能标识一行，例如学生编号、日期或城市名称；如果标签重复，则不能唯一定位一行。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd55dfd9",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "86f9033d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.737194Z",
     "iopub.status.busy": "2026-07-30T13:18:18.737080Z",
     "iopub.status.idle": "2026-07-30T13:18:18.743321Z",
     "shell.execute_reply": "2026-07-30T13:18:18.742790Z"
    }
   },
   "outputs": [],
   "source": [
    "sales_indexed = pd.read_csv(\n",
    "    data_dir + \"城市销量.csv\",\n",
    "    index_col=\"城市\",\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(sales_indexed)\n",
    "print(\"行索引：\", sales_indexed.index)\n",
    "print(\"杭州销量：\", sales_indexed.loc[\"杭州\", \"销量\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18c810fb",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- “城市”不再显示为普通数据列，而是显示在表格左侧作为行索引。\n",
    "- `sales_indexed.index` 可以检查当前行标签。\n",
    "- 设置索引后，可用 `sales_indexed.loc[\"杭州\", \"销量\"]` 按标签取得具体值。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "611aa0f1",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d8d6661",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.744919Z",
     "iopub.status.busy": "2026-07-30T13:18:18.744806Z",
     "iopub.status.idle": "2026-07-30T13:18:18.747248Z",
     "shell.execute_reply": "2026-07-30T13:18:18.746706Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“城市销量.csv”时把“城市”设为行索引，再用 loc 取得宁波的增长率\n",
    "# indexed_practice = pd.read_csv(\n",
    "#     data_dir + \"城市销量.csv\",\n",
    "#     index_col=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(__________)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb41bca7",
   "metadata": {},
   "source": [
    "## 6. 忽略说明行：skiprows 与 comment\n",
    "\n",
    "有些文件开头或数据中混有说明文字：\n",
    "\n",
    "- `skiprows=行数`：从文件开头跳过指定数量的行；\n",
    "- `comment=\"字符\"`：忽略以该字符开始的说明内容。\n",
    "\n",
    "两者都在 pandas 判断表头之前生效。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09a8c215",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2a31899",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.748820Z",
     "iopub.status.busy": "2026-07-30T13:18:18.748662Z",
     "iopub.status.idle": "2026-07-30T13:18:18.752822Z",
     "shell.execute_reply": "2026-07-30T13:18:18.752224Z"
    }
   },
   "outputs": [],
   "source": [
    "sales_with_notes = pd.read_csv(\n",
    "    data_dir + \"带说明行销量.csv\",\n",
    "    skiprows=1,\n",
    "    comment=\"#\",\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(sales_with_notes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c0a963e",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `skiprows=1` 先跳过文件第一行“报表生成时间”。\n",
    "- `comment=\"#\"` 再忽略两行以 `#` 开头的说明。\n",
    "- 处理后，“城市,销量”成为表头，三条城市数据进入 DataFrame。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cd8a02b",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2be12974",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.754469Z",
     "iopub.status.busy": "2026-07-30T13:18:18.754370Z",
     "iopub.status.idle": "2026-07-30T13:18:18.756655Z",
     "shell.execute_reply": "2026-07-30T13:18:18.756078Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“带说明行销量.csv”：跳过第一行，并忽略以 # 开头的说明行\n",
    "# notes_practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     skiprows=__________,\n",
    "#     comment=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(notes_practice)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0db14142",
   "metadata": {},
   "source": [
    "## 7. 识别自定义缺失标记：na_values\n",
    "\n",
    "pandas 默认会把空字段、`NA`、`NULL` 等常见标记识别为缺失值。\n",
    "\n",
    "`na_values=[额外标记列表]` 可以把业务数据中的“缺考”“未知”等字符串也识别为缺失值。读取后用 `.isna().sum()` 按列统计缺失数量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4cd8596",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be7595f1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.758175Z",
     "iopub.status.busy": "2026-07-30T13:18:18.758078Z",
     "iopub.status.idle": "2026-07-30T13:18:18.767619Z",
     "shell.execute_reply": "2026-07-30T13:18:18.766836Z"
    }
   },
   "outputs": [],
   "source": [
    "students = pd.read_csv(\n",
    "    data_dir + \"含缺失值学生.csv\",\n",
    "    na_values=[\"缺考\"],\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(students)\n",
    "print(\"各列缺失数量：\")\n",
    "print(students.isna().sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d51012ee",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- “缺考”通过 `na_values` 被转换为缺失值。\n",
    "- 空城市和默认缺失标记 `NA` 也被识别为缺失值。\n",
    "- 统计结果显示成绩列有 2 个缺失值，城市列有 1 个缺失值。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "450ebefd",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e4ffc37",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.769243Z",
     "iopub.status.busy": "2026-07-30T13:18:18.769147Z",
     "iopub.status.idle": "2026-07-30T13:18:18.771562Z",
     "shell.execute_reply": "2026-07-30T13:18:18.770915Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“含缺失值学生.csv”，把“缺考”识别为缺失值，并统计各列缺失数量\n",
    "# students_practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     na_values=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(students_practice)\n",
    "# print(__________)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60a97ddd",
   "metadata": {},
   "source": [
    "## 8. 控制读取类型：dtype\n",
    "\n",
    "`dtype={列名: 目标类型}` 指定读取后各列的数据类型。\n",
    "\n",
    "编号虽然由数字组成，但通常不参与运算，应按字符串读取，这样可以保留开头的 0。pandas 的字符串类型可写成 `\"string\"`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f7f4aef",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3879c8ba",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.773195Z",
     "iopub.status.busy": "2026-07-30T13:18:18.773077Z",
     "iopub.status.idle": "2026-07-30T13:18:18.778158Z",
     "shell.execute_reply": "2026-07-30T13:18:18.777615Z"
    }
   },
   "outputs": [],
   "source": [
    "goods = pd.read_csv(\n",
    "    data_dir + \"商品编号.csv\",\n",
    "    dtype={\"编号\": \"string\", \"数量\": \"int64\"},\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(goods)\n",
    "print(\"各列类型：\")\n",
    "print(goods.dtypes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e0b842d",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- “编号”按字符串读取，因此 `001、002、010` 的前导 0 得以保留。\n",
    "- “数量”明确按整数读取，可以直接参与数值计算。\n",
    "- 如果不指定“编号”类型，pandas 可能把它推断为整数并显示为 `1、2、10`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6aeeae6c",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e42bd5b3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.780202Z",
     "iopub.status.busy": "2026-07-30T13:18:18.780074Z",
     "iopub.status.idle": "2026-07-30T13:18:18.782355Z",
     "shell.execute_reply": "2026-07-30T13:18:18.781775Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“商品编号.csv”：把“编号”按字符串读取，把“数量”按整数读取\n",
    "# goods_practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     dtype=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(goods_practice)\n",
    "# print(goods_practice.dtypes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a777c76",
   "metadata": {},
   "source": [
    "## 9. 把日期列解析为日期类型：parse_dates\n",
    "\n",
    "`parse_dates=[列名列表]` 在读取时把指定列解析为 pandas 日期时间类型。\n",
    "\n",
    "如果日期仍是普通文本，排序和时间间隔计算容易出错；读取后应使用 `dtypes` 检查解析结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea131729",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5234bc1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.783902Z",
     "iopub.status.busy": "2026-07-30T13:18:18.783793Z",
     "iopub.status.idle": "2026-07-30T13:18:18.791941Z",
     "shell.execute_reply": "2026-07-30T13:18:18.791287Z"
    }
   },
   "outputs": [],
   "source": [
    "daily_sales = pd.read_csv(\n",
    "    data_dir + \"每日销量.csv\",\n",
    "    parse_dates=[\"日期\"],\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "\n",
    "print(daily_sales)\n",
    "print(\"各列类型：\")\n",
    "print(daily_sales.dtypes)\n",
    "print(\"最早日期：\", daily_sales[\"日期\"].min())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78e701dd",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `parse_dates=[\"日期\"]` 让“日期”列不再是普通字符串。\n",
    "- `dtypes` 中的日期类型通常显示为 `datetime64[...]`。\n",
    "- 解析后可以直接使用 `.min()` 找出最早日期，也可以继续排序和计算时间差。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c075971c",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cea72b3b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.793612Z",
     "iopub.status.busy": "2026-07-30T13:18:18.793500Z",
     "iopub.status.idle": "2026-07-30T13:18:18.795802Z",
     "shell.execute_reply": "2026-07-30T13:18:18.795139Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“每日销量.csv”时解析“日期”，并打印最晚日期\n",
    "# dates_practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     parse_dates=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# print(__________)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad42373e",
   "metadata": {},
   "source": [
    "## 10. 分块读取较大文件：chunksize\n",
    "\n",
    "`chunksize=每块行数` 让 `read_csv` 返回一个分块读取器，而不是一次把全部数据装入内存。\n",
    "\n",
    "使用 `for` 循环依次取得每个 DataFrame 数据块。每一块都能使用普通 DataFrame 方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6ddadb9",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "760617a1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.797477Z",
     "iopub.status.busy": "2026-07-30T13:18:18.797367Z",
     "iopub.status.idle": "2026-07-30T13:18:18.803728Z",
     "shell.execute_reply": "2026-07-30T13:18:18.803203Z"
    }
   },
   "outputs": [],
   "source": [
    "reader = pd.read_csv(\n",
    "    data_dir + \"城市销量.csv\",\n",
    "    chunksize=2,\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "total_sales = 0\n",
    "\n",
    "for chunk in reader:\n",
    "    print(\"当前数据块：\")\n",
    "    print(chunk)\n",
    "    total_sales = total_sales + chunk[\"销量\"].sum()\n",
    "\n",
    "print(\"总销量：\", total_sales)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "167a318c",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `chunksize=2` 让前两块各有 2 行。\n",
    "- 循环中的 `chunk` 每次都是一个普通 DataFrame。\n",
    "- 每块先计算销量之和，再累加到 `total_sales`，最终得到全文件总销量。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "564341f1",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c09c045a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.805843Z",
     "iopub.status.busy": "2026-07-30T13:18:18.805710Z",
     "iopub.status.idle": "2026-07-30T13:18:18.807895Z",
     "shell.execute_reply": "2026-07-30T13:18:18.807327Z"
    }
   },
   "outputs": [],
   "source": [
    "# 每次从“城市销量.csv”读取 3 行，并累计所有销量\n",
    "# reader_practice = pd.read_csv(\n",
    "#     data_dir + __________,\n",
    "#     chunksize=__________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# total = 0\n",
    "# for chunk in reader_practice:\n",
    "#     total = total + __________\n",
    "# print(total)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "609a1f11",
   "metadata": {},
   "source": [
    "## 11. 用 to_csv 导出表格文本\n",
    "\n",
    "`df.to_csv(...)` 把 DataFrame 转换为 CSV：\n",
    "\n",
    "- 不传文件路径时，返回 CSV 字符串；\n",
    "- `index=False` 不输出行索引；\n",
    "- `columns=[列名列表]` 只输出指定列，并按列表顺序排列；\n",
    "- `na_rep=\"标记\"` 指定缺失值的输出文本。\n",
    "\n",
    "实际保存时，把路径作为第一个参数，例如 `df.to_csv(\"result.csv\", index=False, encoding=\"utf-8-sig\")`。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5b188f2",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "387b6b4d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.809400Z",
     "iopub.status.busy": "2026-07-30T13:18:18.809286Z",
     "iopub.status.idle": "2026-07-30T13:18:18.816629Z",
     "shell.execute_reply": "2026-07-30T13:18:18.816152Z"
    }
   },
   "outputs": [],
   "source": [
    "sales = pd.DataFrame({\n",
    "    \"城市\": [\"杭州\", \"宁波\", \"温州\"],\n",
    "    \"销量\": [120, None, 135],\n",
    "    \"增长率\": [0.12, 0.08, 0.15]\n",
    "})\n",
    "\n",
    "output_text = sales.to_csv(\n",
    "    index=False,\n",
    "    columns=[\"城市\", \"销量\"],\n",
    "    na_rep=\"缺失\"\n",
    ")\n",
    "\n",
    "print(output_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1e15c91",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- 输出中只有“城市”“销量”两列，并采用这个顺序。\n",
    "- `index=False` 防止额外输出 `0、1、2` 行索引。\n",
    "- 宁波的缺失销量使用“缺失”表示。\n",
    "- 因为没有传文件路径，本例得到字符串而不会在文件夹中产生临时文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0d58aba",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c482aa7b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.818479Z",
     "iopub.status.busy": "2026-07-30T13:18:18.818366Z",
     "iopub.status.idle": "2026-07-30T13:18:18.820611Z",
     "shell.execute_reply": "2026-07-30T13:18:18.819983Z"
    }
   },
   "outputs": [],
   "source": [
    "# 导出“商品”“金额”两列，不输出索引，并把缺失值写成“暂无”\n",
    "# goods = pd.DataFrame({\n",
    "#     \"商品\": [\"A\", \"B\"], \"数量\": [3, 5], \"金额\": [24, None]\n",
    "# })\n",
    "# text = goods.to_csv(\n",
    "#     index=__________,\n",
    "#     columns=__________,\n",
    "#     na_rep=__________\n",
    "# )\n",
    "# print(text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5416498f",
   "metadata": {},
   "source": [
    "## 12. 标准库 JSON：loads 与 dumps\n",
    "\n",
    "Python 标准库 `json` 用于 JSON 与 Python 对象之间的转换：\n",
    "\n",
    "- `json.loads(JSON字符串)`：JSON 字符串转为 Python 列表、字典等对象；\n",
    "- `json.dumps(Python对象)`：Python 对象转为 JSON 字符串；\n",
    "- `ensure_ascii=False`：让输出字符串直接保留中文。\n",
    "\n",
    "函数名中的 `s` 可以理解为处理 string（字符串）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "745345c6",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "545e7e1e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.822568Z",
     "iopub.status.busy": "2026-07-30T13:18:18.822423Z",
     "iopub.status.idle": "2026-07-30T13:18:18.825229Z",
     "shell.execute_reply": "2026-07-30T13:18:18.824582Z"
    }
   },
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "json_text = '[{\"城市\": \"杭州\", \"销量\": 120}, {\"城市\": \"宁波\", \"销量\": 98}]'\n",
    "records = json.loads(json_text)\n",
    "restored_text = json.dumps(records, ensure_ascii=False)\n",
    "\n",
    "print(\"转换后的 Python 对象：\")\n",
    "print(records)\n",
    "print(\"第一条记录的城市：\", records[0][\"城市\"])\n",
    "print(\"重新转为 JSON：\")\n",
    "print(restored_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8f7b4ad",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- 最外层 JSON 方括号转换为 Python 列表，每个 JSON 对象转换为字典。\n",
    "- `records[0][\"城市\"]` 先取第一条记录，再读取其中的“城市”字段。\n",
    "- `dumps(..., ensure_ascii=False)` 重新生成保留中文的 JSON 字符串。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ea927ad",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a1bfa23",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.826860Z",
     "iopub.status.busy": "2026-07-30T13:18:18.826733Z",
     "iopub.status.idle": "2026-07-30T13:18:18.828956Z",
     "shell.execute_reply": "2026-07-30T13:18:18.828377Z"
    }
   },
   "outputs": [],
   "source": [
    "# 把 JSON 字符串转换为 Python 对象，再取出第二条记录的数量\n",
    "# text = '[{\"商品\": \"A\", \"数量\": 3}, {\"商品\": \"B\", \"数量\": 5}]'\n",
    "# records = __________\n",
    "# print(__________)\n",
    "# 再把 records 转回保留中文的 JSON 字符串\n",
    "# restored = __________\n",
    "# print(restored)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d76922b9",
   "metadata": {},
   "source": [
    "## 13. pandas JSON：read_json 与 to_json\n",
    "\n",
    "JSON 文件采用“对象列表”结构时，可以直接读取为 DataFrame：\n",
    "\n",
    "- `pd.read_json(文件路径)`：读取 JSON 文件；\n",
    "- `df.to_json(orient=\"records\")`：把每一行输出为一个 JSON 对象；\n",
    "- `force_ascii=False`：直接保留中文。\n",
    "\n",
    "`orient=\"records\"` 表示按“多条记录”的形式组织数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67324597",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e5f505d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.830744Z",
     "iopub.status.busy": "2026-07-30T13:18:18.830631Z",
     "iopub.status.idle": "2026-07-30T13:18:18.837017Z",
     "shell.execute_reply": "2026-07-30T13:18:18.836509Z"
    }
   },
   "outputs": [],
   "source": [
    "sales_json = pd.read_json(\n",
    "    data_dir + \"城市销量.json\"\n",
    ")\n",
    "output_json = sales_json.to_json(\n",
    "    orient=\"records\",\n",
    "    force_ascii=False\n",
    ")\n",
    "\n",
    "print(\"读取后的 DataFrame：\")\n",
    "print(sales_json)\n",
    "print(\"重新输出的 JSON：\")\n",
    "print(output_json)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86e200cc",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `城市销量.json` 中的四个对象分别成为 DataFrame 的四行。\n",
    "- 每个对象的“城市”“销量”“增长率”键成为列名。\n",
    "- `orient=\"records\"` 再把每一行转换为一个对象，结果外层仍是 JSON 数组。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "423b523e",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "68f6ad5b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.839026Z",
     "iopub.status.busy": "2026-07-30T13:18:18.838902Z",
     "iopub.status.idle": "2026-07-30T13:18:18.841207Z",
     "shell.execute_reply": "2026-07-30T13:18:18.840650Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“城市销量.json”，再按 records 形式输出并保留中文\n",
    "# json_practice = pd.read_json(__________)\n",
    "# output = json_practice.to_json(\n",
    "#     orient=__________,\n",
    "#     force_ascii=__________\n",
    "# )\n",
    "# print(json_practice)\n",
    "# print(output)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f5b1d2c",
   "metadata": {},
   "source": [
    "## 14. Excel：read_excel 与 to_excel\n",
    "\n",
    "`pd.read_excel(文件路径, sheet_name=\"工作表名\")` 读取 Excel 工作表。\n",
    "\n",
    "`df.to_excel(文件路径, index=False, sheet_name=\"工作表名\")` 把 DataFrame 写入 Excel。`index=False` 表示不额外写出行索引。本节先读取配套的 `城市销量.xlsx`，再写出一个课堂演示副本。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1afc748d",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b18ee30",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:18.842781Z",
     "iopub.status.busy": "2026-07-30T13:18:18.842664Z",
     "iopub.status.idle": "2026-07-30T13:18:19.009190Z",
     "shell.execute_reply": "2026-07-30T13:18:19.008589Z"
    }
   },
   "outputs": [],
   "source": [
    "excel_sales = pd.read_excel(\n",
    "    data_dir + \"城市销量.xlsx\",\n",
    "    sheet_name=\"销量\"\n",
    ")\n",
    "\n",
    "excel_sales.to_excel(\n",
    "    data_dir + \"课堂导出结果.xlsx\",\n",
    "    index=False,\n",
    "    sheet_name=\"销量副本\"\n",
    ")\n",
    "\n",
    "restored = pd.read_excel(\n",
    "    data_dir + \"课堂导出结果.xlsx\",\n",
    "    sheet_name=\"销量副本\"\n",
    ")\n",
    "\n",
    "print(\"读取原始 Excel：\")\n",
    "print(excel_sales)\n",
    "print(\"读取刚刚写出的副本：\")\n",
    "print(restored)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5beb9a6",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- 第一次 `read_excel` 从真实文件 `城市销量.xlsx` 的“销量”工作表读取数据。\n",
    "- `to_excel` 把 DataFrame 写入 `课堂导出结果.xlsx`，工作表名为“销量副本”。\n",
    "- `index=False` 避免把 DataFrame 的行索引写成额外的一列。\n",
    "- 第二次 `read_excel` 读回刚生成的副本，用于检查写出结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e78c0423",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d6b01ef5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:19.010790Z",
     "iopub.status.busy": "2026-07-30T13:18:19.010673Z",
     "iopub.status.idle": "2026-07-30T13:18:19.013157Z",
     "shell.execute_reply": "2026-07-30T13:18:19.012497Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“城市销量.xlsx”的“销量”工作表，只保留“城市”和“销量”两列\n",
    "# excel_practice = pd.read_excel(\n",
    "#     data_dir + __________,\n",
    "#     sheet_name=__________\n",
    "# )\n",
    "# selected = excel_practice[__________]\n",
    "# print(selected)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "833ee599",
   "metadata": {},
   "source": [
    "## 15. HTML 表格：read_html\n",
    "\n",
    "`pd.read_html(文件路径)` 查找 HTML 文件中的 `<table>` 表格，并返回 DataFrame 列表。\n",
    "\n",
    "返回列表是因为一个 HTML 文件可能包含多张表；使用 `[0]` 取得第一张表。本节读取本地 `城市销量.html`，不访问网络。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45140151",
   "metadata": {},
   "source": [
    "### 示例代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0619d075",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:19.014747Z",
     "iopub.status.busy": "2026-07-30T13:18:19.014627Z",
     "iopub.status.idle": "2026-07-30T13:18:19.023061Z",
     "shell.execute_reply": "2026-07-30T13:18:19.022555Z"
    }
   },
   "outputs": [],
   "source": [
    "tables = pd.read_html(\n",
    "    data_dir + \"城市销量.html\",\n",
    "    encoding=\"utf-8\"\n",
    ")\n",
    "first_table = tables[0]\n",
    "\n",
    "print(\"找到的表格数量：\", len(tables))\n",
    "print(\"第一张表：\")\n",
    "print(first_table)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f36ad1b5",
   "metadata": {},
   "source": [
    "### 代码解读（对应上方示例）\n",
    "\n",
    "- `城市销量.html` 中只有一个 `<table>`，所以 `len(tables)` 是 1。\n",
    "- `<th>` 中的“城市”“销量”“增长率”成为列名，四个数据行成为 DataFrame 的四行。\n",
    "- `tables[0]` 取出列表中的第一张 DataFrame 表格。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "800f4598",
   "metadata": {},
   "source": [
    "### 动手练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "57562293",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-07-30T13:18:19.024814Z",
     "iopub.status.busy": "2026-07-30T13:18:19.024693Z",
     "iopub.status.idle": "2026-07-30T13:18:19.026837Z",
     "shell.execute_reply": "2026-07-30T13:18:19.026368Z"
    }
   },
   "outputs": [],
   "source": [
    "# 读取“城市销量.html”，并取出第一张 DataFrame\n",
    "# html_tables = pd.read_html(\n",
    "#     __________,\n",
    "#     encoding=\"utf-8\"\n",
    "# )\n",
    "# html_practice = __________\n",
    "# print(html_practice)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f27aeed",
   "metadata": {},
   "source": [
    "## 教材中的阅读拓展\n",
    "\n",
    "以下内容在 `da_6f_read.pdf` 中有介绍，但不列入本次必须掌握范围：\n",
    "\n",
    "- **手工 CSV 解析**：标准库 `csv.reader`、Dialect 和引用规则；常规表格优先使用 `read_csv`。\n",
    "- **XML**：`pd.read_xml` 或 lxml，适合层次结构较复杂的标记数据。\n",
    "- **pickle**：只适合可信来源的 Python 临时对象；不要加载来源不明的 pickle 文件。\n",
    "- **Parquet、ORC、HDF5**：适合更大规模或更高效的二进制存储，通常需要额外依赖。\n",
    "- **Web API**：使用 `requests.get`、状态检查和 JSON 响应；需要网络和 API 规则知识。\n",
    "- **数据库**：使用 `read_sql` 或 `read_sql_query`，还需要 SQL 与数据库连接知识。\n",
    "\n",
    "竞赛或实训拿到文件后，建议先确认：路径、编码、分隔符、表头、列名、缺失标记、数据类型和日期列，再开始筛选与统计。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "916497bf",
   "metadata": {},
   "source": [
    "## 离场检验\n",
    "\n",
    "1. 文件没有表头时，为什么要写 `header=None`？\n",
    "2. `sep=\"\\t\"` 表示使用什么分隔字段？\n",
    "3. `usecols` 与 DataFrame 读取后的列选择有什么不同？\n",
    "4. 为什么编号列常需要指定 `dtype=\"string\"`？\n",
    "5. `parse_dates` 解决什么问题？\n",
    "6. `chunksize=1000` 是否会一次返回完整 DataFrame？\n",
    "7. 导出 CSV 时为什么经常使用 `index=False`？\n",
    "8. `read_html` 为什么返回列表？\n",
    "\n",
    "<details>\n",
    "<summary>点击查看参考答案</summary>\n",
    "\n",
    "1. 防止第一条数据被误当成列名。\n",
    "2. 制表符。\n",
    "3. `usecols` 在解析时就减少读取范围；读取后的列选择是在完整读取之后取子集。\n",
    "4. 保留前导 0，并避免把不参与计算的编号误当作数值。\n",
    "5. 把日期文本解析为日期时间类型，便于正确排序和时间计算。\n",
    "6. 不会；它返回分块读取器，每次产生最多 1000 行的 DataFrame。\n",
    "7. 避免把 DataFrame 的行索引额外写成普通数据列。\n",
    "8. 一个 HTML 页面可能包含多张表。\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
}
