Pandas-学习手册中文第二版-全-

Pandas 学习手册中文第二版(全)

原文:Learning pandas

协议:CC BY-NC-SA 4.0

零、前言

Pandas 是一种流行的 Python 包,用于实际的实际数据分析。 它提供了高效,快速和高性能的数据结构,使数据探索和分析变得非常容易。 本学习指南将帮助您了解 pandas 库提供的一组全面功能,以执行有效的数据操作和分析。

本书涵盖的内容

第 1 章,“Pandas 和数据分析”是对 Pandas 关键功能的动手介绍。 本章旨在为在统计和数据科学领域中使用 Pandas 提供一些背景。 本章将探讨数据科学中的几个概念,并展示 Pandas 如何支持它们。 这将为后续各章设置背景,并提到每章都与数据科学和数据科学过程相关。

第 2 章,“启动和运行 Pandas”指导读者获取和安装 Pandas,并介绍一些 Pandas 的基本概念。 我们还将研究如何使用 iPython 和 Juypter 笔记本呈现示例。

第 3 章,“用序列表示单变量数据”,引导读者逐步使用 pandas 序列,该序列提供一维索引数据表示。 读者将了解如何创建序列对象以及如何操作其中包含的数据。 他们还将学习数据的索引和对齐方式,以及如何使用序列来切片数据。

第 4 章,“用数据帧表示表格和多元数据”引导读者了解 pandas 数据帧的基本用法,该方法提供并索引了多元数据表示形式。 本章将指导读者使用各种静态数据集创建数据帧对象,以及如何执行其中特定列和行的选择。 现在,下一章将处理复杂的查询,操作和索引编制。

第 5 章,“数据帧对象的操作和索引”在上一章中进行了扩展,并指导您如何执行对数据帧的更复杂的操作。 我们首先学习如何添加,删除和删除列和行; 修改数据帧中的数据(或创建修改后的副本); 对其中的数据进行计算; 创建分层索引; 并根据数据帧内容计算常见的统计结果。

第 6 章,“索引数据”显示了如何将数据从外部源加载和保存到序列和数据帧对象中。 本章还介绍了来自多个源的数据访问,例如文件,http 服务器,数据库系统和 Web 服务。 还介绍了 CSV,HTML 和 JSON 格式的数据处理。

第 7 章,“类别数据”指导读者如何使用 Pandas 提供的各种工具来管理脏数据和缺失数据。

第 8 章,“数值和统计方法”涵盖了各种用于组合,分割,合并和合并位于多个 Pandas 对象中的数据的技术,然后是如何使用数据透视,堆叠和融合等概念重塑数据。

第 9 章,“访问数据”讨论了分组和执行聚合数据分析。 在 Pandas 中,这通常称为拆分应用组合模式。 读者将学习如何使用此模式将数据按各种不同的配置进行分组,并且还将应用聚合函数来计算每组数据的结果。

第 10 章,“整理数据”解释了如何以整洁的形式组织数据,该数据可用于数据分析。

第 11 章,“组合,关联和重塑数据”告诉读者如何通过连接,合并和连接之类的概念,在多个 Pandas 对象中获取数据并将其组合。

第 12 章,“数据聚合”深入研究了 Pandas 与 matplotlib 的集成,以可视化 Pandas 数据。 本章将演示如何呈现许多常见的统计和财务数据可视化效果,包括条形图,直方图,散点图,面积图,密度图和热图。

第 13 章,“时间序列建模”涵盖了代表 Pandas 中的时间序列数据。 本章将介绍 Pandas 提供的广泛功能,以便于分析时间序列数据。

第 14 章,“可视化”教您如何基于存储在 Pandas 数据结构中的数据创建数据可视化。 我们从基础学习开始,如何从数据创建简单的图表并控制图表的几个属性(例如图例,标签和颜色)。 我们检查了几种常见图类型的创建,这些图用于表示不同类型的数据,这些类型的图使用这些图类型在基础数据中传达含义。 我们还将学习如何将 Pandas 与 D3.js 集成在一起,以便我们可以创建基于 Web 的丰富可视化。

第 15 章,“历史股价分析”向您展示如何将 Pandas 应用于基本的财务问题。 它将重点关注从 Yahoo 获得的数据。 财务,并将在财务数据中演示许多财务概念,例如计算收益,滚动平均值,波动率和其他几个概念。 学生还将学习如何将数据可视化应用于这些财务概念。

这本书需要什么

本书假定您对编程概念有所了解,但是没有编程经验,尤其是没有 Python 编程经验的人,对这些示例将感到满意,因为它们将重点放在 Pandas 构造上,而不是 Python 或编程上。 这些示例基于 Anaconda Python 2.7 和 pandas 0.15.1。 如果您尚未安装 Pandas,则将在第 2 章,“启动并运行 Pandas”中提供有关在 Windows,OSX 和 Ubuntu 系统上安装 Pandas 的指南。 对于那些对安装任何软件感兴趣的人,还提供了有关使用 Warkari.io 在线 Python 数据分析服务的说明。

这本书是给谁的

本书非常适合希望使用 Pandas 进行数据分析的数据科学家,数据分析师和 Python 程序员,以及任何对数据分析感兴趣的人。 一些统计和编程知识将帮助您充分利用本书,但这不是严格要求的。 也不需要事先接触 Pandas。

约定

在本书中,您会发现许多可以区分不同类型信息的文本样式。 以下是这些样式的一些示例,并解释了其含义。

文本中的代码字如下所示:“可以使用pd.read_csv()函数轻松地将此信息导入到数据帧中,如下所示。”

在 Python 解释器中输入的代码块设置如下:

import pandas as pd
df = pd.DataFrame.from_items([('column1', [1, 2, 3])])
print (df)

任何命令行输入或输出的编写方式如下:

mh@ubuntu:~/Downloads$ chmod +x Anaconda-2.1.0-Linux-x86_64.sh
mh@ubuntu:~/Downloads$ ./Anaconda-2.1.0-Linux-x86_64.sh

新术语重要词以粗体显示。 您在屏幕上看到的字词,例如在菜单或对话框中的字样如下所示:“本书中的快捷方式基于Mac OS X 10.5+方案”。

警告或重要提示会出现在这样的框中。

提示和技巧如下所示。

一、Pandas 与数据分析

欢迎来到《Pandas 学习手册》! 在本书中,我们将进行一次探索我们学习 Pandas 的旅程,这是一种用于 Python 编程语言的开源数据分析库。 pandas 库提供了使用 Python 构建的高性能且易于使用的数据结构和分析工具。 pandas 从统计编程语言 R 中带给 Python 许多好处,特别是数据帧对象和 R 包(例如plyrreshape2),并将它们放置在一个可在内部使用的 Python 库中。

在第一章中,我们将花一些时间来了解 Pandas 及其如何适应大数据分析的需要。 这将使对 Pandas 感兴趣的读者感受到它在更大范围的数据分析中的地位,而不必完全关注使用 Pandas 的细节。 目的是在学习 Pandas 的同时,您还将了解为什么存在这些功能以支持执行数据分析任务。

因此,让我们进入。在本章中,我们将介绍:

  • Pandas 是什么,为什么被创造出来,它给您带来什么
  • Pandas 与数据分析和数据科学之间的关系
  • 数据分析涉及的过程以及 Pandas 如何支持
  • 数据和分析的一般概念
  • 数据分析和统计分析的基本概念
  • 数据类型及其对 Pandas 的适用性
  • 您可能会与 pandas 一起使用的 Python 生态系统中的其他库

Pandas 介绍

pandas 是一个 Python 库,其中包含高级数据结构和工具,这些数据结构和工具已创建来帮助 Python 程序员执行强大的数据分析。 Pandas 的最终目的是帮助您快速发现数据中的信息,并将信息定义为基本含义。

Wes McKinney 于 2008 年开始开发 Pandas。 它于 2009 年开源。Pandas 目前受到各种组织和贡献者的支持和积极开发。

最初设计 Pandas 时要考虑到财务问题,特别是它具有围绕时间序列数据操作和处理历史股票信息的能力。 财务信息的处理面临许多挑战,以下是一些挑战:

  • 表示随着时间变化的安全数据,例如股票价格
  • 在相同时间匹配多个数据流的度量
  • 确定两个或多个数据流的关系(相关性)
  • 将时间和日期表示为实体流
  • 向上或向下转换数据采样周期

要进行此处理,需要使用一种工具,使我们能够对单维和多维数据进行检索,索引,清理和整齐,整形,合并,切片并执行各种分析,包括沿着数据自动对齐的异类数据。 一组公共索引标签。 这是 Pandas 诞生的地方,它具有许多有用而强大的功能,例如:

  • 快速高效的SeriesDataFrame对象,通过集成索引进行数据处理
  • 使用索引和标签进行智能数据对齐
  • 整合处理缺失数据
  • 将杂乱数据转换(整理)为有序数据的工具
  • 内置工具,用于在内存数据结构与文件,数据库和 Web 服务之间读写数据
  • 处理以许多常见格式(例如 CSV,Excel,HDF5 和 JSON)存储的数据的能力
  • 灵活地重塑和透视数据集
  • 大型数据集的基于智能标签的切片,花式索引和子集
  • 可以从数据结构中插入和删除列,以实现大小调整
  • 使用强大的数据分组工具聚合或转换数据,来对数据集执行拆分应用合并
  • 数据集的高性能合并和连接
  • 分层索引有助于在低维数据结构中表示高维数据
  • 时间序列数据的广泛功能,包括日期范围生成和频率转换,滚动窗口统计,滚动窗口线性回归,日期平移和滞后
  • 通过 Cython 或 C 编写的关键代码路径对性能进行了高度优化

强大的功能集,以及与 Python 和 Python 生态系统内其他工具的无缝集成,已使 Pandas 在许多领域得到广泛采用。 它被广泛用于学术和商业领域,包括金融,神经科学,经济学,统计学,广告和网络分析。 它已成为数据科学家表示数据进行操作和分析的最优选工具之一。

长期以来,Python 在数据处理和准备方面一直是例外,但在数据分析和建模方面则例外。 pandas 帮助填补了这一空白,使您能够在 Python 中执行整个数据分析工作流,而不必切换到更特定于领域的语言(例如 R)。这非常重要,因为熟悉 Python 的人比 R(更多的统计数据包),获得了 R 的许多数据表示和操作功能,同时完全保留在一个极其丰富的 Python 生态系统中。

与 IPython,Jupyter 笔记本和众多其他库相结合,与许多其他工具相比,用于在 Python 中执行数据分析的环境在性能,生产力和协作能力方面表现出色。 这导致许多行业的许多用户广泛采用 Pandas。

数据处理,分析,科学和 Pandas

我们生活在一个每天都会产生和存储大量数据的世界中。 这些数据来自大量的信息系统,设备和传感器。 您所做的几乎所有操作以及用于执行此操作的项目都会生成可以捕获或捕获的数据。

连接到网络的服务无处不在的性质以及数据存储设施的大量增加极大地支持了这一点。 这与不断降低的存储成本相结合,使捕获和存储甚至最琐碎的数据都变得有效。

这导致堆积了大量数据并准备好进行访问。 但是,该数据分布在整个网络空间中,实际上不能称为信息。 它往往是事件记录的集合,无论是财务记录,您与社交网络的互动,还是全天跟踪您的心跳的个人健康监控器。 这些数据以各种格式存储,位于分散的位置,并且其原始性质的确能提供很多洞察力。

从逻辑上讲,整个过程可以分为三个主要学科领域:

  • 数据处理
  • 数据分析
  • 数据科学

这三个学科可以而且确实有很多重叠之处。 各方结束而其他各方开始的地方可以解释。 为了本书的目的,我们将在以下各节中对其进行定义。

数据处理

数据分布在整个地球上。 它以不同的格式存储。 它的质量水平差异很大。 因此,需要用于将数据收集在一起并转化为可用于决策的形式的工具和过程。 这需要操作数据以进行分析准备的工具需要执行许多不同的任务和功能。 该工具需要的功能包括:

  • 重用和共享的可编程性
  • 从外部来源访问数据
  • 在本地存储数据
  • 索引数据来高效检索
  • 根据属性对齐不同集合中的数据
  • 合并不同集合中的数据
  • 将数据转换为其他表示形式
  • 清除数据中的残留物
  • 有效处理不良数据
  • 将数据分组到通用篮子中
  • 聚合具有相似特征的数据
  • 应用函数计算含义或执行转换
  • 查询和切片来探索整体
  • 重组为其他形式
  • 为不同类型的数据建模,例如类别,连续,离散和时间序列
  • 将数据重新采样到不同的频率

存在许多数据处理工具。 每个人对此列表中的项目的支持,部署方式以及用户如何使用都各不相同。 这些工具包括关系数据库(SQL Server,Oracle),电子表格(Excel),事件处理系统(例如 Spark)以及更通用的工具(例如 R 和 Pandas)。

数据分析

数据分析是从数据创建含义的过程。 具有量化含义的数据通常称为信息。 数据分析是通过创建数据模型和数学模型来从数据中创建信息的过程。 它经常与数据操作重叠,并且两者之间的区别并不总是很清楚。 许多数据处理工具还包含分析功能,并且数据分析工具通常提供数据处理功能。

数据科学

数据科学是使用统计和数据分析过程来了解数据中现象的过程。 数据科学通常从信息开始,然后对信息进行更复杂的基于领域的分析。 这些领域涵盖许多领域,例如数学,统计学,信息科学,计算机科学,机器学习,分类,聚类分析,数据挖掘,数据库和可视化。 数据科学是多学科的。 它的域分析方法通常非常不同,并且特定于特定域。

Pandas 适合什么?

Pandas 首先在数据处理方面表现出色。 本书将使用 Pandas 满足前面列出的所有需求。 这是 Pandas 的核心,也是本书重点关注的内容。

值得注意的是,Pandas 有一个特定的设计目标:强调数据

但是 Pandas 确实提供了执行数据分析的多种功能。 这些功能通常围绕描述性统计和财务所需的功能(例如相关性)。

因此,Pandas 本身不是数据科学工具包。 它更多是具有某些分析功能的操纵工具。 Pandas 明确地将复杂的统计,财务和其他类型的分析留给了其他 Python 库,例如 SciPy,NumPy,scikit-learn,并依赖于图形库,例如 matplotlibggvis 用于数据可视化。

这种关注点实际上是 Pandas 相对于 R 等其他语言的强项,因为 Pandas 应用能够利用 Python 社区在其他地方已经构建和测试的强大的 Python 框架的广泛网络。

数据分析过程

本书的主要目的是彻底地教您如何使用 Pandas 来操纵数据。 但是,还有一个次要的,也许同样重要的目标,是显示 Pandas 如何适应数据分析师/科学家在日常生活中执行的过程。

Pandas 网站上对数据分析过程中涉及的步骤进行了描述:

  • 清除和清洁数据
  • 分析/建模
  • 组织成适合交流的形式

这个小的清单是一个很好的初始定义,但是它无法涵盖过程的整体范围以及创建 Pandas 中实现的许多功能的原因。 以下内容将对此过程进行扩展,并为整个旅程中的过程设置框架。

过程

所建议的过程将被称为数据流程,并在下图中表示:

该过程建立了一个框架,用于定义处理数据时要采取的逻辑步骤。 现在,让我们快速看一下该过程中的每个步骤,以及作为使用 Pandas 的数据分析员将执行的一些任务。

重要的是要了解这不是纯粹的线性过程。 最好以高度交互和敏捷/迭代的方式完成。

构想

任何数据问题的第一步都是确定要解决的问题。 这就是构想,它提出了我们想要做和证明的事情的构想。 构想通常涉及对可用于做出明智决策的数据模式进行假设。

这些决策通常是在企业范围内,但在其他学科(例如科学和研究)中也是如此。 目前正在流行的事情是了解企业的​​运营,因为在理解数据时通常会赚很多钱。

但是,我们通常希望做出什么样的决定? 以下是几个常见问题的答案:

  • 为什么会发生什么事?
  • 我们可以使用历史数据预测未来吗?
  • 将来如何优化操作?

这份清单绝非详尽无遗,但确实涵盖了任何人进行这些努力的原因的相当大的百分比。 要获得这些问题的答案,必须参与收集和理解与该问题有关的数据。 这涉及定义要研究的数据,研究的收益,如何获得数据,成功的标准是什么以及最终如何传递信息。

Pandas 本身不提供辅助构想的工具。 但是一旦您掌握了使用 Pandas 的知识和技巧,您就会自然地意识到 Pandas 将如何帮助您提出构想。 这是因为您将拥有一个强大的工具,可以用来构建许多复杂的假设。

检索

一旦有了想法,就必须找到数据来尝试并支持您的假设。 这些数据可以来自组织内部或外部数据提供者。 该数据通常以存档数据的形式提供,也可以实时提供(尽管以实时数据处理工具而闻名的 Pandas)。

即使从您创建的数据源或从组织内部获取数据,数据也通常是非常原始的。 原始数据意味着数据可能是杂乱无章的,可能是各种格式,而且是错误的; 相对于支持您的分析,它可能是不完整的,需要手动进行扩充。

世界上有很多免费数据。 许多数据不是免费的,实际上要花费大量金钱。 其中一些可通过公共 API 免费获得,其他一些则通过订阅获得。 您所支付的数据通常更干净,但这并非总是如此。

在这两种情况下,Pandas 都提供了一套强大且易于使用的工具,用于从各种来源检索数据,并且这些数据可能采用多种格式。 pandas 还使我们不仅能够检索数据,还可以通过 pandas 数据结构提供数据的初始结构,而无需手动创建其他工具或编程语言可能需要的复杂编码。

准备

在准备过程中,已准备好原始数据以供探索。 准备工作通常是一个非常有趣的过程。 通常情况下,来自数据的数据会涉及与质量相关的各种问题。 您可能会花费大量时间来处理这些质量问题,而这通常是非常短的时间。

为什么? 嗯,原因有很多:

  • 数据根本不正确
  • 缺少部分数据集
  • 无法使用适合您分析的度量来表示数据
  • 数据格式不便于您分析
  • 数据的详细程度不适合您的分析
  • 并非所有需要的字段都可以从一个来源获得
  • 数据的表示因提供者而异

准备过程着重解决这些问题。 Pandas 为准备数据(通常称为整理数据)提供了许多便利的工具。 这些功能包括处理缺失数据,转换数据类型,使用格式转换,更改测量频率,将来自多组数据的数据连接,将符号映射/转换为共享表示以及将数据分组的智能方法。 我们将深入探讨所有这些内容。

探索

探索涉及能够交互式地对数据进行切片和切块,以尝试快速发现。 探索可以包括各种任务,例如:

  • 检查变量之间的相互关系
  • 确定数据的分发方式
  • 查找和排除异常值
  • 创建快速的可视化
  • 快速创建新的数据表示形式或模型来馈入更永久和详细的建模过程

探索是 Pandas 的一大优势。 虽然可以使用大多数编程语言进行探索,但是每种语言都有其自己的仪式级别 -- 在实际发现之前,必须执行多少非探索性工作。

当与 IPython 和/或 Jupyter 笔记本的读取-求值-打印-循环REPL)性质一起使用时,Pandas 会创建一个几乎没有仪式的探索性环境。 pandas 语法的表现力使您可以简洁地描述复杂的数据操作结构,并且对数据执行的每个操作的结果都将立即呈现出来供您检查。 这使您可以快速确定刚刚执行的操作的有效性,而不必重新编译并完全重新运行程序。

建模

在建模阶段,您将探索过程中发现的发现正式化为对达到数据中包含的所需含义所需的步骤和数据结构的明确解释。 这是模型,是两种数据结构以及从原始数据到您的信息和结论的代码步骤的组合。

建模过程是迭代的,在此过程中,您可以通过浏览数据来选择支持分析所需的变量,组织变量以供输入分析过程,执行模型并确定模型对原始假设的支持程度。 它可以包括数据结构的形式化建模,但也可以结合来自各种分析领域的技术,例如(但不限于)统计,机器学习和运筹学。

为此,Pandas 提供了广泛的数据建模工具。 在此步骤中,您将需要更多的工作,从探索数据到在DataFrame对象中形式化数据模型,并确保创建这些模型的过程简洁。 此外,通过基于 Python,您可以充分利用其强大的功能来创建程序,以使流程从头到尾实现自动化。 您创建的模型是可执行的。

从分析的角度来看,pandas 提供了多种功能,其中最引人注目的是对描述性统计信息的集成支持,这些功能可以使您解决许多类型的问题。 而且由于 Pandas 是基于 Python 的,因此如果您需要更高级的分析功能,可以很容易地将其与广泛的 Python 科学环境的其他部分集成。

演示

该过程的倒数第二个步骤是通常以报告或演示文稿的形式向他人展示您的发现。 您将需要为您的解决方案创建一个有说服力的详尽说明。 通常可以使用 Python 中的各种绘图工具并手动创建演示文稿来完成此操作。

Jupyter 笔记本是一种强大的工具,可为您的 Pandas 分析创建演示文稿。 这些笔记本提供了一种执行代码的方法,并提供了丰富的 Markdown 功能来注释和描述应用中多个点的执行。 这些可用于创建非常有效的可执行演示文稿,这些演示文稿在视觉上富含代码段,样式化文本和图形。

我们将在第 2 章,“运行 Pandas”中简要介绍 Jupyter 笔记本。

复现

研究的一项重要内容是共享并使研究可重现。 人们常说,如果其他研究人员无法复制您的实验和结果,那么您就不会证明任何事情。

幸运的是,对于您来说,通过使用 Pandas 和 Python,您将可以轻松地使分析具有可重复性。 这可以通过共享驱动 Pandas 代码的 Python 代码以及数据来完成。

Jupyter 笔记本还提供了一种方便的方式来打包代码和应用程​​序,并且可以通过安装 Jupyter 笔记本与其他任何人轻松共享。 互联网上有许多免费且安全的共享站点,可让您创建或部署 Jupyter 笔记本进行共享。

关于迭代和敏捷的说明

关于数据操作,分析和科学的非常重要的一点是,它是一个迭代过程。 尽管在前面讨论的阶段中存在自然的前进流程,但是您最终将在此过程中前进和后退。 例如,在探索阶段,您可以识别与准备阶段中与数据纯度问题相关的数据异常,并且需要返回并纠正这些问题。

这是过程乐趣的一部分。 您正在冒险解决最初的问题,同时获得有关正在使用的数据的渐进洞察力。 这些见解可能会导致您提出新问题,更确切的问题,或者意识到您的最初问题不是需要提出的实际问题。 这个过程确实是一个旅程,不一定是目的地。

书和过程的联系

下面提供了该过程中各个步骤的快速映射,您可以在本书中学习这些步骤。 如果该过程前面的步骤在后面的章节中,请不要担心。 该书将按照逻辑顺序逐步引导您学习 Pandas,并可以从各章中回顾到过程中的相关阶段。

流程中的步骤 位置
构想 构想是数据科学中的创新过程。 您需要有个主意。 您正在阅读本文的事实符合您的资格,因为您必须要分析某些数据,并希望在将来进行分析。
检索 数据的检索主要在第 9 章“访问数据”中介绍。
准备 数据准备主要在第 10 章“整理数据”中进行介绍,但这也是贯穿本章大部分内容的常见主题。
探索 探索跨越这本书的第 3 章“用序列表示单变量数据”,直到第 15 章“历史股价分析”。 但是,最需要探讨的章节是第 14 章“可视化”和第 15 章“历史股价分析”, 开始看到数据分析的结果。
建模 建模的重点是第 3 章和“使用 Pandas 序列表示单变量数据”,第 4 章“用数据帧表示表格和多元数据”,第 11 章“组合,关联和重塑数据”,第 13 章“时间序列建模”,以及专门针对金融的第 15 章“历史股价分析”。
演示 演示是第 14 章“可视化”的主要目的。
复现 复现的内容贯穿全书,例如 Jupyter 笔记本提供的示例。 通过在笔记本上工作,默认情况下,您将使用复现工具,并且能够以各种方式共享笔记本。

Pandas 之旅中的数据和分析概念

在学习 Pandas 和数据分析时,您会遇到许多关于数据,建模和分析的概念。 让我们研究其中的一些概念以及它们与 Pandas 的关系。

数据类型

在野外使用数据时,您会遇到几大类数据,这些数据需要被强制转换为 Pandas 数据结构。 了解它们非常重要,因为每种类型所需的工具会有所不同。

pandas 本质上用于处理结构化数据,但提供了多种工具来促进将非结构化数据转换为我们可以操纵的手段。

结构化

结构化数据是在记录或文件中组织为固定字段的任何类型的数据,例如关系数据库和电子表格中的数据。 结构化数据取决于数据模型,数据模型是数据的定义组织和含义以及通常应如何处理数据。 这包括指定数据的类型(整数,浮点数,字符串等),以及对数据的任何限制,例如字符数,最大值和最小值或对一组特定值的限制。

结构化数据是 Pandas 设计要利用的数据类型。 正如我们将首先使用Series然后使用DataFrame所看到的那样,pandas 将结构化数据组织为一个或多个数据列,每个列都是一个特定的数据类型,然后是零个或多个数据行的序列。

非结构化

非结构化数据是没有任何已定义组织的数据,并且这些数据不会特别分解为特定类型的严格定义的列。 这可以包含许多类型的信息,例如照片和图形图像,视频,流式传感器数据,网页,PDF 文件,PowerPoint 演示文稿,电子邮件,博客条目,Wiki 和文字处理文档。

Pandas 不能直接处理非结构化数据,但它提供了许多从非结构化源中提取结构化数据的功能。 作为我们将研究的特定示例,pandas 具有检索网页并将特定内容提取到DataFrame中的工具。

半结构化

半结构化数据适合非结构化数据。 可以将其视为一种结构化数据,但是缺乏严格的数据模型结构。 JSON 是半结构化数据的一种形式。 好的 JSON 具有已定义的格式,但是没有始终严格执行的特定数据架构。 在大多数情况下,数据将处于可重复模式,可以轻松转换为结构化数据类型,例如 pandas DataFrame,但是过程可能需要您提供一些指导以指定或强制数据类型。

变量

在对 Pandas 进行数据建模时,我们将对一个或多个变量进行建模,并寻找值之间或多个变量之间的统计意义。 变量的定义不是编程语言中的变量,而是统计变量之一。

变量是可以测量或计数的任何特征,数量或数量。 变量之所以如此命名,是因为值在总体中的数据单元之间可能会有所不同,并且值可能会随时间变化。 股票价值,年龄,性别,营业收入和支出,出生国家,资本支出,班级等级,眼睛颜色和车辆类型是变量的示例。

使用 Pandas 时,我们会遇到几种广泛的统计变量类型:

  • 类别
  • 连续
  • 离散类别

类别

类别变量是可以采用有限数量(通常是固定数量)的可能值之一的变量。 每个可能的值通常称为水平。 Pandas 中的类别变量用Categoricals表示,这是一种 Pandas 数据类型,与统计中的类别变量相对应。 类别变量的示例是性别,社会阶层,血型,国家/地区,观察时间或等级(例如李克特量表)。

连续

连续变量是一个可以接受无限多个(不可数数量)值的变量。 观察值可以取某个实数集之间的任何值。 连续变量的示例包括高度,时间和温度。 Pandas 中的连续变量用浮点或整数类型(Python 原生)表示,通常在表示特定变量多次采样的集合中表示。

离散

离散变量是一个变量,其中的值基于一组不同的整体值的计数。 离散变量不能是任何两个变量之间的分数。 离散变量的示例包括注册汽车的数量,营业地点的数量和一个家庭中的孩子数量,所有这些都测量整个单位(例如 1、2 或 3 个孩子)。 离散变量通常在 Pandas 中用整数表示(或偶尔用浮点数表示),通常也用两个或多个变量采样集合表示。

时间序列数据

时间序列数据是 Pandas 中的一等实体。 时间为 Pandas 内的变量样本增加了重要的额外维度。 通常,变量与采样时间无关。 也就是说,采样时间并不重要。 但是在很多情况下都是这样。 时间序列在特定的时间间隔形成离散变量的样本,其中观测值具有自然的时间顺序。

时间序列的随机模型通常会反映这样一个事实,即时间上接近的观察比远处的观察更紧密相关。 时间序列模型通常会利用时间的自然单向排序,以便将给定时间段的值表示为以某种方式从过去的值而不是从将来的值中得出。

Pandas 的常见情况是财务数据,其中变量代表股票的价值,因为它在一天中的固定时间间隔内发生变化。 我们通常希望确定特定时间间隔内价格变化率的变化。 我们可能还需要关联特定时间间隔内多只股票的价格。

这是 Pandas 的一项重要而强大的功能,因此我们将花费整整一章来研究这一概念。

分析和统计的一般概念

在本文中,我们将仅探讨统计学的外围和数据分析的技术过程。 但是值得注意的是一些分析概念,其中一些是在 Pandas 内部直接创建的实现。 其他人则需要依赖其他库,例如 SciPy,但是在与 Pandas 一起工作时您可能也会遇到它们,因此大声疾呼非常有价值。

定量与定性数据/分析

定性分析是对可以观察但无法测量的数据的科学研究。 它着重于对数据质量进行分类。 定性数据的示例可以是:

  • 你的皮肤柔软
  • 某人的跑步优雅

定量分析是研究数据中的实际值,并以数据形式对项目进行实际测量。 通常,这些值为:

  • 数量
  • 价格
  • 高度

Pandas 主要处理定量数据,为您提供表示变量观测值的广泛工具。 Pandas 不提供定性分析,但可以让您代表定性信息。

单变量和多变量分析

从某种角度看,统计是研究变量的实践,尤其是对那些变量的观察。 许多统计信息都是基于对单个变量的分析得出的,这称为单变量分析。 单变量分析是分析数据的最简单形式。 它不处理原因或关系,通常用于描述或聚合数据以及在其中查找模式。

多元分析是一种建模技术,其中存在两个或多个影响实验结果的输出变量。 多变量分析通常与诸如相关性和回归之类的概念相关,这有助于我们理解多个变量之间的关系以及这些关系如何影响结果。

Pandas 主要提供基本的单变量分析功能。 这些功能通常是描述性统计数据,尽管对诸如关联的概念有内在的支持(因为它们在金融和其他领域非常普遍)。

