跳转至

Pandas — 一次真实的数据清洗全流程

这一章讲 Pandas——Python 的数据分析库。 原书的教法值得称道:不背 API, 而是拿一份网上找来的、有真实毛病的脏数据,一步步洗到能用。 走完这一遍,你学到的不是函数清单,而是 Pandas 的心智模型: 表格 = 带名字的列;一切操作 = 先造一张「真假表」(掩码),再拿它筛行、改列。

1. 这一章讲什么

Pandas 建立在 NumPy 之上,提供表格和时间序列的数据结构,接口对使用者更友好; 因为底层是 NumPy,所以大数据集也能高效处理。1

书里坦白:「其实 NumPy 什么都能做,用 Pandas 只是省大量功夫。」——这句实话定位了两者的关系。

本章结构不是参考书的 A-Z,而是一条叙事线:

  一份脏数据(txt,制表符分隔,有缺失、有「订房公司」散行)
      → 读入 → 看清结构(head)
      → 定位特殊行(布尔掩码)→ 把公司名回填给每一行(bfill)
      → 删脏行(~掩码)→ 类型修正(float → int)
      → 删多余列 → 造日期列、造价格列
      → 统计(count/sum/corr)→ 排序
  图说:每一步只干一件事——这本身就是第 05/07 章「别合并步骤」的示范。

2. 核心原理:主线例子逐步走

2.0 先立模型:DataFrame 是什么

读代码之前,花十秒钟把 Pandas 的核心对象DataFrame的形状装脑子里—— 它就是一张 Excel 表在 Python 里的样子:13

         Date      Company      Person Name      Room number
  0   1-Jan-2022    Avamba    Anatole Ridehalgh      4008.0    ← 最左列 0,1,2,… 是行索引(自动生成)
  1   1-Jan-2022    Fatz      Aldrich McKevin        2002.0
  …

三样东西就够了:列(有名字,按名字取:data["Room number"])、行(每行挂一个编号,这个编号叫索引)、格子。 之后本章的每个操作,都只是对这三样东西的统一动作:按列取、按行筛、按格改。 NumPy 血统带来的那条性质也在这张图上:对一列的运算作用于整列(500 行和 5 行,写法相同)。

2.0b 这份数据的三宗罪,逐条诊断

在动手之前,书里先对原始数据做了「病历登记」,这个诊断顺序值得学:2

症状 例子 病根
公司名缺失 第 8、9 行 Company 是 NaN 数据本身漏填
「订房公司」写在独立行 第 3 行整行只有 "Hotels" 上游格式反常规:公司名放在它管辖段的下方
房号显示成 4008.0 整列浮点 NaN 把列拖成了浮点(第 2.5 节修复)

作者的态度也分层:缺公司名「不太担心,值会被填成 NaN,机械部分自动处理」; 真正烦的是订房公司行——「这数据本该写成正常的 csv 或 json」。 这个分层是数据工作的常态:机械问题(缺值)留给工具的默认行为,格式问题(结构违反约定)才需要人想办法。 顺序也别反:先看清三宗罪,再写第一行清洗代码。2

2.1 读入与第一眼

数据文件长这样(制表符分隔的四列,其中混着两行「订房公司名」):

Date    Company Person Name Room number
1-Jan-2022  Avamba  Anatole Ridehalgh   4008
1-Jan-2022  Fatz    Aldrich McKevin 2002
...
Hotels                            ← 这一行不是预订,是「上面几行属于 Hotels 这家公司」
...
import pandas as pd
import numpy as np

data = pd.read_csv("data.txt", sep="\t")   # \t 是制表符
print(data.head(12))

pd.read_csv 读入后得到 DataFrame(Pandas 的表格类型:带名字的列 + 自动编号的行); head(12) 打印前 12 行看一眼。第一眼的发现很重要:2

观察 含义
有行的 Date 是 "Hotels"/"Booking" 而 Company 是 NaN 订房公司写在单独一行,位置在它管辖的预订段之后
有些行 Company 是 NaN 缺公司名的普通预订
Room number 显示 4008.0 列被读成了浮点(因为 NaN 是浮点)

NaN(Not a Number,非数——这里泛指「缺失」)是 Pandas 的占位符: 缺了的格子自动填 NaN,这正是「缺失别担心」的机械部分;真正麻烦的是那些「Hotels/Booking 行」—— 它们违反了一行一条记录的约定。3

