《数据科学实战手册(R+Python)》一第2章 汽车数据的可视化分析(R)

简介:

本节书摘来自异步社区《数据科学实战手册(R+Python)》一书中的第2章,第2.1节,作者【美】Tony Ojeda(托尼·奥杰德) , Sean Patrick Murphy(肖恩·派特里克·墨菲) , Benjamin Bengfort(本杰明·班福特) , Abhijit Dasgupta(阿布吉特·达斯古普塔),更多章节内容可以访问云栖社区“异步社区”公众号查看

第2章 汽车数据的可视化分析(R)

数据科学实战手册(R+Python)
本章涵盖如下内容。

  • 获取汽车燃料效率数据
  • 为了你的第一个项目准备好R
  • 将汽车燃料效率数据导入R
  • 探索和描述燃料效率数据
  • 进一步分析汽车燃料效率数据
  • 研究汽车的产量以及车型

简介
本书介绍的第一个项目是分析汽车燃料经济数据。我们首先用R对该数据集进行分析。R常常被称为数据科学通用语言,因为它是目前最流行的统计和数据分析语言。在本书前半部分的各个章节中,你将会看到R在数据处理、建模、可视化方面的过人之处,并开发一些有用的脚本,来完成你的分析工作。

本章的“食谱”大致涵盖数据科学管道中的如下步骤。

  • 获取
  • 探索和理解
  • 改写、整合以及处理
  • 分析和建模
  • 交流和实施

从流程上讲,数据科学管道是数据科学的骨架。为了精通数据科学,你需要通过应用多种工具和方法来实现这些流程,从而获取经验。这样,在有特定的数据集需要分析时,你将会知道哪些方法和工具是适合的。

本章的目的是引导你完成对于汽车燃料效率数据的分析。你将学到数据科学管道的这些步骤。未来其他项目将要进行数据分析时,你就可以应用这些步骤。将本章视为热身吧,更多的挑战将在后续章节展开。

获取汽车燃料效率数据
每一个数据科学的项目都是从数据开始的,本章也并不例外。对于我们的例子而言,我们需要深入研究一个包含燃料效率的数据集,在这个数据集中燃料效率用每英里消耗的燃料加仑数(MPG)来度量。数据集中包含自 1984 年开始记录的美国汽车的相关测量数据。这份数据来自美国能源部和美国环保局。数据集中除了包含燃料效率,还有一些汽车的其他特征。我们可以使用这些特征来对数据做分组汇总统计,从而看到哪个组的汽车燃料利用效率更高,并且我们可以看到这些数据是如何随着时间变化的。这份数据集的最新版本可以在如下地址获取:http://www.fueleconomy.gov/feg/epadata/vehicles.csv.zip。关于这份数据集中各个变量的信息可以在如下地址得到:http://www.fueleconomy. gov/feg/ws/ index.shtml#vehicle。最新的数据是2013年12月4日更新的,本书中使用的数据是2013年12月8日从网站上下载的。


1f328fa8dd5db8b5b4218c6c6a065dda012f6ec8

我们推荐你使用随本书代码一起提供的数据集,这样可以保证代码产出的结果和本书展示的一致。
准备工作
为了完成本章的内容,首先你需要一台可以连接互联网的电脑,电脑上还需要安装一个文本编辑器。

处理流程
按照如下步骤进行,获取本章后续部分所需的数据。

1.在http://www.fueleconomy.gov/feg/epadata/vehicles.csv.zip网站下载数据。

2.在电脑上用解压工具对vehicles.csv文件进行解压,然后把数据移动到你代码的目录下。

3.花一点时间,用微软的Excel或者Google的Spreadsheet或者某个简单的文本编辑器打开解压后的vehicles.csv文件。逗号分隔(csv)的文件是一种很容易处理的文件,这种文件可以用一些很基础而且免费的工具展示和处理。文件打开后,你可以先看看这份即将要处理的数据,找找感觉。

4.浏览网站http://www.fueleconomy.gov/feg/ws/index.shtml#vehicle

5.选择vehicle下面数据描述的部分,并将它们复制粘贴到你本地的一个文本文件中。不要包含emissions部分。把这份文件存在你的工作目录下,命名为varlabels.txt。这份文件的头5行如下。

   atvtype - type of alternative fuel or advanced technology
   vehicle
   barrels08 - annual petroleum consumption in barrels for
   fuelType1 (1)
   barrelsA08 - annual petroleum consumption in barrels for
   fuelType2 (1)
   charge120 - time to charge an electric vehicle in hours at
   120 V
   charge240 - time to charge an electric vehicle in hours at
   240 V


e7919665c4bc07f3463b71cd7001df00b557a33a