可以使用 StatsModels 执行其他更复杂的统计信息。 同样,这本身并不是 Pandas 的弱点,而是一个特殊的设计决定,让这些概念由其他专用的 Python 库处理。

描述性统计

描述性统计信息是聚合给定数据集的函数,通常该数据集表示单个变量(单变量数据)的总体或样本。 他们描述了集中趋势的数据集和形式度量,以及变异性和分散性的度量。

例如,以下是描述性统计信息:

  • 分布(例如,正态,泊松)
  • 集中趋势(例如,均值,中位数和众数)
  • 离散度(例如,方差,标准差)

正如我们将看到的,Pandas SeriesDataFrame对象集成了对大量描述性统计信息的支持。

推断统计

推断统计与描述性统计的不同之处在于,推断统计试图从数据推断得出结论,而不是简单地对其进行概括。 推断统计的示例包括:

  • T 检验
  • 卡方
  • 方差分析
  • 自举

这些推理技术通常从 Pandas 推迟到其他工具,例如 SciPy 和 StatsModels。

随机模型

随机模型是一种统计建模的形式,包括一个或多个随机变量,通常包括使用时间序列数据。 随机模型的目的是估计结果在特定预测范围内的机会,以预测不同情况的条件。

随机建模的一个例子是蒙特卡洛模拟。 蒙特卡罗模拟通常用于金融投资组合评估,它是基于对市场中投资组合的重复模拟来模拟投资组合的表现,该模拟受各种因素和成分股收益的内在概率分布的影响。

Pandas 为我们提供了DataFrame中随机模型的基本数据结构,通常使用时间序列数据来建立和运行随机模型。 尽管可以使用 pandas 和 Python 编写自己的随机模型和分析代码,但在许多情况下,存在特定领域的库(例如 PyMC)可以简化此类建模。

概率与贝叶斯统计

贝叶斯统计是一种从贝叶斯定理(一种基于简单概率公理构建的数学方程式)派生出来的统计推断方法。 它使分析师可以计算任何感兴趣的条件概率。 条件概率就是事件 B 发生时事件 A 的概率。

因此,就概率而言,数据事件已经发生并已被收集(因为我们知道概率)。 通过使用贝叶斯定理,我们便可以计算已观察到的数据给定或以其为条件的各种感兴趣的事物的概率。

贝叶斯建模超出了本书的范围,但是再次使用 Pandas 很好地处理了基础数据模型,然后使用诸如 PyMC 之类的库进行了实际分析。

相关性

相关性是最常见的统计数据之一,直接建立在 Pandas DataFrame中。 相关性是一个单一数字,描述两个变量之间的关系程度,尤其是描述这些变量的两个观测序列之间的关系程度。

使用相关性的一个常见示例是确定随着时间的推移,两只股票的价格彼此密切相关的程度。 如果变化密切,则两个股票之间的相关性很高,如果没有可辨别的格局,则它们之间是不相关的。 这是有价值的信息,可以在许多投资策略中使用。

两只股票的相关程度也可能随整个数据集的时间范围以及间隔而略有变化。 幸运的是,Pandas 具有强大的功能,可让我们轻松更改这些参数并重新运行关联。 本书稍后将在几个地方介绍相关性。

回归

回归是一种统计量度,用于估计因变量和一些其他变量之间的关系强度。 它可以用来了解变量之间的关系。 财务方面的一个例子是理解商品价格与从事这些商品交易的企业股票之间的关系。

最初有一个直接建立在 Pandas 中的回归模型,但是已经移到 StatsModels 库中。 这显示了 Pandas 常见的模式。 Pandas 通常会内置一些概念,但是随着它们的成熟,它们被认为最有效地适合其他 Python 库。 这是好是坏。 最初直接在 pandas 中使用它是很棒的,但是当您升级到新版本的 pandas 时,它可能会破坏您的代码!

其他兼容 Pandas 的 Python 库

Pandas 是 Python 内数据分析和数据科学生态系统的一个很小但重要的组成部分。 作为参考,这里还有一些其他值得注意的重要 Python 库。 该列表并不详尽,但概述了您可能会遇到的几个问题。

数值和科学计算 -- NumPy 和 SciPy

NumPy 是使用 Python 进行科学计算的基础工具箱,并且包含在大多数现代 Python 发行版中 。 实际上,它是构建 Pandas 的基础工具箱,使用 Pandas 时,您几乎肯定会经常使用它。 NumPy 提供了对多维数组的支持,这些数组具有基本的运算和有用的线性代数函数。

NumPy 的数组功能的使用与 Pandas 特别是 Pandas Series对象紧密相关。 我们的大多数示例都将引用 NumPy,但是 pandas Series函数是 NumPy 数组的紧密超集,因此除少数简要情况外,我们将不深入研究 NumPy 的细节。

SciPy 提供了数值算法和特定领域工具箱的集合,包括信号处理,优化,统计, 以及更多。

统计分析 -- StatsModels

StatsModels 是一个 Python 模块,允许用户浏览数据,估计统计模型和执行统计测试 。 描述性统计信息,统计检验,绘图函数和结果统计信息的广泛列表适用于不同类型的数据和每个估计量。 跨领域的研究人员可能会发现 StatsModels 完全满足了他们在 Python 中进行统计计算和数据分析的需求。

功能包括:

  • 线性回归模型
  • 广义线性模型
  • 离散选择模型
  • 稳健的线性模型
  • 时间序列分析的许多模型和功能
  • 非参数估计
  • 作为示例得数据集的集合
  • 广泛的统计检验
  • 输入输出工具,用于生成多种格式的表格(文本,LaTex,HTML)以及将 Stata 文件读入 NumPy 和 Pandas
  • 绘图功能
  • 广泛的单元测试,来确保结果的正确性

机器学习 -- scikit-learn

scikit-learn 是一种由 NumPy,SciPy 和 matplotlib 构建的机器学习库。 它为数据分析中的常见任务提供了简单有效的工具,例如分类,回归,聚类,降维,模型选择和预处理。

PyMC -- 随机贝叶斯建模

PyMC 是一个 Python 模块,实现了贝叶斯统计模型和拟合算法,包括马尔可夫链蒙特卡洛。 它的灵活性和可扩展性使其适用于许多问题。 除核心采样功能外,PyMC 还包括用于聚合输出,绘图,拟合优度和收敛性诊断的方法。

数据可视化 -- Matplotlib 和 Seaborn

Python 有一套丰富的数据可视化框架。 最受欢迎的两个是 matplotlib 和更新的 seaborn

Matplotlib

Matplotlib 是一个 Python 2D 绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版物质量的图形。 Matplotlib 可以在 Python 脚本,Python 和 IPython shell,Jupyter 笔记本,Web 应用服务器以及四个图形用户界面工具包中使用。

pandas 包含与 matplotlib 的紧密集成,包括作为SeriesDataFrame对象的一部分的功能,这些功能会自动调用 matplotlib。 这并不意味着 Pandas 只限于 matplotlib。 正如我们将看到的,可以很容易地将其更改为 ggplot2 和 seaborn 等其他名称。

Seaborn

Seaborn 是一个用于在 Python 中制作引人入胜且内容丰富的统计图形的库。 它基于 matplotlib 构建,并与 PyData 栈紧密集成,包括对 NumPy 和 pandas 数据结构的支持以及 SciPy 和 StatsModels 的统计例程。 它提供了超越 matplotlib 的其他功能,并且默认情况下还展示了比 matplotlib 更丰富,更现代的视觉样式。

总结

在本章中,我们浏览了 Pandas 的工作方式和原因,数据处理/分析和科学。 首先概述了 Pandas 的存在,Pandas 所包含的功能以及它与数据处理,分析和数据科学的概念之间的关系。

然后,我们介绍了数据分析过程,以建立一个框架,说明为什么 Pandas 中存在某些功能。 其中包括检索数据,组织和清理数据,进行探索,然后建立正式模型,展示您的发现以及能够共享和重现分析。

接下来,我们介绍了数据和统计建模中涉及的几个概念。 其中包括涵盖许多常见的分析技术和概念,以便向您介绍这些技术和概念,并在后续各章中对其进行更详细的探讨时使您更加熟悉。

Pandas 还是较大的 Python 库生态系统的一部分,可用于数据分析和科学。 虽然本书仅着眼于 Pandas,但您会遇到其他图书馆,并且已经介绍了它们,因此在它们长大时会很熟悉它们。

我们准备开始使用 Pandas 了。 在下一章中,我们将开始学习 Pandas,从获取 Python 和 Pandas 环境开始,对 Jupyter 笔记本进行概述,然后在深入研究 Pandas SeriesDataFrame对象之前对其进行快速介绍。 Pandas 后续元素的深度更大。

十、整理数据

我们现在在数据处理管道中,需要查看我们检索到的数据并解决在分析过程中可能出现的异常现象。 这些异常可能由于多种原因而存在。 有时,某些数据部分未记录或丢失。 也许有些单位与您系统的单位不匹配。 很多时候,某些数据点可以重复。

这种处理异常数据的过程通常称为整理您的数据,您会发现该术语在数据分析中使用了很多次。 这是管道中非常重要的一步,在进行简单分析之前,它可能会花费大量时间。

整理数据可能是一个单调乏味的问题,尤其是在使用不是为特定数据清理任务而设计的编程工具时。 对我们来说幸运的是,Pandas 拥有许多可用于解决这些问题的工具,同时也可以帮助我们提高效率。

在本章中,我们将介绍整理数据涉及的许多任务。 具体来说,您将学习:

  • 整洁数据的概念
  • 如何处理缺失的数据
  • 如何在数据中查找NaN
  • 如何过滤(删除)缺失的数据
  • Pandas 如何在计算中处理缺失值
  • 如何查找,过滤和修复未知值
  • 对缺失值执行插值
  • 如何识别和删除重复数据
  • 如何使用替换,映射和应用来转换值

配置 Pandas

本章中的示例使用以下 pandas 和 Jupyter 配置:

什么是整理数据?

整洁的数据是 Hadley Wickham 在名为“整洁的数据”的论文中创造的术语。 我强烈建议您阅读本文。 可以从这里下载。

本文涵盖了创建整洁数据的过程的许多细节,最终结果是您拥有的数据毫无意外,可以进行分析。

我们将研究 Pandas 中用于整理数据的许多工具。 存在这些是因为我们需要处理以下情况:

  • 变量的名称与您所需要的不同
  • 缺少数据
  • 值不在您要求的单位中
  • 记录的采样周期不是您所需要的
  • 变量是类别的,您需要定量的值
  • 数据中存在噪声
  • 信息类型不正确
  • 数据围绕错误的轴组织
  • 数据处于错误的规范化级别
  • 数据重复

这是一个完整的列表,我向您保证它不完整。 但这都是我个人遇到的所有问题(而且我敢肯定,您也会遇到)。 当使用未明确构建的工具和语言来处理这些问题时(例如 Pandas),它们很难解决。 在本章中,我们将研究用 Pandas 解决这些问题有多么容易。

如何处理缺失的数据

当数据的NaN值(也称为np.nan -- 来自 NumPy 的形式)时,Pandas 中的缺少。 该NaN值意味着在特定的Series中没有为特定的索引标签指定值。

数据如何丢失? 值可以为NaN的原因有很多:

  • 两组数据的连接没有匹配的值
  • 您从外部来源检索的数据不完整
  • 给定的时间点的NaN值未知,稍后会填充
  • 检索值时发生数据收集错误,但该事件仍必须记录在索引中
  • 重新索引数据导致索引没有值
  • 数据的形状已更改,现在有其他行或列,在重塑时无法确定
  • 可能还有更多原因,但是总的来说,这些情况的确会发生,作为 Pandas 用户,您将需要解决这些情况才能进行有效的数据分析

让我们开始研究如何通过创建具有一些缺失数据点的数据帧来处理缺失数据:

这里不缺少任何数据,所以我们添加一些:

DataFrame现在缺少显示以下特征的数据:

  • 一行仅由NaN值组成
  • 一列仅由NaN值组成
  • 由数值和NaN值组成的几行和几列
  • 现在,让我们研究各种技术来处理缺失的数据。

确定 Pandas 对象中的NaN

可以使用.isnull()方法识别DataFrame对象中的NaN值。 任何True值表示该位置处的项目是NaN值:

我们可以使用.sum()方法将True视为 1 并将False视为 0 的事实来确定DataFrame对象中NaN值的数量:

.sum()应用于结果序列,可得出原始DataFrame对象中NaN值的总数:

这很棒,因为它可以很容易地识别出流程早期是否丢失了数据。 如果您希望数据是完整的,并且此简单检查将得出一个非 0 的值,那么您就需要更深入地了解。

另一种确定方法是使用Series对象和DataFrame.count()方法。 对于Series方法,此方法将返回非NaN值的数量。 对于DataFrame对象,它将在
每列中计算非NaN值的数量:

然后需要将其翻转以求和NaN值的数量,该值可以如下计算:

我们还可以使用.notnull()方法确定某项是否不是NaN,如果该值不是NaN值,则该方法返回True。 否则,返回False

选择或删除缺失的数据

处理缺失数据的一种技术是简单地将其从数据集中删除。 这种情况的一种情况是,以固定的时间间隔对数据进行采样,但是设备处于脱机状态,因此不会记录读数。

Pandas 库使用多种技术使之成为可能。 一种是通过使用.isnull().notnull()的结果进行布尔选择来从Series对象中检索NaN或非NaN的值。 以下示例演示了从DataFramec4列中选择所有非NaN值的方法:

Pandas 还提供了一个便捷函数.dropna(),该函数将这些项放置在Series中,其值为NaN

请注意,.dropna()实际上返回了DataFrame的副本,但没有行。 原始的DataFrame不变:

.dropna()应用于DataFrame对象时,它将删除DataFrame对象中具有至少一个NaN值的所有行。 下面的代码演示了此操作,并且由于每一行至少具有一个NaN值,因此结果中有零行:

如果只想删除所有值均为NaN的行,则可以使用how='all'参数。 以下示例仅删除g行,因为它具有所有NaN值:

通过将axis参数更改为axis=1,这也可以应用于列而不是行。 以下内容删除了c5列,因为它是唯一具有所有NaN值的列:

现在,我们使用稍微不同的DataFrame对象检查该过程,该对象的列c1c3的所有值都不为NaN。 在这种情况下,将删除c1c3以外的所有列:

.dropna()方法还具有参数thresh,该参数在给定整数值时,指定执行删除之前必须存在的NaN值的最小数量。 以下代码删除所有具有至少五个NaN值的列(在这种情况下,这是c4c5列):

再次注意,.dropna()方法(和布尔选择)返回DataFrame对象的副本,并且数据从该副本中删除。 如果要将数据放入实际的DataFrame中,请使用inplace=True参数。

在数学运算中处理 NaN 值

NaN值在 Pandas 中的处理方式与在 NumPy 中的处理方式不同。 我们已经在较早的章节中看到了这一点,但是在这里值得重新讨论。 使用以下示例对此进行演示:

当 NumPy 函数遇到NaN值时,它返回NaN。 Pandas 函数通常会忽略NaN值,并继续处理该函数,就好像NaN值不属于Series对象的一部分一样。

请注意,先前序列的平均值计算为(1 + 2 + 3) / 3 = 2,而不是(1 + 2 + 3) / 4 (1 + 2 + 0 + 4) / 4。 这验证了NaN被完全忽略,甚至没有被计为Series中的项目。

更具体地说,Pandas 处理NaN值的方式如下:

  • 数据求和将NaN视为 0
  • 如果所有值均为NaN,则结果为NaN
  • .cumsum().cumprod()这样的方法会忽略NaN值,但会将它们保留在结果数组中

下面演示了所有这些概念:

但是,当使用传统的数学运算符时,NaN将传播到结果:

填充缺失的数据

.fillna()方法可用于将NaN值替换为特定值,而不是传播或忽略它们。 下面通过用0填充NaN值来说明这一点:

请注意,这会导致结果值有所不同。 例如,以下代码显示了将.mean()方法应用于具有NaN值的DataFrame对象的结果,与DataFrame对象的NaN值填充有0的结果相比 :

向前和向后填充缺失值

可以通过沿Series向前或向后传播非NaN值来填充数据中的间隙。 为了演示,以下示例将向前填充DataFramec4列:

使用时间序列数据时,这种填充技术通常称为“最新已知值”。 我们将在有关时间序列数据的章节中对此进行重新讨论。

可以使用method='bfill'反转填充方向:

为了省去打字的麻烦,Pandas 还具有全局级别的函数pd.ffill()pd.bfill(),它们等效于.fillna(method="ffill").fillna(method="bfill")

使用索引标签来填充

可以使用Series的标签或 Python 字典的键填充数据。 这使您可以根据索引标签的值为不同的元素指定不同的填充值:

仅填充NaN的值。 请注意,标签为a的值不会更改。

另一种常见情况是用列的平均值填充一列中的所有NaN值:

这很方便,因为以这种方式替换的缺失值会使统计平均值偏离(如果插入 0 的话)较小。 在某些统计分析中,当使用 0 值的较大偏差会导致错误故障时,这可能是可以接受的。

对缺失值执行插值

DataFrameSeries都具有.interpolate()方法,默认情况下,该方法执行缺失值的线性插值:

插值的值是通过在NaN值的任何序列之前和之后取第一个值,然后从头开始逐渐增加该值并替换为NaN值来计算的。 在这种情况下,周围的值为 2.0 和 1.0,导致(2.0 - 1.0) / (5 - 1) = 0.25,然后将其通过所有NaN值递增地添加。

这个很重要。 想象一下,如果您的数据代表一组增加的值,例如白天的温度升高。 如果传感器在几个采样周期内停止响应,则可以通过内插法以较高的确定性推断出缺失的数据。 绝对比将值设置为 0 更好。

插值方法还可以指定特定的插值方法。 常用方法之一是使用基于时间的插值。 请考虑以下Series日期和值:

插值的先前形式导致以下结果:

由于在值2.01.0之间存在一个NaN值,因此2014-02-01的值计算为1.0 + (2.0 - 1.0) / 2 = 1.5

需要注意的重要一点是,该序列缺少2014-03-01的条目。 如果我们希望对每日值进行插值,则应该计算两个值,一个用于2014-02-01,另一个用于2014-03-01,从而在插值分子中产生另一个值。

这可以通过将插值方法指定为time来纠正:

这是基于日期的2014-02-01的正确插值。 另请注意,2014-03-01的索引标签和值未添加到Series; 它只是考虑在内插中。

当使用数字索引标签时,也可以指定插值来计算相对于索引值的值。 为了说明这一点,我们将使用以下Series

如果执行线性插值,则将获得标签1的以下值,该值对于线性插值是正确的:

但是,如果我们想插值相对于索引值怎么办? 为此,我们可以使用method="values"

现在,基于索引中的标签,使用相对定位来插值NaN的计算值。 NaN值的标签为1,是

的十分之一,因此插值将为0 + (100 - 0) / 1010

处理重复数据

样本中的数据通常可以包含重复的行。 这只是处理自动收集的数据的现实,甚至是手动收集数据时创建的情况。 在这些情况下,通常认为最好是在具有重复项而不是缺失数据的方面出错,特别是如果可以认为数据是等幂的。 但是,重复数据会增加数据集的大小,并且如果不是幂等的,则不适合处理重复数据。

Pandas 提供了.duplicates()方法,以方便查找重复数据。 此方法返回布尔值Series,其中每个条目表示该行是否重复。 True值表示特定行已早出现在DataFrame对象中,所有列值均相同。

下面通过创建具有
个重复行的DataFrame对象来演示此操作:

现在让我们检查重复项:

可以使用.drop_duplicates()方法从DataFrame中删除重复的行。 此方法返回DataFrame的副本,其中删除了重复的行:

也可以使用inplace=True参数删除行而不进行复制。

请注意,删除重复项时会保留索引。 重复记录可能具有不同的索引标签(在计算重复项时不考虑标签)。 因此,保留的行会影响结果DataFrame对象中的标签集。

默认操作是保留重复项的第一行。 如果要
保留重复项的最后一行,请使用keep='last'参数。
下面演示了使用此参数的结果如何不同:

如果要基于较小的列集检查重复项,则可以指定列名列表:

转换数据

整理数据的另一部分涉及将现有数据转换为另一个表示形式。 由于以下原因,可能需要这样做:

  • 值的单位不正确
  • 值是定性的,需要转换为适当的数值
  • 多余的数据要么浪费内存和处理时间,要么仅被包括在内就可能影响结果

为了解决这些情况,我们可以采取以下一项或多项措施:

  • 使用表查找过程将值映射到其他值
  • 用其他值(甚至另一种类型的数据)明确替换某些值
  • 应用方法来基于算法转换值
  • 只需删除多余的列和行

我们已经了解了如何使用几种技术删除行和列,因此在此不再赘述。 现在,我们将介绍 Pandas 提供的用于根据其内容映射,替换和函数应用来转换数据的功能。

将数据映射到不同的值

数据转换的基本任务之一是将一组值映射到另一组。 Pandas 提供了使用.map()方法使用查找表(通过 Python 字典或 Pandas Series)来映射值的通用功能。

该方法通过首先将外部Series的值与内部Series的索引标签进行匹配来执行映射。 然后,它返回一个新的Series,带有外部Series的索引标签,但具有内部Series的索引标签。

下面的示例显示如何将x索引中的标签映射到y的值:

与其他对齐操作一样,如果 Pandas 未在外部Series的值和内部Series的索引标签之间找到映射,则它将NaN填充该值。 为了演示,以下操作从外部Series删除了3键,这导致该记录的对齐失败,并导致引入了NaN值:

替换值

前面我们已经看到了如何使用.fillna()方法用您自己决定的值替换NaN值。 实际上,可以将.fillna()方法视为将单个值NaN映射到特定值的.map()方法的实现。

甚至更笼统地说,.fillna()方法本身可以被认为是.replace()方法提供的更通用替代品的专业化。 通过能够用另一个值替换任何值(不仅是NaN),此方法提供了更大的灵活性。

.replace()方法的最基本用途是将另一个值
替换为另一个值:

还可以指定多个要替换的项目,还可以通过传递两个列表(第一个要替换的值,第二个要替换的值)来指定它们的替换值:

还可以通过指定用于查找的字典(上一部分中映射过程的变体)来执行替换:

如果在DataFrame上使用.replace(),则可以为每列指定不同的替换值。 这是通过将 Python 字典传递给.replace()方法来执行的。 在此字典中,键表示要进行替换的列的名称,而字典的值指定要进行替换的位置。 方法的第二个参数是用于替换匹配项的值。

以下代码通过创建DataFrame对象,然后将每个列中的特定值替换为 100 来说明这一点:

替换每列中的特定值非常方便,因为它为否则需要编码遍历所有列的循环提供了快捷方式。

也可以替换特定索引位置的项目,就像它们缺少值一样。 以下代码通过将索引位置0处的值向前填充到位置123中来演示此操作:

也可以通过使用ffillbfill作为指定方法来向前和向后填充,但是这些都是练习,您可以自己尝试。

应用函数转换数据

在直接映射或替换无法满足要求的情况下,可以将函数应用于数据以对数据执行算法。 Pandas 提供了将函数应用于单个项目,整个列或整个行的功能,从而为转换提供了难以置信的灵活性。

可以使用方便命名的.apply()方法来应用函数。 当给定 Python 函数时,此方法在从Series传递每个值的同时迭代调用该函数。 如果将 Pandas 应用于DataFrame,Pandas 将以Series的形式通过每一列,或者如果沿着axis=1进行 Pandas,则将以代表每一行的Series形式通过。

下面通过对Series的每个项目应用 lambda 函数来说明这一点:

将函数应用于Series中的项目时,仅每个Series项目的值将传递给函数,而不是索引标签和值。

将函数应用于DataFrame时,默认值为将方法应用于每一列。 Pandas 遍历所有列,并将每个列作为Series传递给您的函数。 结果是一个Series对象,其索引标签与列名称匹配,并且该函数的结果应用于该列:

通过指定axis=1,可以将函数的应用切换为每一行的值:

常见的做法是获取应用操作的结果并将其添加为DataFrame的新列。 这很方便,因为您可以将一个或多个连续计算的结果添加到DataFrame上,从而为自己提供过程每一步结果的渐进表示。

以下代码演示了此过程。 第一步将列a与列b相乘,并创建一个名为interim的新列。 第二步,将这些值和列c相加,并使用这些值创建result列:

如果您想更改现有列中的值,只需将结果分配给现有列即可。 下面将a列的值更改为该行中值的总和:

实际上,用全新的值替换列并不是最好的处理方式,并且经常导致临时(甚至可能是永久性的)精神错乱,试图调试由缺失数据引起的问题。 因此,在 Pandas 中,最好只添加新的行或列(或全新的对象),并且如果以后内存或性能成为问题,请根据需要进行优化。

要注意的另一点是,Pandas DataFrame不是电子表格,在电子表格中为单元分配了公式,并且当公式引用的单元发生更改时可以重新计算。 如果您希望这种情况发生,那么只要相关数据发生变化,就需要执行公式。 从另一方面来说,这比电子表格更有效,因为每个小的更改都不会引起一些操作。

.apply()方法始终将提供的函数应用于Series,列或行中的所有项目。 如果要将函数应用于这些序列的子集,请首先执行布尔选择以过滤不希望处理的项目。

下面通过创建值的DataFrame并将一个NaN值插入第二行来说明这一点。 然后,它仅将函数应用于所有值都不都是NaN的那些行:

我们将在本章中介绍的最后一种方法是使用DataFrame.applymap()方法应用函数。 尽管.apply()方法始终传递整个行或列,但.applymap()函数将函数应用于每个值。

下面演示了通过使用.applymap()方法将DateFrame中的每个值格式化为指定的小数位数的实际用法:

总结

在本章中,我们研究了整理数据的各种技术。 我们介绍了如何识别缺失的数据,将其替换为其他值,或者将其从整个数据集中删除。 然后,我们介绍了如何将值转换为更适合进一步分析的其他值。

现在,我们已经在数据帧或序列中整理了数据,我们希望从专注于数据的整洁度转向更精细的修改数据结构的形式,例如连接,合并,连接和数据透视。 这将是下一章的重点。

十一、合并,连接和重塑数据

数据通常被建模为一组实体,相关值的逻辑结构由名称(属性/变量)引用,并具有按行组织的多个样本或实例。 实体往往代表现实世界中的事物,例如一个人,或者在物联网中,是一个传感器。 然后,使用单个数据帧对每个特定实体及其度量进行建模。

通常需要在模型中的实体上和实体之间执行各种任务。 可能需要将来自多个位置的多个客户实体的数据组合到单个 Pandas 对象中。 客户和订单实体通常与查找订单的送货地址有关。 仅仅因为不同的源对相同类型的实体进行不同的建模,可能还需要将存储在一个模型中的数据重塑为另一个模型。

在本章中,我们将研究这些操作,这些操作使我们可以在模型中合并,关联和重塑数据。 具体而言,在本章中,我们将研究以下概念:

  • 连接多个 Pandas 对象中的数据
  • 合并多个 Pandas 对象中的数据
  • 如何控制合并中使用的连接类型
  • 在值和索引之间转换数据
  • 堆叠和解除堆叠数据
  • 在宽和长格式之间融合数据

配置 Pandas

我们使用以下导入和配置语句开始本章中的示例:

连接多个对象中的数据

连接是将来自两个或多个 Pandas 对象的数据组合到一个新对象中的过程。 Series对象的连接只会产生一个新的Series,并按顺序复制值。

连接DataFrame对象的过程更加复杂。 连接可以应用于指定对象的任一轴,并且 Pandas 沿着该轴对索引标签执行关系连接逻辑。 然后,Pandas 沿着相反的轴对标签进行对齐并填充缺失值。

由于有许多因素需要考虑,因此我们将分解示例分为以下主题:

  • 了解连接的默认语义
  • 切换对齐的轴
  • 指定连接类型
  • 附加而不是连接数据
  • 忽略索引标签

了解连接的默认语义

使用 Pandas 函数pd.concat()进行连接。 连接数据的一般语法是传递要连接的对象列表。 下面演示了两个Series对象s1s2的简单连接:

这将s2的索引标签和值连接到s1的索引标签和值的末尾。 由于在此过程中未执行对齐,因此导致索引标签重复。

两个DataFrame对象也可以以类似的方式连接:

默认功能导致按顺序附加行,并且可能导致沿行索引出现重复的索引标签。

列标签的结果集由指定DataFrame对象中的索引标签的并集定义。 这是应用于所有源对象的对齐方式(可以有两个以上)。 如果结果中的列在当前正在处理的DataFrame对象中不存在,则 Pandas 将插入NaN值。

以下内容演示了在连接过程中两个DataFrame对象的对齐方式,其中有共同的列(ac)和不同的列(df1中的bdf2中的d) :

df1不包含列d,因此结果那部分中的值为NaNdf2和列b也会发生相同的情况。

可以使用keys参数为结果中的每组数据赋予其自己的名称。 这将在DataFrame对象上创建层次结构索引,该索引使您可以通过DataFrame对象的.loc属性独立地引用每组数据。 如果以后需要确定结果DataFrame对象中的数据的来源,这将很方便。

下面通过为每个原始DataFrame对象分配名称,然后检索源自df2对象(现在已标记为'df2'标签)的行来演示此概念:

然后,这些键可用于子选择特定的数据集:

切换对齐的轴

pd.concat()函数允许您指定级联期间在其上应用对齐的轴。 以下内容将两个DataFrame对象沿列轴连接在一起,将对齐方式更改为沿行索引:

现在,此结果包含重复的列。 这是因为连接首先按每个DataFrame对象的行索引标签对齐,然后从第一个DataFrame对象然后是第二个对象填充列,而不考虑行索引标签。

以下内容演示了沿着列轴与两个DataFrame对象(具有多个共同的行索引标签)(23)以及不相交的行(df1df3中的4)。 另外,df3中的几列与df1a)重叠并且不相交(d):

由于对齐是沿着行标签进行的,因此列最终会重复。 然后,行具有NaN值,其中源对象中不存在列。

指定连接类型

默认连接实际上沿着与连接相反的轴(行索引)上的索引标签执行外连接操作。 这使得标签的结果集类似于执行那些标签的并集。

