12.4?使用dplyr管道操作處理數(shù)據(jù)框

關(guān)于數(shù)據(jù)操作的另一個(gè)流行的包是dplyr腮敌,它發(fā)明了一種數(shù)據(jù)操作語(yǔ)法黔牵。dplyr擴(kuò)展包并沒(méi)有使用構(gòu)建子集函數(shù)([ ])米者,而是定義了一系列基礎(chǔ)的變形函數(shù)作為數(shù)據(jù)操作模塊德召,并且引入了一個(gè)管道操作符辑舷,利用管道操作符將這些變形函數(shù)串聯(lián)起來(lái)喻犁,進(jìn)而完成復(fù)雜的多步任務(wù)。

如果還沒(méi)有安裝dplyr何缓,請(qǐng)運(yùn)行以下代碼以從CRAN中安裝:

    install.packages("dplyr")

首先肢础,我們重新加載產(chǎn)品表格,將它們重置為原始形式:

library(readr)
product_info <- read_csv("data/product-info.csv")      
product_stats <- read_csv("data/product-stats.csv")      
product_tests <- read_csv("data/product-tests.csv")      
toy_tests <- read_csv("data/product-toy-tests.csv")

然后碌廓,載入dplyr包:

library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:data.table':
#
##    between, last
## The following objects are masked from 'package:stats':
##
 ##    filter, lag
 ## The following objects are masked from 'package:base':
        ##
        ##    intersect, setdiff, setequal, union

以上輸出信息說(shuō)明dplyr泛化了很多內(nèi)置函數(shù)传轰。加載這個(gè)包之后,這些內(nèi)置函數(shù)便被屏蔽了谷婆。

現(xiàn)在慨蛙,我們可以使用dplyr包提供的變形函數(shù)了辽聊。先使用select( ) 函數(shù)從數(shù)據(jù)框中提取列,并將這些列存儲(chǔ)在新創(chuàng)建的表中:

 select(product_info, id, name, type, class)
 ## Source:local data frame[6x4]
 ##
 ##      id      name  type  class
 ##  <chr>    <chr> <chr>  <chr>
 ## 1  T01    SupCar  toy vehicle
 ## 2  T02  SupPlane  toy vehicle
 ## 3  M01    JeepX model vehicle
 ## 4  M02 AircraftX model vehicle
 ## 5  M03    Runner model  people
 ## 6  M04    Dancer model  people

打印出來(lái)的表格與data.frame和data.table都不太一樣期贫。它不僅顯示了表格本身跟匆,也包括一個(gè)表頭,用于說(shuō)明數(shù)據(jù)框的大小和每一列的數(shù)據(jù)類(lèi)型通砍。

顯然玛臂,select( ) 使用了非標(biāo)準(zhǔn)計(jì)算,所以我們可以直接將數(shù)據(jù)框的列名作為參數(shù)封孙。它和subset( )迹冤、transform( ) 以及with( ) 的工作方式類(lèi)似。

其次敛瓷,我們可以使用filter( ) 函數(shù)叁巨,通過(guò)邏輯條件篩選數(shù)據(jù)框。同樣地呐籽,這個(gè)函數(shù)也是在數(shù)據(jù)框的語(yǔ)義中被計(jì)算:

filter(product_info, released == "yes")
## Source:local data frame[4x5]
##
##      id      name  type  class released
##  <chr>    <chr> <chr>  <chr>    <chr>
## 1  T01    SupCar  toy vehicle      yes
## 2  M01    JeepX model vehicle      yes
## 3  M02 AircraftX model vehicle      yes
## 4  M03    Runner model  people      yes

如果想要根據(jù)多個(gè)條件篩選記錄锋勺,只需要把每個(gè)條件都作為filter() 的參數(shù):