为了你的方便,这份文件已经包含在本章的代码库中。
工作原理
一开始的这些部分,没有太多需要解释的。但是要注意的是,我们这里开始的相对比较简单。在一些数据科学项目中,你是无法如此容易地获取和观察数据的。

为了你的第一个项目准备好R
为完成后续的工作,你需要在电脑上安装一个R的环境(基础的R或者RStudio都可以,但是我们强烈建议你安装强大而免费的RStudio),并且已经在电脑里保存了汽车燃料效率的数据集。请确保你的电脑里已经包含上述本次分析所需要的所有内容。

准备工作
我们需要连接到互联网,而且我们假设你的平台上已经安装了RStudio,安装的过程在前一章已经提供了。

处理流程
如果你正在使用RStudio,只需执行以下三步。

1.打开RStudio。

2.在R的命令窗口,安装这个项目需要的R包:install.packages("plyr") install.packages("ggplot2")install.packages("reshape2")

3.载入这些R包:library(plyr) library(ggplot2) library(reshape2)

工作原理
R的优点在于其社区以及该社区围绕R语言所开发的各种功能的包,这些包对于R社区的所有成员都是可以获取的。目前有4 000多个包,这些包可以令你的数据分析任务变得容易很多。

Hadley Wickham博士是R社区中非常有影响力的成员。他已经产出了一大批优质并且经常被使用的R包。在本章中,你会初步使用他的两大杀器:plyr以及ggplot2。此外,你还会使用第三个包:reshape2。plyr用于对数据集进行分裂——合并的分析,本章稍后会解释它是怎么工作的。ggplot2会使复杂数据的可视化变得更容易。

参考资料

将汽车燃料效率数据导入R
依据前面的指导,你已经安装好了所需的所有原料,现在你可以把数据导入R中,并开始做一些初步的分析,从而对数据的情况有一个初步的感觉。

准备工作
本章中用到的大多数分析是累加的,也就是说前一段分析的方法或者结论会用在后续的分析中。因此,如果你已经完成了前面的步骤,则你已经有了接下来分析所需要的所有东西。

处理流程
以下几步会引导你将数据导入R中。

1.首先,将工作路径设定到本地保存了vehicles.csv的路径下:setwd("path")


301a57cc6de3ff214df4ed169cd0b67ca741d86b

将路径替换为你电脑中对应的实际文件路径。
2.我们可以直接从zip文件中载入数据,只要你知道zip中压缩的文件名:vehicles <- read.csv(unz("vehicles.csv.zip", "vehicles.csv"), stringsAs Factors = F)

3.为了检查数据是否已经载入,我们可以在R中展现头几行,使用如下代码:head(vehicles)你将会看到数据集的头几行打印在你的屏幕上。


301a57cc6de3ff214df4ed169cd0b67ca741d86b

我们也可以使用tail。这个命令将打印数据集的末尾几行,而不是头几行。
4.labels命令给数据集vehicles.csv文件夹的变量贴上标签。注意,labels是一个R的函数。我们可以看到这个数据集的变量名在文件中,并且使用-分割。所以我们读入这个文件时也要设定分隔符为-:labels <- read.table("varlabels.txt", sep = "-", header = FASLE)## Error: line 11 did not have 2 elements

5.居然有错!我们仔细检查一下报错信息,发现文件的第11行包含2个符号。而其他行则有3列。我们需要修改文件读取的方式,从而忽略连字符的影响:labels <- do.call(rbind, strsplit(readLines("varlabels.txt"), "-"))

6.我们用head命令来看数据读入是否奏效。

   head(labels)
        [,1]         [,2]
   [1,] "atvtype" "type of alternative fuel or advanced
   technology vehicle"
   [2,] "barrels08" "annual petroleum consumption in barrels for
   fuelType1 (1)"
   [3,] "barrelsA08" "annual petroleum consumption in barrels for
   fuelType2 (1)"
   [4,] "charge120" "time to charge an electric vehicle in hours
   at 120 V"
   [5,] "charge240" "time to charge an electric vehicle in hours
   at 240 V"

工作原理
我们解析一下上面第五步相对复杂的语句。

首先,我们一行一行地读取文件里的内容。

x <- readLines("varlabels.txt")

每一行都是用字符“ -”分割。空格是非常重要的,这样我们就不会分割带有连字符的词语了(比如11行)。这使得每一行分裂为两部分,形成一个字符串向量,这些向量存在一个列表中。

y <- strsplit(x, " - ")

现在,我们把这些向量堆在一起,形成一个字符串矩阵,第一列是变量名,第二列是变量的描述。

labels <- do.call(rbind,y)

更多内容
聪明的读者可能已经注意到read.csv函数的调用中包含参数stringAsFactors = F。R默认会将字符串转为因子。因子是R中定性变量的称呼,可以被认为对数据的标注或者标签。在R的内部,因子是存为整数的,每个整数映射到因子的一个水平。这项技术可以使得老版本R的存储成本降低。