通过将join='inner'指定为参数,可以将连接的类型更改为内连接。 然后,内连接在逻辑上执行标签的交集而不是并集。 下面的示例对此进行了演示并得出了单行的结果,因为2是唯一的共同行索引标签:

当沿axis=1应用连接时,还可以使用keys参数沿列标记数据组:

可以使用.loc属性和切片来访问不同的组:

追加与连接

DataFrame(和Series)对象还包含.append()方法,该方法将两个指定的DataFrame对象沿着行索引标签连接起来:

与在axis=1上进行连接连接一样,在不考虑创建重复项的情况下复制行中的索引标签,并且以确保在结果中不包含重复的列名的方式连接列标签。

忽略索引标签

如果要确保结果索引没有重复项并保留所有行,则可以使用ignore_index=True参数。 除了新的Int64Index之外,这基本上返回相同的结果:

此操作也可以在连接上使用。

合并和连接数据

Pandas 允许使用pd.merge()函数和DataFrame对象的.merge()方法,将 pandas 对象与类似数据库的连接操作合并。 合并通过在一个或多个列或行索引中查找匹配值来合并两个 Pandas 对象的数据。 然后,基于应用于这些值的类似关系数据库的连接语义,它返回一个新对象,该对象代表来自两者的数据的组合。

合并非常有用,因为它们允许我们为每种类型的数据(拥有整洁数据的规则之一)建模单个DataFrame,但能够使用两组数据中都存在的值来关联不同DataFrame对象中的数据。

合并来自多个 Pandas 对象的数据

合并的一个实际示例是从订单中查找客户名称。 为了在 Pandas 中证明这一点,我们将使用以下两个DataFrame对象。 一个代表客户详细信息列表,另一个代表客户所下的订单以及订单的生成日期。 它们将通过各自的CustomerID列相互关联。

现在假设我们想将订单运送给客户。 我们需要将orders数据与customers详细数据合并,以确定每个订单的地址。 可以使用以下语句轻松执行此操作:

Pandas 能够通过如此简单的代码来完成这项工作,从而为我们做了神奇的事情。 已经意识到,我们的customersorders对象都有一个名为CustomerID的列,并且已经了解了这一点。 它使用在两个DataFrame对象的该列中找到的公共值来关联两个数据,并基于内连接语义形成合并的数据。

为了更详细地说明发生的情况,以下是 Pandas 的具体工作:

  1. 它确定customersorders中带有公共标签的列。 这些列被视为执行连接的键。
  2. 它创建一个新的DataFrame,其列是在步骤 1 中标识的键的标签,然后是两个对象中的所有非键标签。
  3. 它与两个DataFrame对象的键列中的值匹配。
  4. 然后,它为每组匹配的标签在结果​​中创建一行。
  5. 然后,它将来自每个源对象的那些匹配行中的数据复制到结果的相应行和列中。
  6. 它将新的Int64Index分配给结果。

合并中的连接可以使用多个列中的值。 为了演示,下面创建两个DataFrame对象,并使用两个对象的key1key2列中的值执行合并:

此合并标识key1key2列在两个DataFrame对象中是公用的。 这些列的两个DataFrame对象中值的匹配元组分别为[ax和(cz),因此,这将导致两行值。

要显式指定用于关联对象的列,可以使用on参数。 下面通过仅使用两个DataFrame对象的key1列中的值执行合并来演示此操作:

将该结果与前面的示例进行比较,该结果现在具有三个
行,因为在两个对象的该单个列中都有匹配的abc值。

还可以为on参数提供列名列表。 以下内容恢复为同时使用key1key2列,其结果与前面的示例相同,在前一个示例中,这两列被 Pandas 隐式标识:

在两个DataFrame对象中都必须存在用on指定的列。 如果要基于每个对象中具有不同名称的列进行合并,则可以使用left_onright_on参数,将列的名称传递给每个参数。

要与两个DataFrame对象的行索引的标签执行合并,可以使用left_index=Trueright_index=True参数(均需要指定):

这已确定共同的索引标签为12,因此生成的DataFrame具有两行,其中包含这些值和索引中的标签。 然后,Pandas 在结果中为两个对象中的每一列创建一列,然后复制值。

由于两个DataFrame对象都有一个具有相同名称key的列,结果中的这些列将附加_x_y后缀以标识它们源自的DataFrame对象。 _x用于左侧,_y用于右侧。 您可以使用suffixes参数并传递两个项目的序列来指定这些后缀。

指定合并操作的连接语义

pd.merge()执行的默认连接类型是内连接。 要使用另一种连接方法,请使用pd.merge()函数的how参数(或.merge()方法)指定连接类型。 有效选项是:

  • inner:这是两个DataFrame对象的键的交集
  • outer:这是来自两个DataFrame对象的键的并集
  • left:仅使用左侧DataFrame的键
  • right:仅使用右侧DataFrame的键

如我们所见,内连接是默认的,它仅在值匹配的情况下才从两个DataFrame对象返回数据合并。

相比之下,外部连接从左侧和右侧DataFrame对象返回匹配的行的合并和不匹配的值,但是在不匹配的部分填充NaN。 以下代码演示了外部连接:

左连接将返回满足指定列中值连接的行的合并,并且仅返回left中不匹配的行:

右连接将返回满足指定列中值连接的行的合并,并且仅返回right中不匹配的行:

pandas 库还提供了.join()方法,该方法可用于使用两个DataFrame对象的索引标签(而不是列中的值)执行连接。 请注意,如果两个DataFrame对象中的列没有唯一的列名,则必须使用lsuffixrsuffix参数指定后缀(与合并一样,不执行自动后缀)。 以下代码演示了后缀的连接和规范:

执行的默认连接类型是外部连接。 请注意,这与.merge()方法的默认值不同,该方法的默认值为inner。 要更改为内连接,请指定how='inner',如以下示例所示:

请注意,这与Out [29]的早期结果大致相同,除了结果的列名称稍有不同。

也可以执行左右连接,但是它们导致的结果与前面的示例相似,因此为简洁起见,将省略它们。

在值和索引之间转换数据

数据通常以堆积格式存储,也称为记录格式。 这在数据库,.csv文件和 Excel 电子表格中很常见。 在堆叠格式中,数据通常不规范化,并且在许多列中具有重复的值,或者在逻辑上应存在于其他表中的值(违反了整洁数据的另一个概念)。

取得以下数据,这些数据代表来自加速度计上的数据流。

这种数据的组织问题是:如何确定特定轴的读数? 这可以通过布尔选择天真地完成:

这里的问题是,如果您想知道给定时间所有轴的值而不仅仅是x轴,该怎么办。 为此,您可以为轴的每个值执行选择,但这是重复的代码,并且在不更改代码的情况下无法处理将新的轴值插入DataFrame的情况。

更好的表示方式是,列代表唯一的变量值。 要转换为这种形式,请使用DataFrame对象的.pivot()函数:

这已从axis列获取了所有不同的值,并将它们旋转到新DataFrame上的列中,同时为原始DataFrame的适当行和列中的新列填充了值。 这个新的DataFrame证明了现在很容易在每个时间间隔识别XYZ传感器读数。

堆叠

与枢轴函数相似的是.stack().unstack()方法。 堆叠过程将列标签的级别旋转到行索引。 取消堆叠执行相反的操作,即将行索引的某个级别旋转到列索引中。

堆叠/解除堆叠与执行枢轴之间的区别之一是,与枢轴不同,堆叠和解除堆叠函数能够枢转层次结构索引的特定级别。 同样,在枢轴在索引上保留相同数量的级别的情况下,堆叠和非堆叠总是会增加其中一个轴(用于堆叠的列和用于堆叠的行)的索引上的级别,而会降低另一轴上的级别。

使用非分层索引的堆叠

为了演示堆叠,我们将看几个使用带有非分层索引的DataFrame对象的示例。 我们将使用以下DataFrame开始示例:

堆叠会将列索引的一级移到行索引的新级。 由于我们的DataFrame只有一个级别,因此这会将DataFrame对象折叠为具有分层行索引的Series对象:

要访问值,我们现在需要将一个元组传递给Series对象的索引器,该对象仅使用索引进行查找:

如果DataFrame对象包含多个列,则所有列都将移至新Series对象的相同附加级别:

现在可以使用带有索引的元组语法访问以前属于不同列的值:

通过将行索引的高度移动到列轴的高度,解除堆叠将在相反的方向上执行类似的操作。 在下一部分中,我们将检查此过程,因为通常情况下,堆叠假设假定要进行索引的索引是分层的。

使用分层索引的解除堆叠

为了演示分层索引的解除堆叠,我们将重新访问本章前面看到的传感器数据。 但是,我们将在测量数据中增加一列,以表示多个用户的读数,并复制两个用户的数据。 以下设置了此数据:

利用数据中的这种组织,我们可以执行以下操作:仅使用索引检查特定人员的所有读数:

我们还可以使用.xs()间隔1获取所有轴和所有用户的所有读数:

取消堆叠会将行索引的最后一级移动到列索引的新级别,从而导致列具有MultiIndex。 以下内容演示了此索引的最后一层(索引的axis层):

要取消堆叠其他级别,请使用level参数。 以下代码将第一层(level=0)解除堆叠:

通过将级别列表传递到.unstack()可以同时取消堆叠多个级别。 此外,如果已命名级别,则可以通过名称而不是位置来指定它们。 以下按名称拆解whoaxis级别:

确切地说,我们可以重新堆叠这些数据。 下面的代码会将列的who级别堆叠回到行索引中:

关于此结果,有两点值得指出。 首先,堆叠和解除堆叠总是将级别移动到另一个索引的最后级别。 请注意,who级别现在是行索引的最后一个级别,但它较早开始作为第一个级别。 这将对通过该索引访问元素的代码产生影响,因为它已更改为另一个级别。 如果您想将一个级别放回另一个位置,则需要使用堆叠和解除堆叠以外的其他方法来重新组织索引。

其次,随着所有这些数据移动,堆叠和解除堆叠(以及数据透视)不会丢失任何信息。 他们只是改变组织和访问它的方式。

在长格式和宽格式之间融合数据

熔化是一种不可旋转的类型,通常称为将DataFrame对象从宽格式更改为长格式。 这种格式在各种统计分析中很常见,并且您读取的数据可能已经以融合形式提供。 或者,您可能需要将这种格式的数据传递给期望该组织的其他代码。

从技术上讲,熔化是将DataFrame对象整形为
格式的过程,其中通过不旋转variable列中的列标签来创建两个或更多列,分别称为variablevalue ,然后将数据从这些列移到value列中的适当位置。 然后将所有其他列制作为有助于描述数据的标识符列。

通常使用一个简单的例子可以最好地理解熔化的概念。 在此示例中,我们从一个DataFrame对象开始,该对象表示两个变量的测量值,每个变量用其自己的列HeightWeight表示,还有一个附加列表示人并由Name列指定:

下面使用Name列作为标识符列,并使用HeightWeight列作为测量变量来融化DataFrameName列保留,而HeightWeight列未旋转到variable列中。 然后,将这两列中的值重新排列到value列中,并确保与原始数据中已经存在的Namevariable的适当组合值对齐:

现在对数据进行了重组,因此很容易提取variableName的任何组合的值。 此外,采用这种格式更容易添加新的变量和度量,因为可以简单地将数据添加为新行,而不需要通过添加新列来更改DataFrame的结构。

堆叠数据的性能优势

最后,我们将研究为什么要堆叠数据。 可以证明,堆叠数据比通过单个级别索引进行查询然后再进行列查询,甚至与按位置指定行和列的.iloc查找相比,效率更高。 以下内容说明了这一点:

如果需要从DataFrame中重复访问大量标量值,则这对应用性能可能具有极大的好处。

总结

在本章中,我们研究了在一个或多个DataFrame对象中合并和重塑数据的几种技术。 我们通过检查如何组合来自多个 Pandas 对象的数据来开始本章。 然后,我们研究了如何沿行轴和列轴连接多个DataFrame对象。 由此,我们随后研究了如何基于多个DataFrame对象中的值,使用 Pandas 执行类似于数据库的连接和数据合并。

然后,我们研究了如何使用枢轴,堆叠和融合来重塑DataFrame中的数据。 通过这一过程,我们看到了每个过程如何通过改变索引的形状以及将数据移入和移出索引来提供如何移动数据的多种变体。 这向我们展示了如何以有效地从其他形式查找数据的格式组织数据,这可能会给数据提供者带来更多便利。

在下一章中,我们将学习有关分组和对这些组中的数据进行聚合分析的知识,这将使我们能够基于数据中的相似值来得出结果。

十二、数据聚合

数据聚合是根据信息的某些有意义的类别对数据进行分组的过程。 然后对每个组进行分析,以报告每个组的一个或多个摘要统计信息。 在这种意义上,这种概括是一个通用术语,其中聚合可以从字面上是求和(例如,售出的产品总数)或统计计算(例如,均值或标准差)。

本章将研究 Pandas 执行数据聚合的功能。 这包括强大的拆分应用组合模式,用于分组,执行组级别的转换和分析,以及报告聚合 Pandas 对象中每个组的结果。 在此框架内,我们将研究几种对数据进行分组,在组级别上应用函数以及能够过滤数据进出分析的技术。

具体而言,在本章中,我们将介绍:

  • 数据分析的拆分,应用和合并模式概述
  • 按单个列的值分组
  • 访问 Pandas 分组的结果
  • 使用多列中的值进行分组
  • 使用索引级别分组
  • 将聚合函数应用于分组数据
  • 数据转换概述
  • 转换的实际示例:填充均值和 z 得分
  • 使用过滤来有选择地删除数据分组
  • 离散化和分级

配置 Pandas

本章中的示例使用以下导入和配置语句:

分割,应用和合并(SAC)模式

许多数据分析问题利用称为拆分应用合并的数据处理模式。 在这种模式下,采取了三个步骤来分析数据:

  • 根据特定条件将数据集分成较小的部分
  • 这些部分中的每一个都是独立操作的
  • 然后将所有结果合并回一个单元

下图演示了一个简单的拆分应用组合过程,该过程用于计算由基于字符的键(ab)分组的值的平均值:

然后,数据由索引标签分为两组(ab一组)。 计算每组中值的平均值。 然后,将来自该组的结果值组合到一个 Pandas 对象中,该对象将通过代表每个组的标签进行索引。

使用SeriesDataFrame.groupby()方法执行 Pandas 拆分。 给此方法一个或多个索引标签和/或列名; 他们将根据关联的值对数据进行分组。

拆分数据后,可以对每个组执行以下一种或多种操作类别:

  • 聚合:计算聚合统计信息,例如组均值或每个组中项目的计数
  • 转换:执行特定于组或项目的计算
  • 过滤:根据组级计算删除整个数据组

最后一个阶段,合并,由 Pandas 自动执行,Pandas 收集应用阶段的结果并构建单个合并结果。

有关拆分应用合并的更多信息,《统计软件杂志》上有一篇论文,标题为“数据分析的拆分应用合并策略”。 本文将详细介绍该模式,尽管在示例中使用了 R,但对于学习 Pandas 的人来说仍然是有价值的读物。 您可以在这里获得此论文。

示例数据

本章中的示例将利用代表几个设备传感器测量值的数据集。 数据由加速度计和方向传感器的 XYZ 轴上的读数组成:

分割数据

我们对在 Pandas 对象内拆分数据的检查将分为几个步骤。 首先,我们将基于列创建分组,然后检查所创建分组的属性。 然后,我们将检查访问各种属性和分组的结果,以了解所创建组的多个属性。 然后,我们将使用索引标签而不是列中的内容来检查分组。

按单个列的值来分组

传感器数据由三个类别变量(sensorintervalaxis)和一个连续变量(reading)组成。 通过将其名称传递给.groupby(),可以对任何单个类别变量进行分组。 以下代码按传感器列中的值对传感器数据进行分组:

DataFrame上的.groupby()的结果是GroupBy对象的子类,其中DataFrameDataFrameGroupBySeriesSeriesGroupBy。 该对象表示最终将要执行的分组的临时描述。 该对象帮助 Pandas 在执行之前首先验证相对于数据的分组。 这可以帮助优化和识别错误,并为您提供了一个点,您可以在此之前检查某些属性,而这可能是昂贵的计算过程。

此临时对象具有许多有用的属性。 .ngroups属性将检索结果中将形成的组数:

.groups属性将返回一个 Python 字典,该字典的键代表每个组的名称(如果指定了多列,则为元组)。 字典中的值是每个相应组中包含的索引标签的数组:

访问分组结果

可以将grouped变量视为已命名组的集合,并且可用于检查组的内容。 让我们使用以下函数检查这些分组:

此函数将遍历每个组并打印其名称和前五行:

对这些结果的检查为我们提供了有关 Pandas 如何进行分裂的一些见解。 已为sensors列中的每个不同值创建了一个组,并以该值命名。 然后,每个组都包含一个DataFrame对象,该对象由传感器值与该组名称匹配的行组成。

.size()方法返回所有组大小的摘要:

.count()方法返回每个组的每一列中的项目数:

可以使用.get_group()方法检索任何特定的组。 以下代码检索accel组:

.head().tail()方法可用于返回每个组中指定数量的项目。 此代码检索每个组中的前三行:

.nth()方法将返回每个组中的第 n 个项目。 以下代码演示了如何使用它来检索每个组的第二行:

.describe()方法可用于返回每个组的描述性统计信息:

组按其组名升序排序。 如果要防止在分组过程中进行排序,请使用sort=False选项。

使用多列来分组

也可以通过传递列名列表对多个列进行分组。 以下代码按sensoraxis列对数据进行分组:

由于指定了多列,因此每个组的名称现在是一个元组,代表sensoraxis中值的每种不同组合。

使用索引级别来分组

可以使用索引中的值而不是列进行分组。 传感器数据非常适合用于层次结构索引,可用于演示此概念。 让我们使用由sensoraxis列组成的层次结构索引来设置此数据的形式:

现在可以使用分层索引的各个级别执行分组。 该代码将按索引级别 0(传感器名称)分组:

可以通过在列表中传递级别来执行按多个级别分组。 并且,如果MultiIndex具有为级别指定的名称,则可以使用这些名称代替整数。 以下代码演示了按sensoraxis进行分组:

应用聚合函数,变换和过滤器

Apply 步骤允许对每组数据进行三个不同的操作:

  • 应用聚合函数
  • 执行转换
  • 从结果中过滤整个组

让我们检查所有这些操作。

将聚合函数应用于组

可以使用GroupBy对象的.aggregate()(或简称为.agg())方法将聚合函数应用于每个组。 .agg()的参数是将应用于每个组的函数的引用。 对于DataFrame,此函数将应用于组中的每一列数据。

下面的示例演示计算每个sensoraxis的平均值:

由于.agg()将把该方法应用于每组中的每一列,因此 pandas 也会计算间隔值的平均值(可能不太实用)。

聚合的结果将具有与原始数据相同的结构化索引。 as_index=False可用于创建数字索引并将原始索引的级别移入列:

许多聚合函数直接内置在GroupBy对象中,以节省您的键入时间。 具体来说,这些函数是(前缀为gb):

gb.agg        gb.boxplot    gb.cummin     gb.describe   gb.filter    gb.get_group  gb.height     gb.last       gb.median     gb.ngroups    gb.plot       gb.rank       gb.std        gb.transform
gb.aggregate  gb.count      gb.cumprod    gb.dtype      gb.first      gb.groups     gb.hist       gb.max        gb.min        gb.nth        gb.prod       gb.resample   gb.sum        gb.var
gb.apply      gb.cummax     gb.cumsum     gb.fillna     gb.gender     gb.head       gb.indices    gb.mean       gb.name       gb.ohlc       gb.quantile   gb.size       gb.tail       gb.weight

作为演示,以下代码还计算每个sensoraxis组合的平均值:

通过在列表中传递函数,也可以在同一语句中应用多个聚合函数。

通过将 Python 字典传递给.agg(),可以将不同的函数应用于每列。 字典的键表示要应用该函数的列名,每个字典条目的值就是该函数。 以下代码通过计算reading列的平均值并返回该组的长度代替interval值来演示此技术:

也可以使用GroupBy对象上的[]运算符在特定列上执行聚合。 此代码仅计算reading列的平均值:

转换数据组

GroupBy对象提供了.transform()方法,该方法将功能应用于每个组中DataFrame中的所有值。 我们将研究一般的转换过程,然后看两个真实的例子。

转换的一般过程

GroupBy对象的.transform()方法将一个函数应用于数据帧中的每个值,并返回另一个具有以下特征的DataFrame

  • 它的索引与所有组中索引的连接相同
  • 行数等于所有组中的行数之和
  • 它由未分组的列组成,Pandas 已成功将给定函数应用于该列(可以删除某些列)

为了演示实际的转换,让我们从以下数据帧开始:

让我们按Label列分组:

以下代码执行一个转换,该转换应用一个函数将每个值加 10:

pandas 尝试将函数应用于所有列,但是由于LabelOther列具有字符串值,因此转换函数将失败(它将引发异常)。 由于该失败,结果中将省略这两列。

结果也未分组,因为从转换结果中删除了分组结构。 生成的对象将具有与原始DateFrame对象的索引匹配的索引,在这种情况下为VWXYZ

用分组的平均值填充缺失值

使用分组数据进行统计分析的常见转换是用组中非NaN值的平均值替换每个组中的缺失数据。 为了说明这一点,下面的代码创建一个DataFrame,其中Label列带有两个值(AB),以及一个Values列,其中包含整数序列,但其中一个值替换为NaN。 然后,将数据按Label列分组:

每组的平均值可以使用.mean()计算:

现在假设我们需要B组填写所有NaN值,因为其他使用此数据代码的人可能难以处理NaN值。 可以使用以下方法简洁地执行此操作:

通过转换计算归一化的 z 得分

转换的另一个常见示例是在数据组上创建归一化的 z 得分。 为了证明这一点,我们将使用均值为 0.5 且标准差为 2 的正态分布来随机生成值的序列。按天为数据编制索引,并在 100 天的时间范围内计算滚动平均值以生成样本均值:

滚动装置具有以下外观:

在这一点上,我们希望标准化每个日历年的滚动方式。 以下代码按年份对数据进行分组,并报告每组的现有平均值和标准差:

为了执行标准化,此代码定义了 z 得分函数,将其作为变换应用于每个组,并报告新的均值和标准差:

我们还可以比较原始数据和转换后的数据:

从聚合中过滤组

可以从使用.filter()的处理中选择性地删除数据组。 此方法提供了一个功能,可用于在合并后对结果中是否包括整个组做出组级决策。 如果要在结果中包含该组,则该函数应返回True,并排除该组。

我们将使用以下数据检查几种方案:

第一个演示将删除没有最少项目数的组。 具体来说,如果它们只有一项或更少,它们将被丢弃:

下面的示例将忽略具有任何NaN值的组:

下一个示例将仅选择均值大于整个数据集均值的 2.0 的组(基本上,这将选择与整体相比具有异常行为的数据组):

总结

在本章中,我们研究了使用 Pandas 对数据组进行分组和分析的各种技术。 介绍了拆分应用组合模式,并概述了如何在 Pandas 中实现这种模式。 然后,我们学习了如何基于列和索引级别中的数据将数据分为几组。 然后,我们研究了如何使用聚合函数和转换来处理每个组中的数据。 我们快速检查了如何根据数据组的内容过滤数据组。

在下一章中,我们将深入研究 Pandas 最强大,最强大的功能之一 -- 时间序列数据建模。

十三、时间序列建模

时间序列是一个时间段内和特定时间间隔内一个或多个变量的度量。 捕获时间序列后,通常会进行分析以识别时间序列中的模式,实质上是确定随着时间的流逝发生了什么。 分析时间序列数据的能力在现代世界中至关重要,这是为了分析财务信息或监视可穿戴设备上的运动并使您的运动与目标和饮食相匹配。

Pandas 提供了广泛的时间序列数据建模能力。 在本章中,我们将研究许多这些功能,包括:

  • 创建具有特定频率的时间序列
  • 日期,时间和间隔的表示
  • 用时间戳表示时间点
  • 使用Timedelta表示时间间隔
  • 使用DatetimeIndex建立索引
  • 创建具有特定频率的时间序列
  • 用日期偏移量表示数据间隔
  • 将时间段固定到一周,一月,一季度或一年中的特定日期
  • 用时间段建模时间间隔
  • 使用PeriodIndex建立索引
  • 用日历处理假期
  • 使用时区标准化时间戳
  • 移动和滞后时间序列
  • 在时间序列上执行频率转换
  • 向上和向下重新采样时间序列
  • 在时间序列上执行滚动窗口操作

配置 IPython 笔记本

要利用本章中的示例,我们将需要包括以下导入和设置:

日期,时间和间隔的表示

为了开始理解时间序列数据,我们需要首先检查 Pandas 如何表示日期,时间和时间间隔。 pandas 提供了广泛的内置工具来表示这些概念,因为这些概念的表示没有足够强大地由 Python 或 NumPy 实现,无法处理处理时序数据所需的许多概念。

一些附加功能包括能够跨不同频率转换数据并应用不同的日历以在财务计算中考虑诸如工作日和假日之类的事情。

日期时间,日期和时间对象

datetime对象是datetime库的一部分,而不是 Pandas 的一部分。 此类可用于构造表示几种常见模式的对象,例如使用日期和时间的固定时间点,或者简单地是没有时间部分的一天,或者没有日期部分的时间。

datetime对象的准确性不高,涉及时间序列数据的大量计算所涉及的许多数学。 但是,它们通常用于初始化 pandas 对象,pandas 将它们转换为幕后的 pandas 时间戳对象。 因此,在这里仍然值得一提,因为它们在初始化期间会经常使用。

可以使用至少三个参数分别表示年,月和日来初始化datetime对象:

结果已将小时和分钟值默认为0。 还可以使用构造器的另外两个值来指定小时和分钟成分。 下面创建一个datetime对象,该对象还指定下午 5:30:

可以使用datetime.now()函数来确定当前日期和时间,该函数检索本地日期和时间:

datetime.date对象代表特定的日期(没有时间成分)。 可以通过将datetime对象传递给datetime的构造器来创建它:

您可以使用以下方法检索当前的本地数据:

可以使用datetime.time对象并将datetime对象传递给其构造器来创建不带日期成分的time

当前本地时间可以使用以下方法检索:

用时间戳表示时间点

使用pandas.tslib.Timestamp类执行日期和时间的 Pandas 表示。 Pandas Timestamp基于datetime64 dtype,并具有比 Python datetime对象更高的精度。 在 Pandas 中,Timestamp对象通常可以与datetime对象互换,因此通常可以在使用日期时间对象的任何地方使用它们。

您可以使用pd.Timestamppandas.tslib.Timestamp的快捷方式)并通过传递表示日期,时间或日期和时间的字符串来创建Timestamp对象:

也可以指定时间元素:

也可以仅使用一个时间来创建Timestamp,默认情况下还将指定当前的本地日期:

下面演示了如何使用Timestamp检索当前日期和时间:

作为 Pandas 用户,通常不会直接创建Timestamp对象。 使用日期和时间的许多 Pandas 函数都允许您传递datetime对象或日期/时间的文本表示,并且这些函数将在内部执行转换。

使用Timedelta表示时间间隔

为了表示时间上的差异,我们将使用 Pandas Timedelta对象。 这些通常是确定两个日期之间的持续时间或从另一个日期和/或时间开始的特定时间间隔内计算日期的结果。

为了演示Timedelta,下面使用timedelta对象计算从指定日期开始的时间增加一天:

下面演示了如何计算两个日期之间有多少天:

介绍时间序列数据

Pandas 擅长处理时序数据。 这很可能是由于其起源于处理财务信息。 这些功能在其所有版本中都得到了不断完善,以逐步提高其时间序列操纵的能力。

使用DatetimeIndex建立索引

Pandas 中时间序列功能的核心围绕着使用专用索引来表示,该索引表示一个或多个时间戳下的数据度量。 Pandas 中的这些索引称为DatetimeIndex对象。 这些是功能强大的对象,它们使我们能够根据日期和时间自动对齐数据。

有几种方法可以在 Pandas 中创建DatetimeIndex对象。 下面通过将datetime对象的列表传递到Series来创建DateTimeindex

Series已获取datetime对象,并根据日期值构造了一个DatetimeIndex。 该索引的每个值都是一个Timestamp对象。

以下内容验证索引的类型以及索引中的标签的类型:

不需要在列表中传递datetime对象来创建时间序列。 Series对象足够聪明,可以识别出代表datetime的字符串并为您进行转换。 以下等效于先前的示例:

pandas 在pd.to_datetime()中提供了一个工具函数,该函数接受相似或混合类型的对象的列表,pandas 尝试将这些对象转换为Timestamp对象,然后将其转换为DatetimeIndex。 如果序列中的某个对象无法转换,则 Pandas 将创建一个NaT值,这表示不是时间:

请注意,如果pd.to_datetime()函数无法将值转换为Timestamp,则会引发异常:

要强制函数将其转换为日期而不是引发异常,可以使用errors="coerce"参数。 然后,无法转换的值将在结果索引中分配NaN

使用pd.date_range()函数可以轻松创建具有特定频率的时间戳序列。 下面根据连续10天的DatetimeIndex创建一个Series对象:

DatetimeIndex可用于各种索引操作,例如数据对齐,选择和切片。 下面演示了按位置进行切片:

为了演示对齐方式,我们将使用下面创建的Series和刚刚创建的子集的索引:

当我们添加s2date_series时,将执行对齐,并在项目未对齐的地方返回NaN。 每个索引标签上的值将是在相同标签上找到的值的总和:

可以使用表示日期的字符串检索带有DatetimeIndexSeries中的项目,而不必指定datetime对象:

DatetimeIndex也可以使用表示日期的字符串进行切片:

Pandas 的另一个便利功能是可以使用部分日期规范来切片DatetimeIndex。 例如,以下代码创建一个Series对象,其日期跨越两年,然后仅选择 2013 年的那些项目:

也可以选择特定年份和月份中的项目。 以下选择 2014 年 8 月的项目:

这也适用于切片。 以下是 2014 年 8 月和 9 月返回的项目:

创建具有特定频率的时间序列

可以按除每日频率以外的时间间隔创建时间序列数据。 通过使用freq参数,可以使用pd.date_range()生成不同的频率。 该参数默认为代表每日频率的值D