2.2 定位特殊行:布尔掩码

解决什么问题: 把「Hotels/Booking 行」和正常预订行分开。

怎么做: 这些行的特征是「Room number 是 NaN」。造一张布尔掩码(Boolean mask)—— 一个和表格等长、每格 True/False 的序列:

mask = data["Room number"].isna()      # 每行一个判断:该行的 Room number 是不是 NaN
print(mask.head(12))
# 0 False / 1 False / 2 False / 3 True(Hotels 行!) / …

为什么有效: 掩码把「哪几行符合条件」从循环里解放出来—— 后面所有「筛/改」都以它为输入,一步一个意图(书里的纪律:「一次只做一件事」)。4

掩码的形状值得在脑子里固化:data["Room number"] 是一列(每行一个数), .isna() 对它逐格判断,得到的 mask 还是「每行一个值」,只是数变成了 True/False。 表 → 列 → 掩码,三个对象等长对齐——这是 Pandas 全部花式操作的地基: 对齐不上的操作(长度不同)会直接报错,这也是 Pandas 比手写循环安全的地方之一。

# 0 False / 1 False / 2 False / 3 True(Hotels 行!) / 4 False / …
这 12 行输出里,只有第 3、11 行是 True——正好就是 "Hotels"、"Booking" 那两行。 一张掩码就是一张「点名表」:点到的留下(或删去),一句话的事。4

2.2b np.where 的三参数,逐个对位

np.where(mask, data["Date"], np.nan) 是 NumPy 的「三元运算」,三个参数逐个对位:5

  np.where(条件,        条件为 True 时取这个,   条件为 False 时取这个)
            mask        data["Date"]            np.nan
  逐行执行:第 0 行 mask=False → 取 np.nan;第 3 行 mask=True → 取 Date 的值 "Hotels"

  结果:text_value 列 = [NaN, NaN, NaN, "Hotels", NaN, …, NaN, "Booking", NaN, …]

为什么不直接 data["text_value"] = data["Date"] 再删掉非公司行?——因为那样会把所有行的 Date 先抄进 text_value,信息混在一起,后面没法分辨「谁是真公司行」。np.where 的好处是脏数据从未存在过: 非公司行的 text_value 从第一步就是 NaN,干净的起点带来干净的后处理。5

2.3 回填公司名:造列 + bfill

解决什么问题: 把每段的订房公司(在第 3、11 行的 Date 列里!)抄到它上面的每一行。

怎么做,两步:5

# 第一步:新列 text_value —— 是「公司行」就抄 Date 的值,否则 NaN
data["text_value"] = np.where(mask, data["Date"], np.nan)
# 0 NaN / … / 3 Hotels / 4 NaN / … / 11 Booking

# 第二步:bfill(backward fill,向后填充)——把每个 NaN 换成「它下面最近的一个非 NaN」
data["text_value"] = data["text_value"].bfill()
# 0 Hotels / 1 Hotels / 2 Hotels / 3 Hotels / 4 Booking / …

等等——方向对不对?公司行写在段的后面,所以 0、1、2 行的公司要从第 3 行(Hotels)拿: 「向下看」填自己,正是 bfill 的语义。走一遍:第 3 行 Hotels 是非 NaN,保留;第 2 行 NaN → 看下面,第 3 行是 Hotels → 填 Hotels;第 4 行 NaN → 一路向下找到第 11 行 Booking → 填 Booking。5

细节:bfill 不改原列,只返回新列——所以必须 data["text_value"] = data["text_value"].bfill() 把结果赋回去。 这个「返回新对象而非原地改」是 Pandas 的普遍脾气(第 2.5 节的 astype、2.7 节的 sort_values 同理): 好处是每一步都是纯函数,可以随时回退;坏处是忘了赋回,结果就是白干—— 新手在 Pandas 上最常见的「改了没反应」,七成是这个。5

方向再强调一次:公司行写在段的后面,所以要从下面的值往上填(bfill); 如果格式相反(公司名写在段首),就该用 ffill(forward fill,从上面往下填)。 一字之差,方向相反——动手前先确认「信息在段的哪一端」,这是 bfill/ffill 选型的全部依据。5

