新聞動態
公司動態
會議與講座
精彩資料
會議報導
SCI&基金&講座資料
MedSci服務與公司動態
會議資訊
常見問題FAQ
專業知識
疾病標準指南
醫學英語
醫學統計與圖表製作技巧
研究動態與學術教育
SCI論文寫作與基金申請
研究動態
Newsletter
當前位置:首 頁 >> 新聞中心: Sweave:打造一個可重複的統計研究流程

Sweave:打造一個可重複的統計研究流程

添加時間:2015-9-22

我們都痛恨統計造假。我們都對重複性的工作感到厭倦。如果你同意這兩句話或這兩句話適用於你的現狀,那麼本文將介紹一套開源、免費的工具來克服這兩個問題。當然,前提是你願意改變,這裏的工具可以讓這兩種現象沒有藏身之地,但無法改變造假和重複勞動的現實。以下為吊胃口視頻(牆外觀眾可以看Vimeo;牆內看不到視頻的可以任選一個鏈接下載本視頻的AVI文件:鏈接1鏈接2鏈接3):

1. 統計研究流程

“收集、整理、分析和表述數據”是從流程上對統計學的定義,大部分統計研究都會全部或部分包括這四個步驟。其中,收集和整理數據有相當大部分的體力活(抽樣設計除外),分析數據基本上是腦力活(當然也有人把它做成了體力活,Excel可能是個代表),表述數據則是二者兼有。統計造假無非有兩種:一種是捏造數據,另一種是捏造結果。前者很難從工具的角度解決,它是個道德問題,但如果數據源是大家都可以公開獲得的,那麼這個造假也可以從工具角度消除;後者則是我們更容易控製的,比如對於刊物的審稿編輯來說,他可以要求作者提供數據分析的詳細過程,用以檢驗整個分析是否可以在自己手中被重複生成(reproduce)。

整個統計分析的流程如果能一遍成功,那麼我們可能是太幸運了,更多情況是,我們要一遍又一遍重複勞動,因為這個流程中出差錯或變故的可能性太多了。例如,發現數據源中有錯誤數據,或是過了一段時間又有新數據增加進來,或是分析的目標變量改變了,或是變量變換的計算公式變了,等等。通常我們麵對變化的解決方案就是從頭再來:更新數據,重新計算,重新複製,重新粘貼結果,重新調整報告格式……這一係列過程之所以變得不可自動重複進行,本質原因是我們把本應該流程化的操作分解成了不可替代的手工勞動。什麼樣的手工勞動是不可替代的?最常見的可能就是複製粘貼和點菜單。例如從Excel表中複製一部分數據,粘貼到統計軟件中,點菜單“統計分析–>回歸–>選入因變量自變量–>輸出結果–>複製回歸係數–>粘貼到Word中並調整格式”。據我所知,目前計算機還沒有發展到能智能記憶這些手工操作過程、過一段時間之後再自動“回放”的地步。計算機能“記憶”和執行的隻有代碼。

2. 可重複的統計研究

所謂可重複的(reproducible)統計研究,就是一個研究結果既可以在作者手中生成出來,也可以“移植”到他人的平台中用同樣的工具重複生成出來,就像物理或化學或生物實驗一樣,需要每一位實驗者在相同條件下都能觀察到實驗結果。如果一個回歸係數僅僅在作者的結果中顯著,而在他人手中無法重現,那麼這就是不可重複的統計分析。這裏“無法重現”的原因有很多,包括:數據不可獲得、軟件不可獲得、分析代碼不可獲得或分析過程缺乏詳細說明。如果用“讀者可重複”的標準來要求統計論文或報告,當今(尤其國內)恐怕能發表的統計分析可能就不會剩下太多了,因為提供數據和分析過程似乎偏離了論文的主題。(題外話:在統計之都主站發表的文章中,凡是涉及到數據和分析的,都一律提供詳細過程,保證可重複性,這一點對於“珍惜版麵”的紙質刊物來說可能不太現實)

