R語言高效的管道操作magrittr

2021-03-02 表哥有話講

作者:張丹,R語言中文社區專欄特邀作者,《R的極客理想》系列圖書作者,民生銀行大數據中心數據分析師,前況客創始人兼CTO。
個人博客 http://fens.me, Alexa全球排名70k。

前言

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

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


目錄

magrittr介紹

magrittr安裝

magrittr包的基本使用

magrittr包的擴展功能


1. magrittr介紹

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

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

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


2. magrittr安裝

本文所使用的系統環境

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

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


3. magrittr包的使用

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

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

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

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

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

取10000個隨機數符合,符合正態分布。

求這個10000個數的絕對值,同時乘以50。

把結果組成一個100*100列的方陣。

計算方陣中每行的均值,並四捨五入保留到整數。

把結果除以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步

輸出的直方圖:

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

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

輸出的直方圖:

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

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

輸出的直方圖:

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

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

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

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

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

取10000個隨機數符合,符合正態分布。

求這個10000個數的絕對值,同時乘以50。

把結果組成一個100*100列的方陣。

計算方陣中每行的均值,並四捨五入保留到整數。

把結果除以7求餘數,並話出餘數的直方圖。

對餘數求和

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

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

> set.seed(1)> rnorm(10000) %>%+   abs %>% `*` (50)  %>%+   matrix(ncol=100)  %>%+   rowMeans %>% round %>% +   `%%`(7) %>% hist %>% sumError 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)

%$% 的作用是把左側數據的屬性名傳給右側,讓右側的調用函數直接通過名字,就可以獲取左側的數據。比如,我們獲得一個data.frame類型的數據集,通過使用 %$%,在右側的函數中可以直接使用列名操作數據。

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

下面定義一個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 z6   6 -0.8204684 f7   7  0.4874291 g8   8  0.7383247 h9   9  0.5757814 i10 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 z6   6 -0.8204684 f7   7  0.4874291 g8   8  0.7383247 h9   9  0.5757814 i10 10 -0.3053884 j

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

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

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

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

定義一個符合正態分布的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還有哪些功能。

符號操作符定義

%>%對代碼塊的傳遞

%>%對函數的傳遞

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   Species1            5.1         3.5          1.4         0.2    setosa150          5.9         3.0          5.1         1.8 virginica

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

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

天性「懶惰」的程式設計師總是會想各種辦法,來減少自己的代碼,讓代碼變得優雅,同時還能讓程序更可靠。什麼時候能把代碼寫得越來越少,那麼你就越來越接近高手!

R的極客理想:量化投資篇

金融投資學理論

R語言數據處理

量化投資策略

三個維度

教你如何將R語言技術應用於金融市場