2.4 删脏行:~掩码

data = data[~mask]     # ~ 是逐元素取反:保留「不是公司行」的行

~mask 把 True/False 整张翻转;拿布尔序列当索引,True 的行留下。第 3、11 行消失。 书里的诚实时刻:「这段代码我得承认,是我的 AI 工具帮我写的,我自己想不出这个技巧」—— 2026 年编程的日常。6

为什么不用 dropna 一步到位? 书里专门讨论了这条捷径:dropna() 会删掉任何含 NaN 的行—— 不只是 Hotels/Booking 公司行,还包括第 8 行那种「公司名缺失但其余信息完好」的正常预订。 「这样就一步干了两件事:删掉订房公司行 + 筛掉公司名无效的预订。一般建议一次只做一件事。」 先只删公司行(本章 2.4),「要不要筛掉公司名为空的行」留作独立的、可以单独讨论的决定—— 这正是「~mask 删行」存在的原因。11

这个「两步并一步」的诱惑在数据清洗里到处都是:dropna 顺手、query 叠加、assign 连写…… 每一处的代价都一样:出错了不知道是哪一步的错。调试成本 ≈ 步数 × 每步的不确定性; 拆开后每步可单独验证,总成本反而低——和第 15 章测试的「小步红绿」是同一个经济学:可验证的小步,胜过不可验证的大步,无论在代码里还是数据里——这句话写两次都嫌少,所以它出现在两章里,值得出现在你的每一次实操里——从这份酒店数据开始,就来得及——把本章的代码逐行敲一遍,胜过再读三遍——书里每一行都附了输出,正好自查。

删行后注意一个 Pandas 特性:行索引会「粘住」。 看排序后的输出,第一行行号是 166、第二行 118—— 这些是被删行之后的原始编号:DataFrame 删掉行,不会让剩下的行重新从 0 编号。 这不影响运算,但用行号找数据时会踩——记住「行号是标签,不是位置」。12

2.5 类型修正与删列

data["Room number"] = data["Room number"].astype(int)   # 浮点 → 整数:4008.0 → 4008
data.drop(columns=["Company"], inplace=True)             # 删列;inplace=True 表示改原表

astype 转型之所以现在才做,是因为之前 NaN 还在、浮点里塞不下 NaN 之外的整数语义; 脏行删掉后,转型就安全了——顺序本身就在示范「一次一件事」。7

2.5b astype 为什么「现在才安全」

转型这步有个时序讲究:7

  删公司行之前:列里有 NaN → 浮点是唯一能装 NaN 的数字类型 → astype(int) 会报错
  删公司行之后:NaN 全没了 → 格子全是真整数 → astype(int) 安全

这个时序是「一次只做一件事」的副产品:如果当初图省事用 dropna() 一刀切, 转型时机反而说不清(哪些 NaN 是「公司行」、哪些是「真缺公司」全混在一起)。 步骤干净的好处不只是好看——它让每个步骤的前提(这步为什么现在能做)变得可陈述。 可陈述的前提,是别人能接手你的代码的前提。7

2.6 造数据:日期列与价格列

原数据日期全相同,书里造一列「每隔 3 天的日期」让例子活起来(并提醒:别对真实数据做这种随机操作):

data["Date"] = pd.date_range(start="1/1/2022", periods=len(data), freq="3D")
# 2022-01-01 / 2022-01-04 / 2022-01-07 …

data["price"] = 100 + data["Room number"]//100 + data["Date"].dt.month
# 第 0 行:100 + 4008//100(=40) + 1月(=1) = 141

走一遍价格公式:100 基础 + 楼层(房号整除 100:4008 → 40)+ 月份—— 整列运算,不用写循环(NumPy 血统的体现)。8

这一行公式是 Pandas 表达力的浓缩:五个运算符号、四列引用,作用在全部 134 行上。 等价的 Python 循环要六行,而且慢;Pandas 这行除了快,还有「整列即单位」的一致性—— 不会出现「第 37 行忘了除 100」的差一行错误,因为运算没有行号。 数据列运算的格言:如果你能写成一列的公式,就不要写成一行的循环。8

书里造日期那句提醒也原样保留:「当然,你不该对真实数据做这种随机操作——这只是为了演示。」 教科书式的诚实:演示操作与生产操作的边界,画给你看。8