當然,有些統計研究不可重複是可以理解的,典型的如數據需要保密。但期望“透明”總是人類本性,即使我們不把它考慮為一個道德問題,也應該從自身工作便利角度考慮一下這個問題。

經過這一大段雲裏霧裏的介紹,我將要推薦的是用代碼工作,並且盡量在代碼中減少“硬編碼”部分,即:讓代碼具有廣泛適用性,而不要僅僅對一個特定大小的數據的特定變量做特定處理。即使使用“硬編碼”,也要考慮將來維護的便利性,例如隻需要改一下文件名,重新跑一遍程序,所有的流程可以不變就可以一步重新生成最終的報告。這可能嗎?當然。Sweave就是一種工具。

3. Sweave介紹

首先聲明,用代碼工作並不適合所有人和所有情況,盡管磨刀不誤砍柴工,但這把刀並不好磨。如果我們希望統計分析的流程能暢通無阻,那麼這個過程中我們使用的工具必須能相互“溝通”。從統計軟件中複製粘貼結果到Word中這樣的做法很難重複,是因為Word很難自動從統計軟件中獲取結果,換句話說,它們太難相互溝通。軟件之間溝通的最便利途徑就是大家都能基於源代碼工作,或基於純文本工作,因為即使回到石器時代,計算機總是能處理純文本文件的。Sweave生存的重要原因就是,LaTeX(一流的排版工具)和R語言都基於源代碼工作。下麵簡單介紹一些什麼是Sweave。

從形式上來看,Sweave也沒太多新奇之處:它無非就是將LaTeX文檔和R代碼混合起來,先調用R執行一遍這個文檔中有特定標記的代碼,並輸出相應的結果到這個文檔中(源代碼都被相應的計算結果代替),然後再調用LaTeX編譯文檔為PDF。了解動態網頁的人可能覺得這完全是動態網頁的概念——輸出是動態的,而非寫死的結果。舉個例子,Sweave文檔看起來是這樣的:

\documentclass{article}
\title{A Test Sweave Document}
\author{Yihui Xie}
\usepackage{Sweave}

\SweaveOpts{pdf=TRUE, eps=FALSE}

\begin{document}
\maketitle

We can take a look at some random numbers from N(0, 1):
<<test1>>=
x = rnorm(5)
x
@

The 3rd number is \Sexpr{x[3]}.

Draw a plot for the iris data:
<<test2, fig=TRUE, results=hide>>=
plot(iris[, 1:2])
@

\end{document}

表麵看來這就是一篇LaTeX文檔,隻不過其中插入了一些用<<>>=標記開頭、用@結尾的R代碼段(PHP程序員可以想象<?php ?>)。R自帶一個Sweave()函數,可以用來預處理這些代碼段以及其中的\Sexpr{}宏。我們可以將以上代碼保存為一個.Rnw文件如myfile.Rnw,然後在R中Sweave('myfile.Rnw'),這樣myfile.Rnw就被替換為myfile.tex(即LaTeX文檔),而其中的內容則被替換為了相應的輸出:

\documentclass{article}
\title{A Test Sweave Document}
\author{Yihui Xie}

\usepackage{Sweave}

\begin{document}
\maketitle

We can take a look at some random numbers from N(0, 1):
\begin{Schunk}
\begin{Sinput}
> x = rnorm(5)
> x
\end{Sinput}
\begin{Soutput}
[1]  0.74541470 -1.49287775 -1.66834084  0.08252667  0.92772016
\end{Soutput}
\end{Schunk}

The 3rd number is -1.6683408359077.

Draw a plot for the iris data:
\begin{Schunk}
\begin{Sinput}
> plot(iris[, 1:2])
\end{Sinput}
\end{Schunk}
\includegraphics{myfile-test2}

\end{document}

