GNU Parallel 是一款功能強大的命令列工具,能有效提升資料處理效率,尤其在處理大量資料時,可將任務分散至多台機器或多核心 CPU 進行平行運算。本文將探討如何使用 GNU Parallel 安裝設定、分配資料、處理檔案,並結合平行管線技術,更有效率地完成複雜的資料處理任務。文章也涵蓋資料建模與機器學習實務,以葡萄酒品質資料集為例,示範如何使用命令列工具進行資料清理、降維、視覺化分析,並比較 PCA 和 t-SNE 降維技術的應用與差異,提供讀者更全面的技術理解。

分散式處理:使用 GNU Parallel 加速資料處理

GNU Parallel 是一款強大的命令列工具,能夠幫助使用者在多個 CPU 核心或遠端機器上平行執行任務,大幅提升資料處理的效率。本文將介紹如何使用 GNU Parallel 進行分散式處理,包括在遠端機器上安裝 Parallel、分配本地資料給遠端機器、以及在遠端機器上處理檔案等主題。

在遠端機器上安裝 GNU Parallel

首先,我們需要在多台遠端機器上安裝 GNU Parallel。可以使用以下命令在多台機器上同時安裝:

$ parallel --nonall --slf hostnames "sudo apt-get install -y parallel"

內容解密:

  • parallel:啟動 GNU Parallel。
  • --nonall:表示命令將在所有列出的主機上執行,但不傳遞任何引數。
  • --slf hostnames:指定包含遠端主機名稱的檔案。
  • sudo apt-get install -y parallel:在遠端主機上安裝 GNU Parallel。

分配本地資料給遠端機器

假設我們有一個龐大的資料集需要處理,可以使用 GNU Parallel 將資料分配到多台遠端機器上進行平行處理。以下範例展示如何將 1 到 1000 的數字分配到多台機器上,並計算總和:

$ seq 1000 | parallel -N100 --pipe --slf hostnames "(hostname; wc -l) | paste -s -d:"
$ seq 1000 | parallel -N100 --pipe --slf hostnames "paste -sd+ | bc" | paste -sd+ | bc

內容解密:

  • seq 1000:生成 1 到 1000 的數字序列。
  • parallel -N100 --pipe:將輸入資料分成每 100 個一組,並透過管道傳遞給命令。
  • --slf hostnames:指定遠端主機列表檔案。
  • (hostname; wc -l) | paste -s -d::在遠端機器上執行,輸出主機名稱和接收到的輸入行數。
  • paste -sd+ | bc:計算每組數字的總和。
  • 最後透過 paste -sd+ | bc 合計所有遠端計算的結果。

在遠端機器上處理檔案

另一個常見的場景是將檔案傳送到遠端機器進行處理,然後取回結果。以下範例展示如何從 NYC Open Data API 下載資料,並在遠端機器上統計各行政區的服務呼叫次數:

$ seq 0 100 900 | parallel "curl -sL 'http://data.cityofnewyork.us/resource/erm2-nwe9.json?\$limit=100&\$offset={}' | jq -c '.[]' | gzip > nyc-{#}.json.gz"
$ parallel --basefile nyc-process.sh --slf hostnames './nyc-process.sh {}' ::: nyc-*.json.gz

內容解密:

  • seq 0 100 900:生成用於 API 請求的偏移量序列。
  • parallel:下載 JSON 資料並壓縮儲存為多個檔案。
  • --basefile nyc-process.sh:將處理指令碼上傳到遠端機器。
  • ./nyc-process.sh {}:在遠端機器上執行處理指令碼,傳入壓縮的 JSON 檔案。

分散式處理與平行管線

在處理大量資料時,單機的運算能力往往不足。這時,我們可以利用 GNU parallel 工具將任務分散到多台機器或多個 CPU 核心上平行處理,大幅提升處理效率。

使用 parallel 進行分散式處理

以下是一個實際範例,展示如何使用 parallel 將 JSON 檔案處理任務分散到多台遠端機器:

$ ls *.json.gz |
> parallel -v --basefile jq \
> --trc {.}.csv \
> --slf hostnames \
> "zcat {} | ./jq -r '.borough' | tr '[A-Z] ' '[a-z]_' | sort | uniq -c | awk '{
print \$2\",\"\$1}' > {.}.csv"

