
先貼一段第四章的話:
You now have some experience running R code. I didn’t give you many details, but you’ve obviously figured out the basics, or you would’ve thrown this book away in frustration! Frustration is natural when you start programming in R, because it is such a stickler for punctuation, and even one character out of place will cause it to complain. But while you should expect to be a little frustrated, take comfort in that it’s both typical and temporary: it happens to everyone, and the only way to get over it is to keep trying.
你現(xiàn)在對(duì)于運(yùn)行一段R的代碼有了一定的經(jīng)驗(yàn)。我并沒(méi)有給你太多的細(xì)節(jié),但是你可能已經(jīng)清楚地理解了基本內(nèi)容,或者你已經(jīng)失望地把這本書扔掉了。失望對(duì)于剛開始用R編程的你來(lái)說(shuō)太正常了,因?yàn)樗鼘?duì)于標(biāo)點(diǎn)符號(hào)太執(zhí)著了,即使一個(gè)不合適的字符也會(huì)讓它報(bào)錯(cuò)。但是當(dāng)你感到有一點(diǎn)失望時(shí),你也可以在以下這種典型而又暫時(shí)的情況中找到安慰:每個(gè)人都會(huì)遇到這樣的情況,而唯一打敗它的方法就是不斷地嘗試和練習(xí)。
第五章正式開始:
5.1 引言
可視化對(duì)于產(chǎn)生洞察是很重要的工具,但是恰好滿足格式要求的數(shù)據(jù)卻并不是很多。你可能會(huì)經(jīng)常需要?jiǎng)?chuàng)造一些新的變量或者摘要,或者你只是想要你只是想要對(duì)變量重命名,或者對(duì)觀測(cè)重新排序,以便使數(shù)據(jù)更容易處理。你會(huì)在這章中完全學(xué)到該怎么做(甚至更多),包括如何使用dplyr包轉(zhuǎn)換你的數(shù)據(jù),還有一個(gè)新的數(shù)據(jù)集——2013年飛離紐約市的航班統(tǒng)計(jì)。
5.1.1 預(yù)備知識(shí)
這章中我們著重講解如何使用dplyr包,它是另一個(gè)tidyverse中的核心成員。我們將會(huì)通過(guò)運(yùn)用nycflight13包中的數(shù)據(jù)來(lái)展示這些核心思想,并用ggplot2來(lái)幫助我們理解數(shù)據(jù)。
library(nycflights13)library(tidyverse)
當(dāng)你加載tidyverse時(shí),要小心沖突信息的提示。它會(huì)告訴你dplyr包覆蓋了基礎(chǔ)R中的一些函數(shù)。如果你需要在加載了dplyr包后使用這些基本版的函數(shù),你需要使用它們的全名: stats::filter() 和 stats::lag()。
5.1.2 ncyflights13
為了探索dplyr包中的基本數(shù)據(jù)處理動(dòng)詞,我們會(huì)用到nycflights13::flights。這個(gè)數(shù)據(jù)框包含了2013年從紐約離港的336,776架次航班。數(shù)據(jù)來(lái)源是美國(guó)交通統(tǒng)計(jì)局,并且記錄在 ?flights中。
flights#>#Atibble:336,776×19#>yearmonthdaydep_timesched_dep_timedep_delayarr_time#>#>12013115175152830#>22013115335294850#>32013115425402923#>4201311544545-11004#>5201311554600-6812#>6201311554558-4740#>#...with3.368e+05morerows,and12morevariables:#>#sched_arr_time,arr_delay,carrier,flight,#>#tailnum,origin,dest,air_time,#>#distance,hour,minute,time_hour
你可能注意到了這個(gè)和你之前用到過(guò)的其它數(shù)據(jù)框有一點(diǎn)不同:它僅僅在單個(gè)屏幕中顯示前面的幾行和所有的列。(要想看整個(gè)數(shù)據(jù)集,你可以運(yùn)行View(flights),他會(huì)在Rstudio觀測(cè)器中打開該數(shù)據(jù)集)。它顯示方式不同是因?yàn)樗菍儆趖ibble類型。Tibble也是數(shù)據(jù)框,但是做了些輕微的調(diào)整使其在tidyverse中變現(xiàn)更為出色?,F(xiàn)在,讓我們拋開那些不同,我們會(huì)在wrangle中詳細(xì)討論。
你會(huì)注意到第三行(或第四行)中在列名下的字母縮寫。它們描述了不同變量的類型:
· int 表示整數(shù)。
· dbl表示雙精度型或?qū)崝?shù)。
·chr表示字符型向量或字符串。
·dttm表示日期時(shí)間。
還有三種常見的變量類型,雖然這里沒(méi)有用到但你會(huì)在這本書后面的內(nèi)容中遇到:
· lgl 表示邏輯型向量,只包括TRUE和FALSE。
· fctr 表示因子,R用來(lái)表示含有固定的可能值的分類變量。
· date 表示日期。
5.1.3 dplyr基礎(chǔ)
本章你會(huì)學(xué)到五個(gè)dplyr包中的關(guān)鍵函數(shù),這會(huì)讓你解決數(shù)據(jù)處理中的大部分挑戰(zhàn):
根據(jù)值來(lái)挑選觀測(cè)(filter())。
按行重新排序(arrange())。
根據(jù)名字來(lái)挑選變量(select())。
根據(jù)已知變量創(chuàng)建新的變量(mutate())。
把眾多的值整合到單個(gè)總結(jié)當(dāng)中(summarise())。
以上這些函數(shù)可以和group_by()結(jié)合使用,該函數(shù)改變上述各個(gè)函數(shù)的作用范圍,使得數(shù)據(jù)處理從對(duì)整個(gè)數(shù)據(jù)集的處理到分組處理。這六個(gè)函數(shù)就提供了數(shù)據(jù)處理語(yǔ)言所需的動(dòng)詞函數(shù)。
所有的動(dòng)詞函數(shù)原理類似:
1 第一個(gè)自變量是一個(gè)數(shù)據(jù)框。
2 后續(xù)的自變量描述對(duì)于這個(gè)數(shù)據(jù)框做什么處理,這里會(huì)用到變量名(無(wú)引用)。
3 輸出結(jié)果是個(gè)新的數(shù)據(jù)框。
總之,這些屬性使得把多個(gè)簡(jiǎn)單步驟連接起來(lái)獲得復(fù)雜的結(jié)果這件事變得很簡(jiǎn)單。讓我們接下來(lái)深入了解一下這些動(dòng)詞函數(shù)如何使用。
5.2 用filter()來(lái)過(guò)濾行
filter() 讓你可以根據(jù)觀測(cè)的值來(lái)取子集。第一個(gè)自變量是數(shù)據(jù)框的名稱。第二個(gè)以及后續(xù)的自變量是過(guò)濾數(shù)據(jù)框的描述語(yǔ)句。比如,我們可以選擇一月一號(hào)的所有航班,代碼及結(jié)果如下:
filter(flights, month == 1, day == 1)#> # A tibble: 842 × 19#>? ? year month? day dep_time sched_dep_time dep_delay arr_time#>? ? ? ? ? ? ? ? ? ? ? ? #> 1? 2013? ? 1? ? 1? ? ? 517? ? ? ? ? ? 515? ? ? ? 2? ? ? 830#> 2? 2013? ? 1? ? 1? ? ? 533? ? ? ? ? ? 529? ? ? ? 4? ? ? 850#> 3? 2013? ? 1? ? 1? ? ? 542? ? ? ? ? ? 540? ? ? ? 2? ? ? 923#> 4? 2013? ? 1? ? 1? ? ? 544? ? ? ? ? ? 545? ? ? ? -1? ? 1004#> 5? 2013? ? 1? ? 1? ? ? 554? ? ? ? ? ? 600? ? ? ? -6? ? ? 812#> 6? 2013? ? 1? ? 1? ? ? 554? ? ? ? ? ? 558? ? ? ? -4? ? ? 740#> # ... with 836 more rows, and 12 more variables: sched_arr_time ,#> #? arr_delay , carrier , flight , tailnum ,#> #? origin , dest , air_time , distance , hour ,#> #? minute , time_hour
當(dāng)你運(yùn)行這段代碼,dplyr執(zhí)行了相應(yīng)的過(guò)濾粗濾并返回一個(gè)新的數(shù)據(jù)框。dplyr中的函數(shù)不會(huì)修改輸入值,所以若你想要保存結(jié)果,你需要用到賦值運(yùn)算符 <-:
jan1 <- filter(flights, month == 1, day == 1)
R既不會(huì)打印出結(jié)果,也不會(huì)把它們存儲(chǔ)一個(gè)變量。若要實(shí)現(xiàn)這兩個(gè)功能,你可以在賦值語(yǔ)句兩邊加上括號(hào):
(dec25 <- filter(flights, month == 12, day == 25))#> # A tibble: 719 × 19#>? ? year month? day dep_time sched_dep_time dep_delay arr_time#>? ? ? ? ? ? ? ? ? ? ? ? #> 1? 2013? ? 12? ? 25? ? ? 456? ? ? ? ? ? 500? ? ? ? -4? ? ? 649#> 2? 2013? ? 12? ? 25? ? ? 524? ? ? ? ? ? 515? ? ? ? 9? ? ? 805#> 3? 2013? ? 12? ? 25? ? ? 542? ? ? ? ? ? 540? ? ? ? 2? ? ? 832#> 4? 2013? ? 12? ? 25? ? ? 546? ? ? ? ? ? 550? ? ? ? -4? ? 1022#> 5? 2013? ? 12? ? 25? ? ? 556? ? ? ? ? ? 600? ? ? ? -4? ? ? 730#> 6? 2013? ? 12? ? 25? ? ? 557? ? ? ? ? ? 600? ? ? ? -3? ? ? 743#> # ... with 713 more rows, and 12 more variables: sched_arr_time ,#> #? arr_delay , carrier , flight , tailnum ,#> #? origin , dest , air_time , distance , hour ,#> #? minute , time_hour
5.2.1 比較
為了有效地使用過(guò)濾函數(shù),你必須知道如何通過(guò)比較操作來(lái)選擇觀測(cè)變量。R提供了標(biāo)準(zhǔn)的套裝:>, >=, <, <=, != (不等于), and ==(等于)。
當(dāng)你使用R的時(shí)候,最容易犯的錯(cuò)誤就是在測(cè)試等式時(shí),用=代替了==。不信你看下面就是報(bào)錯(cuò)提示:
filter(flights, month = 1)#> Error: filter() takes unnamed arguments. Do you need `==`?
在使用==時(shí),你還會(huì)遇到另一個(gè)常見的問(wèn)題:浮點(diǎn)數(shù)。這些結(jié)果會(huì)嚇到你!
sqrt(2) ^ 2 == 2#> [1] FALSE1/49 * 49 == 1#> [1] FALSE
計(jì)算機(jī)使用的是有限精度算法(它們顯然無(wú)法存儲(chǔ)一個(gè)無(wú)限的數(shù)字?。┧阅阋涀∶恳粋€(gè)你看到的數(shù)字都是估計(jì)。你可以用near()代替==:
near(sqrt(2) ^ 2,? 2)#> [1] TRUE near(1 / 49 * 49, 1)#> [1] TRUE
5.2.2 邏輯型
filter()中的多個(gè)變量用“and”來(lái)結(jié)合:即每個(gè)語(yǔ)句都為真的情況下,對(duì)應(yīng)的那一行才能包括在輸出中。對(duì)于其他類型的組合,你會(huì)用到布爾運(yùn)算符:&是“與”,| 是“或”,! 是“非”。圖5.1現(xiàn)實(shí)了完整的布爾運(yùn)算符集合。

圖5.1 布爾運(yùn)算符集。x是左側(cè)的圓環(huán),y是右側(cè)的圓環(huán),陰影部分為經(jīng)過(guò)相應(yīng)的運(yùn)算后所得出的結(jié)果。
下面的代碼可以找到在11月或12月離港的所有航班:
filter(flights, month == 11 | month == 12)
這種處理的順序并不像英語(yǔ)那樣。你不能寫filter(flights, month == 11 | 12),這只是“找到11月或12月離港的所有航班”的字面翻譯。它反而會(huì)尋找所有等于11 | 12的月份,而這時(shí)一個(gè)為TRUE的表達(dá)式。在數(shù)字環(huán)境下(就像這里),TRUE變成了一,因此它就會(huì)尋找一月份中的所有航班,而不是11月或12月。這真是容易混淆??!
解決這個(gè)問(wèn)題的一個(gè)有用的速記是%in% 。它選擇對(duì)應(yīng)于y中等于x的值的所有行。我們可以將上面的代碼重寫:
nov_dec <- filter(flights, month %in% c(11, 12))
有時(shí)候你可以采用摩根定律來(lái)簡(jiǎn)化復(fù)雜的子集處理:!(x & y)和!x | !y一樣,!(x | y)和!x & !y也是相同的。例如,若你想要找出延誤時(shí)間(到達(dá)或者起飛)不超過(guò)兩小時(shí)的航班,你可以采取下面兩者中的任意一個(gè):
filter(flights, !(arr_delay > 120 | dep_delay > 120))filter(flights, arr_delay <= 120, dep_delay <= 120)
R中不僅有&和|,還有&&和||。這里就別用它們啦!在conditional execution中你會(huì)學(xué)到的。
無(wú)論何時(shí),當(dāng)你在中filter()開始使用復(fù)雜的、多個(gè)部分的表達(dá)式時(shí),最好用明確的變量來(lái)代替它們。這樣做可以更方便地檢查。馬上你就會(huì)學(xué)到如何創(chuàng)建新的變量了。
5.2.3 缺失值
R中一個(gè)很重要的特性使得比較變得難以處理,他就是缺失值,或者NA(“不可用值”)。NA表示未知的值,因此缺失值就變得“有傳染性”:幾乎所有的包含未知值的操作都會(huì)變成未知。
NA > 5#> [1] NA10 == NA#> [1] NANA + 10#> [1] NANA / 2#> [1] NA
最蛋疼的下面這個(gè):
NA == NA#> [1] NA
加點(diǎn)文本你理解起來(lái)就容易多了:
# Let x be Mary's age. We don't know how old she is.x <- NA# Let y be John's age. We don't know how old he is.y <- NA# Are John and Mary the same age?x == y#> [1] NA# We don't know!
你可以用is.na()來(lái)判斷某個(gè)值是否缺失:
is.na(x)#> [1] TRUE
filter()只會(huì)過(guò)濾出判斷為TRUE的行;它包含F(xiàn)ALSE和NA兩種值。如果你想要保護(hù)缺失值,你可以清楚地要求,如:
df <- tibble(x = c(1, NA, 3))filter(df, x > 1)#> # A tibble: 1 × 1#>? ? ? x#>? #> 1? ? 3filter(df, is.na(x) | x > 1)#> # A tibble: 2 × 1#>? ? ? x#>? #> 1? ? NA#> 2? ? 3
5.2.4 練習(xí)
1 找到以下航班:
到達(dá)時(shí)間延誤兩小時(shí)以上的
飛到休斯頓的(IAH or HOU)
由United, American, or Delta操控的
在夏天起飛的(July, August, and September)
晚到兩小時(shí),但是離開時(shí)沒(méi)有遲到的
延誤至少半小時(shí),但是在飛行過(guò)程中節(jié)約了半小時(shí)以上的
在00:00到06:00(含)之間起飛的
答:
library(nycflights13)library(dplyr)#練習(xí)1.1filter(flights, arr_delay >= 120)#練習(xí)1.2filter(flights, dest == "IAH" | dest == "HOU")#練習(xí)1.3filter(flights, carrier %in% c("UA","AA","DL"))#練習(xí)1.4filter(flights, month %in% c(8,9,10))#練習(xí)1.5 filter(flights, arr_delay>=120 & dep_delay<=0)#練習(xí)1.6 dec <- filter(flights, dep_delay >= 60 & (arr_delay-dep_delay <= -30))#練習(xí)1.7 deo <- filter(flights, dep_time >= 000 & dep_time <= 600)
2 dplyr中另一個(gè)有用的過(guò)濾函數(shù)是between()。它是干嘛的?你可以用它來(lái)簡(jiǎn)化之前問(wèn)題的答案嗎?
答:
between(x, left, right)#This is a shortcut for x >= left & x <= right, implemented efficiently in C++ for local values, and translated to the appropriate SQL for remote tables.#練習(xí)2,簡(jiǎn)化練習(xí)1.7 des <- filter(flights, between(dep_time ,000, 600))
3 有多少航班缺失了dep_time?還有別的什么變量缺失了?這些行代表了什么?
答:
#練習(xí)3dek <- filter(flights, is.na(dep_time))#這些行可能表示航班取消了
4 NA ^ 0為什么不是缺失值?NA | TRUE為什么不是缺失值?FALSE & NA為什么不是缺失值?你能夠總結(jié)出一般規(guī)律嗎?(NA * 0是個(gè)有迷惑性的反例)
答:NA ^ 0結(jié)果為1;與True進(jìn)行或運(yùn)算,不管是什么,結(jié)果肯定為真;與False進(jìn)行與運(yùn)算,不管是什么,結(jié)果肯定為假。一般規(guī)律,我也也說(shuō)不清,先記住吧。NA * 0=NA。
其余章節(jié)見原文。