R語言中管道操作 %>%, %T>%, %$% 和 %%(轉)

from : http://blog.csdn.net/fairewell/article/details/72878107

前言

使用R語言進行數據處理是非常方便的,幾行代碼就可以完成很複雜的操作。但是,對於數據的連續處理,還是有人覺得代碼不好看,要麼是長長的函數嵌套調用,有點像Lisp感覺,括號包一切;要麼就是每次操作賦值一個臨時變量,囉嗦。爲什麼就不能像Linux的管道一樣優雅呢?

magrittr包在這樣場景中被開發出來,通過管道的方式讓連續複雜數據的處理操作,代碼更短,更容易讀,甚至一行代碼可以搞定原來10行代碼的事情。

目錄

  1. magrittr介紹
  2. magrittr安裝
  3. magrittr包的基本使用
  4. magrittr包的擴展功能

1. magrittr介紹

magrittr包被定義爲一個高效的管道操作工具包,通過管道的連接方式,讓數據或表達式的傳遞更高效,使用操作符%>%,可以直接把數據傳遞給下一個函數調用或表達式。magrittr包的主要目標有2個,第一是減少代碼開發時間,提高代碼的可讀性和維護性;第二是讓你的代碼更短,再短,短短短…

magrittr包,主要定義了4個管道操作符,分另是%>%, %T>%, %$% 和 %<>%。其中,操作符%>%是最常用的,其他3個操作符,與%>%類似,在特殊的使用場景會起到更好的作用。當正確掌握這幾個操作符後,你一定會愛不釋手的,快去把所有的代碼都重構吧,砍掉原來大段冗長的代碼是一件多麼令人激動的事情啊。

magrittr的項目主頁:https://github.com/smbache/magrittr

2. magrittr安裝

本文所使用的系統環境

  • Win10 64bit
  • R: 3.2.3 x86_64-w64-mingw32/x64 b4bit

magrittr是在CRAN發佈的標準庫,安裝起來非常簡單,2條命令就可以了。


~ R
> install.packages('magrittr')
> library(magrittr)

3. magrittr包的使用

對於magrittr包的使用,其實就是掌握這4個操作符的用法,向右操作符%>%, 向左操作符%T>%, 解釋操作符%$% 和 複合賦值操作符%<>%。

3.1 %>% 向右操作符(forward-pipe operator)

%>%是最常用的一個操作符,就是把左側準備的數據或表達式,傳遞給右側的函數調用或表達式進行運行,可以連續操作就像一個鏈條一樣。

現實原理如下圖所示,使用%>%把左側的程序的數據集A傳遞右側程序的B函數,B函數的結果數據集再向右側傳遞給C函數,最後完成數據計算。

mag1

比如,我們要做下面的事情。(這是一個YY的需求。)

  1. 取10000個隨機數符合,符合正態分佈。
  2. 求這個10000個數的絕對值,同時乘以50。
  3. 把結果組成一個100*100列的方陣。
  4. 計算方陣中每行的均值,並四捨五入保留到整數。
  5. 把結果除以7求餘數,並話出餘數的直方圖。

我們發現上面的5個過程是連續的,正常的代碼我要怎麼實現呢。

設置隨機種子

> set.seed(1)

開始

> n1<-rnorm(10000) # 第1步
> n2<-abs(n1)*50 # 第2步
> n3<-matrix(n2,ncol = 100) # 第3步
> n4<-round(rowMeans(n3)) # 第4步
> hist(n4%%7) # 第5步

輸出的直方圖:

01

上面的代碼寫法是,每一行實現一個條件,但中間多了不少的臨時變量。再看另外一種的寫法,括號包一切。






# 設置隨機種子 > set.seed(1) > hist(round(rowMeans(matrix(abs(rnorm(10000))*50,ncol=100)))%%7)

輸出的直方圖:

02

我分別用兩種常見的代碼風格,實現了我們的需求。再看看%>%的方式,有多麼的不一樣。






# 設置隨機種子 > set.seed(1) # 開始 > rnorm(10000) %>% + abs %>% `*` (50) %>% + matrix(ncol=100) %>% + rowMeans %>% round %>% + `%%`(7) %>% hist

輸出的直方圖:

03

一行代碼,不僅搞定所有的事情,而且結構清楚,可讀性非常強。這就是管道代碼風格,帶來的優雅和簡約。

3.2 %T>% 向左操作符(tee operator)

%T>%向左操作符,其實功能和 %>% 基本是一樣的,只不過它是把左邊的值做爲傳遞的值,而不是右邊的值。這種情況的使用場景也是很多的,比如,你在數據處理的中間過程,需要打印輸出或圖片輸出,這時整個過程就會被中斷,用向左操作符,就可以解決這樣的問題。