为了演示替代频率,下面通过指定freq='T'以 1 分钟的间隔创建DatetimeIndex

这个时间序列使我们能够以更高的分辨率进行切片。 在分钟级别上的以下切片:

下表列出了可能的频率值:

别名 描述
B 业务日频率
C 自定义业务日频率
D 日历日频率(默认)
W 星期频率
M 月结束频率
BM 业务月结束频率
CBM 自定义业务月结束频率
MS 月开始频率
BMS 业务月开始频率
CBMS 自定义业务月开始频率
Q 季度结束频率
BQ 业务季度结束频率
QS 季度开始频率
BQS 业务季度开始频率
A 年结束频率
BA 业务年结束频率
AS 年开始频率
BAS 业务年开始频率
H 小时频率
T 分钟频率
S 每秒频率
L 毫秒频率
U 微秒频率

您可以使用'B'频率来创建仅使用工作日的时间序列:

我们可以看到跳过了两天,就像周末一样。

可以使用periods参数在特定的日期和时间,特定的频率和特定的数范围内创建范围。 下面创建了一个 5 项DatetimeIndex,从2014-08-01 12:10:01开始,间隔为 1 秒:

使用偏移量计算新日期

Pandas 的频率使用日期偏移量表示。 在讨论Timedelta对象时,我们已在本章开头谈到了这一概念。 Pandas 使用DateOffset对象的概念扩展了它们的功能。 它们是代表如何相对于DatetimeIndex对象整合时间偏移量和频率的知识的对象。

用日期偏移量表示数据间隔

通过使用传递的频率字符串,例如'M''W''BM'pd.date_range()freq参数,以各种频率创建DatetimeIndex对象。 在引擎盖下,这些频率字符串被转换为 Pandas DateOffset对象的实例。

DateOffset代表规则的频率增量。 在具有DateOffset各种子类的 Pandas 中,可以表示特定的日期偏移逻辑,例如“月”,“工作日”或“小时”。 DateOffset为 Pandas 提供了智能,使其能够确定如何从参考日期和时间开始计算特定的时间间隔。 与仅使用固定的数字间隔相比,这为 Pandas 用户提供了更大的灵活性,可以表示日期/时间偏移

一个有用且实用的示例是计算第二天的营业时间。 这不是简单地通过在datetime中增加一天来确定的。 如果日期表示星期五,则美国金融市场的下一个工作日不是星期六,而是星期一。 在某些情况下,如果星期一是假日,那么从星期五开始的一个工作日实际上可能是星期二。 Pandas 为我们提供了处理这类棘手情况所需的所有工具。

让我们通过使用'B'作为频率生成日期范围来研究一下这一点:

该时间序列已省略2014-08-302014-08-30,因为它们是星期六和星期日,而不被视为工作日。

DatetimeIndex具有.freq属性,该属性表示索引中时间戳的频率:

注意,Pandas 创建了BusinessDay类的实例来表示此索引的DateOffset单位。 如前所述,Pandas 用DateOffset类的子类表示不同的日期偏移量。 以下是 Pandas 提供的各种内置的日期偏移量类:

描述
DateOffset 默认为一个日历日的通用偏移量
BDay 业务日
CDay 自定义业务日
Week 星期,可以选择固定在一周中的某一天
WeekOfMonth 每月第 y 周的第 x 天
LastWeekOfMonth 每月最后一周的第 x 天
MonthEnd 日历月结束
MonthBegin 日历月开始
BMonthEnd 业务月结束
BMonthBegin 业务月开始
CBMonthEnd 自定义业务月结束
CBMonthBegin 自定义业务月开始
QuarterEnd 季度结束
QuarterBegin 季度开始
BQuarterEnd 业务季度结束
BQuarterBegin 业务季度开始
FYS253Quarter 零售季度(52-53 周)
YearEnd 日历年结束
YearBegin 日历年开始
BYearEnd 业务年结束
BYearBegin 业务年开始
FYS253 零售年(52-53 周)
Hour 小时
Minute 分钟
Second
Milli 毫秒
Micro 微秒

Pandas 使用这种使用DateOffset及其专业知识的策略来编纂逻辑来计算第二天。 这使得使用这些对象既灵活又强大。 DateOffset对象可以在各种情况下使用:

  • 可以将它们相加或相减以获得转换后的日期
  • 可以将它们乘以整数(正数或负数),以便多次应用增量
  • 它们具有rollforwardrollback方法,可以将日期向前或向后移动到下一个或上一个“偏移日期”

可以通过向datetime对象传递代表固定时间段的datetime对象或使用多个关键字参数来创建DateOffset对象。 关键字参数分为两大类。 第一类是代表绝对日期的关键字:年,月,日,小时,分钟,秒和微秒。 第二类代表相对持续时间,可以是负值:年,月,周,日,小时,分钟,秒和微秒。

下面的代码创建 1 天的偏移量并将其添加到datetime中:

以下内容将从给定日期计算下一个工作日:

特定DateOffset的多个单位可以通过乘法表示:

下面的示例演示如何使用BMonthEnd对象从给定日期(在本例中为2014-09-02)计算一个月的最后一个工作日:

以下使用BMonthEnd对象.rollforward()方法计算下个月结束:

可以对多个偏移量类别进行参数设置,以提供对偏移量行为的更好控制。 例如,以下内容将计算2014-08-31之前一周中的星期二(weekday = 1)的日期:

锚定的偏移

锚定偏移是代表给定频率并从特定点开始的频率,例如周,月或年的特定日期。 锚定的偏移量使用特定的速记术语。 例如,以下字符串指定一周中的特定日期:

别名 描述
W-SUN 每周日(与W相同)
W-MON 每周一
W-TUE 每周二
W-WED 每周三
W-THU 每周四
W-FRI 每周五
W-SAT 每周六

例如,下面的代码生成一个索引,该索引由两个指定日期之间的所有星期三的日期组成:

也可以使用年度和季度频率来创建锚定偏移。 这些频率锚的格式为[B][A|Q][S]-[MON],其中B(工作日)和S(开始而不是结束)是可选的,A代表年度,Q代表季度,MON是月份的三位数缩写(JAN,FEB 等)。

为了说明这一点,以下代码生成了 2014 年季度末的业务日期,而当年锚定在 6 月底:

使用时间段表示持续时间

对时序数据进行许多有用的分析操作都需要分析特定时间间隔内的事件。 一个简单的例子是确定在特定时期内发生了多少笔金融交易。

可以使用TimestampDateOffset进行这些类型的分析,在其中计算范围,然后根据这些范围过滤项目。 但是,当您需要处理必须分为多个时间段的事件时,这变得很麻烦,因为您开始需要管理TimestampDateOffset对象集。

为了促进这些类型的数据组织和计算,Pandas 使用Period类将时间间隔作为正式构造。 Pandas 还使用PeriodIndexPeriod对象序列进行形式化,该功能提供了根据与对象相关联的索引对齐数据项的功能。

用时间段建模时间间隔

Pandas 使用Period对象将时间间隔的概念形式化。 Period允许您根据频率(例如每天,每周,每月,每年,每季度等)指定持续时间,它将提供一个特定的开始和结束Timestamp,代表特定的时间间隔。

使用时间戳和频率创建Period,其中时间戳表示用作参考点的锚点,频率是持续时间。 为了说明这一点,下面创建了一个代表一个月的期间,该期间固定在 2014 年 8 月:

Period具有start_timeend_time属性,可告知我们派生的开始时间和结束时间:

当我们指定 2014 年 8 月为时,Pandas 会确定锚点(start_time),然后根据指定的频率计算end_time。 在这种情况下,它将根据start_time计算一个月,并返回该值之前的最后一个时间单位。

Period上的数学运算过载,根据给定值计算另一个Period。 下面基于变量aug2014创建一个新的Period对象,该变量以其表示频率(一个月)的1单位移位:

转变的概念非常重要和强大。 向此Period对象添加1会通知它在时间上以一个正单位移动该对象表示的任何频率。 在这种情况下,它将期限从 1 个月移至 2014 年 9 月。

如果我们检查sep2014变量中表示的开始时间和结束时间,我们会发现 Pandas 已经努力确定代表 2014 年 9 月整个时间的正确日期:

请注意,Period具有知道 9 月是 30 天而不是 31 天的智能。这是Period对象背后的智能的一部分,它节省了很多编码,帮助我们解决了许多困难的日期管理问题。

使用PeriodIndex建立索引

Period对象的序列可以组合成一种特殊形式的 Pandas 索引,称为PeriodIndexPeriodIndex索引可用于将数据与特定时间间隔相关联,并且能够对每个间隔中的事件进行切片和执行分析。

下面创建了一个PeriodIndex,其中包含 2013 年的 1 个月时间间隔:

PeriodIndexDatetimeIndex的不同之处在于索引标签是Period对象。 以下显示索引中所有Period对象的开始和结束时间:

Pandas 已确定每个月的开始和结束,同时考虑了每个特定月份的实际天数。

使用PeriodIndex,我们可以将其用作索引来构造Series对象,并将值与索引中的每个Period相关联:

现在,我们有了一个时间序列,其中特定索引标签上的值表示跨一段时间的度量。 像这样的序列的一个例子是给定月份而不是特定时间的证券的平均值。 当我们将时间序列重新采样到另一个频率时,这变得非常有用。

DatetimeIndex一样,PeriodIndex可用于使用Period来索引值,该字符串表示一个或部分规范。 为了演示,我们将创建另一个类似于上一个序列的序列,但跨度为 2013 年和 2014 年:

可以使用Period对象或代表句点的字符串使用特定的索引标签来选择各个值。 下面演示了如何使用字符串表示形式:

也可以使用部分规格,例如以下内容,它仅检索 2014 年期间的所有值:

PeriodIndex也可以切片。 以下内容检索 2014 年 3 月至 2014 年 6 月之间(含)的所有值:

使用日历处理假期

早前,当我们计算 2014 年 8 月 29 日的下一个工作日时,Pandas 告诉我们该日期是 2014 年 9 月 1 日。在美国,这实际上是不正确的:2014 年 9 月 1 日是美国联邦假日,并且银行在这一天关闭交易。 原因是 Pandas 计算下一个工作日时使用特定的默认日历,并且此默认 Pandas 日历不包括 2014 年 9 月 1 日作为假日。

解决此问题的方法是创建一个自定义日历(我们将不对其进行详细介绍),或仅针对这种情况使用 Pandas 提供的一个自定义日历USFederalHolidayCalendar。 然后,可以将此自定义日历传递给CustomBusinessDay对象,而不是BusinessDay对象。 然后,使用此CustomBusinessDay对象进行的计算将使用新日历并考虑美国联邦假日。

以下内容演示了USFederalCalendar对象的创建以及如何使用它报告其认为假期的日子:

然后,可以使用此日历来计算自 2014 年 8 月 29 日起的下一个工作日:

现在,所得的计算结果将劳动节(不是工作日)考虑在内,并返回了正确的2014-09-02日期。

使用时区标准化时间戳

在使用时序数据时,时区管理可能是最复杂的问题之一。 数据通常是使用当地时间在全球范围内的不同系统中收集的,有时,它需要与在其他时区收集的数据进行协调。

幸运的是,Pandas 为使用不同时区的时间戳提供了丰富的支持。 在后台,Pandas 利用pytzdateutil库来管理时区操作。 dateutil支持是 Pandas 0.14.1 版本的新增功能,目前仅支持固定偏移量和tzfile区域。 Pandas 使用的默认库是pytz,并提供了对dateutil的支持以与其他应用兼容。

时区感知的 Pandas 对象支持.tz属性。 默认情况下,出于时效考虑,支持时区的 Pandas 对象不使用timezone对象。 下面的代码获取当前时间,并演示默认情况下没有时区信息:

这表明 Pandas 默认情况下将Timestamp("now")视为 UTC,但没有时区数据。 这是一个很好的默认值,但请注意这一点。 总的来说,我发现如果您要根据存储的时间来收集数据以供以后访问,或者从多个数据源收集数据,则最好始终定位到 UTC。

同样,默认情况下,DatetimeIndex及其Timestamp对象将不具有关联的时区信息:

可以检索常见时区名称的列表,如以下示例所示。 如果您经常处理时区数据,这些将变得非常熟悉:

本地 UTC 时间可以使用使用的以下内容找到。 Timestamptz_localize()方法并传递UTC值:

通过将时区名称传递给.tz_localize(),可以将任何Timestamp本地化到特定时区:

可以使用pd.date_range()方法的tz参数在特定的时区创建DatetimeIndex

也可以显式构造其他时区。 该模型可以让您更好地控制.tz_localize()中使用哪个时区。 下面的代码创建两个不同的timezone对象,并将Timestamp定位到每个对象:

考虑到时区信息,对多个时间序列对象的操作将在其索引中按Timestamp对齐。 为了证明这一点,我们将使用以下代码,使用两个DatetimeIndex对象创建两个Series对象,每个对象的开始,周期和频率相同,但使用不同的时区:

下面通过将两个Series对象加在一起展示了它们按时区的对齐方式:

将时区分配给对象后,可以使用.tz.convert()方法将该对象转换为另一个时区:

现在,如果将s_pacific添加到s_mountain,则对齐将强制执行相同的结果:

处理时间序列数据

现在,我们将研究对时间序列数据执行的几种常见操作。 这些操作需要重新排列数据,更改样本频率及其值,以及在连续移动的数据子集上计算合计结果,以确定随时间变化的数据值的行为。

移动和滞后

时间序列数据的常见操作是将值在时间上前后移动。 Pandas 方法是.shift(),它将SeriesDataFrame中的值移动索引中指定频率单位的数量。

为了演示移位,我们将使用以下SeriesSeries具有五个值,并按日期从2014-08-01开始索引,并使用每日频率:

以下将值向前移动 1 天:

Pandas 将这些值向前移动了指数频率的一个单位,即一天。 索引本身保持不变。 没有2014-08-01的替代数据,因此已用NaN填充。

滞后是向负方向的偏移。 以下时间比Series落后2天:

索引标签2014-08-042014-08-03现在具有NaN值,因为没有要替换的项目。

使用班次执行的常见计算是计算值的每日变化百分比。 这可以通过将Series对象除以其值偏移 1 来执行:

可以在不同于索引的频率上执行移位。 执行此操作后,索引将被修改并且值保持不变。 例如,以下将Series向前移动一个工作日:

再举一个例子,以下向前移动5小时:

时间序列也可以使用DateOffset进行移位。 以下代码将时间序列向前移动0.5分钟:

.tshift()方法提供了另一种形式的移位。 此方法以指定单位和freq参数指定的频率(要求)移动索引标签。 以下代码通过按-1小时调整索引来演示此方法:

在时间序列上执行频率转换

可以使用时序对象的.asfreq()方法将频率数据转换为 Pandas。 转换频率时,将创建一个新的Series对象和一个新的DatatimeIndex对象。 新Series对象的DatetimeIndex从原始文件的第一个Timestamp开始,并以给定的频率运行,直到原始文件的最后Timestamp。 然后将值与新的Series对齐。

为了演示,我们将使用以下时间序列的连续增量整数映射到 2014 年 8 月的每一天的每一小时:

以下代码使用.asfreq('D')将此时间序列转换为每日频率:

由于数据与每小时时间序列中的新的每日时间序列一致,因此仅复制与确切日期匹配的值。

如果将结果转换回每小时一次,我们将看到许多值是NaN

新索引按小时间隔具有Timestamp对象,因此仅确切日期的时间戳记与每日时间序列一致,从而得到 670 NaN值。

可以使用.asfreq()方法的method参数更改此默认行为。 该值可用于正向填充,反向填充或填充NaN值。

ffill方法将向前填充最后一个已知值(pad也这样做):

bfill方法将从下一个已知值回填值:

向上和向下重新采样时间序列

频率转换提供了一种将时间序列中的索引转换为另一个频率的基本方法。 新时间序列中的数据与旧数据一致,并可能导致许多NaN值。 使用填充方法可以部分解决此问题,但是其填充适当信息的能力受到限制。

重采样的不同之处在于,它不会执行纯对齐。 新序列中放置的值可以使用相同的正向和反向填充选项,但是也可以使用其他 Pandas 提供的算法或您自己的函数来指定它们。

为了演示重采样,我们将使用以下时间序列,该时间序列表示 5 天周期内值的随机游动:

使用.resample()方法并为其传递新的频率可以完成对 Pandas 的重新采样。 为了证明这一点,下面将每秒数据重新采样为分钟。 这是下采样,因为结果的频率较低,并且值较小:

请注意,第一个值为 -8.718220,而原始数据的值为 0.469112。 进行频率转换后,该值应保持在-8.718220。 这是因为重采样不会通过对齐复制数据。 重新采样实际上将根据新的周期将数据拆分为数据桶,然后对每个桶中的数据执行特定操作,在这种情况下,将计算桶的平均值。 可以使用以下方法对此进行验证,该方法将从步行中提取第一分钟的数据并计算其平均值:

在下采样中,由于现有数据是根据新的间隔放入存储桶中的,因此通常可能会问到存储桶两端的值是多少。 例如,上一次重采样的第一个间隔是从2014-08-01 00:00:002014-08-01 23:59:59,还是应该在2014-08-04 00:00:00处结束但在2014-08-03 23:59:59处开始?

默认值是前者,它称为左关闭。 排除左值并包括右值的另一种情况是右关闭,可以使用close='right'参数来执行。 以下内容对此进行了说明,并注意到所得到的间隔和值之间的细微差别:

关于使用左右关闭的决定实际上取决于您和您的数据建模,但是 pandas 为您提供了选择。

计算每个存储桶的平均值只是一个选择。 下面演示了在每个存储桶中获取第一个值:

为了演示上采样,我们将对步行进行重新采样至几分钟,然后又恢复为几秒钟:

上采样为秒数据创建了索引值,但默认情况下插入了NaN值。 可以使用fill_method参数修改此默认行为。 我们在使用向前和向后填充选项更改频率时看到了这一点。 这些也可以重新采样。 下面演示了如何使用正向填充:

还可以使用.interpolate()方法对结果内插缺失值。 这将为重采样期间创建的所有NaN值计算结果中存在的值之间的线性插值:

Pandas 还使用.ohlc()方法提供了一种非常方便的重采样方法,称为开,高,低和关。 以下示例获取了我们的秒数据并计算了小时ohlc值:

时间序列的滚动窗口操作

Pandas 提供了许多函数来计算移动(也称为滚动)统计信息。 在滚动窗口中,pandas 在特定时间段表示的数据窗口上计算统计信息。 然后,该窗口将沿某个间隔滚动,只要该窗口适合时间序列的日期,就将在每个窗口上连续计算统计信息。

通过在序列和数据帧对象上提供.rolling()方法,pandas 为滚动窗口提供了直接支持。 然后,来自.rolling()的结果值可以具有许多调用的不同方法之一,这些方法可以在每个窗口上执行计算。 下表显示了许多这些方法:

函数 描述
.rolling().mean() 窗口中的平均值
.rolling().std() 窗口中值的标准差
.rolling().var() 窗口中值的方差
.rolling().min() 窗口中的最小值
.rolling().max() 窗口中的最大值
.rolling().cov() 窗口中值的协方差
.rolling().quantile() 窗口中值的百分比/样本分位数
.rolling().corr() 窗口中值的相关性
.rolling().median() 窗口中值的中位数
.rolling().sum() 窗口中值的总和
.rolling().apply() 用户函数在窗口中的值的应用
.rolling().count() 窗口中非NaN值的数量
.rolling().skew() 窗口中值的偏度
.rolling().kurt() 窗口中值的峰度

作为一个实际示例,滚动平均值通常用于消除短期波动并突出显示数据的长期趋势,并且在财务时间序列分析中非常常用。 为了演示,在本章前面创建的随机游走的第一分钟,我们将使用窗口 5 计算滚动平均值。

图表的生成将在第 14 章“可视化”中详细介绍

可以看出.rolling().mean()如何提供对基础数据的更平滑表示。 较大的窗口将产生较小的方差,较小的窗口将产生更多的方差(直到窗口大小为 1 为止,这将与原始序列相同)。

下面的示例展示了滚动平均值,其中窗口 2、5 和 10 与原始序列相对应:

请注意,窗口越大,曲线开始处缺失的数据越多。 大小为 n 的窗口在计算度量之前需要 n 个数据点,因此在图的开始处存在间隙。

可以使用.rolling().apply()方法通过滚动窗口来应用任何用户定义的函数。 提供的函数将在窗口中传递值数组,并且应返回一个值。 然后,Pandas 会将每个窗口的结果组合成一个时间序列。

为了说明这一点,以下代码计算了平均平均偏差,使您可以感觉到样本中所有值与总体平均值之间的距离:

可以使用pd.rolling_mean函数的使用的微小变化来计算扩展的窗口平均值,该函数通过始终从时间序列中的第一个值开始重复计算平均值,并且每次迭代都将窗口大小增加一个。 与滚动窗口相比,扩展窗口平均值将更稳定(响应性更差),因为随着窗口大小的增加,下一个值的影响将减小:

总结

在本章中,我们研究了多种方法来表示在特定时间点发生的事件,以及如何对这些值随时间变化进行建模。 这涉及学习 Pandas 的许多功能,包括日期和时间对象,表示时间间隔和周期的时间变化,以及对时间序列数据执行多种类型的操作,例如频率转换,重采样和计算滚动窗口。

在本书的其余两章中,我们将抛弃 Pandas 的机制,而将更多的精力放在数据的可视化以及将 Pandas 应用于财务数据分析上。

十四、可视化

数据分析的最重要部分之一是创建出色的可视化效果,以立即传达数据中的潜在含义。 数据可视化是有效的,因为我们人类是视觉生物,并且已经发展成为能够识别信息布局的含义的方式,当视网膜的冲动击中大脑时,我们的大脑几乎可以立即解释。

多年来,已经进行了大量研究,结果产生了许多有效的可视化技术来传达数据中的特定模式。 这些模式已在可视化库中实现,Pandas 被设计为利用这些模式并使它们的使用非常简单。

本章将介绍其中的几种技术,主要侧重于 matplotlib 和许多常见的可视化。 我们将分三个步骤进行操作。 第一部分介绍了用 Pandas 编程可视化的一般概念,着重介绍了创建时序图的过程。 在此期间,我们将深入研究标记轴的技术以及创建图例,颜色,线条样式和标记的技术。

第二步将集中在 Pandas 和数据分析中常用的多种数据可视化类型,包括:

  • 用条形图显示相对差异
  • 用直方图描绘数据的分布
  • 用箱形图和胡须图描述类别数据的分布
  • 用面积图显示累计总数
  • 散点图与两个变量之间的关系
  • 用核密度图估计分布
  • 散点图矩阵与多个变量之间的相关性
  • 热图与多个变量之间的关系强度

最后一步将检查如何通过将绘图划分为多个子部分来创建合成绘图,以便能够在单个图形画布中渲染多个绘图。 这将帮助可视化的查看者一目了然地关联不同的数据集。

配置 Pandas

本章中的所有示例均基于以下导入和默认设置。 本章的一个小区别是在整个示例中共享的seedval变量的声明:

Pandas 绘图的基础知识

Pandas 库本身执行不执行数据可视化。 为了做到这一点,Pandas 与 Python 生态系统中的其他强大的可视化库紧密集成。 这些集成中最常见的是与matplotlib集成。 因此,本章将把示例重点放在matplotlib上,但我们还将为您指出其他可能的库,以供您自己尝试。 其中两个值得一提。

Seaborn 是另一个基于matplotlib的 Python 可视化库。 它提供了一个高级接口来呈现引人注目的统计图形。 它具有对 NumPy 和 pandas 数据结构的本地支持。 Seaborn 的目标是创建在本质上看起来不太科学的matplotlib图。 要了解有关 Seaborn 的信息,请访问站点

尽管 Seaborn 和matplotlib在渲染数据方面都很出色,但它们都缺乏用户交互性。 尽管可以通过 Jupyter 之类的工具将两者的渲染显示在浏览器中,但是渲染本身并不使用 DOM 创建,也不使用浏览器的任何功能。

为了促进在浏览器中的呈现并提供丰富的交互性,已创建了几个库来将 Python 和 Pandas 与D3.js集成在一起。 D3.js是一个 JavaScript 库,用于处理文档并创建丰富的交互式数据可视化。 其中最流行的一种是 mpld3。 不幸的是,在编写本书时,它不适用于 Python 3.6,因此无法覆盖。 但是请仍然在 d3js.orgmpld3 上查看D3.js

创建时间序列图

时间序列数据是最常见的数据可视化之一。 在 Pandas 中可视化时间序列就像在对时间序列建模的DataFrameSeries对象上调用.plot()一样简单。

下面的示例演示如何创建一个时间序列,该时间序列表示一段时间内价值的随机波动,类似于股票价格的波动:

Pandas 对象的.plot()方法是matplotlib库的plot()函数的包装函数。 它使 Pandas 数据图非常易于创建,因为其实现被编码为知道如何基于基础数据呈现许多可视化。 它处理许多细节,例如选择序列,标记和轴生成。

在前面的示例中,.plot()确定Series包含其索引的日期,因此 x 轴应设置为日期格式。 它还为数据选择默认颜色。

绘制一些数据所产生的结果与使用单列呈现DataFrame相似。 下面的代码通过产生相同的图但略有不同来说明这一点:它为图添加了图例。 从DataFrame生成的图表默认情况下将包含图例。

如果DataFrame包含多列,则.plot()将在图例中添加多个项目,并为每行选择不同的颜色:

如果要使用DataFrame中的数据列作为图的 x 轴上的标签(而不是索引标签),请使用x参数指定表示标签的列的名称。 然后,使用y参数指定将哪些列用作数据:

装饰和设计时间序列图

内置的.plot()方法具有许多选项,可用于更改图形中的内容。 让我们介绍许多绘图中使用的几种常用选项。

添加标题和更改轴标签

可以使用title参数设置图表标题。 调用.plot()之后,不使用.plot()设置轴标签,而是直接使用plt.ylabel()plt.xlabel()函数设置:

指定图例的内容和位置

要更改图例中用于每个数据序列的文本(默认为DataFrame中的列名),请捕获从.plot()方法返回的ax对象,并使用其.legend()方法。 该对象是AxesSubplot对象,可用于在生成图形之前更改其各个方面:

可以使用.legend()loc参数设置图例的位置。 默认情况下,pandas 将该位置设置为'best',这告诉matplotlib检查数据并确定认为放置图例的最佳位置。 但是,您也可以指定以下任意一项来更具体地放置图例(可以使用字符串或数字代码):

字符串 代码
'best' 0
'upper right' 1
'upper left' 2
'lower left' 3
'lower right' 4
'right' 5
'center left' 6
'center right' 7
'lower center' 8
'upper center' 9
'center' 10

下面的示例演示了将图例放置在图形的上部中央部分:

可以使用legend=False关闭图例:

指定线条颜色,样式,粗细和标记

Pandas 会在任何图表上自动设置每个序列的颜色。 要指定自己的颜色,请向绘图函数的style参数提供样式代码。 pandas 具有许多内置的颜色单字符代码,此处列出了其中的一些:

  • b:蓝色
  • g:绿色
  • r:红色
  • c:青色
  • m:洋红色
  • y:黄色
  • k:黑色
  • w:白色

也可以使用#RRGGBB格式的十六进制 RGB 代码指定颜色。 下面的示例通过使用一位数字代码将第一个序列的颜色设置为绿色,以及使用 RGB 十六进制代码将第二个序列的颜色设置为红色来演示这两个示例:

可以使用线型代码指定线型。 通过紧跟在颜色代码之后,可以将它们与颜色样式代码结合使用。 以下是一些有用的线型代码的示例:

  • '-':实线
  • '-':虚线
  • ':':点线
  • '-':点划线
  • '.':点

下图通过绘制五个数据序列来演示这五种线型,每个数据序列都具有以下一种线型:

可以使用lw参数.指定线的粗细。可以通过传递宽度列表或应用于所有线的单个宽度来传递多条线的厚度。 下面的代码以 3 的线宽重绘该图形,使这些线更明显:

可以通过使用样式代码中的缩写来指定行上的标记。 提供了很多标记类型,您可以在这里中查看所有标记类型。 在下一个图表中,我们将通过使每个序列使用与以下不同的标记来检查其中的五个:圆形,星形,三角形,菱形和点:

指定刻度线位置和刻度线标签

刻度线的位置和渲染可以使用各种函数进行自定义。 以下代码引导您完成几个控制其值和渲染的示例。

Pandas 决定在标签位置使用的值可以使用plt.xticks()函数找到。 此函数返回两个值:每个刻度的值和代表实际标签的对象:

此报价数组包含沿 x 轴以实际数据为单位的报价位置。 在这种情况下,Pandas 认为 0 到 4(最小和最大)的范围和 0.5 的间隔是合适的。

如果要使用其他位置,请通过将列表传递到plt.xticks()来提供它们。 以下代码使用从-15的偶数整数对此进行了演示。 这组值将更改轴的范围并删除非整数标签:

可以通过将每个刻度位置的标签作为第二个参数来指定它们。 在以下示例中,通过将 y 轴刻度和标签更改为整数值和连续的字母字符,可以显示出这一点:

使用格式化器格式化轴的刻度日期标签

使用定位器格式化器完成其基础数据类型为datetime的轴标签的格式化。 定位器控制刻度线的位置,格式器控制标签的格式。

matplotlibmaptplotlib.dates中提供了几个类来帮助简化该过程:

  • MinuteLocatorHourLocatorDayLocatorWeekdayLocatorMonthLocatorYearLocator
  • 这些是专用的编码器,用于确定每种类型的日期字段的刻度将在轴上的哪里找到
  • DateFormatter
  • 此类可用于将日期对象格式化为轴上的标签

默认的定位器和格式化器为AutoDateLocatorAutoDateFormatter。 这些可以通过提供不同的对象实现来更改。

为了演示,让我们从前面的示例中的以下随机游走数据子集开始; 它仅代表 2014 年 1 月至 2014 年 2 月的数据。绘制此图可得到以下输出:

该图的 x 轴上的标签有两个序列,次要主要。 此图中的次要标签包含当月的日期,而主要标签则包含年和月(仅第一个月的年份)。 我们可以为每个次要和主要级别设置定位器和格式化器,以更改值。