內容解密:

  1. ls *.json.gz 列出當前目錄下的所有 .json.gz 檔案。
  2. parallel 命令接收這些檔案列表,並將任務分配到多台機器。
  3. --basefile jqjq 二進位制檔案傳輸到每台遠端機器上。
  4. --trc {.}.csv 表示傳輸輸入檔案到遠端機器,執行後將結果檔案傳回本地,並在遠端清理相關檔案。
  5. --slf hostnames 指定包含遠端主機名稱的檔案。
  6. 管線命令:
    • zcat {} 解壓縮輸入的 JSON 檔案。
    • ./jq -r '.borough' 使用傳輸過去的 jq 工具提取 borough 欄位。
    • tr '[A-Z] ' '[a-z]_' 將提取的內容轉換為小寫並將空格替換為底線。
    • sort | uniq -c 統計每個區名出現的次數。
    • awk '{print \$2\",\"\$1}' 將輸出格式調整為「區名,次數」。

合併分散式處理結果

將多個 CSV 檔案的結果合併並匯總:

$ cat nyc*csv | header -a borough,count |
> rush run -t 'group_by(df, borough) %>% summarize(count = sum(count))' - |
> csvsort -rc count | csvlook

內容解密:

  1. cat nyc*csv 合併所有符合模式的 CSV 檔案。
  2. header -a borough,count 為輸入資料新增標頭。
  3. rush run -t '...' 使用 rush 執行 R 語言的資料處理指令碼:
    • group_by(df, borough)borough 欄位分組。
    • summarize(count = sum(count)) 對每組的計數求和。
  4. csvsort -rc countcount 欄位進行降序排序。
  5. csvlook 將結果以表格形式顯示。

或者使用 SQL 聚合結果:

$ cat nyc*csv | header -a borough,count |
> csvsql --query 'SELECT borough, SUM(count) AS count FROM stdin GROUP BY borough ORDER BY count DESC' |
> csvlook

內容解密:

  1. csvsql --query '...' 使用 SQL 陳述式處理 CSV 資料。
  2. SELECT borough, SUM(count) AS count 選擇 borough 欄位並對 count 求和。
  3. GROUP BY boroughborough 分組。
  4. ORDER BY count DESC 按總計數降序排列結果。

建模資料

資料建模是將資料抽象化並提取有價值資訊的過程。本章節將介紹三種常見的資料建模演算法:

  • 降維(Dimensionality Reduction)
  • 迴歸(Regression)
  • 分類別(Classification)

這些演算法來自統計學和機器學習領域。在處理 CSV 資料集時,每一行代表一個資料點,每個資料點具有一個或多個特徵,有時還帶有標籤。

資料集與特徵

以葡萄酒資料集為例,每個資料點代表一瓶葡萄酒,其特徵可能包括化學成分(如酒精含量、酸度等),而標籤可能是葡萄酒的品質評分或類別。

未來探索

GNU parallel 的線上教學提供了更多高階功能,例如輸入規格化、任務日誌記錄、超時控制等。熟練掌握這些功能將使你的命令列操作更高效。接下來的章節將繼續探討 OSEMN 模型的第四步:資料建模。

機器學習實務:以葡萄酒資料為例

本章節將介紹如何使用命令列工具進行機器學習實務,特別是以葡萄酒品質資料集為例。我們將使用 tapkeevwskll 等工具來進行資料降維、品質預測和分類別。

取得與準備資料

首先,我們需要取得葡萄酒資料集。該資料集分為紅酒和白酒兩個檔案,分別包含1599和4898筆資料。每筆資料包含11個物理化學屬性以及一個品質評分。

$ parallel "curl -sL http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-{}.csv > wine-{}.csv" ::: red white
$ cp /data/.cache/wine-*.csv .

接下來,我們檢查資料的內容和數量:

$ < wine-red.csv nl | fold | trim
$ < wine-white.csv nl | fold | trim
$ wc -l wine-{red,white}.csv

內容解密:

  1. 使用 parallel 命令同時下載紅酒和白酒的資料檔案。
  2. nl 命令用於為檔案內容新增行號,fold 用於換行處理,而 trim 則去除多餘的空白。
  3. wc -l 用於統計檔案的行數。

資料清理與合併

由於原始資料使用分號作為分隔符且含有空格,我們需要對其進行清理和轉換:

$ for COLOR in red white; do
  < wine-$COLOR.csv tr '[A-Z]; ' '[a-z],_' | tr -d \" > wine-${COLOR}-clean.csv
done

然後,我們使用 csvstack 將兩個檔案合併,並新增一個名為 type 的欄位來區分紅酒和白酒:

$ csvstack -g red,white -n type wine-{red,white}-clean.csv | xsv select 2-,1 > wine.csv

內容解密:

  1. 使用 tr 命令將大寫字母轉換為小寫,分號替換為逗號,並刪除引號。
  2. csvstack 用於合併多個CSV檔案,並新增一個欄位來標示資料來源。
  3. xsv select 用於重新排列欄位順序,將 type 欄位移至最後。

檢查缺失值