從這個極端簡化的例子來看,我們完全不必複製粘貼結果,比如我們生成了一個變量x,從中提取第三個數字x[3]輸出在正文中,而不必寫死-1.668;圖形也一樣,它可以根據數據動態生成,而不必先畫好一幅圖再插入文檔中。最後我們運行pdflatex編譯這份文檔,就得到了最終的報告(本例PDF下載)。

為什麼Sweave是一個具有“可重複”性質的工具?原因就是代碼控製了流程。比如我們可以用代碼讀取數據,進行變量轉換,建模,輸出我們需要的部分(而不是甭管有用沒用、一氣兒輸出三十頁統計報告)。代碼的靈活性是無限的,它也忠實記錄了統計分析的詳細過程,所以我們不可能捏造結果,另外也能大大減輕手工重複勞動。

4. 懶人的工具

既然Sweave是基於LaTeX的,那麼先說LaTeX:我用了幾年LaTeX,對它是又愛又恨。它在排版上當然是超一流的工具,輸出的PDF文檔整潔利索、結構分明,一看就是科學範兒,但我實在痛恨敲LaTeX命令。傳說中的LaTeX介紹都說它能讓你專注於寫作而不必擔心格式,可對我來說這是不太可能的,確實LaTeX可以自動安排格式,而且調整起來也方便,但我一直覺得寫LaTeX文檔很不直觀,我對章節分布很難形成概念,光是\section{}這種命令,讓人很難有章節標題的感覺;還有圖表,完全不形象,看著\includegraphics{}根本不知道這裏插的是哪幅圖。所以從某種程度上來說,LaTeX本身是很考驗人的記憶力的,你要記住哪個標簽是什麼意思,現在在寫哪一節,等等。直到後來用上了LyX,這些“不滿”才一掃而光。LyX是一個看起來“所見即所得”(WYSIWYG)的工具,但實際上是“所見即所想”(What You See Is What You Mean),換句話說,它是披著Word外衣的LaTeX,盡管內部運行方式和Word截然不同。如果你熟悉LaTeX,那麼打開LyX的菜單你將幾乎處處發現LaTeX的蹤跡。LyX對LaTeX用戶可以說是體貼細致入微:我們不必擔心特殊字符問題(都會被替換為\引導的字符或等價的命令),不必擔心圖片的格式(會自動轉換),不必擔心忘記引用某個需要的LaTeX宏包(比如插入圖片時會自動在導言區加上\usepackage{graphicx}),寫列表的時候不必反複敲\item命令(寫完一個,回車就會自動生成下一個),當文檔含有目錄時會自動調用兩次或三次pdflatex來編譯目錄,數學公式可以直接用眼睛看到,……易用性不亞於Word(前提是你懂LaTeX),排版質量高出Word百倍。這就是個“不給馬兒吃草又要讓馬兒跑得快”的懶人工具。

幸運的是這個懶人工具在設計時竟然也考慮了“文學編程”(literate programming,這翻譯有點奇怪),因此也就為Sweave嵌入LyX留下了鋪墊。先岔開話題回到第3節:Sweave的這種編程方式就是文學編程的一種實現,而文學編程如其字麵意思所指,是將程序融入文學作品中(這裏文學泛指普通文本),這些程序運行之後將輸出結果到文字中。與文學編程相對應的便是常見的結構化編程,也就是我們經常看見的大篇純程序代碼。文學編程的提出者為Donald Knuth——也就是TeX的作者。

LyX給文學編程留下的後路基本上在一個叫literate-scrap.inc的文件中(安裝目錄的Resources/layouts/或者用戶目錄的layouts文件夾下),它定義了文學編程文檔的輸出類型(literate),而另外在LyX的選項文件preferences中,我們又可以定義從literate到LaTeX的轉換器,也就是Sweave()函數或等價的Sweave處理方式——將Rnw文件用R運行得到tex文件。比如我們可以這樣定義:

\converter "literate" "pdflatex" "R -e Sweave($$i)" ""