这将通过将次要标签更改为从每周的星期一开始并包含日期和星期几来演示(现在,图表使用每周,并且仅使用星期五的日期,没有日期名称)。 主要标签将使用每月位置,并且始终包含月份名称和年份:

这几乎是我们想要的。 但是,请注意,该年份报告为0045。 要使用基于自定义日期的标签创建图,需要避开 Pandas .plot()并直接使用matplotlib。 幸运的是,这并不难。 此代码片段会略微更改代码,并以所需的格式呈现我们需要的内容:

我们可以使用 x 轴对象的.grid()方法为短轴和长轴刻度线添加网格线。 第一个参数是True(用于渲染线)或False(用于隐藏线)。 第二个指定滴答集。 以下代码在不绘制主线的情况下重新绘制此图形,同时渲染了次线:

格式化的最终演示将仅使用主要标签,但每周使用YYYY-MM-DD格式。 但是,由于这些会重叠,因此我们将指定它们应该旋转以防止重叠。 此旋转通过fig.autofmt_xdate()函数指定:

统计分析中常用的图

在学习了如何创建,布置和标注时间序列图之后,我们现在将着眼于创建对表示统计信息有用的变量。

用条形图显示相对差异

条形图可用于可视化非时间序列数据值的相对差异。 可以使用.plot()kind='bar'参数创建条形图:

将创建一个多序列条形图,以比较每个 x 轴标签上的多个值:

stacked=True参数可用于堆叠条形,而不必并排:

可以使用kind='barh'将方向变为水平:

用直方图描绘数据的分布

直方图对于可视化数据分布很有用。 以下代码基于正态分布中的1000随机值生成直方图:

直方图的分辨率可以通过指定要分配给图的仓数来控制。 默认值为 10,增加箱的数量可为直方图提供更详细的信息。 此代码将容器数增加到100

如果数据有多个序列,则直方图函数将自动生成多个直方图,每个序列一个:

要将多个直方图叠加在同一张图上(以便快速看到分布的差异),请在.show()之前多次调用pyplot.hist()函数:

用箱形图和胡须图描述类别数据的分布

箱形图来自描述性统计数据,是使用四分位数描述类别数据分布的有用方式。 每个框代表数据的第一和第三四分位数之间的值,并且在中位数处跨框有一条线。 每个胡须伸出手来展示在第一和第三四分位数上下的五个四分位数范围的程度:

用面积图显示累计总数

面积图用于表示一段时间内的累计总数,并演示相关属性之间的时间趋势变化。 也可以将它们“堆叠”以显示所有变量的代表性总计。

通过指定kind='area'生成面积图。 堆积面积图是默认设置:

使用stacked=False会生成未堆积的面积图:

默认情况下,未堆叠的图的 alpha 值为 0.5,因此可以查看多个数据序列如何重叠。

散点图与两个变量之间的关系

散点图显示一对变量之间的相关性。 通过使用.plot()并指定kind='scatter'以及DataFrame源中的 x 和 y 列,可以从DataFrame创建散点图:

可以通过拖放到matplotlib中来创建更精细的散点图。 以下代码演示了 2016 年 Google 股票数据的使用,计算每日收盘价中的增量,然后将收盘量与交易量渲染为从交易量得出的大小不同的气泡:

请注意 x 和 y 轴标签的命名法,这得益于 matplotlib 的内部 LaTeX 解析器和布局引擎,为标签创建了不错的数学风格。

用核密度图估计分布

核密度估计图,而不是通过估计数据的真实分布来将数据平滑化为连续图,而不是数据的纯粹经验表示。 可以使用.plot()方法并设置kind='kde'来创建内核密度估计图。 以下代码生成一组正态分布的数字,将其显示为直方图,并覆盖kde图:

散点图矩阵与多个变量之间的相关性

散点图矩阵是确定多个变量之间是否存在线性相关性的一种流行方法。 此代码创建具有随机值的散点图矩阵,并为每个变量组合以及每个对角线每个变量的 kde 图绘制散点图:

当将其应用于金融时,当计算各种股票的相关性时,我们将再次看到该图。

热图与多个变量之间的关系强度

热图是数据的图形表示,其中矩阵内的值由颜色表示。 这是显示在两个变量的交点处测得的值之间关系的有效方法。

常见的情况是将矩阵中的值归一化为 0.0 到 1.0,并使行和列之间的交点表示两个变量之间的相关性。 相关性较小(0.0)的值为最暗,相关性最高(1.0)的值为白色。

使用.imshow()函数,可以通过 Pandas 和matplotlib轻松创建热图:

下一章还将重新讨论热图,以证明相关性。

在单个图表中手动绘制多个图

通过将多个图彼此相邻显示来对比数据通常很有用。 我们已经看到,pandas 对几种类型的图自动执行此操作。 也可以在同一画布上手动渲染多个图。

要使用matplotlib在画布上渲染多个子图,请多次调用plt.subplot2grid()。 每次经过网格的大小时,子图都将位于(shape=(height, width)上,子图的左上角位置(loc=(row, column))将位于网格上。 尺寸以总列数为单位,而不是以像素为单位。

每次调用plt.subplot2grid()的返回值都是一个不同的AxesSubplot对象,可用于指定子图渲染的位置。

以下代码通过基于两行一列(shape=(2,1))创建一个绘图来演示这一点。 由ax1引用的第一个子图位于第一行(loc=(0,0)),由ax2引用的第二个子图位于第二行(loc=(1,0)):

子图已创建,但我们都尚未参与。

可以在每次调用plt.subplot2grid()的过程中使用rowspancolspan参数指定任何子图的大小。 这种指定跨度的方式与 HTML 表格中的 Speng 跨度非常相似。 这段代码演示了如何使用跨度为每个图指定不同的行,列和跨度来创建五个图的更复杂的布局:

要渲染为特定的子图,请使用ax参数将目标轴对象传递到.plot()。 下面的代码通过从本章开头创建的随机游走中提取每个序列,然后在不同的子图中渲染每个序列,来演示这一点:

使用这种技术,我们可以执行不同序列数据的组合,例如股票收盘价与交易量图。 给定我们在上一个 Google 示例中读取的数据,此代码将绘制成交量与收盘价的关系:

总结

在本章中,我们研究了许多最常见的可视化 Pandas 数据的方法。 数据的可视化是快速了解数据中正在讲述的故事的最佳方法之一。 Python,pandas 和matplotlib(甚至其他一些库)提供了一种非常快速的方法,只需几行代码即可获取基础消息并以精美的方式显示它。

在本书的下一章和最后一章中,我们将研究 Pandas 在金融中的应用,尤其是股票价格分析。

十五、历史股价分析

在最后一章中,我们将使用 Pandas 对从 Google 财经获取的股票数据进行各种财务分析。 这还将涵盖财务分析中的多个主题。 重点将放在使用 Pandas 得出实用的时序股票数据上,而不是在金融理论的细节上。 但是,我们将涵盖许多有用的主题,并学习将 Pandas 应用于此领域和其他领域有多么容易。

具体而言,在本章中,我们将完成以下任务:

  • 从 Google 财经中获取和整理股票数据
  • 绘制时间序列价格
  • 绘制交易量序列数据
  • 计算简单的每日百分比变化
  • 计算简单的每日累计收益
  • 将从数据每日重新采样为每月的收益
  • 分析收益分布
  • 执行滚动平均计算
  • 比较股票的每日平均收益
  • 根据收盘价的每日百分比变化的股票相关性
  • 计算股票波动率
  • 可视化相对于预期收益的风险

配置 IPython 笔记本

本章中的所有示例均基于以下导入和默认设置:

从 Google 获取和整理股票数据

我们的首要任务是编写几个函数,以帮助我们从 Google 财经中检索股票数据。 我们已经看到可以使用 pandas DataReader对象读取此数据,但是我们将需要与 Google 财经提供的数据组织方式稍有不同,因为我们稍后将对这些信息进行各种处理 。

以下函数将获取两个指定日期之间特定股票的所有 Google 财经数据,并将该股票的代码添加到列中(稍后需要进行数据透视)。

数据将包含一个固定的 3 年窗口,范围从 2012 年到 2014 年。以下内容是MSFT代码的 3 年期数据:

既然我们有了一个可以获取单个报价器数据的函数,那么拥有一个可以读取多个报价器的数据并将它们全部返回到单个数据结构中的函数将很方便。 以下代码执行此任务:

本章中的示例将使用苹果AAPL), 微软MSFT),通用电气GE), IBMIBM),美国航空AA), 达美航空DAL),美联航航空公司UAL),百事可乐PEP), 和可口可乐KO)。

选择这些股票是为了在三个不同部门(技术,航空和软饮料)中的每个样本中都有多个股票的样本。 这样做的目的是演示如何在相似行业的选定股票之间的选定时间段内,得出各种股票价格测量值之间的相关性,并演示不同行业之间的股票差异。

我们可以阅读以下内容:

注意:在测试期间,已经确定根据您的位置,这可能会由于 URL 可访问性而导致一些错误。

我们将对Close列中的收盘价特别感兴趣。 但是,如果我们有一个DataFrame对象按日期索引,并且其中每一列都是特定股票的价格,而行是该股票在该日期的收盘价,那么对我们来说更方便。 可以通过旋转数据来完成此操作,这是在读取数据时添加“股票行情指示器”列的原因。 以下函数将为我们做到这一点:

以下使用该函数将数据转移到新组织:

现在,所有股票的收盘价都是每一列中一列的值。 通过这种格式,可以轻松比较每只股票的收盘价和其他股票的收盘价。

绘制价格时间序列

现在,让我们对AAPLMSFT的收盘价进行图形比较。 下图显示了AAPL的调整后收盘价:

下面显示了MSFT

两组收盘价都可以轻松地显示在单个图表上,以进行并排比较(或彼此比较):

绘制交易量序列数据

可以使用条形图绘制交易量数据。 我们首先需要获取交易量数据,这可以使用之前创建的pivot_tickers_to_columns()函数来完成:

现在,我们可以使用此数据绘制条形图。 下图显示了MSFT的交易量:

普通类型的财务图会绘制相对于其收盘价的股票量。 以下示例创建了这种类型的可视化:

计算收盘价的简单每日百分比变化

收盘价的每日简单百分比变化(不包括股息和其他因素)是指股票在一天的交易中价值的百分比变化。 它由以下公式定义:

可以使用.shift()在 Pandas 中轻松计算出:

快速检查确认AAPL2011-09-08上的收益是正确的:

如上图所示,每日百分比变化图通常看起来像噪声。 但是,当我们使用这些值的累积乘积(称为每日累积收益)时,便可以查看股票值随时间的变化。 那是我们的下一个任务。

计算股票的简单每日累计收益

简单的累积每日收益是通过计算每日百分比变化的累积乘积来计算的。 此计算由以下公式表示:

使用.cumprod()方法简洁地计算出:

现在可以绘制累积收益,以查看各种股票的价值随时间变化的情况:

将数据从每日重新采样为每月的收益

要计算每月的回报率,我们可以使用一些 Pandas 魔术,然后对原始的每日回报进行重新采样。 在此过程中,我们还需要舍弃不属于月底的日期,并预先填写所有缺少的值。 可以使用.ffill()完成重采样的结果:

请注意条目的日期,并且它们现在都是月末日期。 值未更改,因为重新采样仅选择了月底的日期,或者如果源中不存在该日期之前的值,则使用该日期之前的值进行填充。

现在我们可以使用它来计算每月百分比变化:

现在,可以计算每月累积回报:

月度收益具有以下可视化效果:

这看起来像每日收益,但总的来说,它并不那么顺利。 这是因为它使用了大约 30% 的数据,并且绑定到月底。

分析收益分布

通过将数据绘制在直方图中,可以感觉到特定股票每日百分比变化的分布差异。 生成数据(例如每日收益)的直方图的一个技巧是选择要聚合值的箱数。 该示例将使用 50 个桶,这使您可以很好地感觉到三年数据中每日变化的分布。

以下显示了AAPL的每日百分比变化的分布:

该可视化告诉我们几件事。 首先,大多数日常运动以 0.0 为中心。 其次,左侧有少量偏斜,但是数据显得相对对称。

我们可以在一个直方图矩阵图中绘制所有股票每日百分比变化的直方图。 这为我们提供了一种快速确定这三年内股票行为差异的方法:

轴上的标签有点挤在一起,但是直方图的形状很重要。

从该图表可以看出这九只股票的表现差异,尤其是偏度(均值一侧有更多例外值)。 我们还可以看到总体分布宽度的差异,从而可以快速查看波动性较大的股票。

执行滚动平均计算

可以使用.rolling().mean()计算股票的滚动平均线。 通过消除股票表现中的“噪音”,滚动平均线将使您对股票在给定时间内的表现有所了解。 滚动窗口越大,图形将越平滑且随机性越小,但是会牺牲准确性。

以下示例使用每日收盘价计算 30 天和 90 天期间MSFT的滚动平均值。 可以从视觉上轻松确定降噪的差异:

股票间的每日平均回报的比较

散点图是一种非常有效的方法,可以直观地确定两个股票之间的股价变化率之间的关系。 下图显示了MSFTAAPL之间的收盘价每日百分比变化的关系:

这使我们可以快速查看两种股票之间的每日收益的整体相关性。 每个点代表两只股票的一天。 每个点沿着AAPL的百分比变化沿垂直方向绘制,而对于MSFT沿水平线绘制。

如果对于AAPL值更改的每个金额,MSFT当天也更改了相同比例的金额,则所有点将从左下部分到右上部分沿理想的垂直对角线落下。 在这种情况下,两个变量将与 1.0 的相关值完全相关。 如果两个变量完全不相关,则相关性以及线的斜率将为 0,即完全水平。

为了演示完美的相关关系,我们可以绘制MSFTMSFT的关系图。 与自己相关的任何此类序列将始终为 1.0:

回到AAPLMSFT的图,不包括几个离群值,由于所有值似乎都集中在中心附近,因此该群集似乎仅显示出两只股票之间的适度相关性。

实际的相关性计算(将在下一节中进行研究)显示相关性为 0.1827(这是回归线的斜率)。 该回归线将比对角线更水平。 这意味着,从统计学上来说,对于AAPL价格的任何特定变化,将无法根据 AAPL 的价格变化预测给定日期MSFT价格的变化。

为了促进对多个相关性的整体视觉分析,我们可以使用散点图矩阵图:

该图中的对角线是核密度估计图。 较窄的曲线与较宽的曲线相比波动性较小,偏斜表示较大的收益或亏损趋势。 结合散点图,可以快速总结具有两种不同视觉指标的任意两种股票的比较。

基于收盘价的每日百分比变化的股票相关性

相关性是两个变量之间关联强度的度量。 相关系数为 1.0 意味着,一组数据中的每个值更改在另一组数据中都有相应的值更改。 0.0 相关性意味着数据集没有关系。 相关性越高,基于一个或另一个预测每个变化的能力就越大。

散点图矩阵使我们快速直观地了解了两种股票之间的相关性,但它不是一个确切的数字。 可以使用.corr()方法计算DataFrame中数据列之间的确切相关性。 这将生成代表列的变量之间所有可能相关性的矩阵。

下面的示例计算样本三年中所有这些股票的收盘价每日百分比变化的相关性:

对角线始终为 1.0,因为股票始终与自身完全相关。 可以使用热图可视化此相关矩阵:

该图的想法是,您可以通过查找垂直和水平变量的交点,通过颜色查看相关程度。 颜色越深,相关性越小; 颜色越浅,相关性越大。

计算股票的波动性

股票的波动率是对特定时间段内股票价格方差变化量的度量。 通常,将一只股票的波动率与另一只股票的波动率进行比较,以获得可能风险较小的感觉,或者将一个市场指数与股票的波动率与整个市场进行比较,这是很常见的。 通常,波动性越高,对该股票进行投资的风险就越大。

波动率是通过对股票变化百分比取滚动窗口标准差(并相对于窗口大小缩放比例)来计算的。 窗口的大小会影响整体结果。 窗口越大,代表的测量值就越不代表。 随着窗口变窄,结果接近标准差。 因此,根据数据采样频率选择适当的窗口大小是一项技巧。 幸运的是,Pandas 使得交互修改非常容易。

作为示例,给定 75 个周期的窗口,以下内容将计算样本中股票的波动率:

图表上较高的线表示总体较高的波动率,并且波动率随时间的变化显示为每条线中的运动。

确定相对于预期收益的风险

有用的分析是将股票每日百分比变化的波动率与其预期收益相关联。 这给人一种股票投资的风险/收益率的感觉。 这可以通过将每日百分比变化的平均值相对于相同值的标准差进行映射来计算。

创建一个散点图,将散点图与我们的一组样本股票的风险和回报相关联,并用气泡和箭头标记点:

这样的结果立即从可视化中跳出来,但是仅通过查看数字表就很难看到:

  • 航空股票(AADALUAL)具有最高的风险,但也具有最高的回报率(这不是一般的投资规则吗?)。
  • 科技股具有中等风险,但回报也中等。
  • 在科技股中,IBMGE是四家中最保守的。
  • 可乐股票的风险最低,但总体回报率也最低。 对于大宗商品,这是有意义的。

总结

在学习 Pandas 及其为数据处理和分析提供的功能方面,我们已经走到了旅程的尽头。 在本章之前,我们花费了大部分时间来学习 Pandas 的功能,并且在许多情况下,使用的是设计用来演示概念的数据,而不是使用实际数据。

在本章中,我们将利用到目前为止所学到的所有知识,来说明使用 Pandas 分析现实世界的数据(尤其是股票数据)并从数据中得出结果是多么容易。 通常,这使我们能够通过可视化来快速得出结论,这些可视化旨在使数据中的模式显而易见。

本章还介绍了一些财务概念,例如每日百分比变化,计算收益和时间序列数据的相关性。 重点不是金融理论,而是证明使用 Pandas 来管理和从数字列表中获取含义是多么容易。

最后,值得注意的是,尽管 Pandas 是由金融分析师创建的(因此它具有在金融领域提供简单解决方案的能力),但 Pandas 绝不仅限于金融。 它是用于数据分析的非常强大的工具,可以同样有效地应用于许多其他领域。 其中有几个新兴市场代表着巨大的机遇,例如社交网络分析或可穿戴计算的应用。 无论您将 Pandas 用于哪个领域,我都希望您能像我一样发现使用 Pandas 很有趣。

二、启动和运行 Pandas

在本章中,我们将介绍如何安装 Pandas 并开始使用其基本功能。 本书的内容以 IPython 和 Jupyter 笔记本的形式提供,因此,我们还将快速使用这两种工具。

本书将利用 Continuum 的 Anaconda 科学 Python 发行版。 Anaconda 是流行的 Python 发行版,其中包含免费和付费组件。 Anaconda 提供了跨平台支持,包括 Windows,Mac 和 Linux。 Anaconda 的基本发行版安装了 Pandas,IPython 和 Jupyter 笔记本,因此入门非常简单。

本章将涵盖以下主题:

  • 安装 Anaconda,Pandas 和 IPython/Jupyter 笔记本
  • 使用 IPython 和 Jupyter 笔记本
  • Jupyter 及其笔记本
  • 设置您的 Pandas 环境
  • Pandas SeriesDataFrame快速入门
  • 从 CSV 文件加载数据
  • 生成 Pandas 数据的可视化

Anaconda 的安装

本书将使用 Anaconda Python 版本 3,特别是 3.6.1。 在撰写本文时,Pandas 的版本为 0.20.2。 默认情况下,Anaconda 安装程序将安装 Python,IPython,Jupyter 笔记本和 pandas。

可以从 Continuum Analytics 网站下载 Anaconda Python。 Web 服务器将识别您的浏览器的操作系统,并为您提供该平台的相应软件下载文件。

在浏览器中打开此 URL 时,将看到一个类似于以下内容的页面:

单击适合您平台的安装程序的链接。 这将为您提供类似于以下内容的下载页面:

下载 3.x 安装程序。 本书将使用的当前版本的 Anaconda 是 4.3.1,带有 Python 3.6.1:

这种情况经常发生变化,到您阅读本文时,它可能已经发生变化。

执行适合您平台的安装程序,完成后,打开命令行或终端并执行python命令。 您应该看到类似于以下内容(在 Mac 上是这样):

您可以通过发出exit()语句退出 Python 解释器:

在终端或命令行中,您可以使用pip show pandas命令验证 Pandas 的安装版本:

已安装的当前版本被验证为 0.20.2。 请确保您使用的是 0.20.2 或更高版本,因为将使用此版本特定于 Pandas 的更改。

现在,我们已经安装了所需的一切,让我们继续使用 IPython 和 Jupyter 笔记本。

IPython 和 Jupyter 笔记本

到目前为止,我们已经从命令行或终端执行了 Python。 这是 Python 随附的默认读取-求值-打印-循环REPL)。 这可以用来运行本书中的所有示例,但是本书将使用 IPython 编写文本和代码包 Jupyter 笔记本中的语句。 让我们简要地看一下两者。

IPython

IPython 是用于与 Python 交互工作的备用 Shell。 它对 Python 提供的默认 REPL 进行了一些增强。

如果您想更详细地了解 IPython,请查看文档

要启动 IPython,只需从命令行/终端执行ipython命令。 启动时,您将看到类似以下内容:

输入提示显示In [1]:。 每次在 IPython REPL 中输入一条语句时,提示中的数字都会增加。

同样,您输入的任何特定条目的输出都将以Out [x]:开头,其中x与相应的In [x]:的编号匹配。 以下屏幕截图演示了这一点:

inout语句的编号对于示例很重要,因为所有示例都将以In [x]:Out [x]:开头,以便您可以继续学习。

请注意,这些数字纯粹是连续的。 如果您遵循文本中的代码,并且输入中发生错误,或者输入其他语句,则编号可能会不正确(可以通过退出并重新启动 IPython 来重新设置编号)。 请纯粹将它们用作参考。

Jupyter 笔记本

Jupyter 笔记本是 IPython 笔记本的演变。 它是一个开源 Web 应用,使您可以创建和共享包含实时代码,方程式,可视化和减价的文档。

最初的 IPython 笔记本仅限于 Python。 Jupyter 笔记本已发展为允许使用多种编程语言,包括 Python,R,Julia,Scala 和 F#。

如果您想更深入地了解 Jupyter 笔记本,请访问该页面,在该页面上将显示类似于以下内容的页面:

Jupyter 笔记本可以独立于 Python 下载和使用。 Anaconda 默认安装。 要启动 Jupyter 笔记本,请在命令行或终端上发出以下命令:

$ Jupyter 笔记本

为了演示,让我们看一下如何运行文本附带的示例代码。 从 Packt 网站下载代码,然后将文件解压缩到您选择的目录中。 在目录中,您将看到类似于以下内容的以下内容:

现在发出jupyter notebook命令。 您应该看到类似于以下内容:

将打开一个浏览器页面,显示 Jupyter 笔记本主页,即http://localhost:8888/tree。 这将打开一个显示该页面的 Web 浏览器窗口,该窗口将是类似于以下内容的目录列表:

单击.ipynb链接可打开笔记本页面。 如果打开本章的笔记本,您将看到类似于以下内容:

显示的笔记本是 Jupyter 和 IPython 生成的 HTML。 它由许多单元格组成,可以是四种类型之一:代码,Markdown,原始 nbconvert 或标题。 本书中的所有示例均使用代码或减价单元。

Jupyter 为每个笔记本运行一个 IPython 内核。 包含 Python 代码的单元在该内核中执行,结果作为 HTML 添加到笔记本中。

双击任何单元格将使该单元格可编辑。 编辑完单元格的内容后,按Shift + Enter,此时 Jupyter/IPython 将求值内容并显示结果。

如果您想进一步了解构成页面基础的笔记本格式,请参阅这里

笔记本顶部的工具栏为您提供了许多操作笔记本的功能。 其中包括在笔记本中上下移动,增加和删除单元格。 还提供用于运行单元,重新运行单元以及重新启动基础 IPython 内核的命令。

要创建一个新笔记本,请转到“新笔记本 -> Python3”:

将在新的浏览器选项卡中创建一个新的笔记本页面。 其名称将为无标题:

笔记本包含一个准备好输入 Python 的代码单元。 在单元格中输入1 + 1并按Shift + Enter执行。

该单元已执行,结果显示为Out [1]:。 Jupyter 还打开了一个新单元,供您输入更多代码或减价。

Jupyter 笔记本会每分钟自动保存您的更改,但是偶尔一次手动保存仍然是一件好事。

Pandas 序列和数据帧简介

让我们开始使用一些 Pandas,并简要介绍一下 Pandas 的两个主要数据结构SeriesDataFrame。 我们将检查以下内容:

  • 将 Pandas 导入您的应用
  • 创建和操纵 Pandas Series
  • 创建和操纵 Pandas DataFrame
  • 将数据从文件加载到DataFrame

导入 Pandas

我们将使用的每个笔记本都首先导入 Pandas 和其他几个有用的 Python 库。 它还将设置几个选项来控制 Pandas 如何在 Jupyter 笔记本中渲染输出。 该代码包含以下内容:

第一条语句导入 NumPy 并将库中的项目引用为np.。 在本书中,我们不会对 NumPy 进行详细介绍,但有时需要使用。

第二次导入使 Pandas 可用于笔记本。 我们将使用pd.前缀引用库中的项目。 from pandas import Series, DataFrame语句将SeriesDataFrame对象显式导入到全局名称空间中。 这使我们可以在没有pd的情况下引用SeriesDataFrame。 字首。 这很方便,因为我们会经常使用它们,这样可以节省很多键入时间。

import datetime语句引入了datetime库,该库通常在 Pandas 中用于时间序列数据。 它将包括在每个笔记本的导入中。

pd.set_option()函数调用设置选项,这些选项通知笔记本如何显示 Pandas 的输出。 第一个告诉状态将SeriesDataFrame输出呈现为文本而不是 HTML。 接下来的两行指定要输出的最大列数和行数。 final 选项设置每行中输出的最大字符数。

您可以在这个 URL 中检查更多选项

敏锐的眼睛可能会注意到此单元格没有Out [x]:。 并非所有单元(或 IPython 语句)都会生成输出。

如果您希望使用 IPython 代替 Jupyter 笔记本进行后续操作,则还可以在 IPython Shell 中执行此代码。 例如,您可以简单地从笔记本单元中剪切并粘贴代码。 这样做可能如下所示:

IPython shell 足够聪明,可以知道您要插入多行并且会适当缩进。 并且请注意,在 IPython shell 中也没有Out [x]:pd.set_option不返回任何内容,因此没有注释。

Pandas 序列

Pandas Series是 Pandas 的基本数据结构。 序列与 NumPy 数组相似,但是它的不同之处在于具有索引,该索引允许对项目进行更丰富的查找,而不仅仅是从零开始的数组索引值。

以下从 Python 列表创建一个序列。:

输出包括两列信息。 第一个是索引,第二个是Series中的数据。 输出的每一行代表索引标签(在第一列中),然后代表与该标签关联的值。

由于创建此Series时未指定索引(接下来将要执行的操作),因此 pandas 自动创建一个整数索引,该索引的标签从 0 开始,对于每个数据项加 1。

然后,可以使用[]运算符访问Series对象的值,并传递所需值的标签。 以下内容获取标签1的值:

这看起来很像许多编程语言中的常规数组访问。 但是,正如我们将看到的那样,索引不必从 0 开始,也不必递增 1,并且可以是许多数据类型,而不仅仅是整数。 以这种方式关联灵活索引的能力是 Pandas 的巨大超级能力之一。

通过在 Python 列表中指定它们的标签,可以检索多个项目。 以下内容检索标签13上的值:

通过使用index参数并指定索引标签,可以使用用户定义的索引创建Series对象。 下面的代码创建一个Series,其值相同,但索引由字符串值组成:

现在,那些字母数字索引标签可以访问Series对象中的数据。 以下内容检索索引标签'a''d'上的值:

仍然可以通过基于[0]的数字位置引用此Series对象的元素。 :

我们可以使用.index属性检查Series的索引:

索引实际上是一个 Pandas 对象,此输出向我们显示了索引的值和用于索引的数据类型。 在这种情况下,请注意索引中的数据类型(称为dtype)是对象而不是字符串。 我们将在本书的后面部分研究如何更改此设置。

Series在 Pandas 中的常见用法是表示将日期/时间索引标签与值相关联的时间序列。 下面通过使用pd.date_range() pandas 函数创建日期范围来说明这一点:

这在 Pandas 中创建了一个称为DatetimeIndex的特殊索引,这是一种特殊的 Pandas 索引,经过优化可对带有日期和时间的数据进行索引。

现在,让我们使用该索引创建一个Series。 数据值表示特定日期的高温:

这种带有DateTimeIndex的序列称为时间序列。

我们可以使用日期作为字符串来查询特定数据的温度:

两个Series对象可以通过算术运算相互应用。 以下代码创建第二个Series并计算两者之间的温度差:

对两个非标量值的Series对象进行算术运算(+,-,/,*,...)的结果将返回另一个Series对象。

由于索引不是整数,因此我们还可以通过从 0 开始的值来查找值:

最后,Pandas 提供了许多描述性的统计方法。 例如,以下内容返回温度差的平均值:

Pandas 数据帧

Pandas Series只能与每个索引标签关联一个值。 要使每个索引标签具有多个值,我们可以使用一个数据帧。 一个数据帧代表一个或多个按索引标签对齐的Series对象。 每个序列将是数据帧中的一列,并且每个列都可以具有关联的名称。

从某种意义上讲,数据帧类似于关系数据库表,因为它包含一个或多个异构类型的数据列(但对于每个相应列中的所有项目而言都是单一类型)。

以下创建带有两列的DataFrame对象,并使用温度Series对象:

产生的数据帧有两列,分别为MissoulaPhiladelphia。 这些列是数据帧中包含的新Series对象,具有从原始Series对象复制的值。

可以使用带有列名或列名列表的数组索引器[]访问DataFrame对象中的列。 以下代码检索Missoula列:

下面的代码检索Philadelphia列:

列名称的 Python 列表也可以用于返回多个列:

Series对象相比,DataFrame对象存在细微的差异。 将列表传递给DataFrame[]运算符将检索指定的列,而Series将返回行。

如果列名没有空格,则可以使用属性样式进行访问:

数据帧中各列之间的算术运算与多个Series上的算术运算相同。 为了演示,以下代码使用属性表示法计算温度之间的差异:

只需通过使用数组索引器[]表示法将另一Series分配给一列即可将新列添加到DataFrame。 以下内容在DataFrame中添加了带有温度差的新列:

可通过.columns属性访问DataFrame中的列名:

可以切片DataFrameSeries对象以检索特定的行。 以下是第二到第四行温度差值的切片:

可以使用.loc.iloc属性检索数据帧的整个行。 .loc确保按索引标签查找,其中.iloc使用从 0 开始的位置。

以下内容检索数据帧的第二行:

请注意,此结果已将行转换为Series,数据帧的列名称已透视到结果Series的索引标签中。 下面显示了结果的结果索引:

可以使用.loc属性通过索引标签显式访问行。 以下代码通过索引标签检索一行:

可以使用整数位置列表选择DataFrame对象中的特定行。 以下从Difference列的整数位置135的行中选择值:

可以基于应用于每行中数据的逻辑表达式来选择数据帧的行。 以下显示Missoula列中大于82度的值:

然后可以将表达式的结果应用于数据帧(和序列)的[]运算符,这仅导致返回求值为True的表达式的行:

该技术在 pandas 术语中称为布尔选择,它将构成基于特定列中的值选择行的基础(例如在 SQL 中使用WHERE子句的查询 -- 但我们将看到它更强大)。

将文件中的数据加载到数据帧中

Pandas 库提供了方便地从各种数据源中检索数据作为 Pandas 对象的工具。 作为一个简单的例子,让我们研究一下 Pandas 以 CSV 格式加载数据的能力。

本示例将使用随本书的代码data/goog.csv提供的文件,该文件的内容表示 Google 股票的时间序列财务信息。

以下语句使用操作系统(从 Jupyter 笔记本或 IPython 内部)显示此文件的内容。 您将需要使用哪个命令取决于您的操作系统:

可以使用pd.read_csv()函数将这些信息轻松导入DataFrame

pandas 不知道文件中的第一列是日期,并且已将Date字段的内容视为字符串。 可以使用以下 pandas 语句对此进行验证,该语句以字符串形式显示Date列的类型:

the pd.read_csv()函数的parse_dates参数可指导 Pandas 如何将数据直接转换为 Pandas 日期对象。 以下通知 Pandas 将Date列的内容转换为实际的TimeStamp对象:

如果我们检查它是否有效,我们会看到日期为Timestamp

不幸的是,这没有使用日期字段作为数据帧的索引。 而是使用默认的从零开始的整数索引标签:

请注意,这现在是RangeIndex,在以前的 Pandas 版本中,它应该是整数索引。 我们将在本书的后面部分探讨这种差异。

可以使用pd.read_csv()函数的index_col参数将其固定,以指定应将文件中的哪一列用作索引:

现在索引是DateTimeIndex,它使我们可以使用日期查找行。

可视化

我们将在第 14 章“可视化”中深入研究可视化,但是在此之前,我们偶尔会对 Pandas 中的数据进行快速可视化。 使用 Pandas 创建数据可视化非常简单。 所有需要做的就是调用.plot()方法。 下面通过绘制股票数据的Close值进行演示:

总结

在本章中,我们安装了 Python 的 Anaconda Scientific 版本。 这还将安装 pandas 和 Jupyter 笔记本,为您设置执行数据处理和分析的环境,并创建用于可视化,呈现和共享分析的笔记本。

我们还对 Pandas SeriesDataFrame对象进行了介绍,展示了一些基本功能。 该博览会向您展示了如何执行一些基本操作,以便在深入学习所有细节之前可以用来启动和运行 Pandas。

在接下来的几章中,我们将深入研究SeriesDataFrame的操作,下一章将重点介绍Series

三、用序列表示单变量数据

Series是 Pandas 的主要构建基块。 它表示单个数据类型的一维类似于数组的值集。 它通常用于为单个变量的零个或多个测量建模。 尽管它看起来像数组,但Series具有关联的索引,该索引可用于基于标签执行非常有效的值检索。

Series还会自动执行自身与其他 Pandas 对象之间的数据对齐。 对齐是 Pandas 的一项核心功能,其中数据是在执行任何操作之前按标签值匹配的多个 Pandas 对象。 这允许简单地应用操作,而无需显式地编码连接。

在本章中,我们将研究如何使用Series为变量的测量建模,包括使用索引来检索样本。 这项检查将概述与索引标签,切片和查询数据,对齐和重新索引数据有关的几种模式。

具体而言,在本章中,我们将涵盖以下主题:

  • 使用 Python 列表,字典,NumPy 函数和标量值创建序列
  • 访问Series的索引和值属性
  • 确定Series对象的大小和形状
  • 在创建Series时指定索引
  • 使用headtailtake访问值
  • 通过索引标签和位置查找值
  • 切片和常用切片模式
  • 通过索引标签来对齐
  • 执行布尔选择
  • 重新索引Series
  • 原地修改值

配置 Pandas

我们使用以下导入和配置语句开始本章中的示例:

创建序列

可以使用多种技术创建Series。 我们将研究以下三个:

  • 使用 Python 列表或字典
  • 使用 NumPy 数组
  • 使用标量值

使用 Python 列表和字典创建序列

可以从 Python 列表中创建Series

数字的第一列表示Series索引中的标签。 第二列包含值。 dtype: int64表示Series中值的数据类型为int64

默认情况下,Pandas 会创建一个索引,该索引由0开始的连续整数组成。 这使该序列看起来像许多其他编程语言中的数组。 例如,我们可以在label 3处查找值:

该查找是通过标签值而不是从 0 开始的位置进行的。 我们将在本章后面详细研究。

可以使用非整数的数据类型。 以下创建字符串值的序列:

要创建由n个相同值v的序列组成的序列,请使用 Python 速记表创建[v]*n。 以下创建2的五个值:

下面是一种类似的速记类型,它使用 Python 速记来将每个字符用作列表项:

可以从 Python 字典直接初始化Series。 使用字典时,字典的键用作索引标签:

使用 NumPy 函数创建

初始化Series对象的各种 NumPy 函数是一种常见的做法。 例如,以下示例使用 NumPy np.arange函数在48之间创建一个整数值序列:

np.linspace()方法的功能类似,但是允许我们指定要在两个指定值之间(包括两个值)创建的值的数量,并具有指定的步骤数:

使用np.random.normal()生成一组随机数也是很常见的。 以下从正态分布生成五个随机数:

使用标量值创建

也可以使用标量值创建Series

Series仅具有单个值,这似乎是简并的情况。 但是,在某些情况下,这很重要,例如,将序列乘以标量值时,如下所示:

在封面下,Pandas 取值2并从该标量值创建一个Series,其索引与s中的索引匹配,然后通过对齐两个Series进行乘法。 在本章的后面,我们将再次更详细地查看此示例。

.index.values属性

每个Series对象均由一些值和一个索引组成。 可以通过.values属性访问这些值:

结果是一个 NumPy 数组对象,如下所示:

出于信息目的将其称为。 我们不会在本书中研究 NumPy 数组。 从历史上看,Pandas 的确在幕后使用 NumPy 数组,因此 NumPy 数组在过去更为重要,但这种依赖在最近的版本中已被删除。 但为方便起见,即使基础表示形式不是 NumPy 数组,.values也会返回 NumPy 数组。

另外,可以使用.index检索该序列的索引:

Pandas 创建的索引类型为RangeIndex。 当不存在这种类型的索引时,这是与本书先前版本相比的 Pandas 更改。 RangeIndex对象代表具有指定step的从startstop值的值范围。 与以前使用的Int64Index相比,这对 Pandas 是有效的。

RangeIndex只是我们将要探索的一种索引类型(第 6 章“索引数据”中的大部分细节)。

序列的大小和形状

Series对象中的项目数可以通过多种技术来确定,其中第一种是使用 Python len()函数:

通过使用.size属性可以获得相同的结果:

获取Series大小的另一种形式是使用.shape属性。 这将返回一个二值元组,但仅指定第一个值并表示大小:

在创建时指定索引

可以使用构造器的index参数在创建Series时指定索引中的标签。 下面创建一个Series并将字符串分配给索引的每个标签:

检查.index属性,我们发现创建了以下索引:

使用这个索引,我们可以问一个类似who is the Dad?的问题:

.head().tail().take()

Pandas 提供了.head().tail()方法来检查Series中前导(头)或后随(尾)行。 默认情况下,它们返回前五行或后五行,但是可以使用n参数进行更改。

让我们检查以下Series的用法:

以下内容检索前五行:

可以使用n参数(或仅通过指定数字)来更改项目数:

.tail()返回最后五行:

指定5以外的数字时,其工作原理类似:

.take()方法返回指定整数位置的行的序列:

通过标签或位置检索序列中的值

Series中的值可以通过两种常规方法检索:通过索引标签或从 0 开始的位置。 Pandas 为您提供了多种方法来执行这两种查找。 让我们研究一些常见的技术。

使用[]运算符和.ix[]属性按标签查找

使用[]运算符执行隐式标签查找。 该运算符通常根据给定的索引标签查找值。

让我们从使用以下Series开始:

仅使用所需项目的索引标签即可查找单个值:

使用索引标签列表可以一次检索多个项目:

我们还可以使用代表位置的整数来查找值:

这纯粹是因为索引未使用整数标签。 如果将整数传递给[],并且索引具有整数值,则通过将传入的值与整数标签的值进行匹配来执行查找。

这可以使用以下Series进行演示:

以下内容在标签1310而非位置1310处查找值:

使用[]运算符进行的查找与使用.ix[]属性相同。 但是,自 Pandas 0.20.1 版以来,.ix[]已被弃用。 弃用的原因是由于整数传递给运算符而造成的混乱,以及取决于索引中标签类型的运算差异。

其后果是[].ix[]均不可用于查找。 而是使用.loc[].iloc[]属性,它们仅按标签或位置明确查找。

使用.iloc[]按位置显式查找

可以使用.iloc[]来按位置查找值。 下面演示使用整数作为参数:

即使索引具有整数标签,也会按位置查找以下内容:

请注意,如果您指定一个不存在的位置(小于零或大于项目数-一个),则将引发异常。

通过.loc[]通过标签进行显式查找

也可以通过使用.loc[]属性来实现按标签查找:

使用整数标签没有问题:

请注意,当传递不在索引中的索引标签时,.loc[].iloc[]具有不同的行为。 在这种情况下,Pandas 将返回NaN值,而不是引发异常:

什么是NaN? 我们将在本章的后面部分更详细地介绍这一点,但是 pandas 使用它来表示无法通过索引查找找到的缺失数据或数字。 它还对各种统计方法产生了影响,我们还将在本章后面进行研究。

将序列切成子集

Pandas Series支持称为切片的功能。 切片是从 Pandas 对象中检索数据子集的强大方法。 通过切片,我们可以根据位置或索引标签选择数据,并更好地控制产生的项目(正向或反向)和间隔(每一项,彼此)的顺序。

切片会使普通数组[]运算符(以及.loc[].iloc[].ix[])过载,以接受切片对象。 切片对象是使用start:end:step语法创建的,表示第一项,最后一项的组件以及要作为step的各项之间的增量。

切片的每个组件都是可选的,并且通过省略切片说明符的组件,提供了一种方便的方法来选择整个行。

要开始演示切片,我们将使用以下Series

我们可以使用start:end作为切片选择连续的项目。 以下选择Series中位置15的五个项目。 由于我们未指定step组件,因此默认为1。 另请注意,结果中不包含end标签:

此结果大致等于以下内容:

大致等效,因为对.iloc[]的使用返回源中数据的副本。 切片是对源中数据的引用。 修改所得切片的内容将影响源Series。 我们将在后面的部分中就位修改Series数据,以进一步研究此过程。

通过指定2步骤,切片可以返回所有其他项:

如前所述,切片的每个组件都是可选的。 如果省略start组件,则结果将从第一项开始。 例如,以下是.head()的简写:

通过指定start组件并省略end,可以选择特定位置及其后的所有项目。 以下选择从4th开始的所有项目:

step也可以在之前的两种情况下使用,以跳过项目:

使用step负值将反转结果。 以下演示了如何反转Series

-2将从开始位置返回所有其他项目,并以相反的顺序向Series的开始工作。 下面的示例返回所有其他项目,包括位置4处的行:

切片的startend的负值具有特殊含义。 -nstart负值表示最后n行:

-nend负值将返回除最后n行之外的所有行:

startend分量可以组合使用。 以下内容首先检索最后四行,然后从中检索除最后一行(即前三行)之外的所有行:

也可以对具有非整数索引的序列进行切片。 为了演示,让我们使用以下Series

使用此Series,对整数值进行切片将根据位置提取项目(如前所述):

但是,当使用非整数值作为切片的组件时,Pandas 将尝试理解数据类型并从序列中选择适当的项目。 例如,从'b''d'的以下片段:

通过索引标签对齐

通过索引标签对Series数据进行对齐是 Pandas 的基本概念,也是其最强大的概念之一。 对齐基于索引标签提供多个序列对象中相关值的自动关联。 使用标准的过程技术,可以在多个集合中节省很多容易出错的工作量匹配数据。

为了演示对齐,让我们举一个在两个Series对象中添加值的示例。 让我们从以下两个Series对象开始,它们代表一组变量(ab)的两个不同样本:

现在假设我们想对每个变量的值求和。 我们可以简单地表示为s1 + s2

Pandas 已经对每个序列中每个变量的测量值进行了匹配,将这些值相加,然后在一个简洁的语句中将每个变量的总和返回给我们。

也可以将标量值应用于Series。 结果将是使用指定的操作将标量应用于Series中的每个值:

还记得前面提到的,我们将返回创建具有标量值的Series吗? 在执行此类操作时,Pandas 实际上会执行以下操作:

第一步是从标量值创建一个Series,但带有目标Series的索引。 然后将乘法应用于两个Series对象的对齐值,由于索引相同,它们完美对齐。

索引中的标签不需要对齐。 如果未对齐,则 Pandas 将返回NaN作为结果:

默认情况下,NaN值是任何 Pandas 对齐的结果,其中索引标签与另一个Series不对齐。 与 NumPy 相比,这是 Pandas 的重要特征。 如果标签未对齐,则不应引发异常。 当某些数据丢失但可以接受时,这会有所帮助。 处理仍在继续,但是 Pandas 通过返回NaN可以让您知道存在问题(但不一定是问题)。

Pandas 索引中的标签不必唯一。 对齐操作实际上在两个Series中形成标签的笛卡尔积。 如果1序列中有n个标签,而2序列中有m个标签,则结果总计为n * m结果中的行。

为了演示这一点,让我们使用以下两个Series对象:

这将产生 6 个'a'索引标签,以及'b''c'NaN

执行布尔选择

索引为我们提供了一种基于其标签在Series中查找值的非常有效的手段。 但是,如果您想基于这些值在Series中查找条目,该怎么办?

为了处理这种情况,Pandas 为我们提供了布尔选择。 布尔选择将逻辑表达式应用于Series的值,并在每个值上返回新的布尔值序列,这些布尔值表示该表达式的结果。 然后,该结果可用于仅提取结果为True的值。

为了演示布尔选择,让我们从下面的Series开始并应用大于大于运算符来确定大于或等于3的值:

这将导致Series具有匹配的索引标签,并且表达式的结果将应用于每个标签的值。 值的dtypebool

然后可以使用该序列从原始序列中选择值。 通过将布尔结果传递到源的[]运算符来执行此选择。

可以通过在[]运算符中执行逻辑运算来简化语法:

不幸的是,普通的 Python 语法不能使用多个逻辑运算符。 例如,以下导致引发异常:

导致上述代码无法正常工作的原因有技术原因。 解决方案是用不同的方式表达方程式,在每个逻辑条件前后加上括号,并为和/或(|&)使用不同的运算符:

使用.all()方法可以确定Series中的所有值是否与给定表达式匹配。 下面的内容询问该序列中的所有元素是否都大于或等于0

如果任何值满足表达式,则.any()方法将返回True。 下面的内容询问是否有任何元素小于2

您可以对结果选择使用.sum()方法来确定有多少项目满足表达式。 这是因为当给定布尔值序列,该序列的.sum()方法会将True视为1False视为0

重新索引序列

在 Pandas 中重新索引是使Series中的数据符合一组标签的过程。 Pandas 使用它来执行大部分对齐过程,因此是一项基本操作。

重新索引实现了以下几项功能:

  • 重新排序现有数据来匹配一组标签
  • 在没有标签数据的地方插入NaN标记
  • 可以使用某种逻辑填充标签的缺失数据(默认为添加NaN值)

重新索引可以很简单,只需为Series.index属性分配一个新索引即可。 下面演示了以这种方式更改Series的索引:

分配给.index属性的列表中的元素数必须与行数匹配,否则将引发异常。 重新索引还就地修改了Series

通过使用.reindex()方法,可以灵活地创建新索引。 一种情况是分配一个新索引,其中标签数与值数不匹配:

以下代码使用一组具有新值,丢失值和重叠值的标签为Series重新编制索引:

关于.reindex(),有几点需要指出。 首先是.reindex()方法的结果是新的Series,而不是就地修改。 新的Series具有带有标签的索引,如传递给函数时所指定。 将为原始Series中存在的每个标签复制数据。 如果在原始Series中找不到标签,则将NaN分配为该值。 最后,将删除Series中带有不在新索引中的标签的行。

当您要对齐两个Series以对两个Series中的值执行操作但Series对象没有由于某种原因对齐的标签时,重新索引也很有用。 一种常见的情况是,一个Series具有整数类型的标签,另一个是字符串,但是值的基本含义是相同的(从远程源获取数据时,这很常见)。 以以下Series对象为例:

尽管两个Series中标签的含义相同,但是由于它们的数据类型不同,它们将对齐。 一旦发现问题,即可轻松解决:

.reindex()方法具有默认操作,即在源Series中找不到标签时,将NaN作为缺少的值插入。 可以使用fill_value参数更改此值。 下面的示例演示使用0代替NaN

当对有序数据(例如时间序列)执行重新索引时,可以执行插值或值填充。 在第 10 章“时间序列数据”中,将对插值和填充进行更详细的讨论,但是以下示例介绍了这一概念。 让我们从以下Series开始:

以下示例演示了前向填充的概念,通常称为最近已知值。 重新索引Series以创建连续的整数索引,并通过使用method='ffill'参数,为任何新的索引标签分配先前已知的非 NaN 值:

索引标签12与标签0的红色,45从标签3green以及红色65blue匹配。

以下示例使用method='bfill'向后填充:

标签6没有先前的值,因此将其设置为NaN4设置为5blue)的值; 21设置为标签3green)的值。

原地修改序列

Series的就地修改是一个有争议的话题。 如果可能,最好执行返回带有新Series中表示的修改的新Series的操作。 但是,如果需要,可以更改值并就地添加/删除行。

通过为尚不存在的index标签分配值,可以在序列中添加一行。 以下代码创建一个Series对象,并向该序列添加一个附加项:

可以通过分配在特定索引标签上的值来更改它:

可以通过将index标签传递给del()函数从Series中删除行。 下面演示了如何删除带有索引标签'a'的行:

要添加和删除不适当的项目,请使用pd.concat()使用布尔选择来添加和删除。

使用切片时要牢记的重要一点是,切片的结果是原始Series的视图。 通过切片操作结果修改值将修改原始的Series

考虑以下示例,该示例选择Series中的前两个元素并将其存储在新变量中:

将值分配给切片元素的以下操作将更改原始Series中的值:

总结

在本章中,您学习了 Pandas Series对象以及如何将其用于表示变量测量值的索引表示。 我们从如何创建和初始化Series及其关联索引开始,然后研究了如何在一个或多个Series对象中操纵数据。 我们研究了如何通过索引标签对齐Series对象以及如何在对齐的值上应用数学运算。 然后,我们检查了如何按索引查找数据,以及如何根据数据(布尔表达式)执行查询。 然后,我们结束了对如何使用重新索引来更改索引和对齐数据的研究。

在下一章中,您将学习如何使用DataFrame以统一的表格结构表示多个Series数据。

四、用数据帧表示表格和多元数据

Pandas DataFrame对象将Series对象的功能扩展为二维。 代替单个值序列,数据帧的每一行可以具有多个值,每个值都表示为一列。 然后,数据帧的每一行都可以对观察对象的多个相关属性进行建模,并且每一列都可以表示不同类型的数据。

数据帧的每一列都是 Pandas Series,并且数据帧可以视为一种数据形式,例如电子表格或数据库表。 但是这些比较并不符合DataFrame的要求,因为数据帧具有 Pandas 特有的非常不同的质量,例如代表列的Series对象的自动数据对齐。

这种自动对齐方式使数据帧比电子表格或数据库更有能力进行探索性数据分析。 结合在行和列上同时切片数据的功能,这种与数据帧中的数据进行交互和浏览的功能对于查找所需信息非常有效。

在本章中,我们将深入研究 Pandas DataFrameSeries会熟悉许多概念,但是会添加一些数据和工具来支持其操作。 具体而言,在本章中,我们将涵盖以下主题:

  • 根据 Python 对象,NumPy 函数,Python 字典,Pandas Series对象和 CSV 文件创建DataFrame
  • 确定数据帧大小
  • 指定和操作数据帧中的列名
  • 创建数据帧期间的行对齐
  • 选择数据帧的特定列和行
  • 将切片应用于数据帧
  • 通过位置和标签选择数据帧的行和列
  • 标量值查找
  • 应用于数据帧的布尔选择

配置 Pandas

我们使用以下导入和配置语句开始本章中的示例:

创建数据帧对象

有多种创建数据帧的方法。 可以从一个或一组多维数据集创建一个数据帧。 我们将研究的技术如下:

  • 使用 NumPy 函数的结果
  • 使用包含列表或 Pandas Series对象的 Python 字典中的数据
  • 使用 CSV 文件中的数据

在检查所有这些内容时,我们还将检查如何指定列名,演示初始化期间如何执行对齐以及查看如何确定数据帧的尺寸。

使用 NumPy 函数结果创建一个数据帧

数据帧可以由一维 NumPy 整数数组(范围从 1 到 5)创建:

输出的第一列显示已创建索引的标签。 由于在创建时未指定索引,因此 Pandas 创建了一个基于RangeIndex的标签,标签的开头为 0。

数据在第二列中,由值15组成。 数据列上方的0是该列的名称。 在创建数据帧时未指定列名称时,pandas 使用从 0 开始的增量整数来命名列。

也可以使用多维 NumPy 数组,并创建多个列:

可以使用.columns属性访问DataFrame的列:

这表明,当未指定列名时,Pandas 将创建一个RangeIndex来表示列。

可以使用columns参数指定列名。 下面创建了一个两列DataFrame,代表两个城市的两个温度样本:

可以使用len()函数找到DataFrame中的行数:

可以使用.shape属性找到DataFrame的尺寸:

使用 Python 字典和 pandas 序列对象创建数据帧

Python 字典可用于初始化DataFrame。 使用 Python 字典时,pandas 将把键用作列名,并将每个键的值用作列中的数据:

创建DataFrame的常用技术是使用将用作行的 Pandas Series对象的列表:

在这种情况下,每个Series代表每个城市在特定测量间隔处的单个测量。

要命名列,我们可以尝试使用columns参数:

此结果与我们可能期望的结果不同,因为这些值已用NaN填充。 这可以通过两种方式纠正。 第一种是将列名称分配给.columns属性:

另一种技术是使用 Python 字典,其中键是列名,每个键的值是Series,代表该特定列中的度量:

请注意,在构建DataFrame时,将对齐提供的Series。 下面通过添加索引值不同的第三个城市来说明这一点:

从 CSV 文件创建数据帧

可以通过使用pd.read_csv()函数从 CSV 文件读取数据来创建数据帧。

pd.read_csv()将在第 9 章“访问数据”中进行更广泛的研究。

为了演示该过程,我们将从一个包含 S&P 500 快照的文件中加载数据。该文件名为sp500.csv,位于代码包的data目录中。

文件的第一行包含每个变量/列的名称,其余 500 行代表 500 种不同股票的值。

以下代码加载数据,同时指定文件中的哪一列用于索引,并且我们只需要四个特定的列(0、2、3 和 7):

使用.head()检查前五行,向我们显示以下结构和所得数据帧的内容:

让我们检查一下该数据帧的一些属性。 它应该具有 500 行数据。 可以通过检查数据帧的长度来检查:

我们希望它具有 500 行和三列的形状:

可以使用.size属性找到数据帧的大小。 此属性返回数据帧中数据值的数量。 我们预计 500 * 3 = 1,500:

数据帧的索引由 500 种股票的符号组成:

这些列由以下三个名称组成:

请注意,尽管我们在加载时指定了四列,但结果仅包含三列,因为源文件中四列之一用于索引。

访问数据帧内的数据

数据帧由行和列组成,并具有从特定行和列中选择数据的结构。 这些选择使用与Series相同的运算符,包括[].loc[].iloc[]

由于存在多个维度,因此应用这些维度的过程略有不同。 我们将通过首先学习选择列,然后选择行,在单个语句中选择行和列的组合以及使用布尔选择来检查这些内容。

此外,pandas 提供了一种构造,用于在我们将要研究的特定行和列上选择单个标量值。 该技术很重要,并且存在,因为它是访问这些值的一种非常高性能的方法。

选择数据帧的列

使用[]运算符选择DataFrame特定列中的数据。 这与Series不同,在Series中,[]指定了行。 可以将[]操作符传递给单个对象或代表要检索的列的对象列表。

以下内容检索名称为'Sector'的列:

当从DataFrame中检索单个列时,结果为Series

通过指定列名列表可以检索多个列:

由于它具有多个列,因此结果是DataFrame而不是Series

列也可以通过属性访问来检索。 只要名称不包含空格,DataFrame将添加代表每列名称的属性。 下面以这种方式检索Price列:

请注意,此名称不适用于Book Value列,因为名称带有空格。

选择数据帧的行

可以使用.loc[]通过索引标签值检索行:

此外,可以使用标签列表检索多行:

可以使用.iloc[]按位置检索行:

可以在特定标签值的索引中查找位置,然后使用该值按位置检索行:

作为本节的最后注解,也可以使用.ix[]进行这些操作。 但是,此方法已被弃用。 有关更多详细信息,请参见这里

使用.at[].iat[]按标签或位置进行标量查找

可以使用.at[]通过标签查找各个标量值,并同时向其传递行标签和列名称:

也可以使用.iat[]按位置查找标量值,同时传递行位置和列位置。 这是访问单个值的首选方法,并且可以提供最高的性能:

使用[]运算符进行切片

在其索引上切片DataFrame在语法上与使用Series执行相同操作相同。 因此,我们将在本节中不介绍切片的各种排列的细节,而仅查看应用于DataFrame的几个代表性示例。

使用[]运算符进行切片时,将在索引而非列上执行切片。 以下内容检索前五行:

并且以下返回从ABT标签到ACN标签开始的行:

切片DataFrame也适用于.iloc[].loc[]属性。 使用这些属性被认为是最佳实践。

使用布尔选择来选择行

可以使用布尔选择来选择行。 当应用于数据帧时,布尔选择可以利用多列中的数据。 考虑以下查询,该查询标识价格低于100的所有股票:

然后可以使用[]运算符将此结果应用于DataFrame,以仅返回结果为True的行:

可以使用括号将多个条件放在一起。 以下内容检索价格在610之间的所有股票的代码和价格:

通常使用多个变量执行选择。 下面通过查找SectorHealth CarePrice大于或等于100.00的所有行来证明这一点:

在行和列中进行选择

通常的做法是选择由一组行和列组成的数据子集。 下面通过首先选择一部分行然后选择所需的列来说明这一点:

总结

在本章中,您学习了如何创建 Pandas DataFrame对象以及基于各种列中的索引和值选择数据的各种方法。 这些示例与Series的示例相似,但是证明,由于DataFrame具有列和关联的列索引,因此语法与Series有所不同。

在下一章中,我们将进一步使用DataFrame深入研究数据操作,并着重于对DataFrame结构和内容进行修改。

五、数据帧的结构操作

Pandas 提供了一个强大的操纵引擎,供您用来浏览数据。 这种探索通常涉及对DataFrame对象的结构进行修改,以删除不必要的数据,更改现有数据的格式或从其他行或列中的数据创建派生数据。 这些章节将演示如何执行这些强大而重要的操作。

具体而言,在本章中,我们将介绍:

  • 重命名列
  • 使用[].insert()添加新列
  • 通过扩展添加列
  • 使用连接添加列
  • 重新排序列
  • 替换列的内容
  • 删除列
  • 添加新行
  • 连接行
  • 通过扩展添加和替换行
  • 使用.drop()删除行
  • 使用布尔选择删除行
  • 使用切片删除行

配置 Pandas

以下代码将为以下示例配置 Pandas 环境。 这还将加载 S&P 500 数据集,以便可以在示例中使用它:

重命名列

可以使用适当命名的.rename()方法重命名列。 可以向此方法传递一个字典对象,其中的键表示要重命名的列的标签,并且每个键的值是新名称。

下面的操作会将'Book Value'列的名称更改为'BookValue',删除空格并允许使用属性符号访问该列的数据。

以这种方式使用.rename()将返回一个新的数据帧,其中的列已重命名,并且数据是从原始数据中复制的。 以下内容验证了原始文件没有被修改。

要在不进行复制的情况下就地修改数据帧,可以使用inplace=True参数。

现在可以使用.BookValue属性访问数据。

使用[].insert()添加新列

可以使用[]运算符将新列添加到数据帧。 让我们添加一个名为RoundedPrice的新列,该列将表示Price列中值的舍入。

Pandas 执行此操作的方式是,首先从sp500中选择Price列的数据,然后将Series中的所有值四舍五入。 然后,pandas 将新的Series与副本DataFrame对齐,并将其添加为名为RoundedPrice的新列。 新列将添加到列索引的末尾。

.insert()方法可用于在特定位置添加新列。 下面在SectorPrice之间插入RoundedPrice列:

通过扩展来添加列

可以使用.loc[]属性和切片添加列。 下面通过向名为PERsp500的子集添加新列,并将所有值初始化为0来演示这一点。

