[原] 數據科學的文檔革命
概述
隨著近年來,Rstudio 通過 shiny 將R語言推向Web化,Rmarkdown 藉助 Shiny 已經不斷演化形成了一個動態可交互文檔生態。對於數據科學的研究可以說已經是Every Thing in Rmd!本文我將介紹Rmd如何以文檔定義應用的方式(Docs As an App)成為數據科學中的標準交付。
什麼是Rmd
Rmd 是Rmarkdown的文件保存格式,而Rmarkdown 是一個由謝益輝老師(XRAN)十年磨一劍的動態可重複文檔解決方案。參考前文:基於RStudio Webinars的統計報告Web化與工程化實踐總結,我們可以知道,Rmarkdown緣起謝大大對LaTex論文寫作的深惡痛絕,在謝大大深入研究各種姿勢的論文撰寫方式後決定自成一派,於是推出了Rmarkdown極大地簡化了論文寫作的排版問題並快速成為了學界的論文寫作新標準。
借用謝大大的例子,你可以運行下面的代碼來生成一個pdf的beamer。
download.file("https://raw.githubusercontent.com/rstudio/webinars/master/13-R-Markdown-Ecosystem/presentation.Rmd",destfile = "presentation.Rmd") # 下載Rmd文件ndownload.file("https://raw.githubusercontent.com/rstudio/webinars/master/13-R-Markdown-Ecosystem/references.bib",destfile = "references.bib") # 下載bib引用nrmarkdown::render("presentation.Rmd", rmarkdown::pdf_document()) # 生成pdfn
yaml中可以指定bibliography的路徑來解決引用文獻的問題。
---ntitle: "Sample Document"noutput: html_documentnbibliography: bibliography.bibn---n
這裡的引用我們可以很容易在Google Scholar生成,當然,除了BibTeX,Rmd 也支持 EndNote,RefMan,RefWorks以及直接在yaml中的引用方式。
---nreferences:n- id: fenner2012an title: One-click science marketingn author:n - family: Fennern given: Martinn container-title: Nature Materialsn volume: 11n URL: http://dx.doi.org/10.1038/nmat3283n DOI: 10.1038/nmat3283n issue: 4n publisher: Nature Publishing Groupn page: 261-263n type: article-journaln issued:n year: 2012n month: 3n---n
Rmd 原理分析
如果有用過APIDoc或者Swagger這樣的API文檔撰寫工具,或者利用Jekyll(Ruby)、Hexo(JS)、Pelican(Python)搭建個人網站的開發者應該很容易就明白,Rmarkdown的原理和這些應用非常類似。
靜態渲染
首先,我們利用markdown事先聲明好一定的文檔格式。其次,利用文檔解析器對markdown中的內容和dom樹做解析。最後,再用相應的css、js做渲染生成h5頁面,這樣一個文檔定義應用的過程就完成了。
動態渲染
而文檔的動態化則是用一個後端伺服器來host這些前端html頁面資源。這裡Rmarkdown可以利用Shiny-server作為後端伺服器,在運行前端h5頁面的同時通過後端的伺服器做實時的數據處理,實現文檔的動態化。(在文章最後會舉一些相應的例子)
注意,這裡如果在shiny-server的相關目錄下同時存在shiny的app.R文件,shiny-server將會優先識別app.R而不是.Rmd。
想要動手嘗試的同學可以參考前文:打造數據產品的快速原型:Shiny的Docker之旅。
數據科學與 Rmd
首先,參考前文:數據科學部門如何使用Python和R組合完成任務,讓我們來回顧一下之前談到了數據科學理論上會經歷的幾個階段:
需求定義=》數據收集=》數據轉化=》數據分析=》數據可視化
而在需求快速變化的項目初期,為了快速確定需求,如何敏捷打造最小可用原型(MVP)顯然比項目工程化來得更有意義。
所以在現實中,這時候流程常常就會縮短成:
需求定義=》數據整理=》數據可視化
場景一:需求分析多格式文檔交付
需求定義階段,我們需要通過大量的Email、word、paper、keynote、訪談等等資料針對相關的業務場景進行初步了解,這一階段我們最大的需求不是著急碼代碼,而是在起跑之前看準方向,系好鞋帶,弄明白真實的業務需求,對實際需求進行優先順序排序(做什麼)。
在需求定義階段,每次訪談、實地考察、活動會議等等都需要我們產出相應的研究紀錄。經過來來回回的幾輪討論,我們還需要畫很多流程圖、示意圖等等,最後產出一份完整的產品需求分析報告(為什麼做)和產品需求文檔(怎麼做)。
在這個過程中,很多細節都需要反覆推敲,多方論證(撕逼),而這個環節也是數據科學最有趣的一環。
面對大量的文檔輸出,很多人跟我一樣都會首先想到馬克飛象。馬克飛象名聲在外,是很優秀的一款產品。不過對於文檔的同步目前是收費的服務,很可惜,因為預算有限,所以我還是沒有考慮它,暫時是通過svn/git的方式解決雲端同步的問題。
參考前文:解密Airbnb的數據科學部門如果構建知識倉庫,作為一個謝大大的死忠,我很自然選擇了 Rmarkdown 作為我文檔輸出的首選工具。一方面,我不僅可以方便地嵌入本地或者網路上的圖片,通過』[@]』語法也可以利用yaml中的信息直接引用相關的參考文獻。另一方面,在網路情況非常糟糕的情況下,我也不必擔心通過Web編輯無法編輯的問題,而所有的編輯結果都會被存放在Rstudio的.Rdata文件中,實現和Web編輯器類似的實時編輯功能。
通過設置自己喜歡的css樣式,我還可以個性化導出html文檔,統一輸出團隊的VI系統,這方面Airbnb是一個很好的榜樣,我們可以參考前文:解密Airbnb的數據科學部門如何使用R語言。你也可以參考V2EX里大家對於Markdown樣式的熱烈討論。
在需求報告的討論環節,在yaml中簡單聲明shiny的runtime,再將Rmd文檔上傳到shiny-server上就可以在網路上(內網或外網)多人多端同步瀏覽,在Mac找不到轉化頭做投影的時候非常有用。
---ntitle: "文檔定義應用:數據科學 Everything in Rmd"nauthor: "Harry Zhu"ndate: "May 13, 2016"noutput: html_documentnruntime: shinyn---n
更為實用的是,如果需要評審修改文檔,我們也可以導出word來互相評審修改,我們甚至還能直接導出目錄,完全不需要自己再手動生成目錄。
在word和html之間自由切換,進可審閱、退可編輯,顯然,在現實中我們沒法決定我們身邊的人是喜歡word還是喜歡html。
當然,除了html和word,Rmd還可以在多種酷炫的keynote、dashboard、pdf之間相互轉化,不過這裡由於篇幅有限暫時不展開討論。
更多高級的Rmarkdown文檔轉換技巧可以前往rmarkdown官網
場景二:數據整理多語言混編
之所以說原來 數據收集=》數據轉化=》數據分析 的流程可以簡化為數據整理主要是因為這裡數據源獲取的方式一般比較dirty,比如從一個Excel的 xlsx文件讀取、通過現場勘查記錄自己製作的臨時數據又或者通過Python爬蟲抓取的一部分數據、從某些文章中直接複製粘貼的數據等等。而處理這些dirty的數據往往都會採用比較crud的方式,並不會做非常詳細的分析工作(很多公司的數據分析實習生工作都是從這裡起步的)。
在數據整理的過程中,我們可能會用到多種編程語言或者工具(比如Python、R、awk)、多種文件格式(比如csv、xlsx、json、pdf、word),這時候為了兼容多種工具並保證研究的可重複性,我們非常需要在一個文檔中運行多種編程語言。
通過knitr的代碼塊管理,我們可以輕鬆選擇代碼編譯環境來兼容多種編程語言,比如這樣:
```{python}
x = 『hello, python world!』
print(x)print(x.split(『 『))import matplotlib.pyplot as pltplt.plot(range(10))plt.show()```當然我們也可以自定義代碼編譯引擎:> system("which python") # 查到 python 引擎的默認路徑n/Users/harryzhu/Library/Enthought/Canopy_64bit/User/bin/pythonn
進一步定義 python 引擎的路徑:
```{r engine=python,engine.path=』/Users/harryzhu/Library/Enthought/Canopy_64bit/User/bin/python』}
x = 『hello, python world!』
print(x)print(x.split(『 『))import matplotlib.pyplot as pltplt.plot(range(10))plt.show()```更多高級的knitr代碼塊使用技巧可以移步謝大大的knitr官網
場景三:數據產品敏捷響應
數據科學部門在推進項目的過程中如果只是拿著一些示意圖和文檔就去和各個部門討論而沒有拿出實際可用的產品必然會出現幾個問題:
交流低效。對產品和需求的理解停留在紙面,往往多方反覆溝通後對產品依賴概念模糊。
信心不足。當我們沒有拿出實際可用的產品,項目戰線上的成員對項目的認可度比較低,各自相應的投入也就不會太多。
眾所周知,數據可視化是R非常擅長的領域。得益於R語言社區大量開發者對大量JS作圖框架熱情洋溢的封裝,我們可以利用R介面調用諸如echats、highcharts、leaflet、datatable這樣的前端組件,輕鬆完成數據產品的快速原型。
現在通過shinydashoboard、flexdashboard,我們可以快速開始數據產品的原型構建,而這一切都可以輕鬆集成在一個.Rmd文件中。
shinydashboard
shinydashboard官網 Demo: https://gallery.shinyapps.io/087-crandash/
GitHub源碼地址:shiny-examples/087-crandash at master · rstudio/shiny-examples · GitHub這裡你可以執行運行示例。
# 安裝依賴ninstall.packages(c("shiny", "dplyr", "htmlwidgets", "digest", "bit"))ndevtools::install_github("rstudio/shinydashboard")ndevtools::install_github("jcheng5/bubbles")ndevtools::install_github("hadley/shinySignals")n# 運行nshiny::runGitHub("rstudio/shiny-examples",subdir = "087-crandash")n
---ntitle: "something"nauthor: "Harry Zhu"ndate: "May 13, 2016"noutput: html_documentnruntime: shinyn---nnlibrary(shinydashboard)ndashboardBody(n tabItems(n # 第一個tabn tabItem(tabName = "dashboard",n fluidRow(n box(plotOutput("plot1", height = 250)),nn box(n title = "Controls",n sliderInput("slider", "Number of observations:", 1, 100, 50)n )n )n ),nn # 第二個tabn tabItem(tabName = "widgets",n h2("Widgets tab content")n )n )n )n# 直接在後面定義作圖函數nset.seed(122)n histdata <- rnorm(500)nn output$plot1 <- renderPlot({n data <- histdata[seq_len(input$slider)]n hist(data)n })n
省去的ui和server的定義,直接渲染Rmd。
flexdashboard
flexdashboard Demo: https://beta.rstudioconnect.com/jjallaire/htmlwidgets-highcharter/
開源源碼如下:
``n—-title: "Sales Report with Highcharter"nauthor: "Joshua Kunst"noutput: n flexdashboard::flex_dashboard:n orientation: columnsn social: menun source_code: embed—-`
``````{r setup, include=FALSE}library(highcharter)library(dplyr)library(viridisLite)library(forecast)
library(treemap)library(flexdashboard)thm <-
hc_theme(colors = c(「#1a6ecc」, 「#434348」, 「#90ed7d」),chart = list(backgroundColor = 「transparent」,style = list(fontFamily = 「Source Sans Pro」)),xAxis = list(
gridLineWidth = 1))```
Column {data-width_=600}
-----------------------------------------------------------------------### Sales Forecast
```{r}
AirPassengers %>%forecast(level = 90) %>%hchart() %>%hc_add_theme(thm)```### Sales by State
```{r}
data(「USArrests」, package = 「datasets」)data(「usgeojson」)USArrests <- USArrests %>%
mutate(state = rownames(.))n <- 4
colstops <- data.frame(q = 0:n/n,c = substring(viridis(n + 1), 0, 7)) %>%list.parse2()highchart() %>%
hc_add_series_map(usgeojson, USArrests, name = 「Sales」,value = 「Murder」, joinBy = c(「woename」, 「state」),dataLabels = list(enabled = TRUE,format = 『{point.properties.postalcode}』)) %>%hc_colorAxis(stops = colstops) %>%hc_legend(valueDecimals = 0, valueSuffix = 「%」) %>%hc_mapNavigation(enabled = TRUE) %>%hc_add_theme(thm)```Column {.tabset data-width_=400}
-----------------------------------------------------------------------### Sales by Category
```{r, fig.keep=』none』}
data(「Groceries」, package = 「arules」)dfitems <- tbl_df(Groceries@itemInfo)set.seed(10)
dfitemsg <- dfitems %>%
mutate(category = gsub(「 「, 「-「, level1),subcategory = gsub(「 「, 「-「, level2)) %>%group_by(category, subcategory) %>%summarise(sales = n() ^ 3 ) %>%ungroup() %>%sample_n(31)tm <- treemap(dfitemsg, index = c(「category」, 「subcategory」),
vSize = 「sales」, vColor = 「sales」,type = 「value」, palette = rev(viridis(6)))highchart() %>%
hc_add_series_treemap(tm, allowDrillToNode = TRUE,layoutAlgorithm = 「squarified」) %>%hc_add_theme(thm)`### Best Sellers`nn```{r}nset.seed(2)nnnprods <- 10nndfitems %>% n sample_n(nprods) %>% n .$labels %>% n rep(times = sort(sample( 1e4:2e4, size = nprods), decreasing = TRUE)) %>% n factor(levels = unique(.)) %>% n hchart(showInLegend = FALSE, name = "Sales", pointWidth = 10) %>% n hc_add_theme(thm) %>% n hc_chart(type = "bar")nnn
我們可以看到這裡的排版方式被大大的簡化了,我們只需要定義好Column和Row,然後在相應的代碼塊里盡情的作圖就可以在前端頁面上可視化了,相比之下css真是操碎了心。
更多關於flexdashboard的高級技巧可以前往flexdashboard官網
echarts
由於大家對echarts的呼聲很高,這裡再演示一下echart在R中的調用。
# 下載包ninstall.packages(n recharts,n repos = c(http://yihui.name/xran, http://cran.rstudio.com)n)n# 畫圖nrecharts::echart(iris, ~Sepal.Length, ~Sepal.Width, series = ~Species)n
另外一個牛逼的功能是,通過REmap這個包(已經封裝了各種百度API),我們可以將物流的收貨地址直接輸入不必太規則的中文文本即可實現地圖數據的可視化。而且,最近特別火的莆田系醫院數據爬取及分布可視化大多也是通過REmap包實現的。
更多echarts的作圖這裡不一一介紹,高級技巧可以前往recharts官網或者SupStat 郎大為:REmap。
Notebook
根據雪晴數據網的最新消息,RStudio 也已經實現了類似iPython的Notebook功能。
下面是雪晴數據網的教程:
配置步驟
- 下載最新的RStudio每日版
- 下載最新版本的Rmarkdown包:
devtools::install_github("rstudio/rmarkdown")n
- 設置選項:Tools -> Global Options -> Rmarkdown -> Enable R Notebook -> Apply
- 像往常一樣打開一個新的Rmarkdown文件
- 設置YAML輸出選項:將output: html_document 改為 output: html_notebook: default
躺坑排雷
在安裝最新版本後,由於我的鏡像源設成了 MRAN,導致這裡更新的 evaluate和knitr包的版本過低需要指定CRAN源重新升級。
> getOption("repos")n CRAN n"https://mran.revolutionanalytics.com/snapshot/2015-08-27"n
install.packages(c("evaluate","knitr"),repos="http://mirror.bjtu.edu.cn/cran/")n
Notebook API
通過 Notebook API,我們還可以把任意的Rmd文檔輸出成Notebook。
rmarkdown::render("FinanceR.Rmd",out_format="html_notebook")nnotebook <- parse_html_notebook("FinanceR.nb.html")n
參考資料
- R Markdown: Integrating A Reproducible Analysis Tool into Introductory Statistics
- R Markdown example showing figures & tables with captions, equations, inline R values and references with a Zotero library
- Playing with R, Shiny Dashboard and Google Analytics Data
- 阿里巴巴阿外:基於R的數據分析平台
- R Markdown Notebooks
- Setting Up New R Notebook
推薦工具
文末,推薦一款類似於 Airbnb 用 Flask 生成博客的解決方案,可以有效在團隊內部做知識倉庫的共享:
- Leanote:專為碼農打造的免費開源私有雲筆記
比較可惜的是,我嘗試了多個 Leanote 的Docker方案都沒有成功,如果讀者有成功案例歡迎在留言區留言。
另外,還推薦一個類似於 Endnote 的論文神器:
- Zotero
利用 Zotero 我們可以輕鬆的通過 drap and drop 實現多人協同論文跟蹤管理的功能,並且支持Chrome插件、多平台客戶端。
作為分享主義者(sharism),本人所有互聯網發布的圖文均遵從CC版權,轉載請保留作者信息並註明作者 Harry Zhu 的 FinanceR專欄:FinanceR - SegmentFault,如果涉及源代碼請註明GitHub地址:harryprince (HarryZhu) · GitHub。微信號: harryzhustudio
商業使用請聯繫作者。
推薦閱讀:
※老將陳彤加盟一點資訊,移動資訊戰事升級至「人」
※9月|新媒體營銷運營熱點日曆表
※她是好奇心日報的總編輯,她想分享好奇心日報是如何運營的 | MindTalk
※給不了你結婚證,那就給你房產證吧!
※最全互聯網代理項目一覽,你聽過幾個?