這裏麵的血腥細節我就不詳細介紹了,後麵我會給出普通用戶可以接受的配置方式。總之這裏大意就是:literate類型的LyX文檔可以輸出為Sweave文檔,通過R執行轉化之後可以生成tex文檔,LyX此時再進來調用pdflatex編譯生成報告。這就是Sweave在LyX中的執行過程。在外麵看來,也就是本文開頭的視頻中所展示的過程。

5. 自動配置工具

配置這一套工具對於初學者來說顯得有些困難,因為這裏麵的新概念和高級概念太多了。比如可能大多數Windows用戶一輩子都不會遇到一個叫“PATH環境變量”的東西,還有LaTeX宏包的安裝過程,以及大量的命令行工作細節(例如命令行重定向),等等。即使拋開配置不談,光是LaTeX和R的學習也夠折騰好一陣子了。抱著“苦不苦想想紅軍兩萬五”的信念,這裏的三個月學習時間也許能替代十年的重複勞動(也許,隻是也許,不要輕易相信我的廣告)。

如果你已經裝好了LyX(及其配套LaTeX程序如MikTeXTeXLiveMacTeX)和R(版本 >= 2.12.0),整個配置過程隻需要在R中執行一句話:

source('http://gitorious.org/yihui/lyx-sweave/blobs/raw/master/lyx-sweave-config.R')

R會自動處理那些血腥細節:下載文件並解壓縮、複製到正確的文件夾,安裝需要的LaTeX宏包和更新LaTeX的文件名數據庫、重新配置LyX、設定環境變量PATH(放入R的bin路徑)。這段代碼適用於Windows、Ubuntu(理論上其它Linux發行版也不會有問題)和Mac OS。

大部分血腥細節的解釋參見:How to Start Using (pgf)Sweave in LyX in One Minute

這裏需要說明的是,我傾向於用pgfSweave包,它是對Sweave的一個擴展,R自身的Sweave盡管已經很強大了,但我對它有幾點不滿(非偏執狂請略過):

  • 它對代碼的自動整理功能:大多數人都痛恨閱讀源代碼,尤其是亂糟糟的源代碼(沒有空格沒有縮進),或者是沒有注釋的源代碼,為了不要折磨讀者,我們應該保持代碼的整潔,而另一方麵我們也不希望折磨寫代碼的人,因此我們需要一個自動整理代碼的工具。R自身的Sweave中有一個keep.source選項,若為TRUE,則完整保留用戶輸入的代碼並原樣輸出,這在很多情況下都很糟糕;若為FALSE,則會自動整理代碼,但代價是除掉注釋,這也很糟糕。這件事長期以來無法找到平衡點:既自動整理代碼,又保留注釋。最終這個問題被邱怡軒以一個小技巧部分解決了,但這種技巧並不“優雅”,所以指望R core收錄是無望了,最終我把這段代碼提供給pgfSweave包的作者被收錄在這個包中(嘿嘿,俺們草根有力量),所以pgfSweave有自動整理代碼的功能,並且可以保留注釋;注意pgfSweave包默認是不整理代碼的,必須在全局選項中設置tidy為TRUE才行,即插入LaTeX命令:\SweaveOpts{tidy=TRUE},然後在最開頭的R代碼段中使用options(keep.blank.line = FALSE),這樣能得到最好的輸出。
  • 圖形格式和大小設定:Sweave默認會生成PDF和postscript格式的圖片(分別可用pdf = TRUE/FALSE和eps = TRUE/FALSE控製),通常矢量圖當然比位圖強百倍,美觀、無損縮放,但R圖形有個小問題(純屬挑剔)就是字體,默認情況下圖形都是用無襯線字體(Windows底下如Arial字體),這和LaTeX文檔的字體可能不一致(盡管這個不一致可以在一定程度上減輕),而pgfSweave直接從根本上解決了這個問題,因為它是以tikz格式記錄R圖形,這種格式實際上就是LaTeX代碼,因此編譯文檔的時候圖形也會被重新編譯,所以圖中的文本的字體和正文就完全一致了();另外,默認Sweave中圖形大小的控製方式也很不直觀,選項width和height是控製圖形設備的寬和高的,而非實際LaTeX文檔中圖形的寬高,我們隻能通過LaTeX命令\setkeys{Gin}{width=?}來設定(盡管這個也有辦法繞過去),而pgfSweave則默認采取了直觀的指定寬高方式,即:直接在選項中指定。
  • Sweave本身沒有緩存功能,這對於大批量的計算來說是個災難:所謂緩存就是計算結果被緩存在某個位置,下次運行Sweave文檔的時候如果代碼沒有修改,這些結果則可以不必被重新計算一遍,直接從緩存裏讀出來就可以了,這是cacheSweave包的功能; pgfSweave在這個基礎上更進一步,讓圖形也有緩存功能了。緩存可以讓整篇文檔在R層麵上的編譯速度加快(但事實上由於pgfSweave用tikz圖形而這種圖形需要LaTeX編譯,所以整體速度未必真的加快了);

