利用 STATA 處理巨量資料

CW Wayne Yeh
6 min readSep 21, 2021

--

這節整理利用 STATA 處理大資料時的注意事項與建議。從典型、實證上常用的行政資料到社群網路的行為資料(pageview、click)與平台交易的資料,分析人員有越來越多機會接觸巨量資料或所謂的大數據。一旦分析的資料量變得很大,分析資料的成本、犯錯的成本必然會劇增。這節我想整理一些利用巨量資料研究時的心得。我不算是 CS 背景的人,因此不會從原理的角度提出建議,而是從一個研究人員的角度提出注意事項與建議。

儲存

使用龐大的資料時,資料的存取就值得我們多花點心思。我們可能不希望佔用太多電腦的儲存空間,另外我們也會需要小心避免把記憶體撐爆,尤其是在合併資料時。在 2–1 的序言中,我提到 STATA 的變數類型基本上就是字串與數值這兩種,不同的變數類型攸關他們可以怎麼被操作與運算。不過字串與數值還可以依照儲存類型再進一步地分類:

  • 數值變數可以儲存成 byte、int、long、float、double。這幾種儲存類型分別占 1byte(位元組)、2byte、4byte、4byte、8byte。不同的儲存類型可表示的數值範圍也有差異,比如說:1byte 只能表示 -127 到 100 的整數。更多的整理可以參考擷取自 help data type 的 Fig 5-3。¹
STATA 資料儲存類型
Fig 5–3 STATA 資料儲存類型
  • 字串變數則是存成 str<n> 的形式,其中 <n> 是字串變數的長度(幾 byte),比如說 ”neko”可以存成 str4,但無法存成 str3。另外,中文字的長度和英文數字與符號不同,要看中文字是如何編碼而定。
  • 很長的字串變數會佔用很多空間,像是身份證字號就佔了 10byte(str10),更不說長串的中文地址了。
  • compress 可以在不影響資料的前提下,藉由改變儲存類型以節省空間。
  • recast 可以改變變數的儲存類型。請注意,改成更省空間的儲存方式時,要小心避免影響數值(超出可儲存的範圍)。
  • gen <storage type> <var>= 我們也可以在生成變數時主動指定儲存類型。比如說,當我們要生成一個 dummy,可以主動地指定存成 byte,像是 gen byte student = 1 if age <= 22
  • describe 其實也會展示每個變數的儲存類型。

替代跑得慢的指令

換成較快的 STATA 指令
Fig 5–4 換成較快的 STATA 指令

STATA 有些指令其實跑得很慢,當資料龐大時會很明顯。這時我們可以嘗試一些替代的指 令,以期更快達成目的。以下我擷取了一些例子說明:²

  • egen 指令其實跑得不快。在許多情形下其實可以被花點巧思的 gen 取代。比如說 egenmax() 可以改寫成像 Fig 5-4 中的指令。根據模擬資料的測試:在 1000 萬筆、200 萬個組別的資料中,egen 花了 2.05 秒,然而 gen 只花了約 0.26 秒。
  • 彙整資料至組別的 collapse 是一個很慢的指令。我在 3–2 也有提到,我們其實可以利用 byegengen 做到同樣的事。
  • reshape 是個很花時間的指令,建議不要在大資料中使用。³

那麼我們是否就完全捨棄那些比較慢的指令呢?其實不一定。首先,替代掉這些指令常常意味著要多寫幾行 code;相較之下,原本的指令大多都有很好的可讀性(collapse 是我覺得很好讀且 informative 指令之一)。其次,取代掉這些指令意味著要冒著結果變動(或犯錯)的風險。STATA 既有的指令大多都很 robust,如果要改成自有的流程的話,需要注意可能的後果。最後,作為研究與分析人員,效率面的優化雖然重要,但仍然應該以分析邏輯與解讀為首要目標。

監測時間

這邊也稍微提一下 timer 的指令。 timer 可以用來計算執行某段 code 的時間。掌握執行時間很重要,這決定了你開始執行某段 code 後可以睡多久(笑)。其他原因還有:管理研究流程、比較不同程序、估計花費時間等等。

用 preserve 暫存資料

使用大檔案時,我們有時會希望分析或存取子樣本,並同時保持目前的大檔案不被影響。下列幾個可能應用到的場景:

  1. 想像你在 id time 的資料中,計算了每個時點(time)的平均薪資並希望繪製生涯薪資軌跡(以 time 為 x 軸,平均薪資為 y 軸)。你先把資料把資料彙整至 time 的 level 上(collapseby time: ),但麻煩的是:你後來又想新增分性別的平均薪資。這時要再把原本的資料讀回再加上前處理,就會花上不少時間。
  2. 又比如說,你想把大檔案依照 time 切分儲存成數個小檔案,想一下應該就會發現,不斷地讀取大檔案、篩選並存成小檔案,其實很花時間。

說起來,我們其實是希望暫時別因為彙整或篩選等操作,影響手上的大檔案,但根本的問題在於 STATA 很任性地只允許讀取一份檔案。此外 STATA 也沒有標準的回復指令,不過呢,STATA 有個功能相似的 preserve ,可以幫助我們處理這些困擾。以下說明:

  • preserve 可以記得現在資料的狀態,並在指令結束後(run/do),回復 preserve 前資料的狀態。
  • preserve 的指令結束前(run/do),如果遇到 restore 的話,就會強制回復 preserve 前的狀態。
  • 基本上,使用 preserve 的時機都是當我們暫時不想更動手上、已讀入的資料時,尤其當重新讀取回資料的成本很高。

補充

條列其他處理巨量資料的技巧

  • 查閱資料時不一定要全部讀進來,可以先用 describe using <path> ,來查閱變數列表、樣本數以及(很重要的)資料大小等資訊。
  • 用 merge 合併資料時可以善用 keepsing() 來挑選需要使用的變數。
  • use <varlist> if <cond> using <path> 可以依條件 <cond> 讀入檔案<path>以及特定變數 <varlist>

[1]: Fig 5-3 上下界不對稱是為了保留給 missing(STATA 內其實並不只有一種 missing,雖然幾乎只會用到 . )。

[2]: 這些例子考自 https://back.nber.org/stata/efficient/。我自己也有實際在我的 STATA 上用模擬資料測試過。

[3]: 從這則 STATA Forum 的討論中可以看到,reshape 其實可以被 byvar[j]expand 等語法給取代並大大地縮短時間。

--

--

CW Wayne Yeh

資料分析/閱讀筆記/生活雜感。我是葉政維,台大經研畢,目前是樹鋸分析師🪚,正在職場站穩腳步,也在探索什麼是好的生活。