現實原理如下圖所示,使用%T>%把左側的程序的數據集A傳遞右側程序的B函數,,B函數的結果數據集不再向右側傳遞,而是把B左側的A數據集再次向右傳遞給C函數,最後完成數據計算。

mag2

我們把上面的需求稍微進行調整,在最後增加一個要求,就會用到向左操作符。

  1. 取10000個隨機數符合,符合正態分佈。
  2. 求這個10000個數的絕對值,同時乘以50。
  3. 把結果組成一個100*100列的方陣。
  4. 計算方陣中每行的均值,並四捨五入保留到整數。
  5. 把結果除以7求餘數,並話出餘數的直方圖。
  6. 對餘數求和

由於輸出直方圖後,返回值爲空,那麼再繼續管道,就會把空值向右進行傳遞,這樣計算最後一步時就會出錯。這時我們需求的是,把除以7的餘數向右傳遞給最後一步求和,那麼就可以用到 %T>% 了

直接使用%>%向右傳值,出現異常。


> set.seed(1)
> rnorm(10000) %>%
+   abs %>% `*` (50)  %>%
+   matrix(ncol=100)  %>%
+   rowMeans %>% round %>% 
+   `%%`(7) %>% hist %>% sum
Error in sum(.) : invalid 'type' (list) of argument

使用 %T>% 把左邊的值,再向右傳值,則結果正確。


> rnorm(10000) %>%
+   abs %>% `*` (50)  %>%
+   matrix(ncol=100)  %>%
+   rowMeans %>% round %>% 
+   `%%`(7) %T>% hist %>% sum
[1] 328

3.3 %$% 解釋操作符(exposition pipe-operator)

% %,在右側的函數中可以直接使用列名操作數據。

現實原理如下圖所示,使用%$%把左側的程序的數據集A傳遞右側程序的B函數,同時傳遞數據集A的屬性名,作爲B函數的內部變量方便對A數據集進行處理,最後完成數據計算。

mag3

下面定義一個3列10行的data.frame,列名分別爲x,y,z,或缺x列大於5的數據集。使用 %$% 把列名x直接傳到右側進行判斷。這裏.代表左側的完整數據對象。一行代碼就實現了需求,而且這裏不需要顯示的定義中間變量。


> set.seed(1)
> data.frame(x=1:10,y=rnorm(10),z=letters[1:10]) %$% .[which(x>5),]
    x          y z
6   6 -0.8204684 f
7   7  0.4874291 g
8   8  0.7383247 h
9   9  0.5757814 i
10 10 -0.3053884 j

如果不使用%$%,我們通常的代碼寫法爲:


> set.seed(1)
> df<-data.frame(x=1:10,y=rnorm(10),z=letters[1:10])
> df[which(df$x>5),]
    x          y z
6   6 -0.8204684 f
7   7  0.4874291 g
8   8  0.7383247 h
9   9  0.5757814 i
10 10 -0.3053884 j

從代碼中可以發現,通常的寫法是需要定義變量df的,df一共要被顯示的使用3次,就是這一點點的改進,會讓代碼看起來更乾淨。

3.4 %<>% 複合賦值操作符(compound assignment pipe-operator)

%<>%複合賦值操作符, 功能與 %>% 基本是一樣的,對了一項額外的操作,就是把結果寫到左側對象。比如,我們需要對一個數據集進行排序,那麼需要獲得排序的結果,用%<>%就是非常方便的。

現實原理如下圖所示,使用%<>%把左側的程序的數據集A傳遞右側程序的B函數,B函數的結果數據集再向右側傳遞給C函數,C函數結果的數據集再重新賦值給A,完成整個過程。

mag4

定義一個符合正態分佈的100個隨機數,計算絕對值,並按從小到大的順序排序,獲得並取前10個數字賦值給x。


> set.seed(1)
> x<-rnorm(100) %<>% abs %>% sort %>% head(10)
> x
 [1] 0.001105352 0.016190263 0.028002159 0.039240003 0.044933609 0.053805041 0.056128740
 [8] 0.059313397 0.074341324 0.074564983

是不是太方便了,一行就實現了一連串的操作。但是這裏同時有一個陷阱,需要注意一下 %<>% 必須要用在第一個管道的對象處,才能完成賦值的操作,如果不是左側第一個位置,那麼賦值將不起作用。


> set.seed(1)
> x<-rnorm(100)