一般而言,定性变量是没有顺序的。下面的代码,我们快速地造出一个例子,将一个有4个值的字符串类型的变量转变成因子,然后进行比较。

colors <- c('green', 'red', 'yellow', 'blue')
colors_factors <- factor(colors)
colors_factors
[1] green red yellow blue
Levels: blue green red yellow
colors_factors[1] > colors_factors[2]
[1] NA
Warning message:
In Ops.factor(colors_factors[1], colors_factors[2]) :
>not meaningful for factors

然而,也存在有顺序的分类变量,在统计学中也被称为序数。序数也是一种分类变量,除了其是有序的之外,和分类变量没有什么区别。分类变量的一个值可以说是大于另外一个值的,但是大多少就难以测量了。

此外,当我们将数据导入R时,我们经常会遇到某一列明明是数值型的,但是其中却夹杂着非数值的情况。在这种情况下,R可能会将这一列当作factor来导入,而导入之后的数据可能已经不是数据科学家原想的那样了。因子和字符之间的转变很平常,但是因子和数值之间的转变则非常诡异。

R可以导入很多类型的数据。在本章中,我们处理的是CSV文件,但是我们也可以直接导入微软Excel文件。CSV文件之所以被选择,是因为它对于任何操作系统都是可用的,而且更便携。此外,R还可以从其他流行的统计软件中导入数据,比如SPSS、Stata和SAS。

参考资料

探索和描述燃料效率数据
现在我们已经在R中导入了汽车燃料效率的数据集,还学习了一点数据导入的细微差异。接下来,将要对数据集进行一些初步分析。这种分析的目的是对数据进行探索,掌握一些R的基本命令。

准备工作
如果我们已经完成了前面所有的步骤,那么现在我们应该已经有了所有需要的东西。那么继续吧。

处理流程
接下来的步骤将引导我们进行分析,我们会对数据集计算一些基本的参数。

1.首先,我们看看数据集有多少行。

   nrow(vehicles) 
   ## 34287

2.接下来,我们看看共有多少变量。

   ncol(vehicles) 
   ## 74

3.现在,我们要看看数据集中各列的含义。我们使用name函数:

   > names(vehicles)

结果如下。


78a4d581d0699325dd8ba3ada09ce3c4b11f6a77

幸运的是,很多变量的名字本身就代表了变量的含义。请记住,这些变量更精确的含义描述可以在这里查到:http://www.fueleconomy.gov/feg/ws/index.shtml#vehicle

4.我们看看数据集中包含几年的数据,只需要计算year这一列的不同取值的向量,然后计算这个向量的长度即可。

   length(unique(vehicles[, "year"]))
   ## 31

5.现在,我们可以看到数据集中保存1的数据起始年份,使用max和min函数。

   first_year <- min(vehicles[, "year"])
   ## 1984
   last_year <- max(vehicles[, "year"])
   ## 2014


015b60f62e92dc6336f28b4403b04061f668dead

注意取决于你下载数据集的时间,last_year的取值可能比2014更大。
6.此外,因为我们可能多次会使用year变量,因此,让我们保证我们有每一年的数据。从1984到2014的列表应该包含31个不同的值。为了测试这一点,我们使用如下命令。

   > length(unique(vehicles$year))
   [1] 31

7.接下来,让我们找出燃料类型都有哪些。

   table(vehicles$fuelType1)
   ##     Diesel      Electricity Midgrade Gasoline
   Natural Gas
   ##       1025        56       41
   57
   ##  Premium Gasoline  Regular Gasoline
   ##                  8521    24587

由此可以看到,大多数数据集中的汽车都使用普通的汽油,第二普遍的燃油类型是较高端的汽油。

8.让我们探索一下这些汽车使用的传动方式。首先,我们把缺失值用NA填补。

   vehicles$trany[vehicles$trany == ""] <- NA

9.现在,trany这一列是文本,我们仅仅关注车辆的传动方式是自动还是手动。因此,我们使用substr函数提取trany的值的前四个字符,然后确定这个是自动还是手动。我们生成一个新的变量:trany2。这个变量取值要么是手动,要么是自动。

   vehicles$trany2 <- ifelse(substr(vehicles$trany, 1, 4) ==
   "Auto", "Auto", "Manual")

10.最后,我们将这个新变量变成因子,然后使用table函数来看不同类型传动方式的记录各有多少。

    vehicles$trany <- as.factor(vehicles$trany)
    table(vehicles$trany2)
    ##    Auto Manual
    ##   22451  11825

我们可以看到自动挡的汽车数量大概是手动挡的两倍。