大多數機器學習演算法無法處理缺失值,因此我們需要檢查資料集中是否存在缺失值:

$ csvstat wine.csv --nulls

內容解密:

  1. csvstat 命令用於統計CSV檔案的相關資訊,加上 --nulls 引數可以檢查是否存在缺失值。

視覺化分析

接下來,我們使用 rushggplot2 來視覺化葡萄酒品質的分佈和酒精含量與品質之間的關係:

$ rush run -t 'ggplot(df, aes(x = quality, fill = type)) + geom_density(adjust = 3, alpha = 0.5)' wine.csv > wine-quality.png
$ display wine-quality.png
$ rush plot --x alcohol --y quality --color type --geom smooth wine.csv > wine-alcohol-vs-quality.png
$ display wine-alcohol-vs-quality.png

內容解密:

  1. 使用 rush 命令呼叫 ggplot2 進行資料視覺化,繪製密度圖來比較紅酒和白酒的品質分佈。
  2. 使用 --x--y--color 引數來指定X軸、Y軸和顏色變數,繪製酒精含量與品質之間的關係圖。

使用Tapkee進行降維處理

降維的主要目標是將高維度的資料點對映到較低維度的空間,同時盡量保持相似資料點之間的鄰近關係。我們的紅白酒資料集包含13個特徵,為了便於視覺化,我們將採用兩種降維技術:主成分分析(PCA)和t-Distributed Stochastic Neighbor Embedding(t-SNE)。

介紹Tapkee

Tapkee是一個C++範本函式庫,用於實作多種降維演算法,包括:

  • 區域性線性嵌入(Locally Linear Embedding)
  • 等距對映(Isomap)
  • 多維度縮放(Multidimensional Scaling)
  • 主成分分析(PCA)
  • t-SNE

雖然Tapkee主要作為一個可以被其他應用程式包含的函式庫,但它也提供了一個命令列工具tapkee。我們將使用這個工具對紅白酒資料集進行降維處理。

線性和非線性對映

首先,我們需要對特徵進行標準化處理,以確保每個特徵的重要性相同。這通常能改善機器學習演算法的表現。

$ rush run --tidyverse --output wine-scaled.csv \
> 'select(df, -type) %>%
> scale() %>%
> as_tibble() %>%
> mutate(type = df$type)' wine.csv
$ csvlook wine-scaled.csv

內容解密:

  1. select(df, -type):移除type欄位,因為scale()函式只能對數值型欄位進行操作。
  2. scale():對資料進行標準化處理,使每個特徵具有相同的尺度。
  3. as_tibble():將處理後的矩陣轉換回資料框架。
  4. mutate(type = df$type):將原本的type欄位增加回資料框架中。

接下來,我們使用Tapkee進行降維處理,先採用PCA方法:

$ xsv select '!type' wine-scaled.csv |
> header -d |
> tapkee --method pca |
> tee wine-pca.txt | trim
$ < wine-pca.txt header -a pc1,pc2 |
> paste -d, - <(xsv select type wine-scaled.csv) |
> tee wine-pca.csv | csvlook

內容解密:

  1. xsv select '!type' wine-scaled.csv:選取除type欄位以外的所有欄位。
  2. header -d:移除標頭。
  3. tapkee --method pca:使用PCA方法進行降維。
  4. header -a pc1,pc2:為輸出的資料新增標頭pc1pc2
  5. paste -d, - <(xsv select type wine-scaled.csv):將原本的type欄位增加回處理後的資料中。

然後,我們使用Rio-scatter繪製散點圖(Figure 9-3):

$ rush plot --x pc1 --y pc2 --color type --shape type wine-pca.csv > wine-pca.png
$ display wine-pca.png

圖示說明:

此圖示展示了使用PCA進行線性降維後的結果,紅白酒樣本在二維空間中的分佈情況。

同樣地,我們使用t-SNE方法進行降維處理:

$ xsv select '!type' wine-scaled.csv |
> header -d |
> tapkee --method t-sne |
> header -a x,y |
> paste -d, - <(xsv select type wine-scaled.csv) |
> rush plot --x x --y y --color type --shape type > wine-tsne.png
$ display wine-tsne.png

圖示說明:

此圖示展示了使用t-SNE進行非線性降維後的結果,紅白酒樣本在二維空間中的分佈情況。相比於PCA,t-SNE更好地分隔了紅白酒樣本,表明資料集具有一定的結構。

結果分析

透過比較PCA和t-SNE的結果,我們發現t-SNE在分隔紅白酒樣本方面表現更好,這意味著資料的特徵與紅白酒的分類別之間存在著非線性的關係。因此,在處理類別似的資料集時,應考慮使用非線性的降維技術以獲得更好的結果。