用C++做資料分析 | CERN ROOT 教學[01] - 介紹


前言

用C++做數據分析?你瘋了吧?不是都用Python、R或是Julia之類的語言嗎?確實是如此,但是C++在這領域也有他的用武之地。像是必須在有限的資源裡作到最快速的分析、需要與底層系統溝通或是實時計算是最大要求時(或是當你在粒子物理界工作)。這系列文章希望能帶讀者使用C++與ROOT這個框架運行資料分析。

另外本系列假設讀者已經熟悉Linux和C++14的語法與使用。不會在另外講解語法。

ROOT

ROOT歐洲核子研究組織 (CERN) 為了分析大強子對撞機 (LHC) 於1994年開始開發的資料分析框架。作為科學界使用的分析程式,製作複雜圖表的功能是不可或缺的。ROOT除了基本的繪圖功能外更支援嵌入/輸出為LaTeX (如果讀者不清楚他是什麼,請當作他是進階版的Markdown)、3D圖表甚至動態修改圖表內容。

CMS實驗圖表。By Own work, CC BY-SA 3.0

除了繪圖以外,由於開始開發的年代相當早,其主要設計理念集中在如何有效的利用有限的資源上。所以ROOT內建C++直譯器/JIT Compiler、支援直接在硬碟上處理資料而不須預先載入至記憶體中,不管主機的記憶體有多少都能夠在一台機器上分析幾百GB甚至TB等級的資料。她所內建的PROOF分散式計算框架更算是當紅的Hadoop的先驅。ROOT也有強大的函數擬合(fitting)功能,能將任意的1元函數擬合至資料上。

安裝

如果您是Arch Linux的使用者,那安裝root相當簡單。

sudo pacman -S root

Fedora或是RedHat的使用者一樣可以透過套件管理器安裝ROOT

sudo dnf install root

Windows、OS X或是其他Linux發行版的使用者請到ROOT的下載頁面下載並安裝ROOT。筆者也不知道怎麼安裝。Arch Linux都幫我做好了:3

基本操作

ROOT基本的操作方式與Python、Juila跟R一樣。在終端機中輸入root後就會進入REPL模式。在這裡你可以輸入任何的C++程式碼,按下Enter後就會被執行。

❯ root
   ------------------------------------------------------------
  | Welcome to ROOT 6.18/04                  https://root.cern |
  |                               (c) 1995-2019, The ROOT Team |
  | Built for linuxx8664gcc on Sep 11 2019, 15:38:23           |
  | From tags/v6-18-04@v6-18-04                                |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

root [0] cout << "Hello ROOT!\n";
Hello ROOT!
root [1]

熟悉C++的讀者可能會有幾個疑問。「恩?C++的REPL?」與「沒有#include <iostream>using std::cout?」

針對第一個問題:對的,ROOT包含一個由著名的C++編譯器clang改寫而成的C++直譯器cling。預設ROOT支援C++14的所有語法,在Arch Linux的套件包裡面甚至支援了C++17。至於沒有#include的問題,ROOT會自動#include幾乎所有的標準函數庫(包括vector, algorithm, tuple 等等)並自動加入using namespace std。所以就算輸入也能夠使用STL

輸入.q或是exit(0) (其實就是呼叫STL的exit函數)即可離開ROOT。同時在啟動ROOT時加上-l參數可以讓ROOT不要顯示logo.

root [1] .q

❯ # 回到shell了!

值得注意的是,由於其語言是C++。所以編寫上可能會有些不方便。所以ROOT對C++的語法做了一些些微的修改。首先ROOT在需要時會自動宣告變數

root [0] i = 0; // i 並沒有被宣告。但是這行不會執行失敗
                // ROOT會自動翻譯成 `auto i = 0;`
root [1] cout << i << endl;
0
root [2]

另外如果程式碼的尾巴沒有分號也沒關係。ROOT會把她當作要求將結果(連同型別)輸出至螢幕上

root [0] i = 0
(int) 0
root [1] v = vector<float>(3) // 或是更複雜的型別
(std::vector<float, std::allocator<float> > &) { 0.00000f, 0.00000f, 0.00000f }
root [2]

除此之外就跟一般的C++一樣了。

C++腳本

有REPL固然好,但是實際使用上有些麻煩。這時候腳本就派上用場了。你可以將C++程式碼先寫好處存在檔案中,並在需要時執行她。編寫腳本的方式很簡單,在檔案裡面寫一個跟檔案同名的C++函數即可。

// macro1.cpp
void macro1()
{
    float pi = 3.1415;
    cout << "PI = " << pi << "...\n" << flush;
}

然後透過.x指令執行腳本。

❯ root -l
root [0] .x macro1.cpp 
PI = 3.1415...
root [1]

依照C++的變數生命週期規則 macro1裡的pi變數無法在外部使用。

root [1] pi
input_line_9:2:3: error: use of undeclared identifier 'pi'
 (pi)
  ^
Error in <HandleInterpreterException>: Error evaluating expression (pi).
Execution of your code was aborted.
root [2]

如果希望macro裡的變數能在外部使用(雖然我很不推薦這種寫法),你可以將函數的宣告移除。這樣ROOT就會將所有在macro中所有變數的生命週期延伸出來。

//macro2.cpp
{
    float pi = 3.1415;
    cout << "PI = " << pi << "...\n" << flush;
}

一樣透過.x 執行她

❯ root -l
root [0] .x macro2.cpp 
PI = 3.1415...
root [1] pi
(float) 3.14150f
root [2]

如果不想透過REPL執行腳本,你也可以在指令界面指定要執行哪個腳本。要注意的是ROOT預設會在執行完腳本後進入REPL。

❯ root -l macro1.cpp
root [0] 
Processing macro1.cpp...
PI = 3.1415...
root [1] // 繼續執行REPL

若想要執行完後馬上離開則要加上-q參數

❯ root -l macro1.cpp -q

Processing macro1.cpp...
PI = 3.1415...
❯

繪製函數圖形

講了這麼多,我們還是來做點跟資料分析有關的事情好了。像是畫出一些函數圖形!ROOT中使用TF1類別(T是所有類別的前綴,F1代表1維函數)來處理基本函數。

❯ root -l
root [0] f1 = new TF1("f1", "tanh(x)", -4, 4);
root [1] f1->Draw();
Info in <TCanvas::MakeDefCanvas>:  created default TCanvas with name c1
root [2]

下圖的視窗就會出現在你的螢幕上

簡單的還數圖形

點擊右上角的File->Save 即可將圖存成不同格式甚至LaTeX與C++程式檔。會將檔案存到當前工作目錄下。

儲存圖檔

當然我們也可以試試複雜一點的函數(我就不節圖了,直接存檔)

sin(cos(x)*pi)/x .....

小結

這是本系列的第一篇文章。在本文中介紹了ROOT的用途與基本操作。再接下來的文章中會講解如何匯入資料、執行分析等等。
有不好的第方還請見諒。

#ROOT #C++ #AI #Big Data #CERN







你可能感興趣的文章

[FE102] part 3

[FE102] part 3

JS 與瀏覽器的溝通與網頁事件處理

JS 與瀏覽器的溝通與網頁事件處理

Python 字典 dict 和集合 set 入門教學

Python 字典 dict 和集合 set 入門教學






留言討論