工作原理
数据框是R所提供的非常有利的数据类型,本章中我会好好介绍它。数据框允许我们将不同类型的数据(数值型、字符型、逻辑型、因子等)组织在一起。比如用户信息,使用数据框,我们每一行可以保存用户的姓名(字符型)、年龄(数值型)、性别(因子),还有标签标注是否为当前的消费者(布尔型)。如果你很熟悉关系型数据库,那么你应该知道,这和数据库中的表很像。

更多地,我们学习了如何快速地浏览R中的数据。最明显的是,我们使用table函数来观察fuelType1这个变量不同取值所对应的记录数。这个函数其实还有很多其他用途,比如计算交叉列联表等。

with(vehicles, table(sCharger, year))

上面的代码给出的结果如下。


5b1a1a173f87642d34941a50af245deaba371d64

这里,我们看到带或者不带增压充电器的车的数量以及年份的关系(我们由此看到增压充电器似乎最近几年比过去更流行了)。

此外,注意我们使用了with命令。这个命令告诉R使用vehicles数据集作为接下来命令的默认数据集。在这个例子中,接下来将用table函数。因此,我们可以直接用变量sCharger和year,而不必再使用美元符号和数据框名称组合来引用了。

更多内容
为了提供关于数据导入的一个有深意的故事,我们再仔细看看sCharger和tCharger。这两列表示这辆车是否包含涡轮增压机或者增压充电器。

我们先看看sCharger。我们看看这个变量的类型,以及都有哪些取值。

> class(vehicles$sCharger)
[1] "character"
> unique(vehicles$sCharger)
[1] ""  "S"

接下来,我们看看tCharger,我们期待结果一致。

> class(vehicles$tCharger)
[1] "logical"
> unique(vehicles$tCharger)
[1]   NA TRUE

然而,我们发现这两个看起来差不多的变量却是不同的数据类型。tCharger是一个逻辑型变量,也被称为布尔型变量。这种变量被用来记录真或假、是或否。而sCharger是字符型。这有时候是错误的。在这个例子中,因为我们可以,所以我们来检查一下原始数据。幸运的是,这个数据保存在一个csv文件中,我们可以使用文本编辑器打开读取它(我们推荐Windows的Notepad或者Unix系统的vi。当然,你可以用你自己喜欢的,比如文本编辑器之类)。当我们打开文件,可以看到sCharger和tCharger这两列所对应的行要么是空,要么是S或者T。

因此,R读取tCharger这一列的T时把它当作了布尔型变量的值TRUE。这不是一个致命的错误,可能并不一定影响分析。然而,这样的被忽略的程序漏洞可能在随后的分析中带来问题并导致大量的重复劳动。

进一步分析汽车燃料效率数据
目前为止,我们已经在R中导入了数据,并且通过一些重要的基本统计量对数据集有了一个初步的基本理解,比如数据集中包含哪些值,以及哪些特征会频繁地出现。在这里,我们将通过了解油耗参数与时间和其他数据点之间存在的关系来继续探索这个数据集。

准备工作
如果之前的步骤都已经进行了,那么你已经有了接下来所需要的一切。

处理流程
接下来将使用plyr和ggplot2来探索数据集。

1.首先,我们看看平均MPG是否随着时间有一个趋势上的变化。为此,我们使用plyr包的ddply函数来操作vehicles数据集,按年份整合,然后对每个组计算highway、city和combine的燃油效率。这个结果将赋值给一个新的数据框:mpgByYr。注意,这是我们的第一个分割-应用的例子。我们将数据框按照年份分割,然后使用均值函数到每一个变量上,再将组合的结果存在一个新的数据框中。

   mpgByYr <- ddply(vehicles, ~year, summarise, avgMPG =
   mean(comb08), avgHghy = mean(highway08), avgCity =
   mean(city08))

2.为了对新的数据框得到一个更好的理解,我们将它传入ggplot函数中,用散点图绘制avgMPG和year之间的关系。此外,我们还会标明我们需要的坐标轴的命名、图的标题,一级加上一个平滑的条件均值,geom_smooth()在图片上增加一个阴影的区域。

   ggplot(mpgByYr, aes(year, avgMPG)) + geom_point() +
   geom_smooth() + xlab("Year") + ylab("Average MPG") +
   ggtitle("All cars")
   ## geom_smooth: method="auto" and size of largest group is
   <1000, so using
   ## loess. Use 'method = x' to change the smoothing method.

上述代码会给你一个如下的图像。


d92ca7a4bd09d88372b1613d08d0fabd6e154f30

3.基于这个可视化结果,我们或许能得出一个结论:近年来销售的汽车的燃油经济性有了显著的增长。然而,如下数据显示近年来混合动力和非燃油汽车的销量在增加,因此,上述结论还是有一定的误导性。

   table(vehicles$fuelType1)
   ##     Diesel        Electricity Midgrade Gasoline
   Natural Gas
   ##       1025        56                   41
   57
   ## Premium Gasoline Regular Gasoline
   ##      8521       24587

