⚠ 交易存在重大亏损风险。本网站的所有内容仅供教育用途,不构成财务建议。
第 3 课,共 13 课 · 免费

pandas 基础:Series、DataFrame、DatetimeIndex、选择与筛选,以及把日线重采样为周线

理解 Series 和 DataFrame,按日期为价格数据建立索引,用 loc 和 iloc 选择行,用布尔掩码进行筛选,并把日线汇总为周线。

作者: Moustafa Hijazi内容审核日期: 2026-10-30阅读约 8 分钟自学课程

你将学到什么

  • 什么是 Series 和 DataFrame,以及为什么 DatetimeIndex 是价格分析的骨架。
  • 基于标签的 loc 与基于位置的 iloc 有何区别,以及布尔掩码如何筛选行。
  • 如何用 resample 并为每一列采用正确的聚合方式,从日线构建周线或月线。
  • pandas 3.0 版本改变了哪些默认行为,以及如何编写在 2.2 和 3.x 上表现一致的代码。

讲解

Series 是一维的带标签数组:一列收盘价,每个值都附带一个日期。DataFrame 是共享同一个索引的多个 Series 组成的表格:开盘价、最高价、最低价、收盘价和成交量并排排列。索引保存行标签;对于价格数据,它应当是 DatetimeIndex,这是一种专门的时间戳索引,支持按日期切片、重采样和时区处理。惯例写法:import pandas as pd。

用一个由列组成的字典和一个日期索引创建数据框:

idx = pd.bdate_range("2025-03-03", periods=10)

df = pd.DataFrame({"open": [...], "high": [...], "low": [...], "close": [...], "volume": [...]}, index=idx)

bdate_range 只生成工作日,对交易所假期一无所知;真实数据来自文件(第 4 课)。用 df.head()、df.info()(类型和缺失值)以及 df.describe() 进行检查。df.index.dtype 必须显示 datetime64;如果显示 object,说明日期仍然是文本。

选择。df["close"] 返回一个 Series;df[["open", "close"]](一个列表)返回一个 DataFrame。df.loc[row_labels, columns] 按标签选择:df.loc["2025-03-05":"2025-03-07", "close"] 给出这三个日期的收盘价;与 Python 列表不同,使用 loc 时切片的两端都包含在内。df.iloc[0:3] 按整数位置选择(前三行,不含终点),df.iloc[-1] 是最新的一根 K 线。

筛选。对 Series 进行比较会得到一个布尔值 Series,即掩码:up = df["close"] > df["open"]。传入掩码即可选出匹配的行:df[up]。用 &(与)、|(或)和 ~(非)组合掩码,每个条件放在各自的括号中:df[(df["close"] > df["open"]) & (df["volume"] > 1200)]。up.sum() 统计 True 值的个数,up.mean() 则是上涨日所占的比例。

添加列就是赋值:df["range"] = df["high"] - df["low"]。运算是向量化的,即一次作用于整列,这正是 pandas 速度快的原因。shift(1) 把值向下移动一行(把昨天的收盘价放到今天这一行),pct_change() 给出百分比变化;两者都会在第一行留下 NaN(not a number,非数值)。

重采样。resample 把行分组到固定的时间区间中。每一列都需要自己的规则:周开盘价是第一个日开盘价,最高价取最大值,最低价取最小值,收盘价取最后一个值,成交量取总和:

rules = {"open": "first", "high": "max", "low": "min", "close": "last", "volume": "sum"}

weekly = df.resample("W-FRI").agg(rules)

代码 "W-FRI" 用每周的星期五来标记该周。月线使用 "ME"(月末)。较早的教程写作 "M";pandas 2.2 已弃用该别名,改用 "ME"("QE" 和 "YE" 同理),使用新别名编写的代码在 2.2 和 3.x 上都能运行。

值得了解的 pandas 3.0 变化。写时复制(Copy-on-Write)现在是唯一的模式:筛选出的数据框是一个独立副本,因此 df[mask]["close"] = 0 永远不会修改 df;请写成 df.loc[mask, "close"] = 0。文本列的 dtype 现在是 str,而不是 object。

实例演算

假设示例:从 2025 年 3 月 3 日(星期一)到 3 月 14 日(星期五)的十根日线,价格以欧元计,没有假期。

