在本章中,我们将介绍:
- Jupyter 入门
- 探索 Jupyter 笔记本
- 准备分析汽车燃油效率
- 使用 Python 探索和描述燃油效率数据
- 使用 Python 分析一段时间内的汽车燃油效率
- 使用 Python 研究汽车的品牌和型号
在有关 R 的第一章(第 2 章,“使用 R 进行汽车数据驾驶视觉分析”)中,我们完成了一个分析项目,该项目使用 R 统计编程语言检查了汽车燃油经济性数据。 。 该数据集可从这个页面获得,其中包含美国所有品牌和型号的汽车随时间推移的燃油效率性能指标。 美国。 该数据集还包含除燃油经济性以外的汽车模型的许多其他特征和属性,从而提供了汇总和分组数据的机会,以便我们可以识别有趣的趋势和关系。
与 R 的第一章不同,我们将使用 Python 执行整个分析。 但是,我们将再次遵循数据科学流程,提出与之前相同的问题,并按照相同的步骤顺序进行操作。 通过学习,您将可以看到两种语言之间的异同,从而进行几乎相同的分析。
在这里,我们在 NumPy 和 SciPy 的帮助下使用了大多数纯 Python,它们直接来自 Python 命令行-也称为 Read-Eval-Print Loop(REPL)-或来自可执行文件 脚本文件。 在本章中,我们将采用一种非常不同的方法,以类似于 R 的交互方式将 Python 用作脚本语言。我们将向读者介绍 Python,IPython 和 Jupyter 笔记本的非正式交互环境,展示如何 生成可读性良好且有据可查的分析脚本。 此外,我们将利用相对较新但功能强大的 Pandas 库的数据分析功能及其提供的宝贵数据框数据类型。 熊猫通常允许我们用更少的代码行来完成复杂的任务。 这种方法的缺点是,虽然您不必为常见的数据处理任务重新发明轮子,但您必须学习完全不同的程序包(即 Pandas)的 API。
本章的目的不是指导您完成已经完成的分析项目,而是向您展示如何用另一种语言完成该项目。 更重要的是,我们希望让您(读者)对您自己的代码和分析更加自省。 不仅要考虑如何完成某事,还应该考虑为什么要用这种特定语言来完成某件事。 语言如何影响分析?
IPython 是 Python 的交互式计算外壳,它将改变您对交互式外壳的思考方式。 它带来了许多非常有用的功能,这些功能很可能会成为默认工具箱的一部分,包括魔术功能,制表符补全,易于访问的命令行工具等等。 我们仅在这里进行介绍,强烈建议您继续探索使用 IPython 可以完成的操作,有关更多详细信息和选项,请参见这个页面。
如果您已经完成了第一章中的安装说明,则应该准备解决以下问题。 请注意,主要版本 IPython 6.1 已于 2017 年 5 月启动。IPython 会话已于 2017 年 3 月在较早版本的交互式软件上运行。
以下步骤将使您开始使用 IPython 环境:
- 在计算机上打开一个终端窗口,然后输入
ipython
。 您应该立即看到以下内容:
请注意,您的版本可能与前面的命令行输出中显示的版本略有不同。
- 只是为了向您展示 IPython 的强大之处,请键入
ls
,您将会看到目录清单! 是的,您可以直接在 Python 解释器中的 Python 提示符下访问常见的 Unix 命令。 - 现在,让我们尝试更改目录。 在提示符下键入
cd
,打空格,然后单击 选项卡。 应该为您提供当前目录中可用目录的列表。 开始输入目标目录的前几个字母,然后再次单击 选项卡。 如果只有一个选项匹配,则自动单击 选项卡 键将插入该名称。 否则,可能性列表将仅显示与您已经键入的字母匹配的名称。 当您按下 选项卡 时,输入的每个字母都充当过滤器。
- 现在,键入
?
,您将快速了解 IPython 的功能并对其进行概述。
- 让我们看一下魔术功能。 这些是 IPython 可以理解的特殊功能,并且始终以
%
符号开头。%paste
函数就是这样的一个例子,它对于在不丢失适当缩进的情况下将 Python 代码复制和粘贴到 IPython 中来说是惊人的。 一个简单的例子是在任何文本编辑器中键入2+3
并复制它,然后在 IPython 中执行%paste
。 - 我们将尝试
%timeit
魔术功能,该功能可以智能地对 Python 代码进行基准测试。 输入以下命令:
In [2]: n=10000
...: %timeit range(n)
...:
我们应该得到这样的输出:
The slowest run took 4.74 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 1.83 mus per loop
请注意,Python 3.x 版本中的 range 函数是早期 Python 2.x 版本中的xrange
函数。Â
- 您还可以通过在命令前面加上感叹号来轻松运行系统命令。 尝试以下命令:
In [3]: !ping www.google.com
您应该看到以下输出:
Pinging www.google.com [216.239.32.20] with 32 bytes of data:
Reply from 216.239.32.20: bytes=32 time=35ms TTL=57
Reply from 216.239.32.20: bytes=32 time=34ms TTL=57
Reply from 216.239.32.20: bytes=32 time=33ms TTL=57
Reply from 216.239.32.20: bytes=32 time=35ms TTL=57
Ping statistics for 216.239.32.20:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 33ms, Maximum = 35ms, Average = 34ms
- 最后,IPython 提供了出色的命令历史记录。 只需按向上箭头键即可访问先前输入的命令。 继续按向上箭头键可在会话的命令列表中向后浏览,而向下箭头键则可向前移动。 同样,神奇的
%history
命令允许您跳到会话中的特定命令编号。 键入以下命令以查看您输入的第一个命令:
In [4]: %history 1
IPython 会话的完整屏幕截图如下:
现在,键入exit
以退出 IPython 并返回到系统命令提示符。
这里没有太多要解释的东西,我们只是从头开始了解 IPython 可以做的事情。 希望我们引起了您对更深层次的兴趣,特别是 IPython 6.0 提供的众多新功能,包括动态和用户可控制的数据可视化。
Jupyter Notebook 是 Python 的完美补充。在实践中,Jupyter Notebook 使您可以在代码中插入注释和图像以及其他可能有用的东西。 您可以使用 Jupyter 笔记本进行各种操作,从演示文稿(PowerPoint 的绝佳替代品)到电子实验室笔记本或教科书。
如果您已经完成了第一章中的安装说明,则应该准备解决以下问题。
这些步骤将使您开始探索功能异常强大的 Jupyter Notebook 环境。 我们敦促您超越这些简单的步骤,以了解该工具的真正功能:
-
使用
mkdir test
在终端上创建一个文件夹,例如 test,然后使用cd test
将其移动到该文件夹。 -
在命令提示符下键入
jupyter notebook
。 您应该在终端窗口中看到一些文本快速滚动,然后,以下屏幕应在默认浏览器中加载(对我来说,这是 Firefox)。 请注意,URL 应为localhost:8888/tree
,表示浏览器已连接至本地计算机上运行的服务器,该服务器的端口为8888
。 -
您不会在浏览器中看到任何笔记本(注意 Jupyter Notebook 文件具有
.ipynb
扩展名),因为 Jupyter Notebook 在从其启动目录中搜索笔记本文件。 现在创建一个笔记本。 单击下拉菜单New
,找到 Notebook 的选项,然后单击 Python 3 选项(尽管您可能有所不同):
- 应该会打开一个新的浏览器选项卡或窗口,向您显示类似于以下屏幕截图的内容:
-
从上至下,您可以看到基于文本的菜单,其后是用于发布常用命令的工具栏,然后是您的第一个单元格,该单元格应类似于 IPython 中的命令提示符。
-
将鼠标光标放在第一个单元格中,然后输入
5+5
。 接下来,导航至Cell
|Run
或按 Shift + 输入 作为键盘快捷键,以使单元格的内容得以解释。 现在,您应该会看到类似于以下屏幕截图的内容。 基本上,我们只是在第一个 Jupyter Notebook 的第一个单元格中执行了一个简单的 Python 语句。 -
单击第二个单元格,然后导航到
Cell
|Cell Type
|Markdown
。 现在,您可以轻松地在单元中编写markdown
,以用于记录。 -
关闭两个浏览器窗口或选项卡(笔记本和笔记本浏览器)。
-
返回您在其中键入
jupyter notebook
的终端,按 Ctrl +C
,然后按Y
,然后按 输入。 这将关闭 Jupyter Notebook 服务器。
对于那些来自更传统的统计软件包(例如 Stata,SPSS 或 SAS)或更传统的数学软件包(例如 MATLAB)的用户 ,Mathematica 或 Maple,您可能已经习惯了各自公司提供的非常图形化且功能丰富的交互式环境。 在这种背景下,Jupyter Notebook 可能看起来有些陌生,但希望它比传统的 Python 提示符更加用户友好并且不那么吓人。 此外,Jupyter Notebook 提供了交互性和顺序工作流程的有趣组合,特别适合于数据分析,特别是在原型设计阶段。 R 有一个名为Knitr
的库,它提供 Jupyer Notebook 的报告生成功能。
当您输入jupyter notebook
时,您将启动在本地计算机上运行的服务器,而 Jupyter Notebook 本身实际上是使用服务器-客户端体系结构的 Web 应用。 根据 ipython.org 的 Jupyter Notebook 服务器使用具有 ZeroMQ 和龙卷风的两进程内核体系结构。 ZeroMQ 是用于高性能消息传递的智能套接字库,可帮助 Jupyter 管理分布式计算集群以及其他任务。 Tornado 是一个 Python Web 框架和异步联网模块,可为 Jupyter Notebook 的 HTTP 请求提供服务。 该项目是开源的,如果您愿意,可以为源代码做贡献。
Jupyter Notebook 还允许您使用名为nbconvert
。 可用的导出格式包括 HTML,LaTex,reveal.js
HTML 幻灯片,Markdown,简单的 Python 脚本,以及 Sphinx 文档的reStructuredText
。
最后,还有 Jupyter Notebook Viewer(nbviewer
),它是一项免费的 Web 服务,您可以在其中发布并浏览托管在远程服务器上的静态 HTML 版本的笔记本文件(这些服务器目前由 Rackspace 捐赠)。 因此,如果您创建了一个想要共享的.ipynb
惊人文件,则可以将其上传到这个页面,让全世界看到您的努力。
我们将尽量不要大声地赞美 Markdown,但如果您不熟悉该工具,我们强烈建议您尝试一下。 Markdown 实际上是两种不同的东西:一种以易于转换为结构化文档的方式格式化纯文本的语法,以及一种将所述文本转换为 HTML 和其他语言的软件工具。 基本上,Markdown 使作者可以使用任何所需的简单文本编辑器(VI,VIM,Emacs,Sublime 编辑器,TextWrangler,Crimson Editor 或 Notepad)可以捕获纯文本,但仍描述相对复杂的结构,例如不同级别的标题,有序和无序列表以及块引号以及某些格式,例如 粗体和斜体。 Markdown 基本上提供了一种非常易于阅读的 HTML 版本,类似于 JSON,并且提供了一种易于阅读的数据格式。
在本食谱中,我们将开始对汽车燃油效率数据进行基于 Python 的分析。
如果您成功完成了第一章,则应该准备开始。
以下步骤将指导您设置工作目录和 IPython 以进行本章的分析:
- 创建一个名为
fuel_efficiency_python
的项目目录。 - 从这个页面下载汽车燃油效率数据集,并将其存储在前面的目录中。 将
vehicles.csv
文件从 zip 文件中提取到同一目录中。 - 打开一个终端窗口,然后将当前目录(
cd
)更改为fuel_efficiency_python
目录。 - 在终端上,键入以下命令:
jupyter notebook
- 将新页面加载到 Web 浏览器中后,单击
New Notebook
。 - 单击笔记本的当前名称
untitled0
,然后输入此分析的新名称(我的名称为fuel_efficiency_python
)。 - 让我们将最上面的单元格用于
import
语句。 输入以下命令:
In [5]: import pandas as pd
...: import numpy as np
...: from ggplot import *
...: %matplotlib inline
然后,按 Shift + 输入 以执行单元。 这将同时导入pandas
和numpy
库,并为它们分配本地名称以在键入命令时节省一些字符。 它还导入ggplot
库。 请注意,在 Python 中使用from ggplot import *
命令行不是最佳实践,它会将ggplot
程序包内容倒入我们的默认名称空间中。 但是,我们这样做是为了使我们的ggplot
语法与 R ggplot2
语法最相似,而这并不是 Python 语言。 最后,我们使用魔术命令告诉 Jupyter Notebook 我们想要matploblib
图形在笔记本中呈现。
- 在下一个单元格中,让我们导入数据并查看前几条记录:
In [6]: vehicles = pd.read_csv("vehicles.csv")
但是,请注意,出现红色警告消息,如下所示:
C:\Users\prabhanjan.tattar\AppData\Local\Continuum\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py:2717: DtypeWarning: Columns (70,71,72,73,74,76,79) have mixed types. Specify dtype option on import or set low_memory=False.
这告诉我们列70,71,72,73,74,76, and 79
包含混合数据类型。 让我们使用以下命令查找对应的名称:
In [7]: column_names = vehicles.columns.values
...: column_names[[70,71,72,73,74,76,79]]
...:
Out[7]: array(['cylinders', 'displ', 'fuelType2', 'rangeA', 'evMotor', 'mfrCode'], dtype=object)
混合数据类型听起来可能是有问题的,因此请牢记这些列名称。 请记住,数据清理和整理通常会占用项目时间的 90%。
使用此配方,我们只需设置工作目录并创建一个新的 Jupyter Notebook,即可用于分析。 我们已经导入了 Pandas 库,并非常快速地将vehicles.csv
数据文件直接读取到数据框中。 从经验上来讲,熊猫强大的数据导入功能将为您节省大量时间。
尽管我们直接从逗号分隔的值文件中将数据导入到数据框中,但是 pandas 能够使用阅读器功能处理许多其他格式,包括 Excel,HDF,SQL,JSON,Stata,甚至剪贴板。 我们还可以使用从数据帧对象访问的写入器功能以多种格式从数据帧中写出数据。
使用熊猫中Data Frame
类的绑定方法head
,我们收到了非常有用的数据框摘要,其中包括每列非空值的计数以及各种数据类型的计数 跨列。
数据帧是一个非常强大的概念和数据结构。 数据框架中的思维对于许多数据分析至关重要,但与数组或矩阵运算中的思维也有很大不同(例如,如果您来自 MATLAB 或 C 作为主要开发语言)。
对于数据框,每一列代表不同的变量或特征,并且可以是不同的数据类型,例如浮点数,整数或字符串。 数据帧的每一行都是一个单独的观察值或实例,具有自己的一组值。 例如,如果每一行代表一个人,则列可以是age
(整数)和gender
(类别或字符串)。 通常,我们将要选择与特定特征(例如,所有雄性)相匹配的一组观察值(行),并检查该子组。 从概念上讲,数据框与关系数据库中的表非常相似。
现在,我们已经将汽车燃油效率数据集导入 Jupyter,并见证了熊猫的力量,下一步是复制前一章在 R 中执行的初步分析,以了解熊猫的一些基本功能。
我们将继续发展和发展我们在上一个食谱中开始的 Jupyer Notebook。 如果您已完成上一个食谱,则应具备继续进行所需的一切。
- 首先,让我们使用以下命令找出数据中有多少个观测值(行):
In [8]: len(vehicles)
...:
Out[8]: 38120
如果在 R 和 Python 之间来回切换,请记住,在 R 中,函数为length
,而在 Python 中,函数为len
。
- 接下来,让我们使用以下命令找出数据中有多少个变量(列):
In [9]: len(vehicles.columns)
...:
Out[9]: 83
Let's get a list of the names of the columns using the following command:
In [10]: print(vehicles.columns)
...:
Index(['barrels08', 'barrelsA08', 'charge120', 'charge240', 'city08',
'city08U', 'cityA08', 'cityA08U', 'cityCD', 'cityE', 'cityUF', 'co2',
'co2A', 'co2TailpipeAGpm', 'co2TailpipeGpm', 'comb08', 'comb08U',
'combA08', 'combA08U', 'combE', 'combinedCD', 'combinedUF', 'cylinders',
'displ', 'drive', 'engId', 'eng_dscr', 'feScore', 'fuelCost08',
'fuelCostA08', 'fuelType', 'fuelType1', 'ghgScore', 'ghgScoreA',
'highway08', 'highway08U', 'highwayA08', 'highwayA08U', 'highwayCD',
'highwayE', 'highwayUF', 'hlv', 'hpv', 'id', 'lv2', 'lv4', 'make',
'model', 'mpgData', 'phevBlended', 'pv2', 'pv4', 'range', 'rangeCity',
'rangeCityA', 'rangeHwy', 'rangeHwyA', 'trany', 'UCity', 'UCityA',
'UHighway', 'UHighwayA', 'VClass', 'year', 'youSaveSpend', 'guzzler',
'trans_dscr', 'tCharger', 'sCharger', 'atvType', 'fuelType2', 'rangeA',
'evMotor', 'mfrCode', 'c240Dscr', 'charge240b', 'c240bDscr',
'createdOn', 'modifiedOn', 'startStop', 'phevCity', 'phevHwy',
'phevComb'],
dtype='object')
每个字符串前面的u
字母表示这些字符串以 Unicode 表示
- 让我们找出此数据集中包含多少个独特年份的数据,以及前几年和下一年使用以下命令的内容:
In [11]: len(pd.unique(vehicles.year))
...:
Out[11]: 34
In [12]: min(vehicles.year)
...:
Out[12]: 1984
In [13]: max(vehicles["year"])
...:
Out[13]: 2017
再次注意,我们使用了两种不同的语法来引用车辆数据框中的各个列。
- 接下来,让我们找出哪种类型的燃料用作汽车的主要燃料类型。 在 R 中,我们具有
table
函数,该函数将返回变量各种值出现的次数。 在大熊猫中,我们使用以下内容:
In [14]: pd.value_counts(vehicles.fuelType1)
...:
Out[14]:
Regular Gasoline 26533
Premium Gasoline 10302
Diesel 1014
Electricity 134
Midgrade Gasoline 77
Natural Gas 60
Name: fuelType1, dtype: int64
- 现在,如果我们要探索这些汽车具有哪种类型的变速器,我们立即尝试以下命令:
In [15]: pd.value_counts(vehicles.trany)
但是,这会导致一些意外且冗长的输出:
Out[15]:
Automatic 4-spd 11042
Manual 5-spd 8323
Automatic 3-spd 3151
Automatic (S6) 2684
Manual 6-spd 2448
Automatic 5-spd 2191
Manual 4-spd 1483
Automatic 6-spd 1447
Automatic (S8) 981
Automatic (S5) 827
Automatic (variable gear ratios) 702
Automatic 7-spd 675
Automatic (S7) 270
Auto(AM-S7) 266
Automatic 8-spd 259
Automatic (S4) 233
Auto(AM7) 166
Auto(AV-S6) 153
Automatic (A1) 125
Auto(AM6) 120
Automatic 9-spd 105
Auto(AM-S6) 87
Auto(AV-S7) 80
Manual 3-spd 77
Manual 7-spd 73
Automatic (S9) 29
Auto(AV-S8) 28
Manual 4-spd Doubled 17
Auto(AM5) 12
Automatic (AV-S6) 11
Automatic (S10) 8
Auto(AM-S8) 6
Auto(AM8) 5
Automatic (AV) 4
Automatic (A6) 4
Manual(M7) 3
Auto(L3) 2
Auto(L4) 2
Automatic (AM5) 2
Auto (AV) 2
Auto (AV-S8) 1
Manual 5 spd 1
Auto (AV-S6) 1
Auto(AM-S9) 1
Automatic 6spd 1
Automatic (AM6) 1
Name: trany, dtype: int64
我们真正想知道的是具有自动和手动变速箱的汽车数量。 我们注意到,trany
变量表示自动变速器时始终以字母A
开头,而对于手动变速器则始终以字母M
开头。 因此,我们创建一个新变量trany2
,它包含trany
变量的第一个字符,该字符是一个字符串:
In [16]: vehicles["trany2"] = vehicles.trany.str[0]
...: pd.value_counts(vehicles.trany2)
...:
前面的命令产生的答案是我们想要的,或者是手动生成的答案的两倍:
Out[16]:
A 25684
M 12425
Name: trany2, dtype: int64
在本食谱中,我们介绍了 Python 和 pandas 的一些基本功能。 我们使用了两种不同的语法(vehicles['trany']
和vehicles.trany
)来访问数据帧内的变量。 我们还使用了一些熊猫的核心功能来探索数据,例如非常有用的unique
和value_counts
功能。
在数据科学管道方面,我们在一个配方中涉及了两个阶段:数据清理和数据探索。 通常,当使用较小的数据集来完成特定操作的时间很短并且可以在我们的笔记本电脑上完成时,我们将非常快速地进行流水线的多个阶段,然后根据结果循环返回。 通常,数据科学管道是一个高度迭代的过程。 我们完成步骤的速度越快,可以在固定时间内进行的迭代就越多,并且通常我们可以创建更好的最终分析。
在本食谱中,我们将研究一段时间内以及与其他数据点相关的一些燃油效率指标。 为此,我们将不得不在 Python 中复制两个非常流行的 R 库的功能,它们是plyr
和ggplot2
。 plyr
R 库非常方便地涵盖了拆分应用组合数据分析功能,但是熊猫开箱即用的方式处理方式也一样。 ggplot2
的数据可视化功能-图形语法的 R 库实现-不能像在本食谱中看到的那样容易处理。
如果您已经完成了上一个食谱,那么您应该拥有继续进行所需的几乎所有操作。 但是,我们将为 R 使用ggplot2
库的 Python 克隆,它的方便命名为ggplot
。 如果您没有完成整个设置章节并且尚未安装ggplot
软件包,请打开一个终端并输入以下内容:
pip install ggplot (or sudo pip install ggplot)
这也适用于 Windows 计算机。 等待安装完成。 完成此操作后,必须重新启动 IPython Notebook 服务器才能导入此新安装的ggplot
库。
我们将通过以下步骤进入分析阶段:
- 让我们开始看看 mpg 平均是否随时间变化的总体趋势。 我们首先要按年份对数据进行分组:
In [17]: grouped = vehicles.groupby("year")
- 接下来,我们要通过前面的分组计算三个独立列的平均值:
In [18]: averaged = grouped['comb08', 'highway08','city08'].agg([np.mean])
这将产生一个新的数据帧,该数据帧具有三列,分别包含comb08
,highway08
和city08
变量的平均值。 请注意,我们正在使用 NumPy(np
)提供的mean
函数。
- 为了使生活更轻松,我们将重命名这些列,然后创建一个名为
year
的新列,其中包含数据框的索引:
In [19]: averaged.columns = ['comb08_mean','highway08_mean','city08_mean']
...: averaged['year'] = averaged.index
请注意,与我们在 R 中要做的相比,重命名列有多么容易! 数据框的 columns 属性包含列的名称以及为重命名列而需要修改的内容。
- 最后,我们想使用 Python 库的新
ggplot
包将结果绘制为散点图:
In [20]: ggplot(averaged, aes('year', 'comb08_mean')) + geom_point(
...: color='steelblue') + xlab("Year") + ylab("Average MPG"
...: ) + ggtitle("All cars")
请参考下图:
由于近来具有出色行驶里程的混合动力汽车变得越来越流行,因此该图可能会产生误导。 让我们看看是否可以筛选出这些汽车品牌。 精明的观察者将认识到,该图在 R 章中不包括匹配图像的geom_smooth()
方法。 尽管当前的ggplot
库(截至 2014 年 2 月 11 日为为 0.4.7)具有可能等效的stat_smooth()
方法,但当前版本仍存在一些错误,这些错误会导致绘制错误的结果(而不是错误的结果)。 如图所示)。**
- 为了删除混合动力汽车,我们创建了三个布尔数组。
criteria1
数组选择fuelType1
为Regular Gasoline
,Premium Gasoline
或Midgrade Gasoline
的数据帧的那些行。criteria2
数组确保行包含fuelType2
的空值,并且criteria3
确保atvType
不是Hybrid
。 我们可以对这三个布尔数组一起执行逻辑AND
操作,以仅从数据帧中选择所需的行:
In [21]: criteria1 = vehicles.fuelType1.isin(["Regular Gasoline",
...: "Premium Gasoline", "Midgrade Gasoline"])
...: criteria2 = vehicles.fuelType2.isnull()
...: criteria3 = vehicles.atvType != "Hybrid"
...: vehicles_non_hybrid = vehicles[criteria1 & criteria2 &
...: criteria3]
...: len(vehicles_non_hybrid)
...:
Out[21]: 34990
- 我们将结果数据框按年份分组,然后计算每年的平均组合燃油效率,得出以下数据框:
In [22]: grouped = vehicles_non_hybrid.groupby(['year'])
...: averaged = grouped['comb08'].agg([np.mean])
...: print(averaged)
...:
前面的命令将产生以下输出:
mean
year
1984 19.121622
1985 19.394686
1986 19.320457
1987 19.164568
1988 19.367607
1989 19.141964
1990 19.031459
1991 18.838060
1992 18.861566
1993 19.137383
1994 19.092632
1995 18.872591
1996 19.530962
1997 19.368000
1998 19.329545
1999 19.239759
2000 19.169345
2001 19.075058
2002 18.950270
2003 18.761711
2004 18.967339
2005 19.005510
2006 18.786398
2007 18.987512
2008 19.191781
2009 19.738095
2010 20.466736
2011 20.838219
2012 21.407328
2013 22.228877
2014 22.279835
2015 22.424555
2016 22.749766
2017 22.804085
根据前面的数据,即使消除混合动力,我们仍可以看到每加仑的平均英里数仍显着增加。
- 我们要问的下一个问题是,最近是否有较少的大型发动机汽车制造? 如果这是真的,那可以解释每加仑平均英里数的增加。 首先,让我们验证一下,大型发动机汽车的每加仑行驶里程会变差。 为此,我们需要深入研究
displ
变量,该变量代表以升为单位的发动机排量。 记住,熊猫给了我们有关包含多种数据类型的变量的警告,因此让我们计算唯一的displ
值:
In [23]: pd.unique(vehicles_non_hybrid.displ)
...:
Out[23]:
array([ 2\. , 4.9, 2.2, 5.2, 1.8, 1.6, 2.3, 2.8, 4\. , 5\. , 3.3,
3.1, 3.8, 4.6, 3.4, 3\. , 5.9, 2.5, 4.5, 6.8, 2.4, 2.9,
5.7, 4.3, 3.5, 5.8, 3.2, 4.2, 1.9, 2.6, 7.4, 3.9, 1.5,
1.3, 4.1, 8\. , 6\. , 3.6, 5.4, 5.6, 1\. , 2.1, 1.2, 6.5,
2.7, 4.7, 5.5, 1.1, 5.3, 4.4, 3.7, 6.7, 4.8, 1.7, 6.2,
8.3, 1.4, 6.1, 7\. , 8.4, 6.3, nan, 6.6, 6.4, 0.9])
- 我们看到有些值可能不是数字,包括
nan
值。 让我们从vehicles_non_hybrid
数据帧中删除所有具有nan
displ
值的行,然后对comb08
变量执行相同的操作。 在此过程中,让我们使用astype
方法来确保每个值的类型均为float
,以防万一:
In [24]: criteria = vehicles_non_hybrid.displ.notnull()
...: vehicles_non_hybrid = vehicles_non_hybrid[criteria]
...: vehicles_non_hybrid.displ = vehicles_non_hybrid.displ.astype('float')
...:
In [25]: criteria = vehicles_non_hybrid.comb08.notnull()
...: vehicles_non_hybrid = vehicles_non_hybrid[criteria]
...: vehicles_non_hybrid.comb08 = vehicles_non_hybrid.comb08.astype('float')
- 最后,我们将使用
ggplot
库再次生成结果的散点图:
In [26]: ggplot(vehicles_non_hybrid, aes('displ', 'comb08')) + geom_point(
...: color='steelblue') + xlab("Engine Displacement") +ylab(
...: "Average MPG") + ggtitle("Gasoline cars")
请参考下图:
前面的曲线似乎证实了燃油经济性与发动机排量之间的负相关关系。
现在,最近生产的带有大型发动机的汽车已经减少了吗?
- 让我们看看以后几年是否平均生产了较小的汽车:
In [27]: grouped_by_year = vehicles_non_hybrid.groupby(['year'])
...: avg_grouped_by_year = grouped_by_year['displ',
...: 'comb08'].agg([np.mean])
- 接下来,让我们在同一图上绘制平均
displ
值和year
的平均comb08
值,以查找趋势。 为此,我们需要调整avg_grouped_by_year
数据框的形状,以将其从宽格式转换为长格式:
In [28]: avg_grouped_by_year['year'] = avg_grouped_by_year.index
...: melted_avg_grouped_by_year = pd.melt(avg_grouped_by_year,id_vars='year')
然后,让我们创建多面图:
In [29]: p = ggplot(aes(x='year', y='value', color = 'variable_0'),
...: data=melted_avg_grouped_by_year)
...: p + geom_point() + facet_wrap("variable_0")
请参考下图:
让我们忽略分析的实际发现,因为它们与本章中使用 R 的发现相同。真正有趣的部分是本章中使用的许多重要数据分析技术。 让我们分别分解每种技术。
首先,让我们看一下通常的数据分析模式,称为 split-apply-combine,该模式先前在随附的 R 章中已提到。 分析数据集时,通常希望按一个或多个特征对数据进行分组,对分组的数据子集执行操作,然后将结果放在一起。 在本章中,我们将数据按年分组,计算不同变量的平均值,然后将这些结果合并。 在上一章中,我们使用了 Hadley Wickham 的plyr
程序包。 对于plyr
,我们调用ddply
函数,该函数传递要分析的数据帧,要分组的特征或特征集,然后在分组数据上使用该函数。 然后ddply
函数返回结果数据帧。 一行代码执行 split-apply-combine 模式。
Python 中的 Pandas 库采用的方法稍有不同,并拆分了plyr
中单个函数调用中包含的功能。 首先,我们可以按照指定的特征(如grouped_by_year = vehicles_non_hybrid.groupby(['year'])
这行代码)将熊猫对象(如数据帧)分组-在此处,我们通过year
变量将vehicles_non_hybrid
数据帧的行分组。 请注意,我们不仅限于按单个变量进行分组,还可以创建具有多个特征的分组(例如,年份和汽车公司)。
一旦有了grouped_by_year
对象,如果我们想要执行以下操作,便可以遍历这些组:
for (name, group) in grouped_by_year:
print name
print group
这将打印出每个组名和结果数据框。 接下来,我们使用 NumPy 库(np.mean
)中的mean
函数对GroupBy
对象使用aggregrate
方法。 在以下代码中,我们选择仅从grouped_by_year
数据帧中聚合单个变量comb08
:
averaged = grouped['comb08'].agg([np.mean])
在熊猫中,库中内置了非常强大的“拆分应用组合”功能,而我们仅从表面上了解了它的功能。
我们还使用了 yhat 中的ggplot
包,而不是古老的 matplotlib。 图形语法提供了一种非常简洁的(尽管高度非 Pythonic 的)图形描述方式。 开创性书籍图形语法,Leland Wilkinson,Springer 的封底指出:
“图形语法为产生几乎所有在科学期刊,报纸,统计软件包和数据可视化系统中发现的定量图形提供了独特的基础。尽管这项工作的实质成果是多个可视化软件库,但本书着重介绍 关于从数据生成定量图形所涉及的深层结构。饼图,条形图,散点图,函数图,地图,镶嵌图和雷达图的生成所依据的规则是什么?”
R 中的ggplot2
软件包是 R 的最大资产之一,并且 Python 现在具有正常运行的ggplot
克隆。 不幸的是,如本章中的一些经验所示,ggplot
Python 库目前功能还不完善。
由于 Python ggplot
库仍在开发中(平滑功能存在一些问题),您可能想知道有一个 Python 库可以在您的 Python 程序中使用 R。 位于这个页面的rpy2
软件包提供了从 Python 到 R 的低层和高层接口。 低级接口有点类似于 R 的 C API。 高级接口将 R 对象公开为 Python 类的实例。 为了使用rpy2
,请确保您已在系统上安装 R。 您从 Python 调用的所有软件包都必须在 R 中可用!
- 熊猫:建立索引并选择数据
- 《Matplotlib 和 Python 中可视化的未来》
- Hadley Wickham 的主页
- 适用于 Python 的
ggplot
软件包 - 更多适用于 Python 的
ggplot
- 《数据分析的拆分应用组合策略》文章
为了继续研究此数据集,我们将更仔细地检查各种汽车的制造商和模型,在从 R 转换为 Python 的同时重复上一章中的许多步骤。
如果您已完成上一个食谱,则应具备继续操作所需要的一切。
以下步骤将引导我们完成调查:
- 让我们看一下汽车的品牌和型号如何向我们介绍一段时间内的燃油效率。 首先,让我们看一下在美国销售的汽车的制造商和型号的频率,重点是四缸汽车。 要选择 4 缸汽车,我们首先使
cylinders
变量唯一,以查看可能的值是:
In [30]: pd.unique(vehicles_non_hybrid.cylinders)
...:
Out[30]: array([ 4., 12., 8., 6., 5., 10., 2., 3., 16., nan])
4.0
和4
均被列为唯一值; 这个事实应该引起您的怀疑。 记住,当我们导入数据时,熊猫警告我们几个变量是混合类型,其中一个变量是cylinders
。
- 让我们将
cylinders
变量转换为float
,以便我们可以轻松地对数据帧进行子集化:
In [31]: vehicles_non_hybrid.cylinders = vehicles_non_hybrid.cylinders.astype('float')
...: pd.unique(vehicles_non_hybrid.cylinders)
...:
Out[31]: array([ 4., 12., 8., 6., 5., 10., 2., 3., 16., nan])
In [32]: vehicles_non_hybrid_4 = vehicles_non_hybrid[(vehicles_non_hybrid.cylinders == 4.0)]
- 现在,让我们看一下在可用的时间范围内拥有 4 缸汽车的品牌数量:
In [35]: grouped_by_year_4_cylinder = vehicles_non_hybrid_4.groupby(['year']).make.nunique()
...: fig = grouped_by_year_4_cylinder.plot()
...: fig.set_xlabel('Year')
...: fig.set_ylabel('Number of 4-Cylinder Makes')
...: print fig
请注意,在尝试绘制序列对象时,我们已从ggplot
切换到matplotlib
。 在 Python 中,用随机的import
语句乱七八糟的代码被认为是不好的形式。 因此,我们将import
语句移至 IPython Notebook 的顶部。 请记住,如果重新启动 IPython Notebook,请确保首先执行 Notebook 顶部的import
语句,以便其余代码可以运行。 请参考下图:
我们可以在上图中看到,自 1980 年以来,可用 4 缸发动机的品牌数量有所下降。但是,请注意,该图可能会产生误导作用,因为我们不知道可用总数 每年的收入在同一时间段内发生了变化。
- 我们可以看看这项研究每年提供的品牌吗? 首先,我们希望找到本研究每年都使用的 4 缸发动机汽车制造清单。 为此,我们首先计算每个型号年份的唯一品牌清单:
In [36]: grouped_by_year_4_cylinder = vehicles_non_hybrid_4.groupby(['year'])
...: from functools import reduce
...:
In [37]: unique_makes = []
...: for name, group in grouped_by_year_4_cylinder:
...: unique_makes.append(set(pd.unique(group['make'])))
...:
...: unique_makes = reduce(set.intersection, unique_makes)
...: print(unique_makes)
...:
{'Nissan', 'Chevrolet', 'Chrysler', 'Toyota', 'Honda', 'Dodge', 'Volkswagen', 'Jeep', 'Ford', 'Subaru', 'Mitsubishi', 'Mazda'}
我们发现,在此期间,每年只有 12 家制造商生产 4 缸汽车。
- 现在,我们问这些汽车制造商的模型在燃油效率方面的表现如何。 为此,我们决定走很长的路。 首先,我们创建一个空列表,最终将由布尔值填充。 然后,我们使用
iterrows
生成器遍历数据帧中的每一行,该生成器同时生成索引和行(我们选择对循环中的索引不执行任何操作)。 然后,我们测试当前行的构造是否在先前计算的unique_makes
集合中,并将布尔值附加到Boolean_mask
集合中。 循环完成后,我们将数据帧子集化为仅包含unique_makes
集合中带有 make 的行:
In [38]: boolean_mask = []
...: for index, row in vehicles_non_hybrid_4.iterrows():
...: make = row['make']
...: boolean_mask.append(make in unique_makes)
...:
...: df_common_makes = vehicles_non_hybrid_4[boolean_mask]
...:
- 接下来,我们必须按
year
和make
对数据帧进行分组,然后计算每次分组的平均值:
In [39]: df_common_makes_grouped = df_common_makes.groupby(['year',
...: 'make']).agg(np.mean).reset_index()
- 最后,根据
ggplot
,我们使用多面图显示了我们的工作结果:
In [40]: ggplot(aes(x='year', y='comb08'), data =
...: df_common_makes_grouped) + geom_line() + facet_wrap('make')
请参考下图:
在配方本身中详细说明了很多步骤,因此我们不会在这里浪费要点。 但是,有些事情当然值得指出。 首先,您是否在最后一个“拆分-应用-合并”步骤的末尾注意到了.reset_index()
调用? 以下命令显示了这一点:
df_common_makes.groupby(['year','make']).agg(np.mean).reset_index()
当执行groupby
步骤时,pandas 返回将行分组为索引而不是简单数据列的键。 在使用多个分组键的情况下,pandas 返回一个多级索引。 不幸的是,这不适用于ggplot
,我们需要告诉[pandas]使用.reset_index()
方法将这些索引视为数据列。
其次,我们使用for
循环,在数据帧的行上进行迭代,以确定品牌是否在感兴趣的unique_makes
列表中。 如果这段代码看起来有些冗长,那是因为。 Pandas 内置了大量功能,我们可以使用.isin()
方法执行行选择,如以下命令所示:
test =
vehicles_non_hybrid_4[vehicles_non_hybrid_4['make'].isin(unique_makes
)]
如果您希望对数据框执行数据分析步骤,则很可能已经构建了一种方法来执行此操作。
从性能的角度来看,通过将循环附加到列表,我们在for
循环中犯了一个非常明显的错误,因此每次迭代都增加了列表的大小。 为了加快速度,我们应该将列表预先分配为填充有错误布尔值的数据框中的行数大小。 预分配数组是一种非常通用的技术,可以加速大多数语言中的代码。 此技巧在matplotlib
中特别强大。
为了执行我们用来识别每年数据中存在的所有制造商的集合交集,我们需要sets
Python 程序包,该程序包是 Python 发行版的一部分。 同样,我们将这个 import 语句移到脚本的顶部,以遵循最佳的 Python 做法。