LyX和pgfSweave包生成的圖形

6. 演示和問題

如果工具都準備齊全了,那麼可以下載兩個演示文件了解一下LyX/pgfSweave的效果(demo 1;或demo 2和相應的BibTeX文獻庫),其中一個例子簡單,一個複雜。簡單例子就像前麵視頻中顯示的一樣,隻是感覺一下基本功能;複雜例子實際上是我自己的一次作業,略作改編:它演示了從URL讀入數據,並進行一些極大似然估計、似然比檢驗和基於Delta方法的區間估計,還有簡單的t檢驗和畫圖(包括base圖形和ggplot2圖形)。其中圖表都是動態生成,幾乎沒有任何寫死的數字。

這個例子的數據在所有人都可以公開訪問的網站上,所以數據有可重複性;分析代碼全都嵌在LyX文檔中,所以分析過程也有可重複性。這裏麵不涉及到生成隨機數,所有計算都是確定性的,因此所有人用相同的工具可以重複生成我的結果。

除了可重複性之外,由於分析流程全都用代碼記錄,所以我不必擔心數據的提供者突然更換源數據中的幾個數值,如果發生這種情況,我隻需要重新點一次按鈕就可以了。當然,有時候要是數據發生了很大的變化,導致結論都變了,那麼就需要修改正文文本了(實際上結論也是有可能動態化的,這裏對於一份普通作業來說就沒必要搞那麼複雜了)。

另外,LyX文檔本身其實也是純文本文件,這也為文件的版本控製留出了一條路。我們可以使用版本控製工具SVN或GIT來管理我們的文檔,每次的修改和更新部分一目了然,而且可以團隊合作共同寫一份報告(版本控製工具會自動合並各位成員的修改),這比Word裏麵笨重的“審核修訂”功能又強了百倍,你再也不必總是發送郵件附件“某主題報告張三20101104.doc”或“某主題報告李四20101103.doc”給你的同事,然後用肉眼修訂。LyX本身支持SVN,(預計)到本年年末,LyX 2.0橫空出世時,這個版本控製功能將極大增強。這似乎有點離題,但我要說的是“透明”工作方式帶來的天然優勢。