filter(product_info,
released == "yes", type == "model")
## Source:local data frame[3x5]
##
##      id      name  type  class released
##  <chr>    <chr> <chr>  <chr>    <chr>
## 1  M01    JeepX model vehicle      yes
## 2  M02 AircraftX model vehicle      yes
## 3  M03    Runner model  people      yes

mutate( )函數(shù)可以創(chuàng)建一個(gè)新的數(shù)據(jù)框,這個(gè)數(shù)據(jù)框包含新列狡蝶,或者替換原數(shù)據(jù)框的列庶橱。它與transform( )類(lèi)似,不同的是贪惹,如果數(shù)據(jù)是data.table苏章,它也能支持原地賦值:=:

mutate(product_stats, density = size / weight)
## Source: local data frame [6 x 5]
##
##      id material  size weight  density
##  <chr>    <chr> <int>  <dbl>    <dbl>
## 1  T01    Metal  120  10.0 12.000000
## 2  T02    Metal  350  45.0  7.777778
## 3  M01 Plastics    50    NA        NA
## 4  M02 Plastics    85    3.0 28.333333
## 5  M03    Wood    15    NA        NA
## 6  M04    Wood    16    0.6 26.666667

arrange( ) 函數(shù)也是用于創(chuàng)建一個(gè)新的數(shù)據(jù)框,這個(gè)數(shù)據(jù)框是按一個(gè)或多個(gè)列排序后的奏瞬。desc( ) 函數(shù)表示降序排列:

arrange(product_stats, material, desc(size), desc(weight))
## Source: local data frame [6 x 4]
##
##      id material  size weight
##  <chr>    <chr> <int>  <dbl>
## 1  T02    Metal  350  45.0
## 2  T01    Metal  120  10.0
## 3  M02 Plastics    85    3.0
## 4  M01 Plastics    50    NA
## 5  M04    Wood    16    0.6
## 6  M03    Wood    15    NA

dplyr包提供了豐富的連接函數(shù)枫绅,包括inner_join( )、left_join( )硼端、right_join( )并淋、full_join( )、semi_join( ) 和anti_join( )珍昨。如果要連接的兩個(gè)表存在無(wú)法匹配的記錄县耽,這些連接操作的行為會(huì)有很大差別。對(duì)于product_info和product_tests镣典,它們的記錄可以完全匹配兔毙,所以left_join( ) 的返回結(jié)果和merge( ) 相同:

product_info_tests <- left_join(product_info, product_tests, by = "id")
product_info_tests
## Source: local data frame [6 x 8]
##
##    id    name  type  class released quality durability
##  <chr>  <chr>  <chr>  <chr>    <chr>  <int>      <int>
## 1 T01    SupCar  toy vehicle      yes      NA        10
## 2 T02  SupPlane  toy vehicle      no      10          9
## 3 M01    JeepX model vehicle      yes      6          4
## 4 M02 AircraftX model vehicle      yes      6          5
## 5 M03    Runner model  people      yes      5        NA
## 6 M04    Dancer model  people      no      6          6
## Variables not shown: waterproof (chr)

運(yùn)行?dplyr::join了解這些連接操作的更多差異兄春。

為了對(duì)數(shù)據(jù)進(jìn)行分組匯總澎剥,我們需要先利用group_by( ) 創(chuàng)建一個(gè)分組后的表格。然后使用summarize( ) 匯總數(shù)據(jù)赶舆。例如哑姚,我們想把product_info_tests按照type和class分割開(kāi)趾唱,然后對(duì)每一組計(jì)算quality和durability的平均值:

summarize(group_by(product_info_tests, type, class),
          mean_quality = mean(quality, na.rm = TRUE),
          mean_durability = mean(durability, na.rm = TRUE))

## Source: local data frame [3 x 4]
## Groups: type [? ]
##
##    type  class mean_quality mean_durability
##  <chr>  <chr>        <dbl>          <dbl>
## 1 model  people          5.5            6.0
## 2 model vehicle          6.0            4.5
## 3  toy vehicle        10.0            9.5