2.7 统计与排序

number_of_bookings = data["price"].count()     # 134 —— 有效条目数
total_price = data["price"].sum()              # 18148
corr = data["price"].corr(data["Date"].dt.month)  # 0.104… —— 与月份的相关性

data = data.sort_values(by="price", ascending=False)   # 按价格降序

书里的解读:「如预期,价格和月份有正相关——因为价格公式里本来就含月份。」 (这是个诚实的「人造相关」:数据是公式造的,相关当然存在——真实的相关分析要先排除这种结构性来源。)9

2.8 排序输出怎么读

书里的最后一张输出表,值得教一下读法:9

          Date        Person Name Room number text_value   price
  166  2022-12-12   Licha Attyeo        7002     Hotels     182
  118  2022-09-01   Dukey Hansod        7001     Expedia    179
  ...
  • 最左的 166、118、33 是原始行号(删行后索引不粘重排,见 §2.4 的说明);
  • 降序所以价格从 182 往下走;7002 房 → 楼层 70 → 100+70+12(12 月)= 182,公式对得上账;
  • 每行的 text_value(订房公司)和 Date 都是干净字段——清洗的目标达成了:从「看不出结构」到「每列都能直接统计」。

把公式代入第一行验算:price = 100 + 7002//100 + 12 = 100 + 70 + 12 = 182。✓ 第二行:100 + 7001//100 + 9 = 100 + 70 + 9 = 179。✓ 整列运算的正确性,用两行手算就能抽查出来——这是数据清洗的土办法质检,比任何断言都快。9

2.9 统计三行的读法

number_of_bookings = data["price"].count()     # 134
total_price = data["price"].sum()              # 18148
corr = data["price"].corr(data["Date"].dt.month)  # 0.104…
  • count() 数的是非缺失条目——134 就是「有效的预订行数」(公司行已删);
  • sum() 是全列求和:18148 除以 134 ≈ 135,与价格区间(111–182)对得上账;
  • corr() 是相关性(−1 到 1,0 表示无关):0.104 是弱正相关——书里的解读很诚实: 「如预期有正相关,因为价格公式里本来就含月份」——数据是造的,相关是设计出来的。 真实分析里,这种「结构性相关」是噪音,不是发现;发现相关前,先排除「我的公式造出来的相关」。9

3. 这一章的方法论落点

原书收尾的自白比技术更重要:「这个例子有点人工——我拿了一份随机数据集,把想展示的操作挨个往上套。 现实中你必须反过来:先理解问题,再选择合适的操作去解决它。」10

这一句是全书「需求先于实现」在数据分析里的版本(第 11 章需求工程的回声): Pandas 的函数不用背全,背「问题 → 操作」的映射思路: 筛选问题想掩码、缺值问题想 fill/dropna、类型问题想 astype、汇总问题想 groupby/聚合。

把本章用过的操作按「问题类型」归一下类(这张表是我们对全章操作的归拢,方便日后检索):10

你面对的问题 本章用过的操作 输出的形状
数据从哪读进来 pd.read_csv(sep=…) 新 DataFrame
先看清长什么样 data.head(12) 打印,不改数据
找出「符合条件的行」 造列条件 → mask = data[列].isna() 布尔序列(掩码)
按条件筛行 data[~mask]、data[mask] 新 DataFrame(行变少)
造新列(按条件取值) np.where(mask, A, B) 新列
缺值按方向填充 列.bfill()(注意赋回) 新列
删行/删列 data[~mask] / data.drop(columns=[…]) 新 DataFrame
改类型 列.astype(int) 新列
造序列/造派生列 pd.date_range(...)、列间四则运算 新列
统计一个数 列.count() / 列.sum() / 列.corr(另一列) 单个数
排序后看前几行 data.sort_values(by=…, ascending=False) 新 DataFrame

这张表覆盖了日常清洗的七八成;更复杂的分组聚合(groupby)与表连接(merge)本章没出现,见边界。

这张表还有一层用法书里没点破:它同时是「搜索关键词表」。 遇到不会的操作,拿「问题」列的词(筛选、缺值、类型、排序……)去搜文档或问 AI, 比描述症状(「我的表乱了」)命中率高得多——「问题 → 操作」的映射,本就是人类语言和库函数之间的翻译表。