# 左側第一個位置,賦值成功 > x %<>% abs %>% sort %>% head(10) > x [1] 0.001105352 0.016190263 0.028002159 0.039240003 0.044933609 0.053805041 0.056128740 [8] 0.059313397 0.074341324 0.074564983 # 左側第二個位置,結果被直接打印出來,但是x的值沒有變 > x %>% abs %<>% sort %>% head(10) [1] 0.001105352 0.016190263 0.028002159 0.039240003 0.044933609 0.053805041 0.056128740 [8] 0.059313397 0.074341324 0.074564983 > length(x) [1] 10 # 左側第三個位置,結果被直接打印出來,但是x的值沒有變 > x %>% abs %>% sort %<>% head(10) [1] 0.001105352 0.016190263 0.028002159 0.039240003 0.044933609 0.053805041 0.056128740 [8] 0.059313397 0.074341324 0.074564983 > length(x) [1] 10

4. magrittr包的擴展功能

我們已經瞭解了magrittr包的4個操作符的使用,除了操作符,我們再看一下magrittr還有哪些功能。

  • 符號操作符定義
  • %>%對代碼塊的傳遞
  • %>%對函數的傳遞

    4.1 符號操作符定義

    爲了讓鏈條傳遞看起來更友好,magrittr對於常見的計算符號操作符進行的重新定義,讓每個操作都對應用一個函數,這樣所有的傳遞調用代碼都是風格統一的。比如,add()函數和`+`是等價的。

    下面列出對應的列表:

    
    extract                   `[`
    extract2              `[[`
    inset                     `[<-`
    inset2                    `[[<-`
    use_series            `$`
    add                   `+`
    subtract              `-`
    multiply_by           `*`
    raise_to_power            `^`
    multiply_by_matrix    `%*%`
    divide_by             `/`
    divide_by_int             `%/%`
    mod                   `%%`
    is_in                     `%in%`
    and                   `&`
    or                    `|`
    equals                    `==`
    is_greater_than           `>`
    is_weakly_greater_than    `>=`
    is_less_than              `<`
    is_weakly_less_than   `<=`
    not (`n'est pas`)     `!`
    set_colnames              `colnames<-`
    set_rownames              `rownames<-`
    set_names             `names<-`
    

    我們來看一下使用的效果。對一個包括10個隨機數的向量的先*5再+5。

    
    
    
    
    
    
# 使用符號的寫法 > set.seed(1) > rnorm(10) %>% `*`(5) %>% `+`(5) [1] 1.8677309 5.9182166 0.8218569 12.9764040 6.6475389 0.8976581 7.4371453 8.6916235 [9] 7.8789068 3.4730581 # 使用函數的寫法 > set.seed(1) > rnorm(10) %>% multiply_by(5) %>% add(5) [1] 1.8677309 5.9182166 0.8218569 12.9764040 6.6475389 0.8976581 7.4371453 8.6916235 [9] 7.8789068 3.4730581

上面計算結果是完全一樣的,用函數替換了符號。其實,這種轉換的操作在面向對象的封裝時是非常有用的,像hibernate封裝了所有的SQL,XFire封裝了WebServices協議等。

4.2 %>%傳遞到代碼塊

有些時候,我們對同一個數據塊的要進行次行的處理,一條語句是很難完成的,這些就需要一個代碼塊也進行處理。把數據集傳遞到{}代碼塊中,傳入的數據集以.來表示,通過一段代碼來完成操作,而不是一句話完成操作。

比如,對一個包括10個隨機數的向量的先*5再+5,求出向量的均值和標準差,並從小到大排序後返回前5條。


> set.seed(1)
> rnorm(10)    %>%
+   multiply_by(5) %>%
+   add(5)         %>%
+   { 
+     cat("Mean:", mean(.), 
+         "Var:", var(.), "\n")
+     sort(.) %>% head
+   }
Mean: 5.661014 Var: 15.23286 
[1] 0.8218569 0.8976581 1.8677309 3.4730581 5.9182166 6.6475389

通過{}包裝的代碼塊,就可以很方便的完成多少處理的複雜操作。

4.3 %>%傳遞到函數

傳遞到函數和傳遞到代碼塊設計是類似的,是把一個數據集傳給一個匿名函數,進行復雜的數據數據的操作。在這裏,我們會顯示的定義數據集的名字作爲匿名函數的參數。

比如,對鳶尾花數據集進行處理,只保留第一行和最後一行作爲結果。


> iris %>%
+     (function(x) {
+         if (nrow(x) > 2) 
+             rbind(head(x, 1), tail(x, 1))
+         else x
+     })
    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1            5.1         3.5          1.4         0.2    setosa
150          5.9         3.0          5.1         1.8 virginica

這裏x就是iris數據集,作爲了函數的顯示參數,被應用於後續的數據處理過程。

通過對magrittr的學習,我們掌握了一些特殊的R語言代碼的編程技巧,用magrittr包寫出的R語言程序,與傳統的R語言代碼是有區別,可以你的程序很簡單、很高效。

                </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章