具有现有数据的Series也可以通过这种方式添加。 下面将PER列与随机数据的序列相加。 由于这使用对齐方式,因此有必要使用与目标数据帧相同的索引。

使用连接来添加列

[]运算符和.insert()方法都就地修改目标数据帧。 如果需要一个带有附加列的新数据帧(保持原来的不变),则可以使用pd.concat()函数。 此函数创建一个新的数据帧,其中所有指定的DataFrame对象均按规范顺序连接在一起。

下面的代码创建了一个新的DataFrame,其中的一列包含了四舍五入的价格。 然后,它使用pd.concat()axis=1来表示给定的DataFrame对象应沿着列轴连接(与使用axis=0的行相比)。

在第 11 章(合并,关联和重塑数据)中将更详细地介绍连接。

连接可能会导致重复的列名。 为了演示这种情况,让我们重新创建rounded_price,但将其命名为Price列。

现在,连接将导致重复的列。

有趣的是,您可以使用.Price属性检索这两列。

如果要在场景中获取特定的Price列,则需要按位置而不是名称进行检索。

对列重新排序

通过按所需顺序选择列,可以重新排列列的顺序。 下面通过反转列进行演示。

实际上,没有一种方法可以就地更改列的顺序。 参见这里

替换列的内容

通过使用[]运算符将新的Series分配给现有列,可以替换DataFrame的内容。 以下演示了用rounded_price中的Price列替换Price列。

列的数据也可以使用切片替换(就地)。

删除列

可以使用数据帧的del关键字或.pop().drop()方法从DataFrame中删除列。 这些行为的差异略有不同:

  • del将从DataFrame中删除Series(原地)
  • pop()将同时删除Series并返回Series(也是原地)
  • drop(labels, axis=1)将返回一个已删除列的新数据帧(原始DataFrame对象未修改)

下面演示了如何使用delsp500数据的副本中删除BookValue列:

以下使用.pop()方法删除Sector列:

.pop()方法的优势在于它为我们提供了弹出的列。

.drop()方法可用于删除行和列。 要使用它删除列,请指定axis=1

追加新行

使用DataFrame.append()方法执行行的追加。 附加过程将返回一个新的DataFrame,并首先添加来自原始DataFrame的数据,然后再添加第二行的数据。 追加不会执行对齐,并且可能导致索引标签重复。

以下代码演示了附加两个从sp500数据中提取的DataFrame对象。 第一个DataFrame由行(按位置)012组成,第二个DataFrame由行(按位置)10112组成。 两者中都包含位置2处的行(带有标签ABBV),以演示重复索引标签的创建。

追加中使用的DataFrame对象的列集不必相同。 结果数据帧将由两个列的并集组成,缺少的列数据填充有NaN。 以下内容通过使用与df1相同的索引创建第三个数据帧,但只有一个列的名称不在df1中来说明这一点。

现在,我们附加df3df1

ignore_index=True参数可用于附加,而无需强制从DataFrame保留索引。 当索引值的意义不大并且您只希望将具有顺序递增的整数的级联数据用作索引时,这很有用:

请注意,结果DataFrame具有默认的RangeIndex,并且索引(Symbol))中的数据已从结果中完全排除。

连接行

可以使用pd.concat()函数并通过指定axis=0将来自多个DataFrame对象的行彼此连接。 沿行轴在两个DataFrame对象上进行pd.concat()的默认操作的方式与.append()方法相同。

通过重建前面的附加示例中的两个数据集并将其连接起来,可以证明这一点。

如果所有DataFrame对象中的列集都不相同,则 Pandas 将用NaN填充这些值。

从源对象中逐行复制行会导致重复的索引标签。 keys参数可用于帮助区分一组行源自哪个数据帧。 下面通过使用keys向表示源对象的索引添加一个级别进行演示:

我们将在第 6 章“处理索引”中更详细地研究层次结构索引。

通过扩展来添加和替换行

也可以使用.loc属性将行添加到DataFrame.loc的参数指定要放置行的索引标签。 如果标签不存在,则使用给定的索引标签将值附加到数据帧。 如果标签确实存在,则将替换指定行中的值。

以下示例获取sp500的子集,并添加带有标签FOO的行:

请注意,无论是添加还是替换行,都会进行此更改。

使用.drop()删除行

DataFrame.drop()方法可用于删除行。 .drop()方法获取要删除的索引标签列表,并返回DataFrame的副本,其中删除了指定的行。

使用布尔选择删除行

布尔选择也可以用于从DataFrame中删除行。 布尔选择的结果将返回表达式为 True 的行的副本。 要删除行,只需构造一个表达式,为要删除的行返回False,然后将该表达式应用于数据帧。

下面的示例演示删除Price大于300的行。 首先,构造表达式。

根据此结果,我们现在知道有 10 行的价格大于 300。要获得删除了这些行的数据帧,请选择选择的补码。

使用切片删除行

切片可用于从数据帧中删除记录。 这是一个与布尔选择类似的过程,在该过程中,我们选择了除要删除的行以外的所有行。

假设我们要从sp500中除去除前三个记录以外的所有记录。 执行此任务的片是[:3],它返回前三行。

请记住,由于这是切片,因此结果是原始数据帧的视图。 这些行尚未从sp500数据中删除,对这三行的更改将更改sp500中的数据。 防止这种情况的正确措施是制作切片的副本,这会导致复制指定行的数据的新数据帧。

总结

在本章中,您学习了如何使用 Pandas DataFrame对象执行几种常见的数据操作,特别是通过添加或删除行和列来更改DataFrame结构的操作。 此外,我们看到了如何替换特定行和列中的数据。

在下一章中,我们将更详细地研究索引的使用,以便能够有效地从 pandas 对象内检索数据。

六、索引数据

索引是用于优化查询序列或数据帧中的值的工具。 它们很像关系数据库中的键,但是功能更强大。 它们为多组数据提供了对齐方式,还带有如何处理数据的各种任务(如重采样到不同频率)的语义。

您将对 Pandas 执行的许多建模工作很大程度上取决于您如何设置索引。 正确实现的索引将优化性能,并成为推动分析的宝贵工具。

我们之前曾简要地使用过索引,在本章中,我们将进行更深入的探讨。 在这次深入学习中,我们将详细了解:

  • 索引的重要性
  • Pandas 索引的类型,包括RangeIndexInt64IndexCategoricalIndexFloat64IndexDatetimeindexPeriodIndex
  • 设置和重置索引
  • 创建分层索引
  • 使用分层索引选择数据

配置 Pandas

我们从 Pandas 的标准配置开始,但是我们也加载了 S&P 500 数据,以供几个示例使用。

索引的重要性

Pandas 索引允许有效地查找值。 如果不存在索引,则将需要对我们所有数据进行线性搜索。 索引使用直接查找而不是搜索过程为特定数据项创建优化的快捷方式。

为了开始检查索引的值,我们将使用以下10000随机数的DataFrame

假设我们要查找key==10099处的随机数的值(我明确选择了此值,因为它是DataFrame中的最后一行)。 我们可以使用布尔选择来做到这一点。

从概念上讲,这很简单。 但是,如果我们想重复执行此操作怎么办? 可以使用%timeit语句在 Python 中进行模拟。 以下代码重复执行查找并报告性能。

该结果表明,1,000 个循环执行了三次,并且这三次中最快的一次平均每个循环花费了 0.00535 秒(一组 1,000 个循环总计 5.35 秒)。

现在让我们尝试使用索引来帮助我们查找值。 以下代码设置此DataFrame的索引以匹配keys列的值。

现在可以使用.loc[]查找该值。

那只是一个查询。 让我们用%timeit计时。

使用索引的查找大约快五倍。 由于具有更高的性能,因此通常最好的方法是在可能的情况下按索引执行查找。 使用索引的不利之处在于构造索引可能会花费一些时间,并且还会消耗更多的内存。

很多时候,您会天生就知道索引应该是什么,您可以直接创建索引并开始探索。 其他时间,首先需要进行一些探索才能确定最佳指数。 通常,您可能没有足够的数据或适当的字段来创建适当的索引。 在这些情况下,您可能需要使用返回多个半歧义结果的部分索引,并且仍然对该集合执行布尔选择以获得所需的结果。

在执行探索性数据分析以首先加载数据并使用查询/布尔选择进行探索时,这是最佳实践。 如果您的数据自然支持一个索引,或者您确实需要提高速度,则创建索引。

Pandas 索引类型

Pandas 提供许多内置索引。 每种索引类型都根据特定的数据类型或数据模式设计用于优化查找。 让我们看看其中几种常用的。

基本类型 - Index

这种类型的索引是最通用的,表示一组有序和可切片的值。 它包含的值必须是可哈希的 Python 对象。 这是因为索引将使用此哈希来形成与该对象的值相关联的值的有效查找。 尽管哈希查找比线性查找更受青睐,但还有其他类型的索引可以进一步优化。

列索引通常是这种通用类型。 以下代码演示了如何将这种索引类型用作DataFrame的列。

虽然这种类型的索引通常对于字母数字列名非常有效,但是如果需要,可以使用其他类型的索引作为列索引。

使用Int64IndexRangeIndex的整数索引标签

Int64Index表示映射到值的不可变的 64 位整数数组。 直到更新版本的 pandas 为止,这是未指定索引或使用整数的默认索引类型,如以下代码片段所示:

使用此索引,DataFrame中的行查找非常高效,因为它们是使用连续的内存中数组执行的。

Pandas 的最新版本添加了RangeIndex作为Int64Index的优化。 它具有表示基于整数的索引的能力,该索引从特定的整数值开始,具有结束的整数值,并且还可以指定步骤。

使用开始,停止和步进是一种常见的模式,因此需要向 Pandas 添加自己的子类。 通过使用这三个值,可以节省内存,并且执行时间与Int64Index中的顺序相同。

RangeIndex已成为 Pandas 对象的默认索引。 以下内容对此进行了演示,该示例创建了默认为RangeIndex的整数值的序列。

使用Float64Index的浮点标签

通过使用Float64Index,浮点数可用作索引标签。

请注意,此片返回了 11 行,而不是前 5 行。 这是因为Float64Index,并且 Pandas 从一开始就将我们的意思表示为 5.0 之前的所有值。

使用IntervalIndex表示离散间隔

可以使用IntervalIndex表示不同的标签桶。 该间隔在一端(左端或右端)关闭,这意味着该间隔的该端值包含在该间隔中。 以下代码显示了使用间隔作为索引创建DataFrame的过程。

作为索引的类别值 - CategoricalIndex

CategoricalIndex用于表示基础类别的稀疏填充索引。 下面创建一个DataFrame,其中一列为“类别”。

将类别列(B)移到DataFrame的索引中可以看到现在有CategoricalIndex

然后可以使用基础类别中的现有值执行查找。

类别将在第 7 章“类别数据”中进行详细检查。

使用DatetimeIndex的日期时间索引

DatetimeIndex用于表示一组日期和时间。 这些在时间序列数据中得到了广泛使用,在这些时间序列数据中,以特定的时间间隔采样。 为了简要说明这一点,下面的代码创建了5-每小时时间段的范围,并将它们用作该序列的索引。

索引的类型可以看作是DatetimeIndex

日期/时间的基本表示形式是 64 位整数,这使得按日期和时间进行查找非常有效。

使用PeriodIndex的时间段索引

能够表示诸如日,月或年之类的时间段也很重要。 这很像一个间隔,但要持续一段时间。 可以通过使用PeriodIndex并为索引中的时间段指定特定频率来对这些场景进行建模。

下面通过对从2017-01开始的三个 1 个月周期进行建模进行演示。

然后可以在SeriesDataFrame中使用该索引。

使用索引

在介绍了创建各种类型的 Pandas 索引之后,现在让我们检查一下这些索引的几种常见使用模式。 具体来说,我们将检查:

  • 对序列或数据帧创建和使用索引
  • 用索引选择值的方法
  • 在索引之间移动数据
  • 重新索引 Pandas 对象

对序列或数据帧创建和使用索引

索引可以显式创建,也可以让 Pandas 隐式创建。 当您使用构造器的index参数分配索引时,会显式创建。

以下代码通过首先独立创建DatetimeIndex进行演示。

您可以单独使用此索引,也可以将其与SeriesDataFrame关联。 此代码利用索引创建一个DataFrame

通过设置.index属性,也可以将索引直接分配给现有的DataFrame或序列。

使用索引选择值

可以使用[]运算符使用索引或使用SeriesDataFrame的以下属性索引器来查找值:

.loc[] 通过标签而不是位置查找。 但小心点; 如果标签是整数,则整数将被视为标签!
.at[] 类似于.loc[],但这只能检索单个值。
.iloc[] 查找基于基于0的位置,而不是基于索引标签。
.ix[] 混合,当给出整数时将尝试基于0的查找; 其他类型是基于标签的。 将不建议使用此属性,因此请保留其他三个属性。

可以使用[]运算符在Series中查找值,如以下DataFrame所示,该运算符已检索到b值。

Series上使用[]进行查找等同于使用.loc[]属性。

[]运算符应用于DataFrame时,检索列而不是行。

若要使用DataFrame通过行索引进行查找,必须使用属性索引器之一。

属性索引器表单也可以使用切片。

并且还可以传递值列表。

在索引之间移动数据

通过使用.reset_index()可以重置DataFrame对象的索引。 通常将其用于将DataFrame对象的索引的内容移到一个或多个列中。

以下代码将sp500索引中的符号移到一列中,并将索引替换为默认的整数索引。

可以使用.set_index()方法并通过指定要移动的列将数据列移动到DataFrame对象的索引。 以下代码将Sector列移至索引。

可以将多个列移至索引,从而形成一个层次/多索引。 层次结构索引将在本章后面介绍。

重新索引 Pandas 对象

可以使用.reindex()方法重新索引DataFrame。 重新索引使DataFrame符合新索引,将旧索引中的数据与新索引对齐,并在对齐失败的地方填充NaN

此代码演示将sp500重新索引到三个指定的索引标签。

此过程将创建具有指定行的新DataFrame。 如果未找到特定值的行,则将插入NaN值,如'FOO'标签所示。 这种方法实际上是一种基于索引标签过滤出数据的好技术。

重新索引也可以在列上完成,如下面的代码所示,用于指定的三个列名称:

由于NewCol的值未包含在原始数据中,因此将为其插入NaN值。

分层索引

分层索引是 Pandas 的一项功能,它允许每行结合使用两个或多个索引。 层次结构索引中的每个索引都称为一个级别。 索引中多个级别的规范允许使用每个级别的值的不同组合来有效选择数据的不同子集。 从技术上讲,具有多个层次结构的 Pandas 索引称为MultiIndex

以下代码演示了使用sp500数据通过MultiIndex创建和访问数据。 假设我们要通过SectorSymbol的值来组织此数据,以便我们可以基于来自两个变量的值的组合来有效地查找数据。 我们可以使用以下代码将SectorSymbol的值移动到MultiIndex中,以完成此操作:

.index属性现在显示索引为MultiIndex对象:

如上所述,MultiIndex对象包含两个或多个级别,在这种情况下为两个:

每个级别都是一个不同的Index对象:

可以通过.get_level_values()方法检索每行特定级别的索引本身的值:

使用.xs()方法通过层次索引访问DataFrame对象中的值。 此方法的工作方式类似于.ix属性,但是提供了用于指定索引多维性的参数。

以下代码选择索引标签为Industrials的所有项目:0Sector):

结果DataFrame的索引由未指定的级别组成,在这种情况下为Symbol。 从结果索引中删除为其指定值的级别。

level参数可用于选择在指定级别具有特定索引值的行。 以下代码选择索引的Symbol分量为ALLE的行。

为了防止电平下降(如果您未指定每个级别),可以使用drop_levels=False选项。

要从索引层次结构中进行选择,可以将具有不同级别的.xs()调用链接在一起。 下面选择级别为0Industrials和级别为1UPS的行。

另一种语法是将层次结构索引的每个级别的值作为元组传递。

请注意,.xs()仅可用于获取,而不能用于设置值。

总结

在本章中,我们更深入地研究了在 Pandas 中使用索引来组织和检索数据。 我们研究了许多有用的索引类型,以及它们如何与不同类型的数据一起使用以有效访问值而无需查询行中的数据。 最后,我们对使用分层索引的研究进行了总结,该分层索引能够有效地检索与多个索引中的标签匹配的数据,从而为我们提供了选择数据子集的有力手段。

至此,我们已经涵盖了 Pandas 的许多基本建模部分。 在下一章中,我们将研究用 Pandas 表示分类变量。

七、类别数据

类别变量是统计信息中的一种变量,代表一组有限的且通常是固定的值。 这与连续变量相反,连续变量可以表示无限数量的值。 类别变量的常见类型包括性别(其中有两个值,分别是男性和女性)或血液类型(可以是一小批血液类型之一,例如 A,B 和 O)。

pandas 可以使用一种称为Categorical的 pandas 对象来表示类别变量。 这些 Pandas 对象旨在有效地表示分组为一组存储桶的数据,每个存储桶由代表其中一个类别的整数代码表示。 这些基础代码的使用使 Pandas 能够有效地表示类别集,并可以跨多个类别变量执行数据的排序和比较。

在本章中,我们将学习有关类别法的以下内容:

  • 创建类别
  • 重命名类别
  • 追加新类别
  • 删除类别
  • 删除未使用的类别
  • 设置类别
  • 描述性统计
  • 值的计数
  • 最小,最大和众数
  • 如何使用类别根据学生的数字等级为学生分配字母等级

配置 Pandas

我们使用以下导入和配置语句开始本章中的示例:

创建类别

Pandas 类别用于表示类别变量。 类别变量由一组有限的值组成,通常用于将值映射到一组类别中,并跟踪每个类别中存在多少个值。 另一个目的是将连续值的各个部分映射到一组离散的命名标签中,其一个示例是将数字等级映射到字母等级。 在本章的最后,我们将研究如何执行这种映射。

有几种创建类别 Pandas 的方法。 以下屏幕截图演示了如何直接从列表创建类别:

此类别是从包含五个字符串和三个不同值的列表创建的:低,中和高。 创建类别时,Pandas 会确定列表中的每个唯一值并将其用作类别。

可以使用.categories属性检查以下类别:

类别创建了一个索引,该索引由提供的列表中的三个不同值组成。

类别的值可以使用.get_values()检索:

类别的每个类别都分配有一个整数值。 此值称为代码。 可以使用.codes属性访问这些分配的代码。

这些代码有效地表示每个值在类别索引中的位置。 在这种情况下,映射是low=1high=0mid=2。 此排序可能没有逻辑意义,由 Pandas 通过串行处理lmh_values数组中的字符串来确定。

通过使用categories参数指定类别,可以更好地控制此排序。

请注意在构造器中指定的类别的新顺序。 由于此顺序,代码现在为:

现在,它表示low=0mid=1high=2的映射。这更加有用,因为它可用于按与每个类别的含义及其与其他类别的关系相匹配的顺序对值进行排序。 在类别类别时,将使用代码而不是实际值进行类别。 以下演示lmh_cat的排序:

类别变量也可以表示为一个序列,其dtype被指定为"category"。 以下屏幕快照演示了如何获取类别列表并创建具有dtype类别的Series

使用类别创建Series的另一种方法是先创建Series,然后使用.astype('category')方法将一列转换为类别。 下面的屏幕截图通过创建一个数据帧并将其值转换为category的第二列来说明这一点,该数据帧的一列然后是第二列。

创建为类别的序列包含.cat属性,该属性允许访问Series使用的类别:

该变量是CategoricalAccessor对象,它允许访问基础类别的各种属性。 例如,以下代码获取类别:

几个 Pandas 函数还返回类别。 一种是pd.cut(),它会在特定值范围内创建对象箱。 以下屏幕截图演示了将代表0100之间的5随机数的值的序列切入 10 个类别箱,每个箱子宽 10 个整数:

Group列表示由cut函数创建的类别变量,每个索引级别的值表示该值已与之关联的特定类别。

可以使用ordered = True指定类别的显式顺序。 此信息意味着类别的顺序很重要,并且可以比较多个类别的类别变量中的值。 为了说明这一点,下面的代码创建了一个有序的类别,代表三种金属(以及这三个类别的四个值):

创建的类别具有bronze的严格排序,其重要性低于silver,而silver的重要性低于gold

此排序可用于将一个类别类别或与另一个类别进行比较。 为了说明这一点,让我们创建以下类别,将其取反:

这两个类别变量可以相互比较。 以下代码显示了每种金属是否比另一种类别中相同索引标签上的相应金属价值低:

Pandas 通过对每个值匹配基础代码来执行此比较。 金属变量具有以下代码:

并且metals_reveresed_values具有以下代码。

逻辑运算符的应用导致前面显示的结果。

作为创建类别的最后一个示例,以下屏幕截图演示了如何创建一个类别,该类别指定的值(copper)不是指定类别之一。 在这种情况下,Pandas 将用NaN代替该值。

此技术可用于在创建时筛选出不适合类别的值。

重命名类别

通过为.categories属性分配新值或使用.rename_categories()方法,类别人员可以重命名值。

请注意,这是就地修改。 为了防止就地修改,我们可以使用.rename_categories()方法。

我们可以验证这不是就地完成的。

追加新类别

可以使用.add_categories()方法附加类别。 以下代码将copper类别添加到我们的金属中。 除非明确告知 Pandas 这样做,否则不会就地进行:

删除类别

可以使用.remove_categories()方法删除类别。 删除的值将替换为np.NaN。 以下屏幕截图通过删除bronze类别来演示此操作:

删除未使用的类别

可以使用.remove_unused_categories()删除未使用的类别,如下所示:

设定类别

也可以使用.set_categories()方法在一个步骤中添加和删除类别。 鉴于以下Series

以下代码将类别设置为"one""four"

结果将NaN替换为现在不存在的类别。

类别的描述性信息

类别上的.describe()方法将以类似于SeriesDataFrame的方式产生描述性统计信息。

结果为我们提供了每个类别的实例数量以及每个类别的频率分布。

可以使用.value_counts()获得每个类别的值计数。

最小值,最大值和众数可以使用相应的方法找到。

处理学校成绩

现在让我们看一下应用类别法来帮助我们根据类别而不是数字来组织信息。 我们将要研究的问题是根据学生的数字等级为其分配字母等级。

该数据帧代表每个学生的原始分数。 接下来,我们将数字等级分解为字母代码。 以下代码定义每个等级的箱以及每个箱的关联字母等级:

使用这些值,我们可以执行分配字母等级的剪切。

检查基础类别,将显示以下代码的创建方式以及字母等级与值的关系:

要确定每个年级有多少学生,我们可以使用.cat.value_counts()

由于字母等级的类别具有字母等级的逻辑顺序,因此通过使用它在类别列上进行排序,我们可以从最高到最低字母等级对学生进行排序。

总结

在本章中,我们研究了如何使用 pandas 类别对类别变量建模。 我们首先回顾了创建类别的方法,并查看了几个如何使用基础整数代码对每个类别进行类别的示例。 然后,我们研究了创建类别后修改类别的几种方法。 本章以使用类别将数据分解为一组命名容器的示例作为结尾。

在下一章中,我们将对 Pandas 数据进行数值和统计分析。

八、数值统计方法

pandas 在建模和处理数据方面非常强大,但它也提供了许多用于数值和统计分析的强大工具。 这些功能与 Pandas 数据结构紧密集成在一起,因此一旦对数据建模就可以使复杂的计算变得非常简单。

本章将研究许多这些功能。 它从常见的数值方法开始,例如跨多个对象对齐的算术,以及查找特定的值(例如最小值和最大值)。 然后,我们将研究 Pandas 的许多统计能力,例如使用分位数,值排名,方差,相关性以及许多其他功能。

最后但并非最不重要的一点,我们将研究 Pandas 提供的一种非常强大的功能,称为滚动窗口。 滚动窗口提供了一种应用各种方法的方法,例如对规则数据子集进行均值计算。 这些类型的操作对于各种分析都是至关重要的,例如确定随着时间变化的股票数据的几个特征。 该概念将在本章中介绍,我们将在后面的章节中更详细地介绍它。

本章涉及很多内容,包括:

  • 对 Pandas 对象执行算术运算
  • 获取值的计数
  • 确定唯一值(及其计数)
  • 查找最小值和最大值
  • 找到 n 个最小和 n 个最大的值
  • 计算累计值
  • 检索摘要描述性统计
  • 衡量集中趋势(均值,中位数和众数)
  • 计算方差,标准差,协方差和相关性
  • 执行数据离散化和量化
  • 计算值的排名
  • 计算序列中每个样本的百分比变化
  • 执行滚动窗口操作
  • 执行数据随机抽样

配置 Pandas

我们将使用标准的 Pandas 导入和配置,从以下环境配置开始。 除了正常导入和格式化外,我们还将导入 S&P 500 数据和每月股票数据:

在 Pandas 对象上执行数值方法

Pandas 提供了丰富的功能和操作集,这些功能和操作有助于执行算术运算和计算数据的各种数值特征。 在本节中,我们将研究其中的许多内容,包括:

  • 在数据帧或序列上执行算术
  • 获取值的计数
  • 确定唯一值(及其计数)
  • 查找最大值和最小值
  • 找到 n 个最小和 n 个最大的值
  • 计算累计值

在数据帧或序列上执行算术