通過(guò)前面的代碼示例,我們掌握了這些變形函數(shù): select( )蜻懦、filter( )甜癞、mutate( )、arrange( )宛乃、group_by( ) 和summarize( )悠咱。這些函數(shù)的設(shè)計(jì)初衷都是對(duì)數(shù)據(jù)進(jìn)行一個(gè)小操作,但是將它們合理地組合到一起征炼,就可以完成復(fù)雜的數(shù)據(jù)處理操作析既。除了這些函數(shù),dplyr包還從magrittr包中引入了管道操作符 %>%谆奥,利用 %>% 將函數(shù)連接起來(lái)眼坏,組合使用。

假設(shè)現(xiàn)在有product_info和product_tests酸些。我們需要對(duì)已發(fā)布的產(chǎn)品進(jìn)行分析宰译,對(duì)于每種類(lèi)型和類(lèi)對(duì)應(yīng)的組,計(jì)算該組產(chǎn)品的質(zhì)量和耐久性的平均值魄懂,并將結(jié)果數(shù)據(jù)按照質(zhì)量均值降序排列沿侈。通過(guò)使用管道操作符將dplyr變形函數(shù)連接起來(lái),可以漂亮地完成這個(gè)任務(wù):

product_info %>% filter(released == "yes") %>%
          inner_join(product_tests, by = "id") %>%
          group_by(type, class) %>%
          summarize(
            mean_quality = mean(quality, na.rm = TRUE),
            mean_durability = mean(durability, na.rm = TRUE)) %>%
          arrange(desc(mean_quality))

## Source: local data frame [3 x 4]
## Groups: type [2]
##
##    type  class mean_quality mean_durability
##  <chr>  <chr>        <dbl>          <dbl>
## 1 model vehicle            6            4.5
## 2 model  people            5            NaN
## 3  toy vehicle          NaN            10.0

但是 %>% 是如何工作的呢市栗?其實(shí)缀拭,管道操作符基本上只負(fù)責(zé)一件事情:把符號(hào)左側(cè)返回的結(jié)果,作為符號(hào)右側(cè)調(diào)用函數(shù)的第1個(gè)參數(shù)填帽。也就是說(shuō)蛛淋,x %>% f(...) 等價(jià)于f(x, ...)。因?yàn)?%>% 是一個(gè)由包定義的二元操作符篡腌,所以允許我們將函數(shù)調(diào)用連接起來(lái)褐荷,一方面避免存儲(chǔ)多余的中間值,另一方面將嵌套調(diào)用分解哀蘑,使每一步操作流程清晰地展現(xiàn)出來(lái)诚卸。

假設(shè)將d0轉(zhuǎn)化為d3需要3個(gè)步驟葵第。在每一步的函數(shù)調(diào)用中绘迁,需要將前面一步的結(jié)果作為參數(shù)。如果像這樣操作數(shù)據(jù)卒密,可能會(huì)有很多中間結(jié)果缀台,當(dāng)數(shù)據(jù)量很大的時(shí)候,會(huì)消耗很多內(nèi)存:

d1 <- f1(d0, arg1)
d2 <- f2(d1, arg2)
d3 <- f3(d2, arg3)

想要避免中間結(jié)果哮奇,就不得不寫(xiě)嵌套調(diào)用膛腐。這個(gè)任務(wù)看起來(lái)一點(diǎn)都不友好睛约,特別是在每個(gè)函數(shù)調(diào)用都有多個(gè)參數(shù)的時(shí)候:

f3(f2(f1(d0, arg1), arg2), arg3)

使用管道操作符,工作流便可以像下面這樣重新組織:

d0 %>% f1(arg1) %>% f2(arg2) %>% f3(arg3)