4.我们只看燃油汽车,虽然非燃油汽车数量并不多。为了做这个,我们使用subset函数来生成一个新的数据框:gasCars。这个数据框只包含fuelType1的取值为如下取值的记录。

   gasCars <- subset(vehicles, fuelType1 %in% c("Regular
   Gasoline", "Premium Gasoline", "Midgrade Gasoline") &
   fuelType2 == "" & atvType != "Hybrid")
   mpgByYr_Gas <- ddply(gasCars, ~year, summarise, avgMPG =
   mean(comb08))
   ggplot(mpgByYr_Gas, aes(year, avgMPG)) + geom_point() +
   geom_smooth() + xlab("Year") + ylab("Average MPG") +
   ggtitle("Gasoline cars")
   ## geom_smooth: method="auto" and size of largest group is
   <1000, so using
   ## loess. Use 'method = x' to change the smoothing method.


75d21ca61535cecb77af50d23277688bddfdf25f

5.是否近年来大马力的车产量降低了?如果是这样,就可以解释这种增长。首先,我们要明确是否大功率的汽车燃油效率更低。我们注意变量displ,表示引擎的排量,单位为升。现在它还是字符串类型,我们需要把它变为数值型。
   typeof(gasCars$displ)
   ## "character"
   gasCars$displ <- as.numeric(gasCars$displ)
   ggplot(gasCars, aes(displ, comb08)) + geom_point() +
   geom_smooth()

   ## geom_smooth: method="auto" and size of largest group is
   >=1000, so using
   ## gam with formula: y ~ s(x, bs = "cs"). Use 'method = x' to
   change the
   ## smoothing method.
   ## Warning: Removed 2 rows containing missing values
   (stat_smooth).
   ## Warning: Removed 2 rows containing missing values
   (geom_point).


9946ba7b8e7d6b2d614e384a5b0ae49abe337cb3

散点图让我们看到,引擎排量和燃油效率变量之间确实是负相关的,也就是说,小的车燃油效率会更高。

6.现在,让我们看看是否近年生产了更多的小车,这样就可以解释燃油效率最近有大幅的提升了。

   avgCarSize <- ddply(gasCars, ~year, summarise, avgDispl =
   mean(displ))
   ggplot(avgCarSize, aes(year, avgDispl)) + geom_point() +
   geom_smooth() + xlab("Year") + ylab("Average engine
   displacement (l)")

   ## geom_smooth: method="auto" and size of largest group is
   <1000, so using
   ## loess. Use 'method = x' to change the smoothing method.
   ## Warning: Removed 1 rows containing missing values
   (stat_smooth).
   ## Warning: Removed 1 rows containing missing values
   (geom_point).


6b083abd1b2f20050a6d2fadcc802da162391e9e

7.从前图,我们可以看到平均的引擎排量在2008年之后有一个显著的下降。为了更好地看到这可能会影响到燃油效率的提升,我们逐年绘制出MPG和排量之间的关系。我们使用ddply函数,生成一个新的数据框byYear,包含每年的平均燃油效率和平均引擎排量。

   byYear <- ddply(gasCars, ~year, summarise, avgMPG =
   mean(comb08), avgDispl = mean(displ))
   > head(byYear)
     year   avgMPG avgDispl
   1 1984 19.12162 3.068449
   2 1985 19.39469       NA
   3 1986 19.32046 3.126514
   4 1987 19.16457 3.096474
   5 1988 19.36761 3.113558
   6 1989 19.14196 3.133393

8.head函数向我们展示了生成的新的数据框,这个数据框包含3列:year、avgMPG以及avgDispl。我们将要使用ggplot2包中分面的功能,在同一张图但是不同的面上来逐年显示平均油耗以及平均排量之间的关系。我们必须分解这个数据框,把一个宽的数据框变成一个长的数据框。

   byYear2 = melt(byYear, id = "year")
   levels(byYear2$variable) <- c("Average MPG", "Avg engine
   displacement")

   head(byYear2)
     year    variable    value
   1 1984 Average MPG 19.12162
   2 1985 Average MPG 19.39469
   3 1986 Average MPG 19.32046
   4 1987 Average MPG 19.16457
   5 1988 Average MPG 19.36761
   6 1989 Average MPG 19.14196

如果我们使用nrow函数,我们可以看到数据框byYear2有62行,而byYear只有31行。byYear中的两列(avgMPG和avgDispl)现在溶解在byYear2的一列中(value)。

ggplot(byYear2, aes(year, value)) + geom_point() +
geom_smooth() + facet_wrap(~variable, ncol = 1, scales =
"free_y") + xlab("Year") + ylab("")
## geom_smooth: method="auto" and size of largest group is
<1000, so using
## loess. Use 'method = x' to change the smoothing method.
## geom_smooth: method="auto" and size of largest group is
<1000, so using
## loess. Use 'method = x' to change the smoothing method.
## Warning: Removed 1 rows containing missing values
(stat_smooth).
## Warning: Removed 1 rows containing missing values
(geom_point).


41090ee7f0454be64dfa7b08a944ce2f3308ed40

从这个图中,我们看到:

引擎的大小在2008年之前总体是在增加,尤其是2006年到2008年大车的引擎有一个明显的增大。
从2009起,车辆的平均大小有了显著的下降,这在一定程度上解释了燃油效率的显著提升。
直到2005年,车的平均大小一致在提升,但是燃油效率基本上是一个常数。这意味着在这些年,引擎的效率一直在提升。
2006年到2008年比较有趣。虽然平均的引擎大小有一个突然的增加,但是MPG和前几年差不多。看起来我们要对这个问题进行更多的研究。
9.我们将这一趋势放到小排量的引擎上,来看看是否自动挡或者手动挡传动比四缸发动的油耗更加高效,以及油耗是如何随时间变化的。

   gasCars4 <- subset(gasCars, cylinders == "4")

   ggplot(gasCars4, aes(factor(year), comb08)) + geom_boxplot()
   + facet_wrap(~trany2, ncol = 1) + theme(axis.text.x = element_
   text(angle = 45)) + labs(x = "Year", y = "MPG")


53cfbfd51f72729f14c76692004385ca78c0a6b3

这一次,ggplot2生成了一个箱线图。这个图帮助展示了每一年值的分布情况(而不只展示像均值这样的单一数值)。

10.接下来,我们来看看每一年手动挡的车的占比情况。

    ggplot(gasCars4, aes(factor(year), fill = factor(trany2))) +
    geom_bar(position = "fill") + labs(x = "Year", y = "Proportion
    of cars", fill = "Transmission") + theme(axis.text.x =
    element_text(angle = 45)) + geom_hline(yintercept = 0.5,
    linetype = 2)


740a4a753aa2577158629d3c7d184bbb9ae64ad6

在第9步,我们看到手动传动方式貌似比自动传动方式更有效,并且平均来看,从2008年开始,二者呈现出同样的增长。然而,这里有个很奇怪的事情。最近几年,自动传动的车中有一些非常高效(MPG小于40)的车,而同时,手动传动的车中几乎没有看到同样高效的。而在早几年,这个现象是反过来的。每一年手动挡的车的占比是否有变化?答案是肯定的。这些非常高效的车到底是什么?在下一节,我们将从数据集中探索这些车的制造商以及车的模式。

工作原理
在这一步中,我们使用了两个非常重要的R包:plyr以及ggplot2,带读者对数据进行了比较详尽的分析。传统的软件开发已经设计了一些通用框架的分析模块,少部分模块正在集成进入数据科学的领域。这其中特别值得注意的是Hadley Wickham博士的分割-应用-合并模式。使用这种策略,我们可以对一个问题进行切割,变成多个小的问题,这些小问题往往更容易处理。当我们完成这种数据整理之后,我们就可以对新的分组的数据进行其他的分析和操作,最后将分析结果整合在一个新的数据结构中。正如你在这一步中所看到的,我们重复地使用这种分割-应用-整合的策略,从不同的角度审视数据。

除了plyr,在这一步我们也倚重ggplot2包。这个包也值得我们专门介绍。目前网上已经有很多针对ggplot2包的很不错的入门介绍,因此我们在这里不做过多介绍。重要的是,你需要理解ggplot2为什么能够让你生成一些非常复杂的统计的可视化,而生成的代码是如此精简。

ggplot2是基于Wilkinson、Anand和Grossman所设计的绘图语法的R的开源版本。这种语法将统计数据可视化进行解构,将数据可视化的过程解构为不同的模块,从而让我们能够更好地理解统计图形是如何绘制的。基于ggplot2,Hadley Wickham继承了这些想法,并且实现了一种分层绘制的方法。这种方法可以让用户非常便捷地使用不同的模块的组合来绘制复杂的可视化图形。比如本章我们绘制的第一幅图,我们绘制了每一年所有车型的平均燃油效率。

ggplot(mpgByYr, aes(year, avgMPG)) + geom_point() + geom_smooth() +
xlab("Year") + ylab("Average MPG") + ggtitle("All cars")

为了生成这个图形,首先告诉ggplot我们所要使用的数据框为mpgByYr,然后告诉ggplot2哪个变量会映射在图像中进行绘制。在本例中,aes(year, avgMPG)明确指定了年份被映射到横轴,平均MPG将映射到纵轴。Geompoint()告诉这个包,我们是通过点的方式绘制图形,而geomsmooth()则在图中加上一个阴影区域,来展示平滑的均值,并且加上一个置信度为0.95的置信区间。最后,xlab()和ylab()以及ggtitle()函数在这幅图中加上标签、标题等。这样,我们就生成了一幅比较复杂并且品质相当不错、可以直接出版的图形,而所使用的代码只是寥寥数行。而ggplot2能做的远不止这些。

同样,你也需要注意ggplot2这种绘图语法并没有告诉你如何最佳地展现你的数据,这种语法只是提供了一种便捷的展现数据的工具。如果你对可视化感性,还想要更多的建议,那么我们强烈推荐你去看Edward Tufte的工作。他著有很多数据可视化方面的书籍,比如《定量信息的可视化呈现》(The Visual Display of Quantitative Information, Graphics Press USA)。此外,ggplot2并不能做动态的数据可视化。

参考资料
分割-应用-合并策略的相关论文:http://www.jstatsoft.org/v40/i01/paper
参考绘图的语法一书(The Grammar of Graphics,Leland Wilkinson, Springer Science&Business Media)
ggplot2包的帮助文档:http://cran.r-project. org/web/packages/ggplot2/ggplot2.pdf
分层绘图的相关文献:http://vita.had.co.nz/ papers/layered-grammar.pdf
研究汽车的产量以及车型
基于上一节遗留的问题,我们继续对数据集进行更深入的分析。

准备工作
如果你已经完成了前述的所有工作,那么你已经准备好了。

处理流程
接下来我们将研究汽车的生产商和型号是如何随时间改变的。

1.让我们看看生产商和车型随时间的变化如何影响燃油的效率。首先,我们看看美国这些年不同的车型和生产商出现的频次,然后将注意力放在四缸发动的车上。

   carsMake <- ddply(gasCars4, ~year, summarise, numberOfMakes =
   length(unique(make)))

   ggplot(carsMake, aes(year, numberOfMakes)) + geom_point() +
   labs(x = "Year", y = "Number of available makes") + ggtitle("Four
   cylinder cars")

我们看到生产商数量有一个明显的下降,而在最近几年又有小幅上升。


894334141b17c4b68da27c1587dddbba2dede9c7

2.我们可以看看每一年的生产商吗?我们发现,在这段时间内,每年只有12个制造四缸发动机汽车的制造商。

   uniqMakes <- dlply(gasCars4, ~year, function(x)
   unique(x$make))
   commonMakes <- Reduce(intersect, uniqMakes)
   commonMakes
   ##   [1] "Ford"  "Honda"   "Toyota"  "Volkswagen"
   "Chevrolet"
   ##   [6] "Chrysler" "Nissan"  "Dodge"   "Mazda"
   "Mitsubishi"
   ##  [11] "Subaru"  "Jeep"

3.这些制造商每年生产出来的汽车的燃油效率如何?我们看到大多数制造商的燃油效率在逐年提升,有一些制造商在最近5年在燃油效率上有一个飞速的提升。

   carsCommonMakes4 <- subset(gasCars4, make %in% commonMakes)
   avgMPG_commonMakes <- ddply(carsCommonMakes4, ~year + make,
   summarise, avgMPG = mean(comb08))

   ggplot(avgMPG_commonMakes, aes(year, avgMPG)) + geom_line() +
   facet_wrap(~make, nrow = 3)


fa586abbc7a792ce855b3530d84ddecb4bb86e4c

工作原理
在第二步,我们看到一个很有趣的魔法,只需要几行代码就做了很多的工作。这正是R在解决问题上的优美,这种优美表现在对复杂问题的精练表述上。然而,如果你不熟悉R强大的库,那么你可能会觉得R代码太过难以理解。

代码的第一行,我们使用dlply(注意,不是ddply)来操作gasCars4数据框,按照年来分割数据集,然后对每一块的make变量应用一个函数。对每一年,计算出独立的制造商列表,然后dlply返回每一年的列表。dlply输入一个数据框(data frame)返回一个列表(list),而ddply输入一个数据框(data frame)返回一个数据框(data frame)。

uniqMakes <- dlply(gasCars4, ~year, function(x) unique(x$make))
commonMakes <- Reduce(intersect, uniqMakes)
commonMakes

第二行代码更加有趣。使用Reduce函数来做更高阶的排序。这个Reduce函数和map reduce编程中的reduce过程的想法是一样的,而map reduce是Google提出的基于Hadoop的编程模式。从某种角度来讲,R是一个函数式编程语言,其核心包含一些高阶的函数。所谓高阶函数,是指其输入是其他的函数。在这行代码中,我们将intersect函数作为输入赋值给Reduce函数,这个函数将会对数据集uniqMakes的每一个元素求交集,而这个数据集是我们之前已经生成的每一年不同制造商的列表。最终,结果放在一个新的列表中,这个列表展现了每一年都会出现的制造商。

虽然代码只有短短的两行,但是解释它是如何工作的却花了两个段落。

更多内容
本章最后一幅图展现了ggplot2非凡的分面绘图能力。代码中加入+ facet_wrap (~make, nrow = 3)告诉ggplot2我们想要对坐标系进行分割,将每一个制造商的数据绘制在一个子图上,展现在不同的行。这种数据可视化的技术很强大,使得我们可以将制造商本身作为变量来看不同制造商之间的模式差别。

在我们的第一个数据科学的项目里,我们保证事情是简单的。这个数据集本身很小——只有12 MB左右,易于存储,个人笔记本电脑就可以处理。我们使用R导入数据集,检查了数据集的一部分(而不是全部),对数据进行了汇总。之后我们对数据进行了探索,设置了很多问题,然后用两个关键的包plyr以及ggplot2来处理数据,对数据进行可视化。在这个数据科学项目中,最终的结果就是给出了我们对数据的总结,并用ggplot2对数据进行了可视化。

参考资料
参考John Myles White 写得非常棒的 R 中高阶函数的介绍文档:www.johnmyleswhite.com/notebook/2010/09/23/higher-orderfunctions- in-r/

相关文章
|
19天前
|
数据采集 缓存 定位技术
网络延迟对Python爬虫速度的影响分析
网络延迟对Python爬虫速度的影响分析
|
18天前
|
机器学习/深度学习 数据采集 数据可视化
Python在数据科学中的应用:从入门到实践
本文旨在为读者提供一个Python在数据科学领域应用的全面概览。我们将从Python的基础语法开始,逐步深入到数据处理、分析和可视化的高级技术。文章不仅涵盖了Python中常用的数据科学库,如NumPy、Pandas和Matplotlib,还探讨了机器学习库Scikit-learn的使用。通过实际案例分析,本文将展示如何利用Python进行数据清洗、特征工程、模型训练和结果评估。此外,我们还将探讨Python在大数据处理中的应用,以及如何通过集成学习和深度学习技术来提升数据分析的准确性和效率。
|
19天前
|
机器学习/深度学习 数据可视化 数据处理
Python数据科学:从基础到实战
Python数据科学:从基础到实战
26 1
|
21天前
|
数据采集 存储 JSON
Python爬虫开发中的分析与方案制定
Python爬虫开发中的分析与方案制定
|
20天前
|
图形学 Python
SciPy 空间数据2
凸包(Convex Hull)是计算几何中的概念,指包含给定点集的所有凸集的交集。可以通过 `ConvexHull()` 方法创建凸包。示例代码展示了如何使用 `scipy` 库和 `matplotlib` 绘制给定点集的凸包。
26 1
|
21天前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
|
22天前
|
数据采集 Web App开发 iOS开发
如何使用 Python 语言的正则表达式进行网页数据的爬取?
使用 Python 进行网页数据爬取的步骤包括:1. 安装必要库(requests、re、bs4);2. 发送 HTTP 请求获取网页内容;3. 使用正则表达式提取数据;4. 数据清洗和处理;5. 循环遍历多个页面。通过这些步骤,可以高效地从网页中提取所需信息。
|
28天前
|
数据可视化 开发者 Python
Python GUI开发:Tkinter与PyQt的实战应用与对比分析
【10月更文挑战第26天】本文介绍了Python中两种常用的GUI工具包——Tkinter和PyQt。Tkinter内置于Python标准库,适合初学者快速上手,提供基本的GUI组件和方法。PyQt基于Qt库,功能强大且灵活,适用于创建复杂的GUI应用程序。通过实战示例和对比分析,帮助开发者选择合适的工具包以满足项目需求。
85 7
|
27天前
|
存储 数据处理 Python
Python科学计算:NumPy与SciPy的高效数据处理与分析
【10月更文挑战第27天】在科学计算和数据分析领域,Python凭借简洁的语法和强大的库支持广受欢迎。NumPy和SciPy作为Python科学计算的两大基石,提供了高效的数据处理和分析工具。NumPy的核心功能是N维数组对象(ndarray),支持高效的大型数据集操作;SciPy则在此基础上提供了线性代数、信号处理、优化和统计分析等多种科学计算工具。结合使用NumPy和SciPy,可以显著提升数据处理和分析的效率,使Python成为科学计算和数据分析的首选语言。
32 3
|
28天前
|
存储 机器学习/深度学习 算法
Python科学计算:NumPy与SciPy的高效数据处理与分析
【10月更文挑战第26天】NumPy和SciPy是Python科学计算领域的两大核心库。NumPy提供高效的多维数组对象和丰富的数学函数,而SciPy则在此基础上提供了更多高级的科学计算功能,如数值积分、优化和统计等。两者结合使Python在科学计算中具有极高的效率和广泛的应用。
46 2