免費的午餐是不存在的,用LyX和pgfSweave也要付出一些代價,這些可能的痛苦我都在上麵提到的英文博客中解釋了,這裏簡要提一下:

  1. 查錯略有些麻煩:如果R代碼沒有錯誤,那麼萬事大吉,點一下按鈕就可以生成PDF報告了,但這種情況恐怕少之又少,當代碼運行出錯的時候,LyX無法知道R出了什麼錯,它不會記錄R運行的日誌,這個問題可以通過命令行重定向解決。我在自動配置文件中已經寫好了,讀者可以不必關心細節,如果R運行出錯,那麼可以到LyX臨時目錄下找一個*.Rnw.log文件,這裏的*是你的LyX文檔名稱,臨時目錄可以在LyX菜單“工具–>選項–>路徑–>臨時文件夾”中找到,也可以自行設置(我通常把默認的目錄換成一個更“淺”一些的目錄,方便我查看日誌,要不然得走到係統默認的很深的隱藏目錄中去)。這個日誌文件會記錄R的運行過程,你可以從最後幾行看出來到底運行到哪個代碼段出了錯;
  2. 警惕LaTeX特殊字符:LaTeX本身大概有十來個特殊字符,如果要使用它們本身,則需要用\引導,比如百分號需要寫成\%,否則%意思是注釋;由於pgfSweave生成的是LaTeX代碼圖形,裏麵要是含有特殊字符則會讓LaTeX編譯失敗,這種情況可以手工處理,比如plot(x_1, y)會生成x軸標簽x_1,我們可以手工指定x軸標簽plot(x_1, y, xlab = "x\\_1"),這樣就不會編譯出錯了;另外一個辦法是看tikzDevice包的幫助文檔,它有辦法自動處理這樣的特殊字符;最後一個辦法就是不用tikz圖形,改用PDF圖形,在代碼段的選項中設定<<tikz=FALSE, pdf=TRUE>>=,損失樣式,換取“安全”;
  3. 超大圖形:如果一幅圖形非常複雜,則相應的tikz文件也會非常大,這會讓LaTeX抱怨內存不夠用,編不過去,這種情況下也可以取道pdf(例如我的碩士論文圖1和圖9);
  4. 使用lattice和ggplot2圖形的時候一定要記得print()圖形,否則圖不會被畫出來,這和base圖形是不一樣的;

可以看到,盡管這些痛苦存在,但並非跨不過去。隻是初學者一定要注意,免得處處受挫。

7. 小結

本文看似很長,但如果僅僅從使用角度來說,可能五分鍾就足夠你完成配置並寫出一篇動態統計分析報告了。這裏麵的細節太繁雜,為了避免“欲練此功,揮刀自宮”,我把配置過程揉進了一個R腳本,隨後的事情就是理解各種工具的工作原理(理不理解其實關係也不大)、學習基本用法(主要是各種Sweave選項,參見?RweaveLaTeX?pgfSweave),我想新手上手可能也不一定就那麼難。我要強調的是時刻保持挑剔的心態,不要製造垃圾代碼,讓文檔盡量簡潔。如果真把LyX當Word用(這裏選12號字、那裏強製換行),那就糟糕了——你沒領悟LaTeX的排版哲學。

統計的透明,僅僅靠道德宣傳和約束恐怕是永遠都不可能實現的,我們需要使用透明的工具。很多人認為工具隻是低層次的東西,然而也許我們能通過工具推動一套製度。本文也可以說是象牙塔中的一種想象,它是否能得到實際應用,還要經過實踐檢驗。不管怎樣,如果能真正步入Sweave殿堂,一切報表工具將是浮雲。

總之,自由軟件的自由,不僅僅是開源這麼簡單。

8. 參考資料

致謝

這裏要特別感謝愛荷華州立大學統計係579課程的同學們不斷向我反饋程序問題,才能使得這段程序日臻完善,讓看起來遙不可及的Sweave能飛入尋常百姓家。

醫學統計與圖表製作技巧相關的新聞


臨床研究學院課程推薦2:臨床醫學統計課程
臨床研究學院課程推薦4:SCI發表支持課程
臨床研究學院課程推薦3:SCI論文寫作指導課程
核查知情同意書的十個點
跟我一起從零學習stata統計軟件,從此統計不求人
臨床醫生為什麼要用數據庫開展臨床研究?
論文數據要慎用條圖
SparkR:數據科學家的新利器
大數據分類利器---支持向量機(SVM)入門介紹
web对话
live chat