這樣的代碼看起來(lái)更加簡(jiǎn)潔和直觀哲身。整個(gè)表達(dá)式不止看起來(lái)像一個(gè)管道辩涝,其工作方式也像一個(gè)管道。d0 %>% f1(arg1) 等價(jià)于f1(d0, arg1)勘天,并會(huì)被送往f2(., arg2)怔揩,緊接著又會(huì)被送往f3(., arg3)。每一步的輸出結(jié)果都會(huì)成為下一步的輸入脯丝。

而且商膊,管道操作符不止在dplyr的函數(shù)中生效,對(duì)其他所有的函數(shù)也都是適用的宠进。假設(shè)我們想要對(duì)鉆石價(jià)格畫(huà)一個(gè)密度圖晕拆,如圖所示。

data(diamonds, package = "ggplot2")
plot(density(diamonds$price, from = 0),
     main = "Density plot of diamond prices")
image

使用管道操作符材蹬,我們可以像這樣重寫(xiě)代碼:

diamonds$price %>%
          density(from = 0) %>%
          plot(main = "Density plot of diamonds prices")

與data.table類(lèi)似实幕,dplyr也提供了do( ) 函數(shù)來(lái)對(duì)每組數(shù)據(jù)進(jìn)行任意操作。例如堤器,將diamonds按cut分組茬缩,每組都按log(price) ~ carat擬合一個(gè)線性模型。和data.table不同的是吼旧,我們需要為操作指定一個(gè)名稱(chēng)凰锡,以便將結(jié)果儲(chǔ)存到列中。而且圈暗,do( ) 中的表達(dá)式不能直接在分組數(shù)據(jù)的語(yǔ)義下計(jì)算掂为,我們需要使用.來(lái)表示數(shù)據(jù):

models <- diamonds %>%
          group_by(cut) %>%
          do(lmod = lm(log(price) ~ carat, data = .))
models
## Source: local data frame [5 x 2]
## Groups: <by row>
##
##        cut    lmod
##      <fctr>  <chr>
## 1      Fair <S3: lm>
## 2      Good <S3: lm>
## 3 Very Good <S3: lm>
## 4  Premium <S3: lm>
## 5    Ideal <S3: lm>

注意到一個(gè)新列l(wèi)mod被創(chuàng)建了。這不是一個(gè)典型的原子向量列员串,而是一個(gè)包含了線性回歸對(duì)象的列表勇哗,也就是說(shuō),每一個(gè)cut的值對(duì)應(yīng)的模型會(huì)以列表的形式儲(chǔ)存在lmod列的對(duì)應(yīng)位置中寸齐。我們可以使用索引來(lái)獲得每個(gè)模型:

models$lmod[[1]]
##
## Call:
## lm(formula = log(price) ~ carat, data = .)
##
## Coefficients:
## (Intercept)        carat
##      6.785        1.251

在需要完成高度定制的操作時(shí)欲诺,do( ) 函數(shù)的優(yōu)勢(shì)就更加明顯了。舉個(gè)例子渺鹦,假如我們需要分析toy_tests數(shù)據(jù)扰法,要對(duì)每種產(chǎn)品的質(zhì)量和耐久性進(jìn)行匯總。如果只需要樣本數(shù)最多的3個(gè)測(cè)試記錄毅厚,并且每個(gè)產(chǎn)品的質(zhì)量和耐久性是經(jīng)樣本數(shù)加權(quán)的平均數(shù)塞颁,考慮下我們應(yīng)該做什么。

使用dplyr包的函數(shù)和管道操作符,上述任務(wù)可以通過(guò)以下代碼輕松完成:

toy_tests %>%
      group_by(id) %>%
      arrange(desc(sample)) %>%
      do(head(., 3)) %>%
      summarize(
         quality = sum(quality *  sample) / sum(sample),
         durability = sum(durability *  sample) / sum(sample))
## Source:local data frame[2x3]
##
##      id  quality durability
##  <chr>    <dbl>      <dbl>
## 1  T01 9.319149  9.382979
## 2  T02 9.040000  8.340000