可以使用+-/*运算符在数据帧(和序列)上执行算术运算。 尽管它们在本质上看似微不足道,但 Pandas 通过对等式左侧和右侧的值进行对齐来增加强大的效果。 因此,索引在算术中起着很大的作用,Pandas 用户必须了解索引如何影响结果。

此外,Pandas 不仅提供了算术的标准运算符,而且还提供了几种方法.add().sub().mul().div(),它们在指定应用坐标轴时提供了更高的性能和更大的灵活性。

使用标量值的算术运算将应用于DataFrame的每个元素。 为了说明这一点,我们从以下四列随机数的数据帧开始:

默认情况下,所有算术运算都将应用于数据帧的所有行和列。 这将导致包含结果的新DataFrame对象(使original保持不变):

当在DataFrameSeries之间执行操作时,Pandas 将Series索引沿DataFrame列对齐,执行所谓的逐行广播。 这可能有点违反直觉,但是在逐行的基础上在每列中应用不同的值时,它是非常强大的。

为了说明这一点,下面的示例检索DataFrame的第一行,然后从每一行中减去该行,从根本上导致每一行的值与第一行之差:

当通过从Series对象中减去DataFrame来反转顺序时,此过程也适用,因为 Pandas 足够聪明,可以找出正确的应用:

由算术运算得到的一组列将是序列索引和DataFrame对象的列索引中的标签的并集(根据对齐规则)。 如果在SeriesDataFrame对象中均未找到表示结果列的标签,则这些值将用NaN填充。 以下代码通过创建一个Series来演示此操作,该索引的索引表示DataFrame中列的子集,但带有一个附加标签:

pandas 将df的索引标签与s2的索引标签对齐。 由于s2在列中没有AD标签,因此结果在这些列中包含NaN。 并且由于df没有E标签,因此它也充满了NaN(即使Series中存在E)。

两个DataFrame对象之间的算术运算将同时按列标签和索引标签对齐。 以下代码提取了df的一小部分,并将其从完整的数据帧中减去。 结果表明,对齐的值减去 0,而其他值设置为NaN

使用DataFrame对象提供的算术方法可以获得对算术运算的附加控制。 这些方法提供了特定轴的规范。 下面的代码演示了从每列中减去A列值:

获取值的计数

.count()方法为我们提供Series中非NaN项目的计数。

确定唯一值(及其计数)

可以使用.unique()获得序列中唯一值的列表:

可以使用.nunique()获得唯一值的数目(NaN除外):

要在结果中包含NaN,请使用dropna=False作为参数。

可以使用.value_counts()(此过程也称为直方图)确定每个唯一值的计数:

查找最大值和最小值

可以使用.min().max()确定最小值和最大值。

一些 Pandas 统计方法被称为间接统计,因为它们不返回实际值,而是间接的相关值。 例如,.idxmin().idxmax()返回存在最小值和最大值的索引位置。

找到 n 个最小和 n 个最大值

有时我们需要知道数据集中的 n 个最小值和 n 个最大值。 这些可以使用.nsmallest().nlargest()找到。 下面的代码通过返回MSFT的 4 个最小值来说明这一点。

同样,最大的 4 个值。

序列的形式略有不同,因为没有要指定的列。

计算累计值

累加是通过将Series中的下一个值连续应用于运行结果来确定值的统计方法。 很好的例子是一个序列的累积乘积和累积和。 以下代码演示了累积积的计算。

结果是另一个Series,代表每个位置的累加值。 这是相同Series的累加总和的计算。

对 Pandas 对象执行统计过程

描述性统计数据使我们能够理解描述基础数据特定特征的大量数据度量。 内置于 Pandas 中的是这些描述性统计操作的几类,它们可以应用于序列或数据帧。

让我们研究一下 Pandas 提供的统计分析/技术的几个方面:

  • 摘要描述性统计
  • 衡量集中趋势:均值,中位数和众数
  • 方差和标准差

检索摘要描述性统计

pandas 对象提供.describe()方法,该方法返回一组对象数据的摘要统计信息。 当应用于DataFrame时,.describe()将计算每列的摘要统计信息。 以下代码为omh中的两只股票计算这些统计数据。

通过一次快速的方法调用,我们计算了两个序列股票数据的计数,均值,标准差,最小值和最大值,甚至 25% ,50% 和 75%。

.describe()也可以应用于序列。 以下代码仅计算MSFT的摘要统计信息。

并且只有平均值可以如下获得。

非数字数据将导致一组稍微不同的摘要统计信息,返回项目总数(count),唯一值的计数(unique),最频繁出现的值(top)和出现的次数(freq):

衡量集中趋势:均值,中位数和众数

平均值和中位数为我们提供了几种有用的数据度量方式,可帮助我们开始理解值的分布以及该分布的形状。 这三个值的关系为我们提供了形状的快速聚合概念,如下图所示:

现在,让我们研究一下如何使用 Pandas 查找这些值。

计算平均值

该平均值通常称为平均值,它使我们可以测量数据的中心趋势。 通过将所有测量值相加然后除以测量值数来确定。

可以使用.mean()计算平均值。 以下代码计算AAPLMSFT的平均价格:

Pandas 选择了每一列,并独立计算了每一列的平均值。 它以列名索引的序列中的值形式返回结果。 默认设置是将方法应用于axis=0,将函数应用于每一列。 此代码切换轴并返回每天所有股票的平均价格:

求中位数

中位数使我们在值的序列中处于中心位置。 根据定义,中位数是数据中存在相同数量的其他值均小于或大于该值的值。 中位数很重要,因为它不受外部值和非对称数据的影响,而不是均值。

值的中位数使用.median()确定:

确定众数

该众数是序列的最常见值,可通过.mode()找到。 以下代码确定给定序列的众数:

注意,这没有返回表示众数的标量值,而是返回了一个序列。 这是因为序列的众数可以有多个值。 下面的示例对此进行了演示:

计算方差和标准差

在概率论和统计学中,标准差和方差使我们感觉到一些数字与平均值之间的距离有多远。 让我们简要地检查一下。

计算方差

方差使我们感觉到平均值的均值散布量。 定义如下:

本质上,这说明对于每次测量,我们都会计算出值与平均值之间的差值。 这可以是正值或负值,因此我们对结果求平方以确保负值对结果有累积影响。 然后将这些值相加并除以测量值减 1,得出差值平均值的近似值。

在 Pandas 中,使用来计算方差。 var()方法。 以下代码计算两种股票的价格差异:

求标准差

标准差与方差相似。 它是通过计算方差的平方根确定的,定义如下:

请记住,方差是所有测量值与均值之差的平方。 因此,方差不在相同的单位和实际值之间。 通过使用方差的平方根,标准差的单位与原始数据集中的值相同。

使用.std()方法计算标准差,如下所示:

确定协方差和相关性

协方差和相关性描述了两个变量之间的关系。 此关系可以是以下之一:

  • 如果变量沿相同方向移动,则它们呈正相关。
  • 如果变量沿相反方向移动,则它们成负相关。

协方差和相关性都表明变量是正相关还是逆相关。 相关性还告诉您变量趋于一起移动的程度。

计算协方差

协方差指示两个变量之间的关系。 正协方差意味着变量是正相关的,而负协方差意味着它们是负相关的:

可以使用.cov()方法计算协方差。 以下代码计算MSFTAAPL之间的协方差:

确定相关性

协方差可以帮助确定值是否相关,但是并不能给变量一起移动的程度提供感觉。 为了衡量变量一起移动的程度,我们需要计算相关性。 通过将协方差除以两组数据的标准差的乘积来计算相关性:

相关性标准化了两个变量之间相互依存的度量,因此可以告诉您两个变量的移动程度。 相关度量称为相关系数,将始终取 1-1 之间的值,该值的解释如下:

  • 如果相关系数为 1.0 ,则变量具有完全正相关。 这意味着,如果一个变量移动给定的数量,则第二个变量按相同方向成比例地移动。 小于 1.0 但大于 0.0 的正相关系数表示小于理想的正相关,并且相关强度随着数字接近 1.0 而增长。
  • 如果相关系数为 0.0 ,则变量之间不存在关系。 如果一个变量移动,则无法对另一个变量的移动做出任何预测。
  • 如果相关系数为 -1.0 ,则变量将完全负相关(或成反相关),并且彼此相对移动。 如果一个变量增加,则另一个变量按比例减少。 大于 -1.0 但小于 0.0 的负相关系数表示不理想的负相关,并且相关强度随着数字接近 -1 而增加。

使用.corr()方法计算 Pandas 中的相关性。 以下代码计算MSFTAAPL的相关性。

这表明在此期间MSFTAPPL的价格显示出很高的相关性。 这并不意味着它们是因果关系,一个因素会影响另一个因素,而是对价值有共同的影响,例如在相似的市场中。

执行数据离散化和量化

离散化是将连续数据切成一组桶的一种方法。 然后,每个值都与一个代表性的容器关联。 然后可以使用每个仓中值计数的结果分布来了解跨不同仓的数据的相对分布。

使用pd.cut()pd.qcut()函数在 Pandas 中离散化。 为了演示,让我们从使用普通随机数生成器创建的以下10000随机数集开始:

这段代码向我们展示了该数据集的平均值和标准差,随着数据集样本量的增加(因为这是正常的),我们期望它们接近 0 和 1:

我们可以使用pd.cut()将数字分成相等大小的桶。 以下代码创建五个大小均等的容器,并确定值的映射:

生成的bins对象是 pandas 类别变量。 它由一组标签和一个描述数据拆分方式的索引组成。 的。 categories属性将返回索引,并指定 Pandas 确定的间隔(给定值的范围和指定的仓数):

.codes属性是一个数组,用于指定已为每个项目分配哪些仓位(间隔):

间隔的符号遵循标准的数学间隔,其中的括号表示末尾是开放的,而方括号表示封闭的。 封闭端包含该确切数字的值。 默认情况下,pandas 关闭间隔的右侧。 可以使用pd.cut()right=False参数将封闭端移到间隔的左侧:

您可以传递一个代表桶的范围的值数组,而不是传递整数个 bin 以将数据切入其中。 一个常见的示例涉及将年龄映射到年龄段存储桶中。 为了证明这一点,以下代码生成了 645 之间的 50 年龄:

我们可以通过将桶传递到数组中来指定桶的范围,其中相邻值代表每个桶的范围。 此代码将数据切入指定的桶中,并报告在每个桶中找到的年龄的分布。

要为每个桶指定一个不同于标准数学符号的名称,请使用labels参数:

标签的这种分配不仅方便文本输出,而且在绘制桶时都非常方便,因为 Pandas 会传递桶名称以在图表上进行绘制。

也可以使用pd.qcut()根据指定的分位数对数据进行切片。 此函数会将值切成桶,以便每个桶具有相同数量的项目。 根据此结果,我们可以确定值计数均匀分布的桶的范围。

以下代码将随机值从之前拆分为5分位数箱:

代替指定整数的箱数,还可以指定分位数范围。 以下代码根据正负 321 个标准差分配桶。 由于这是正态分布的数据,因此我们期望值的百分之 0.1,2.1,13.6 和 34.1 位于均值的每一边。

这些正是我们期望从该分布中获得的结果。

计算值的排名

排名有助于我们确定以下两项中的一项是排名较高还是排名较低。 排名将度量减少为序数序列,这些序数可用于评估复杂标准,并基于结果顺序。

为了演示排名,我们将使用以下数据序列:

然后可以使用.rank()对这些值进行排名,默认情况下,该值告诉我们标签从最低值到最高值的顺序:

结果代表数值从最小到最大的顺序(本质上是一种排序)。 最低的是1.0处的d(其最低值是 -0.555730),并且在5.0处的排名上升到e(值 1.965781)。

您可以使用许多选项来更改此默认行为,例如,指定自定义排名函数以及在出现平局时如何确定排名。

计算序列中每个样本的百分比变化

可以使用.pct_change()方法来计算给定时间段内的百分比变化。 百分比变化的示例用法是计算股票价格的变化率。 以下代码显示了MSFT的代码:

执行滚动窗口操作

Pandas 提供了许多函数来计算移动(也称为滚动)统计信息。 滚动窗口根据指定的数据间隔计算指定的统计信息。 然后将窗口沿数据移动特定的时间间隔并重新计算。 该过程一直持续到窗口在整个数据集上滚动为止。

为了证明这一点,我们将从1000随机数的序列开始,这些随机数被累加起来形成一个随机游动:

放大前 100 个值,我们可以看到以下图所示的数据移动:

要开始创建滚动窗口,我们使用带有指定窗口的.rolling()方法创建一个Rolling对象。 在这种情况下,我们要创建3的滚动窗口:

rolling对象指定我们希望窗口的宽度,但尚未执行实际的计算。 为此,我们可以选择表示统计操作的rolling对象的多种方法之一(下图显示了其中的几种):

该代码演示了如何计算数据的滚动平均值:

因为我们的窗口大小是N = 3,所以结果的第一个均值在索引标签 2 处。 我们可以验证该值是前三个数字的平均值:

然后,窗口沿数据滚动一个间隔。 计算的下一个值在标签 3 处,代表标签 123 的平均值:

将滚动平均值的结果绘制在第一个 100 值上可以得出以下结果:

通过将该图与上一个图进行比较可以看出,滚动平均值可以使区间中的数据变得平滑。

执行数据随机抽样

随机采样是从随机位置的数据样本中选择值的过程。 从 pandas 0.19.2 开始,此功能已添加到 pandas SeriesDataFrame对象,而在以前的版本中,您必须自己编写此过程。

为了演示随机采样,让我们​​从下面的DataFrame开始,它代表 50 行随机数的四列:

在指定要检索的样本数量的同时,我们可以使用.sample()方法获取数据样本。 以下代码对三个随机行进行采样:

另一种形式是指定要随机选择的数据百分比。 此代码选择 10% 的行。

可以在有或没有替换的情况下对 Pandas 采样,默认情况下不替换。 要指定我们要使用替换,我们只需使用replace=True参数:

总结

在本章中,您学习了如何对 Pandas 对象执行数值和统计分析。 这包括检查许多常用方法,这些方法将用于计算值和执行各种分析。 我们从基本的算术运算以及数据对齐如何影响运算和结果开始。 然后,我们介绍了 Pandas 提供的许多统计操作,从描述性统计到离散化再到滚动窗口和随机抽样。 这些课程将为您进行许多实际数据分析做好准备。

在下一章中,我们将进行调整,并研究如何从各种数据源(例如本地文件,数据库和远程 Web 服务)中加载数据。

九、存取数据

在几乎所有实际数据分析中,您都需要从程序外部加载数据。 由于 pandas 是基于 Python 构建的,因此您可以使用 Python 中可用的任何方式来检索数据。 这样就可以从几乎无限的资源集中访问数据,包括但不限于文件,Excel 电子表格,网站和服务,数据库以及云服务。

但是,使用标准 Python 函数加载数据时,需要将 Python 对象转换为 Pandas SeriesDataFrame对象。 这增加了代码的复杂性。 为了帮助管理这种复杂性,pandas 提供了许多功能,可以将各种来源的数据直接加载到 pandas 对象中。 我们将在本章中研究其中的许多内容。

具体而言,在本章中,我们将介绍:

  • 将 CSV 文件读入数据帧
  • 读取 CSV 文件时指定索引列
  • 数据类型推断和规范
  • 指定列名
  • 指定要加载的特定列
  • 将数据保存到 CSV 文件
  • 使用一般的字段分隔数据
  • 处理字段分隔数据中格式的变体
  • 以 Excel 格式读写数据
  • 读写 JSON 文件
  • 从网络读取 HTML 数据
  • 读写 HDF5 格式文件
  • 从 SQL 数据库读写
  • 从 Yahoo 和 Google 财经中读取股票数据
  • 从 Google 财经中读取期权数据
  • 从圣路易斯 FRED 读取经济数据
  • 访问 Kenneth French 的数据
  • 访问世界银行数据

配置 Pandas

我们从 Pandas 的标准导入和选择导入,以方便举例说明。

处理 CSV 和文本/表格格式数据

CSV 格式的数据可能是您可能在 Pandas 中使用的最常见的数据形式之一。 许多基于 Web 的服务以 CSV 格式提供数据,以及企业内的许多信息系统。 它是一种易于使用的格式,通常用作电子表格应用(例如 Excel)的导出格式。

CSV 是由多行基于文本的数据组成的文件,其值用逗号分隔。 可以将其视为类似于电子表格程序中单个工作表的数据表。 数据的每一行都在文件中自己的一行中,每一行的每一列都以文本格式存储,并用逗号分隔每一列中的数据。

有关 CSV 文件的详细信息,请随时访问这里

由于 CSV 非常普遍且易于理解,因此我们将花费大部分时间来描述如何以这种格式读取和写入 Pandas 数据。 从 CSV 方法学到的经验教训也将适用于其他格式,并且在涵盖这些其他格式时可以更加方便。

检查示例 CSV 数据集

我们将从读取一个简单的 CSV 文件data/msft.csv(在本书的源代码data文件夹中)开始。 该文件是MSFT股票代码的股票值的快照。 可以使用!head命令检查此文件的前几行(在 Windows 系统上,使用type命令):

文件的第一行包含数据中表示的所有列的名称,每个列均以逗号分隔。 然后,每一行代表特定日期的值的样本。

将 CSV 文件读入数据帧

data/MSFT.CSV中的数据非常适合读入DataFrame。 它的所有数据都是完整的,并且在第一行中具有列名。 将数据读入DataFrame所需要做的就是使用 Pandas pd.read_csv()函数:

哇,那很容易! Pandas 已经意识到,文件的第一行包含列名和从数据中批量读取到数据帧的名称。

读取 CSV 文件时指定索引列

在前面的示例中,索引是数字的,从0开始,而不是按日期。 这是因为 pandas 并不假定文件中的任何特定列都应用作索引。 为了解决这种情况,您可以使用index_col参数,通过为它指定要用作索引的列的从零开始的位置,来指定在read_csv()调用中哪些列应该是索引。

以下内容读取数据,并告诉 Pandas 使用文件中0位置的列作为索引(Date列):

数据类型推断和规范

检查每一列的类型表明,pandas 试图从其内容中推断出列的类型:

要强制使用列的类型,请使用pd.read_csv()dtypes参数。 以下命令将Volume列也设置为float64

指定列名

也可以使用names参数在读取数据时指定列名称:

由于我们指定了列名,因此我们需要跳过文件中的“列名”行,该行是使用header=0执行的。 如果不这样做,Pandas 将假定第一行是数据的一部分,这将在以后的处理中引起一些问题。

指定要加载的特定列

还可以指定读取文件时要加载的列。 如果文件中有很多列,而您对分析不感兴趣,并且您希望节省读取和存储它们所需的时间和内存,这将很有用。 使用usecols参数指定要读取的列,可以将其传递给列名称或列偏移量列表。

为了演示,以下仅读取DateClose列,并使用Date作为索引:

将数据帧保存到 CSV 文件

可以使用.to_csv()方法从DataFrame保存 CSV 文件。 为了演示如何将数据保存到 CSV 文件,我们将带有修改后的列名的df2对象保存到名为data/msft_modified.csv的新文件中:

有必要告诉方法使用index_label='date'将索引标签保存为date的列名。 否则,索引不会在文件的第一行中添加名称,这将导致难以正确读取。

为了检查它是否正常工作,我们可以使用!head命令浏览新文件以查看其某些内容(如果在 Windows 系统上,请使用!type命令):

处理一般的字段分隔数据

CSV 实际上是所谓的字段分隔数据的特定实现。 在以字段分隔的数据中,每行中的项目由特定符号分隔。 就 CSV 而言,它恰好是逗号。 但是,其他符号也很常见,例如|(管道)符号。 使用|字符时,该数据通常称为管道分隔的数据。

Pandas 提供pd.read_table()函数,以方便读取字段分隔的数据。 下面的示例使用此函数通过将逗号指定为sep参数的值来读取data/MSFT.CSV文件:

Pandas 没有提供.to_table()方法作为.to_csv()的类似写入方法。 但是,可以使用.to_csv()方法使用与逗号不同的分隔符来写入字段分隔的数据。 例如,以下内容将写入数据的管道分隔版本:

处理字段分隔数据中格式的变体

字段分隔文件中的数据可能包含无关的页眉和页脚。 示例包括顶部的公司信息,例如发票编号,地址和摘要页脚。 在某些情况下,数据每隔一行存储一次。 这些情况在加载数据时会导致错误。 为了处理这些情况,Pandas pd.read_csv()pd.read_table()方法具有一些有用的参数来帮助我们。

为了演示,对MSFT股票数据进行以下变体,该数据具有多余的行,这些行可以称为噪声信息:

可以使用skiprows参数来处理这种情况,该参数通知 Pandas 跳过023行:

另一个常见情况是文件的内容位于文件末尾,应将其忽略以防止出错。 以以下数据为例:

该文件将在读取过程中导致异常,但是可以使用skip_footer参数来处理,该参数指定要忽略文件末尾的多少行:

注意,我们必须指定engine = 'python'。 至少对于 Anaconda,如果没有此选项,则会给出警告,因为默认的底层 C 实现未实现此选项。 这迫使它使用 Python 实现。

假设文件很大,您只想读取前几行,因为您只希望数据位于文件的开头,而不希望将其全部读取到内存中。 可以使用nrows参数来处理:

您也可以在文件的开头跳过特定数量的行,然后读取到末尾,或者一旦到达文件中的该点,您就只能读取几行。 为此,请使用skiprows参数。 以下示例跳过100行,然后读取下一个5

前面的示例还跳过了标题行的读取,因此有必要通知该过程不要查找标题并使用指定的名称。

以 Excel 格式读写数据

Pandas 支持使用pd.read_excel()函数或通过ExcelFile类读取 Excel 2003 及更高版本的数据。 在内部,这两种技术都使用XLRDOpenPyXL包,因此您需要确保其中之一已安装在 Python 环境中。

为了演示,示例数据提供了一个data/stocks.xlsx文件。 如果在 Excel 中打开它,您将看到与以下屏幕截图类似的内容:

该工作簿包含两张表,msftaapl,它们分别保存了每种股票的股票数据。

然后,以下代码将data/stocks.xlsx文件读入DataFrame

该文件仅读取 Excel 文件(msft工作表)中第一工作表的内容,并将第一行的内容用作列名。 要阅读其他工作表,可以使用sheetname参数传递工作表的名称:

pd.read_csv()一样,对列名,数据类型和索引也有许多假设。 我们为pd.read_csv()涵盖的所有用于指定此信息的选项,也适用于pd.read_excel()函数。

可以使用DataFrame.to_excel()方法编写 Excel 文件。 写入 XLS 格式要求包含XLWT包,因此在尝试之前,请确保已将其加载到 Python 环境中。

以下将我们刚刚获取的数据写入stocks2.xls。 默认值是将DataFrame存储在Sheet1工作表中:

在 Excel 中打开它会显示以下内容:

您可以使用sheet_name参数指定工作表的名称:

在 Excel 中,我们可以看到该工作表已命名为MSFT

要使用单独工作表上的每个DataFrame对象将多个DataFrame写入单个 Excel 文件,请使用ExcelWriter对象以及with关键字。 ExcelWriter是 Pandas 的一部分,但您需要确保将其导入,因为它不在 Pandas 的顶级命名空间中。 下面的代码将两个DataFrame对象写入一个 Excel 文件中的两个不同的工作表:

我们可以看到 Excel 文件中有两个工作表:

写入 XLSX 文件使用相同的功能,但将.XLSX指定为文件扩展名:

读写 JSON 文件

Pandas 可以读写以 JavaScript 对象表示法JSON)格式存储的数据。 这是我的最爱之一,因为它具有跨平台和多种编程语言使用的能力。

为了演示如何保存为 JSON,我们将首先将刚刚读取的 Excel 数据保存到 JSON 文件中,然后检查其内容:

可以使用pd.read_json()函数读取基于 JSON 的数据:

请注意此处的两个细微差异,这是由于从 JSON 读取/写入数据引起的。 首先,这些列已按字母顺序重新排序。 其次,DataFrame的索引尽管包含内容,但仍按字符串排序。 这些问题很容易解决,但为简洁起见,此处不再赘述。

从网络读取 HTML 数据

Pandas 支持从 HTML 文件(或 URL 的 HTML)读取数据。 在封面的下方,Pandas 使用LXMLHtml5LibBeautifulSoup4包。 这些包提供了一些令人印象深刻的读取和写入 HTML 表的功能。

您的 Anaconda 默认安装可能不包括这些包。 如果使用此功能遇到错误,请使用 Anaconda Navigator 根据错误安装适当的库:

另外,您可以使用pip

pd.read_html()函数将从文件(或 URL)读取 HTML,并将内容中找到的所有 HTML 表解析为一个或多个 Pandas DataFrame对象。 该函数始终返回DataFrame对象的列表(实际上,为零或更多,取决于在 HTML 中找到的表的数量)。

为了演示,我们将从 FDIC 失败银行列表中读取表数据。 查看页面,您会看到有很多失败银行的列表。

实际上,使用 Pandas 及其pd.read_html()函数可以很容易地读取这些数据:

再次,那几乎太容易了!

可以使用.to_html()方法将DataFrame写入 HTML 文件。 此方法创建一个仅包含数据的<table>标签(而不是整个 HTML 文档)的文件。 以下内容将我们之前阅读的股票数据写入 HTML 文件:

在浏览器中查看此结果类似于以下屏幕快照所示:

这很有用,因为您可以使用 Pandas 编写要包含在网站中的 HTML 片段,并在需要时对其进行更新,从而使新数据静态地可供网站使用,而不必通过更复杂的数据查询或服务调用。

读写 HDF5 格式文件

HDF5 是用于存储和管理数据的数据模型,库和文件格式。 它通常用于科学计算环境。 它支持无限多种数据类型,专为灵活高效的 I/O 以及大容量和复杂数据而设计。

HDF5 具有可移植性和可扩展性,从而允许应用在使用 HDF5 时不断发展。 HDF5 技术套件包括用于管理,操纵,查看和分析 HDF5 格式数据的工具和应用。 HDF5 是:

  • 通用的数据模型,可以表示非常复杂的数据对象和各种元数据
  • 完全可移植的文件格式,对集合中数据对象的数量或大小没有限制
  • 一个软件库,可在从笔记本到大规模并行系统的各种计算平台上运行,并使用 C,C++,Fortran 90 和 Java 接口实现高级 API
  • 丰富的集成性能函数集,可优化访问时间和存储空间
  • 用于管理,操纵,查看和分析集合中数据的工具和应用

HDFStore是类似于字典的分层对象,可将 Pandas 对象读取和写入 HDF5 格式。 在幕后,HDFStore使用PyTables库,因此,如果要使用此格式,请确保已安装该库。

以下演示将DataFrame写入 HDF5 格式。 输出显示 HDF5 存储有一个名为df的根级对象,该对象是DataFrame,其形状是八行三列:

以下内容读取 HDF5 存储并检索DataFrame

DataFrame在分配给存储对象的点写入 HDF5 文件。 此后对DataFrame所做的更改将不会保留,至少要等到该对象再次分配给数据存储对象后才能保留。 下面通过更改DataFrame,然后将其重新分配给 HDF5 存储,从而更新数据存储来演示此操作:

在网络上访问 CSV 数据

从网络和互联网读取数据非常普遍。 Pandas 使从网络读取数据变得容易。 还可以为我们检查的所有 Pandas 函数提供一个 HTTP URL,FTP 地址或 S3 地址,而不是本地文件路径,并且它们的全部功能与处理本地文件的方式相同。

以下内容演示了使用现有的pd.read_csv()函数直接发出 HTTP 请求是多么容易。 以下内容通过其 HTTP 查询字符串模型直接从 Google 财经网络服务检索 Microsoft 在 2017 年 4 月的每日股票数据:

从 SQL 数据库读写

Pandas 可以从任何支持遵守 Python DB-API 的 Python 数据适配器的 SQL 数据库读取数据。 使用pandas.io.sql.read_sql()函数执行读取,并使用DataFrame.to_sql()方法完成对 SQL 数据库的写入。

为了演示,以下内容从msft.csvaapl.csv中读取股票数据。 然后,它与 SQLite3 数据库文件建立连接。 如果该文件不存在,则会动态创建。 然后将MSFT数据写入名为STOCK_DATA的表中。 如果该表不存在,那么也会创建它。 如果确实存在,则将所有数据替换为MSFT数据。 最后,然后将AAPL股票数据附加到该表:

为了证明已创建此数据,可以使用诸如 SQLite 数据浏览器之类的工具打开数据库文件。 以下屏幕截图显示了数据库文件中的几行数据:

可以使用 SQL 使用pd.io.sql.read_sql()函数从数据库中读取数据。 以下内容演示了如何使用 SQL 查询stocks.sqlite中的数据并将其报告给用户:

也可以在 SQL 中使用WHERE子句以及选择列。 为了演示,以下选择MSFT的容量大于29200100的记录:

最后一点是,这些示例中的大多数代码是 SQLite3 代码。 这些示例中唯一的 Pandas 部分是.to_sql().read_sql()方法的使用,因为这些函数采用一个连接对象,该对象可以是任何与 Python DB-API 兼容的数据适配器,您可以或多或少地使用任何受支持的数据库来处理数据,只需创建适当的连接对象即可。 对于任何受支持的数据库,Pandas 级别的代码应保持相同。

从远程数据服务读取数据

0.19.0 之前的 Pandas 在pandas.io.data名称空间中直接支持各种基于 Web 的数据源类。 这已经发生了变化,因为功能已从 Pandas 中重构出来并放入pandas-datareader包中。

该包提供对许多有用数据源的访问,包括:

  • Yahoo 的每日历史股价或 Google 财经
  • 雅虎和 Google 期权
  • Enigma,结构化数据的提供者
  • 美联储经济数据库
  • Kenneth French 的数据库
  • 世界银行
  • 经合组织
  • 欧盟统计局
  • EDGAR 指数
  • TSP 基金数据
  • 奥安达货币历史汇率
  • 纳斯达克交易者代码定义

请注意,由于这些数据来自外部数据源,并且实际值会随时间变化,因此,当您运行代码时,可能会获得与本书中不同的值。

从 Yahoo 和 Google 财经中读取股票数据

首先,我要说的很遗憾,Yahoo 已更改了他们的 API,目前这破坏了pandas-datareader中的实现。 不知道这是否会解决。 因此,这些示例将仅使用 Google 财经。

要使用pandas-datereader包,我们使用以下导入:

然后,可以使用DataReader函数来反对来自 G​​oogle 财经的股票数据,方法是向其传递股票代码,数据源(在本例中为'google')以及开始日期和结束日期:

从 Google 财经中检索期权数据

Pandas 提供实验支持,可通过Options类检索 Google 财经期权数据。 在以下示例中,.get_all_data()方法用于从 Google 下载AAPL的选项数据:

使用此结果,可以通过使用.expiry_dates属性来确定到期日期:

使用.get_options_data()读取实际数据:

生成的DataFrame对象包含一个层次结构索引,可用于轻松提取数据的特定子集。 为了演示,让我们研究几个使用索引对值进行切片的示例。

以下将以Strike价格为 30 美元返回所有put选项。 使用slice(None)作为用于按索引选择的元组中的值之一,将包含所有Expiry日期:

我们可以通过指定日期切片而不是slice(None)来缩小日期范围。 以下将结果缩小到Expiry日期在2015-01-172015-04-17之间的日期:

从圣路易斯联邦储备银行读取经济数据

圣路易斯的美联储经济数据FRED可从超过 76 个数据源中下载超过 240,000 个美国和国际时间序列,并且它还在不断增长。

可以通过使用FredReader类并通过将特定序列标签作为name参数来指定 FRED 数据。 例如,以下内容检索两个指定日期之间的 GDP 信息:

要选择另一个序列,只需在第一个参数中指定序列标识符。 可以方便地在网站上浏览序列和直接在网站上可视化的数据。 例如,以下屏幕截图显示了“雇员补偿”序列:工资和薪金:

该数据序列由A576RC1A027NBEA ID 表示,我们可以使用以下代码下载它:

访问 Kenneth French 的数据

Kenneth R. French 是达特茅斯大学塔克商学院金融学教授。 他创建了一个广泛的经济数据库,可通过 Web 下载。 其数据的网站包含数据集。

该站点上可用的数据可通过 ZIP 文件下载,并且可以通过指定数据集的文件名(不带.zip)并使用FameFrenchReader函数直接读取到数据帧中。 例如,以下内容读取全局因子数据:

从世界银行读数据

世界银行有成千上万的数据提要,可以直接将其读入 Pandas DataFrame对象中。 可以在 http://www.worldbank.org/ 浏览世界银行数据目录。

世界银行的数据集使用指示符进行标识,指示符是代表每个数据集的文本代码。 可以使用pandas.io.wb.get_indicators()函数检索指标的完整列表。 在撰写本文时,共有 16,167 个指标。 以下内容检索指标并显示前五个指标:

可以使用世界银行的网站对这些指标进行调查,但是,如果您想了解要采样的指标,则只需进行搜索即可。 例如,以下使用wb.search()函数来搜索具有与预期寿命相关的数据的指标:

每个指标均分为不同的国家。 可以使用wb.get_countries()函数检索国家/地区数据的完整列表:

可以使用wb.download()函数并使用indicator参数指定数据集来下载指标数据。 以下是从19802014下载国家的预期寿命数据:

默认情况下,仅返回美国,加拿大和墨西哥的数据。 通过检查上一个查询的结果索引可以看出这一点:

要获取更多国家的数据,请使用country参数明确指定它们。 以下获取所有已知国家/地区的数据:

我们可以用这些数据做一些有趣的事情。 我们将看的示例确定哪个国家的预期寿命最低。 为此,我们首先需要对数据进行透视处理,以使索引为国家名称,而年份为列。 我们将在后面的章节中更详细地介绍数据透视,但是目前,仅知道以下内容将数据沿索引和跨列的年份重新组织到了国家/地区中。 同样,每个值都是每个国家在特定年份的预期寿命:

使用这种格式的数据,我们可以使用.idxmin(axis=0)确定哪个国家的预期寿命最低:

每年的实际最小值可以使用.min(axis=0)检索:

然后,可以将这两个结果合并为一个新的DataFrame,该值告诉我们哪个国家/地区的预期寿命最短,其值是多少:

总结

在本章中,我们研究了 Pandas 如何使访问各种位置和格式的数据变得简单,如何将这些格式的数据自动映射到数据帧对象。 我们从学习如何从 CSV,HTML,JSON,HDF5 和 Excel 格式的本地文件中读取和写入数据开始,直接读取和写入数据帧对象,而不必担心将包含的数据映射到这些各种数据中的细节。 格式。

然后,我们研究了如何从远程源访问数据。 首先,我们看到与本地文件配合使用的功能和方法也可以从 Web 和云数据源中读取。 然后,我们研究了 Pandas 对访问各种形式的基于 Web 和基于 Web 服务的数据的支持,例如 Yahoo 金融和世界银行。

既然我们已经能够加载数据,那么使用它的下一步就是执行数据的清理,因为通常情况下,检索到的数据存在诸如信息丢失和内容格式错误的问题。 下一章将集中在整理数据的过程中,这些问题通常称为整理你的数据

posted @ 2025-10-23 15:13  绝不原创的飞龙  阅读(1)  评论(0)    收藏  举报