日期 开盘价 最高价 最低价 收盘价 成交量
3 月 3 日 100 102 99 101 1,000
3 月 4 日 101 104 100 103 1,200
3 月 5 日 103 104 101 102 900
3 月 6 日 102 105 101 104 1,100
3 月 7 日 104 106 103 105 1,500
3 月 10 日 105 106 103 104 1,300
3 月 11 日 104 107 103 106 1,000
3 月 12 日 106 108 105 107 1,400
3 月 13 日 107 109 106 108 1,200
3 月 14 日 108 110 106 107 1,600

重采样后得到两行。截至 3 月 7 日的一周:开盘价 = 第一个开盘价 = 100;最高价 = max(102, 104, 104, 105, 106) = 106;最低价 = min(99, 100, 101, 101, 103) = 99;收盘价 = 最后一个收盘价 = 105;成交量 = 1,000 + 1,200 + 900 + 1,100 + 1,500 = 5,700。截至 3 月 14 日的一周:开盘价 105,最高价 110,最低价 103,收盘价 107,成交量 = 1,300 + 1,000 + 1,400 + 1,200 + 1,600 = 6,500。

筛选:(df["close"] > df["open"]).sum() 返回 7,因为只有 3 月 5 日、10 日和 14 日收盘价低于开盘价。df.loc["2025-03-05":"2025-03-07", "close"] 返回 102、104、105。

常见错误

混淆 loc 与 iloc,并忘记 loc 包含终点df.iloc[0:5] 返回五行(位置 0 到 4),但 df.loc["2025-03-03":"2025-03-07"] 返回直到 3 月 7 日(含)的每一行。“前 N 根”或“最后一根 K 线”请用 iloc,时间段请用带明确日期的 loc。

练习活动

使用上面的十根 K 线表格,在纸上完成:(a) 写出选择 3 月 10 日至 12 日(含两端)K 线的 pandas 表达式;(b) 说明 df.iloc[-3:]["close"].tolist() 返回什么;(c) 计算那一根月线,即 df.resample("ME").agg(rules) 会生成的结果;(d) 解释如果有人对所有列都写 df.resample("ME").last() 会出什么问题。

显示答案

(a) df.loc["2025-03-10":"2025-03-12"],三行,两端都包含在内。

(b) 最后三个收盘价:[107, 108, 107]。

(c) 一行,标签为 2025 年 3 月 31 日(月末):开盘价 100(第一个),最高价 110(最大值),最低价 99(最小值),收盘价 107(最后一个),成交量 12,200(5,700 + 6,500)。

(d) .last() 对每一列都取最后一个值:月最低价会变成 106 而不是 99,开盘价变成 108 而不是 100,成交量变成 1,600 而不是 12,200。务必传入按列设定的规则。

局限性:这个例子没有假期或缺失的交易日;在真实数据中,包含假期的一周只会汇总四根 K 线;即使最后一个交易日是 3 月 28 日,月标签仍会显示为“3 月 31 日”。

要点回顾

  • 价格数据存放在带有 DatetimeIndex 的 DataFrame 中;在做任何事之前先检查 df.index.dtype。
  • loc 按标签选择并包含切片终点;iloc 按位置选择且不包含终点。
  • 用 &、| 和 ~ 组合的布尔掩码可以代替循环进行筛选,对掩码调用 .sum() 可统计匹配数。
  • 对 OHLCV 重采样时使用 first/max/min/last/sum,使用 "W-FRI" 和 "ME" 别名,并在赋值时记住 pandas 3.0 的写时复制机制。

下一步

第 4 课引入真实数据:读取 CSV 文件和免费 API,理解复权价格与未复权价格,以及处理缺失值和时区。

自测

回答问题后核对答案。你可以随意重试多次。

1. 哪一个聚合字典能把日线 OHLCV 数据正确转换为周线?

2. 一个 DataFrame 有 250 行日线数据。如果第二个区间恰好包含 10 个交易日(含两个端点日期),df.iloc[0:10] 和 df.loc['2025-01-02':'2025-01-15'] 分别返回多少行?

3. 在 pandas 3.0 中,你运行 df[df['volume'] > 1000]['close'] = 0,却发现 df 没有变化。为什么?

供您自行复习的自动自测。您的答案在浏览器中核对,我们不会查看或评估。这不是考试,也不是资格证书。

您的进度

“已完成”仅表示您将本课标记为已学完,并不是对您理解程度的测试。 学习进度和笔记仅保存在此设备的此浏览器中,而非账户中。