注意到祠锣,當(dāng)數(shù)據(jù)分組后酷窥,所有的后續(xù)操作都是按組進(jìn)行的。為了查看中間結(jié)果伴网,我們可以運(yùn)行do(head(., 3)) 之前的代碼蓬推,如下所示:

toy_tests %>%
    group_by(id) %>%
    arrange(desc(sample))

## Source: local data frame [8 x 5]
## Groups: id [2]
##
##      id    date sample quality durability
##  <chr>    <int>  <int>  <int>      <int>
## 1  T0120160405    180      9        10
## 2  T0120160302    150      10          9
## 3  T0120160502    140      9          9
## 4  T0120160201    100      9          9
## 5  T0220160403    90      9          8
## 6  T0220160502    85      10          9
## 7  T0220160303    75      8          8
## 8  T0220160201    70      7          9

這樣我們就得到了按樣本數(shù)降序排列的所有記錄。然后澡腾,do(head(., 3)) 將會(huì)對(duì)每一個(gè)組計(jì)算head(. 3)拳氢,其中,.表示每組數(shù)據(jù):

toy_tests %>%
    group_by(id) %>%
    arrange(desc(sample)) %>%
    do(head(., 3))
## Source: local data frame [6 x 5]
## Groups: id [2]
##
##      id    date sample quality durability
##  <chr>    <int>  <int>  <int>      <int>
## 1  T0120160405    180      9        10
## 2  T0120160302    150      10          9
## 3  T0120160502    140      9          9
## 4  T0220160403    90      9          8
## 5  T0220160502    85      10          9
## 6  T0220160303    75      8          8

現(xiàn)在蛋铆,我們得到了每一組的樣本數(shù)最多的3條記錄馋评,如此匯總數(shù)據(jù)是很方便的。

dplyr函數(shù)定義了一種非常直觀的數(shù)據(jù)操作語(yǔ)法刺啦,并且提供了便于使用管道操作符的高性能變形函數(shù)留特。更多內(nèi)容,請(qǐng)閱讀包的指南(https://cran.rstudio.com/web/packages/dplyr/vignettes/introduction.html)玛瘸,并且訪問(wèn)DataCamp上的交互式教程(https://www.datacamp.com/courses/dplyr-data-manipulation-r-tutorial)蜕青。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市糊渊,隨后出現(xiàn)的幾起案子右核,更是在濱河造成了極大的恐慌,老刑警劉巖渺绒,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贺喝,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宗兼,警方通過(guò)查閱死者的電腦和手機(jī)躏鱼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)殷绍,“玉大人染苛,你說(shuō)我怎么就攤上這事≈鞯剑” “怎么了茶行?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)登钥。 經(jīng)常有香客問(wèn)我畔师,道長(zhǎng),這世上最難降的妖魔是什么怔鳖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任茉唉,我火速辦了婚禮,結(jié)果婚禮上结执,老公的妹妹穿的比我還像新娘度陆。我一直安慰自己,他們只是感情好献幔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布懂傀。 她就那樣靜靜地躺著,像睡著了一般蜡感。 火紅的嫁衣襯著肌膚如雪蹬蚁。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天郑兴,我揣著相機(jī)與錄音犀斋,去河邊找鬼。 笑死情连,一個(gè)胖子當(dāng)著我的面吹牛叽粹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播却舀,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼虫几,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了挽拔?” 一聲冷哼從身側(cè)響起辆脸,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎螃诅,沒(méi)想到半個(gè)月后啡氢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡术裸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年空执,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穗椅。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辨绊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出匹表,到底是詐尸還是另有隱情门坷,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布袍镀,位于F島的核電站默蚌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏苇羡。R本人自食惡果不足惜绸吸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锦茁,春花似錦攘轩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至稿存,卻和暖如春笨篷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓣履。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工率翅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人袖迎。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓安聘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親瓢棒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浴韭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容