3.1 「先理解问题再选操作」的操作版

原书收尾的自白比技术更重要:「这个例子有点人工——我拿了一份随机数据集,把想展示的操作挨个往上套。 现实中你必须反过来:先理解问题,再选择合适的操作去解决它。」10

把这句话落成可执行的三问:

  问 1  我要回答什么问题?(比如「哪家订房公司贡献最多收入」)
  问 2  回答它需要哪几列、干净到什么程度?(公司列必须无缺失 → bfill 那步是必需的)
  问 3  哪些操作是「为了演示」而其实可以不做?(造日期列、造价格列——真实问题里没有这两问)
  图说:三问过滤后,17 章的十步操作可能只剩五步;省下的五步不是浪费,是演示税。

这道过滤也是区分「教程数据」和「业务数据」的分水岭:教程里每个操作都有展示价值, 业务里每个操作都要有问题价值——操作不是问题驱动的,再熟练也是杂技。

4. 作者的判断与证据

书里给了证据的: 每步操作的中间输出(掩码、bfill 前后的列)都完整打印,可直接对照; 「AI 帮我写」的自白是少见的诚实。56

那句自白也值一句展开:「我必须承认这段代码是拿 AI 工具生成的,这个技巧我自己想不出来」—— 2026 年的编程书里出现这句话,比任何一章都更能说明这本书的「诚实」基调: 作者不装全知,技巧来源照实交代。读者该学的不是「别用 AI」,而是用 AI 之后依然逐行看懂—— 看不懂的代码进不了自己的系统,这条规矩不因生成工具而改变。

作者的判断:

  • 「先理解问题,再选操作」——正确且被普遍忽视;
  • 「一次只做一件事」(删公司行和筛无效公司不该合并成一步)——流程纪律,与第 05 章字符串原则同构;
  • 价格与月份的相关性解读——他点明了这是人造相关,没有过度解读。

判断(我们的,不是书里的): 这份数据的真正病根不在 NaN,而在「订房公司写在段尾行」这个反常规格式—— Pandas 技巧(bfill)只是止痛药,根治是让上游按 CSV/JSON 的正常形状给数据(书里也这么批评了原始数据)。 工程上请记住优先级:修上游 > 读时规范化 > 清洗时打补丁。 如果错,会错在: 你控制不了上游(别人的导出、历史遗产)时,「读时规范化」也做不到, bfill 这类补丁就是主战场——所以技巧本身必须会,只是别把它当成数据设计的范本。

另一条判断(我们的): 这一章最值得模仿的不是任何函数,是作者「边洗边打印中间结果」的节奏—— 每步之后 head() 一次,肉眼对账。数据清洗的错误模式是「一步写十行、跑完才发现错了十行」; 他的节奏把错误隔离在一步之内。这个节奏与 TDD 的红绿循环是同构的:小步、可见、可回退。 如果错,会错在: 数据大到 head() 看不见代表性问题时(十亿行里的 0.1% 脏数据), 抽样检查会给出虚假安全感;那时要换「分布检查」(每列的取值统计)而不是「眼看几行」。

4.5 本章在全书里的位置

这一章夹在 Matplotlib 和数值数学之间,位置不是随手排的:

  16 章:画出数据的形状(单变量直觉)
  17 章:把脏数据洗成能算的形状(本章)
  18 章:对干净数据做计算(积分、仿真)
  图说:科学计算的流水线 = 看见 → 洗净 → 计算;Pandas 是「洗净」环节的默认工具。

读完三章连起来,你会拿到科研日常的真正高频组合:read_csv → 清洗 → 画图确认 → 算指标。 原书把这三章排在书的最后三分之一,也是同一个意思:语言课在前,流水线的每个环节各一章在后。

5. 边界与局限

  • groupby(分组聚合)——Pandas 最高频的操作——本章完全没有;count/sum/corr 只是单列入门;
  • merge(表连接)没提;时间序列按周按月重新对齐没提;
  • inplace=True 的用法在现代 Pandas 社区已被建议避免(链式写法更安全),书里还在用;
  • NaN 的处理策略(删掉 vs 填补 vs 保留)只给了「视目标而定」一句话,没有决策框架; 补一个最小框架(书外):看这一列对问题是否关键——关键列缺值就查上游,非关键列缺值才谈填补或丢弃;
  • 没有讲「清洗脚本要可重跑」:所有操作从原始文件单向流到结果,中间不落盘改原始数据—— 书里没写,但这是数据清洗的第一纪律(覆盖了原始文件,就没有后悔药);
  • 「相关性」一段是演示而非分析:真实分析要先排除结构性来源(书里点到了,未展开)。