相關焦點

  • R語言中的管道操作
    即便我使用R語言快2年多了,但是讀這本書還是受益頗多。(好吧,這兩年來我一直在舒適區原地踏步)這一篇學習筆記對應第13章:使用magrittr進行管道操作。關於管道這個概念,我最早在Linux系統中接觸,它是Unix系統設計哲學的體現,「組合小功能完成大任務」,比如說BWA比對後排序用管道的寫法就是bwa mem ref 1.fq 2.fq | samtools sort > align.bam在R語言接觸管道符號"%>%"是在學習 dplyr包時候,那個時候我以為這個符號是 Hadley
  • R語言必知必會之tidyverse(一):管道操作
    強烈建議大家,拋棄流傳甚廣的陳舊的數據操作方面的 R 語法,全面改用 tidyverse。——————————————————————————————下面先從第零篇,管道操作,開始。一. 簡介magrittr 包引入了管道操作,能夠通過管道將數據從一個函數傳給另一個函數,從而用若干函數構成的管線依次變換你的數據。
  • R語言 | 數據操作dplyr包
    [更新~] Python網絡爬蟲與文本數據分析公眾號只帶著Python字眼,卻分享著R語言,不務正業,任性了~dplyr簡介dplyr是R語言的數據分析包,很像python中的pandas,能對dataframe
  • R語言學習指南(3) tidyverse的基礎使用
    tidyverse是為數據科學設計的R軟體包,它包含(ggplot2、dplyr、tidyr、stringr、magrittr、tibble)等一系列熱門軟體包,學好tidyverse的使用可也讓你站上另一個高度,從而高效的處理數據,因此本文檔不僅僅做一些案例介紹,而是希望以較為正確的學習方法來介紹R語言,使大家少走彎路,快速入門掌握R語言
  • R tips:使用{}自由的使用管道
    一個管道操作的示意如下:library("magrittr")library("tidyverse")# 使用管道:rnorm(10) %>% sort() %>% plot# 如果不使用管道,函數嵌套變多,難以閱讀plot(sort(rnorm(10)))# 或者產生很多中間變量x <
  • 從另一個視角看 R 語言的方言 Tidyverse
    而 Tidy 則不然,需要更改成> library(dplyr)> thue2 <- thuesen %>% filter(blood.glucose < 7)老師首先要講授管道操作符 %>%,又一份額外的複雜性。
  • R數據處理基礎篇(一)
    在數據處理上,R語言本身自帶這一些操作方法,但是有三個主要的缺陷用法不夠簡潔易記在處理大量數據時,速度太慢完成有些需求過於麻煩,或者無法達成。但是這是R的開源好處就體現出來了,用戶開發了幾個高效處理數據的R包,它們不僅提高了運行速度,而且代碼簡明易記,如果熟練掌握,可以成為處理數據的一大利器。
  • 如何高效運行R語言
    進入正題 ==>高效的代碼減少函數調用和C/C++等語言強調數據類型不同,R語言為用戶隱藏了數據類型,這為R用戶帶來了方便,但是付出的代價就是運行效率。但是自3.4版本之後,R語言可以自動實現對自定義函數的預編譯。高效讀寫(I/O)R有很多文件讀寫函數。對於文本文件的讀寫,特別是表格文件,我們常用read.csv,readr::read_csv或者data.table::fread。
  • R語言可視化(二):折線圖繪製
    base#### other attached packages:## [1] reshape2_1.4.3 plyr_1.8.4 ggplot2_3.2.0#### loaded via a namespace (and not attached):## [1] Rcpp_1.0.1 knitr_1.23 magrittr
  • R語言data manipulation學習筆記之subset data
    taoyan:R語言中文社區特約作家,偽碼農,R語言愛好者,愛開源。
  • R語言數據清洗實戰——高效list解析方案
    如果你對R語言的list結構非常熟悉,又熟練控制流等函數的操作,自然可以通過構建循環來完成目標數據的提取。但是在數據量大、結構及其複雜的情形下,自建循環無論是性能還是代碼量上都很不經濟。在R語言環境中,我們最常遇到的list操作場景大概有以下三類(當然不含全部):1、統計模型的輸出結果:因為統計模型在跑完之後,通過會輸出一系列各種指標,比如及置信區間、判定指標和擬合值等,這些對象因為大小和長度不等,類型不一但是又必須輸出,所以只能交給list這個容器來盛。
  • R語言ggplot繪製峰巒圖繪製
    attached):## [1] Rcpp_1.0.5 pillar_1.4.2 compiler_3.6.0 plyr_1.8.4## [5] tools_3.6.0 digest_0.6.20 evaluate_0.14 tibble_2.1.3## [9] gtable_0.3.0 pkgconfig_2.0.2 rlang
  • R語言可視化(二十四):和弦圖繪製
    circlize")library(circlize)## Warning: package 'circlize' was built under R version 3.6.3## ========================================## circlize version 0.4.10## CRAN page: https://cran.r-project.org
  • 左手用R右手Python系列——模擬登陸教務系統
    最近在練習R語言與Python的網絡數據抓取內容,遇到了煩人的驗證碼問題,走了很多彎路,最終總算解決了。
  • R Markdown 詳細使用指南(一)
    建議:先閱讀《RMarkdown》後繼續向下閱讀在新建 R Markdown 文件後,利用編輯區域上方的 Knit 圖標以及右側的設置圖標,右鍵打開後,可以進行更為詳細的設置,比如:Output Options 中事實上,新建文件後,正文區域,第一段代碼十分重要,關係到全局設置:```{r setup
  • R語言可視化之UpSetR包
    base packages:## [1] stats graphics grDevices utils datasets methods base#### other attached packages:## [1] UpSetR_1.3.3#### loaded via a namespace (and not attached):## [1] Rcpp_0.12.13 knitr_1.17 magrittr
  • R 語言中常用的文件操作及字符串相關函數
    目前,談到 「R語言文件及路徑操作」,國內能夠搜索到的內容,基本上都是千篇一律,要麼是水文,搬運函數名和對應的功能,簡單羅列;要麼就異常豐富,直接把基礎函數裡所有相關的函數名全部羅列了出來。在這裡面,能夠追溯到較早的文章,在 2015 年左右,甚至更早。
  • R語言從入門到精通:Day6-R語言數據操作進階及控制結構
    最近經過前面幾次推文的學習,我們的R語言教程已經講解了快1/3的內容,相信大家對R語言已經有了初步的了解,特別是感受了R語言在數據處理領域的強大功能
  • 英語音標發音:清輔音[tr]的發音方法
    新東方網>英語>英語學習>口語>實用口語>正文英語音標發音:清輔音[tr]的發音方法 2012-12-12 11:56 來源:人人聽力 作者:
  • 市政排水管道快速高效勘察神器之無線高清管道潛望鏡
    管道潛望鏡適用於快速檢測、診斷管道內部的堵塞、坍塌、破裂等缺陷狀況,通過雲臺操作控制機頭的俯仰,從而獲得管道缺陷位置處的清晰圖像信息,同時可以對缺陷進行編碼和文字描述,併疊加字符到錄製的視頻文件中,管道潛望鏡實現市政排水管道快速高效勘察和維護養護方案的有效制定