5.5 版本时效

书写于 2026 年,Pandas 2.x 时代的语法(read_csv 参数、字符串列的 .str 访问器) 与今天一致;但 Pandas 生态正在分叉——Polars(更快的替代品)2026 年的份额持续上升(书外观察)。 学本章的心智模型(表、列、掩码),两个库通用;具体函数名以你项目里实际用的那个为准。

另外两条书外但关键的工程习惯,与本章直接相关:其一,清洗脚本要可重跑——从原始文件单向流到结果, 中间绝不改原始数据;其二,每一步的删除操作前先数行(len(data) 前后各打一次), 删多了立刻发现。两条都是「不可逆操作」的保险带,书里没写,但补上只需两行代码,省下的可能是半天的事故排查——以及在同事面前重新解释「数据去哪了」的尴尬。

6. 可带走的

本章主线例子,一行写完: read_csv(sep="\t") 读脏表 → isna() 造掩码 → np.where + bfill 回填公司名 → ~mask 删脏行 → astype(int) 修类型 → 整列运算造价格 → sort_values 排序。2

这一行流水线如果漏背了,记这个最小骨架就够:读 → 看 → 掩码 → 填 → 筛 → 改 → 算 → 排—— 八个字,本章每节各占一个。2

  1. Pandas = NumPy 上的表格层:DataFrame 是带名字的列;大表也高效。
  2. 心智模型:先造布尔掩码,再拿它筛/改——别用循环逐行。
  3. head() 先看清结构再动手;读入第一眼要诊断的三件事:有没有特殊行、缺什么、类型对不对。
  4. NaN 是缺失的占位符;它会把整列拖成浮点,删空后记得 astype 修回来。
  5. bfill 从下面最近的有效值回填;它返回新列,要赋回去。
  6. 整列运算是常态:100 + 列//100 + 列.dt.month 不需要循环。
  7. 一次只做一件事:删公司行和筛公司别合并;dropna 会把两件事混成一步,慎用。11
  8. 行索引删行后不粘重排——行号是标签不是位置。12
  9. 统计前先想数据的来历:公式造的数据,相关是人为的。
  10. 先理解问题再选操作;函数不用背,背「问题 → 操作」的映射。
  11. 表→列→掩码等长对齐是 Pandas 的地基;对齐不上的操作会直接报错。
  12. bfill 看信息在段的哪一端;「返回新对象」的脾气意味着:记得赋回去。
  13. 每步的前提要可陈述(为什么现在能转型),可陈述才可交接。
  14. 删改前先数行:不可逆操作前 len(data) 打两次,两行代码买半天排查时间——这买卖在任何市场都该做,在数据市场尤其该做——数据不会自己报错的,它只会悄悄错给你看——在你最不需要的时候——比如汇报前的那个晚上——所以,白天就把这两行写上,别等晚上。

7. 原文地图

全章操作速查与出处的对照:上面 §2 的每个小节都能在下表找到原文行,读不懂某一节时, 先回原书看那一段,再回来——大多数卡点是因为跳过了原书的一句铺垫——原书不贵,卡点时值得打开;打不开的时候,这张表就是你的临时原书——它把每一节的论证地基都钉在了原文的具体段落上——这张表本身就是「出处纪律」的一次演示:每条论断都能被一枚短语找回,这在这种体量的拆解里并不常见,值得珍惜——尤其是当多年后某个断言需要复核时——能找回出处的断言,才敢继续往上盖楼;找不回的,迟早要拆——拆的时候,别问是谁写的——多半是你自己。

主题 原书章 原文位置
Pandas 定位(基于 NumPy) 12 Pandas text/28-ch12-12-pandas.txt:4(搜「built on top of NumPy」)
数据集的来历与毛病诊断 12 Pandas text/28-ch12-12-pandas.txt:16(搜「barely readable」) · text/28-ch12-12-pandas.txt:44(搜「hard to understand the data」)
read_csv 与 head 12 Pandas text/28-ch12-12-pandas.txt:52(搜「read_csv」) · text/28-ch12-12-pandas.txt:53(搜「head(12)」)
NaN 与脏行观察 12 Pandas text/28-ch12-12-pandas.txt:46(搜「NaN」)
布尔掩码 12 Pandas text/28-ch12-12-pandas.txt:73(搜「mask」)
np.where 造列 12 Pandas text/28-ch12-12-pandas.txt:92(搜「np.where」)
bfill 与「要赋回去」 12 Pandas text/28-ch12-12-pandas.txt:110(搜「bfill」) · text/28-ch12-12-pandas.txt:203(搜「in place」)
~mask 删行与「AI 帮我写」 12 Pandas text/28-ch12-12-pandas.txt:162(搜「element wise」) · text/28-ch12-12-pandas.txt:166(搜「AI tool」)
astype 修类型 12 Pandas text/28-ch12-12-pandas.txt:184(搜「astype」)
drop 列与 inplace 12 Pandas text/28-ch12-12-pandas.txt:202(搜「drop」)
造日期与造价格 12 Pandas text/28-ch12-12-pandas.txt:225(搜「date_range」) · text/28-ch12-12-pandas.txt:246(搜「price」)
count/sum/corr 12 Pandas text/28-ch12-12-pandas.txt:267(搜「number_of_bookings」) · text/28-ch12-12-pandas.txt:273(搜「corr」)
排序 12 Pandas text/28-ch12-12-pandas.txt:280(搜「sort_values」)
「先理解问题」收尾 12 Pandas text/28-ch12-12-pandas.txt:298(搜「opposite approach」)

  1. 出处:「12 Pandas」第 3–9 段(text/28-ch12-12-pandas.txt:4,搜「built on top of NumPy」)。 ↩

  2. 出处:「12 Pandas」第 11–71 段(text/28-ch12-12-pandas.txt:52,搜「read_csv」;「Hotels/Booking 行」的诊断见第 40–47 段)。 ↩↩↩↩↩

  3. 出处:「12 Pandas」第 46–47 段(text/28-ch12-12-pandas.txt:46,搜「filled with a NaN」)。 ↩

  4. 出处:「12 Pandas」第 72–90 段(text/28-ch12-12-pandas.txt:76,搜「Boolean mask」)。 ↩↩

  5. 出处:「12 Pandas」第 90–148 段(text/28-ch12-12-pandas.txt:92,搜「np.where」;「backward fill」与缩写解释见第 110–111 段;「不改原列」见第 115 段,搜「in place」)。 ↩↩↩↩↩↩↩

  6. 出处:「12 Pandas」第 160–167 段(text/28-ch12-12-pandas.txt:163,搜「~mask」;「AI 工具帮我写」见第 166 段,搜「AI tool」)。 ↩↩

  7. 出处:「12 Pandas」第 182–204 段(text/28-ch12-12-pandas.txt:183,搜「float and not int」;drop 见第 203 段,搜「inplace」)。 ↩↩↩

  8. 出处:「12 Pandas」第 221–262 段(text/28-ch12-12-pandas.txt:225,搜「date_range」;价格公式见第 246–247 段)。 ↩↩↩

  9. 出处:「12 Pandas」第 264–295 段(text/28-ch12-12-pandas.txt:274,搜「corr」;解读见第 277–278 段,搜「relates positively」)。 ↩↩↩↩

  10. 出处:「12 Pandas」第 296–299 段(text/28-ch12-12-pandas.txt:298,搜「opposite approach」)。 ↩↩↩

  11. 出处:「12 Pandas」第 150–161 段(text/28-ch12-12-pandas.txt:155,搜「dropna」;「two things in one step」见第 160 段,搜「only one thing at a time」)。 ↩↩

  12. 出处:「12 Pandas」排序输出(text/28-ch12-12-pandas.txt:284,搜「166」——排序后首行行号 166,即原始行号)。 ↩↩

  13. 出处:「12 Pandas」第 51–69 段(text/28-ch12-12-pandas.txt:57,搜「Room number」——head(12) 的打印输出,行索引列在最左)。 ↩