在資料科學領域,處理不同格式的資料是家常便飯。本文將介紹如何使用命令列工具有效地合併 CSV 檔案、進行資料格式轉換,以及利用 Make 管理專案,提升整體工作流程效率。合併 CSV 檔案時,可根據檔案結構選擇水平串接或連線方式。水平串接適用於欄位相同但內容不同的檔案,可使用 paste 指令合併;連線則適用於欄位不同但分享某些共同欄位的檔案,可使用 csvjoin 或 SQL 語法合併。此外,我們也將探討如何將 HTML 和 JSON 格式的資料轉換為 CSV 格式,以便進行後續分析。常用的工具包括 curl、pup、xml2json、jq 和 json2csv 等。最後,我們將介紹如何使用 Make 管理專案,透過 Makefile 定義工作流程和依賴關係,自動化執行任務,並確保結果的可重現性。
合併多個CSV檔案的技術探討
在處理CSV檔案時,經常需要將多個檔案合併以進行進一步的資料分析或處理。根據CSV檔案的結構和內容,可以採用不同的合併方法,包括水平串接(concatenation)和連線(joining)。
水平串接多個CSV檔案
當多個CSV檔案具有相同的欄位時,可以使用水平串接的方式將它們合併。假設我們有三個CSV檔案:bills.csv、customers.csv和datetime.csv,分別包含不同的欄位,但具有相同的資料列數。
步驟一:使用csvcut擷取特定欄位
首先,使用csvcut指令從原始CSV檔案中擷取特定的欄位,並將結果儲存到新的CSV檔案中。例如:
$ < tips.csv csvcut -c bill,tip | tee bills.csv | head -n 3 | csvlook
這將從tips.csv中擷取bill和tip欄位,並將結果儲存到bills.csv。
步驟二:使用paste指令合併CSV檔案
使用paste指令可以將多個CSV檔案水平串接起來。例如:
$ paste -d, {bills,customers,datetime}.csv | head -n 3 | csvlook -I
這將把bills.csv、customers.csv和datetime.csv三個檔案合併成一個新的CSV檔案,使用逗號作為分隔符。
#### 內容解密:
此指令的關鍵在於使用-d引數指定分隔符為逗號,並使用大括號擴充套件語法列出要合併的檔案。輸出的結果是一個新的CSV檔案,包含了原始檔案的所有欄位。
連線多個CSV檔案
當多個CSV檔案具有不同的欄位,但分享某些共同的欄位時,可以使用連線(joining)的方式將它們合併。假設我們有兩個CSV檔案:iris.csv和irismeta.csv,分別包含不同的欄位,但分享species欄位。
步驟一:使用csvjoin指令連線CSV檔案
使用csvjoin指令可以根據分享的欄位將兩個CSV檔案連線起來。例如:
$ csvjoin -c species iris.csv irismeta.csv | csvcut -c sepal_length,sepal_width,species,usda_id | sed -n '1p;49,54p' | csvlook
這將根據species欄位將iris.csv和irismeta.csv兩個檔案連線起來,並擷取特定的欄位。
#### 內容解密:
此指令的關鍵在於使用-c引數指定連線的欄位,並使用管道指令對輸出的結果進行進一步的處理,例如擷取特定的欄位和列印特定的列。
使用SQL語法合併CSV檔案
除了使用特定的指令之外,也可以使用SQL語法來合併CSV檔案。例如:
$ csvsql --query 'SELECT i.sepal_length, i.sepal_width, i.species, m.usda_id FROM iris i JOIN irismeta m ON (i.species = m.species)' iris.csv irismeta.csv | sed -n '1p;49,54p' | csvlook
這將使用SQL語法將iris.csv和irismeta.csv兩個檔案連線起來,並擷取特定的欄位。
#### 內容解密:
此指令的關鍵在於使用SQL語法指定連線的條件和要擷取的欄位,並使用管道指令對輸出的結果進行進一步的處理。需要注意的是,csvsql指令預設使用SQLite作為資料函式庫,因此SQL語法的寫法需要符合SQLite的規範。
處理XML/HTML與JSON資料
本章節將介紹幾個命令列工具,用於在不同資料格式之間進行轉換。首先,我們將探討為何需要進行資料轉換。許多視覺化和機器學習演算法需要資料以表格形式呈現,如資料函式庫表格或試算表。CSV檔案本身即為表格形式,但JSON和HTML/XML資料可能具有深層巢狀結構。
為何需要轉換資料格式
- 轉換為表格形式:許多分析工具和演算法需要資料以結構化的表格形式呈現。
- 適用於命令列工具:傳統的命令列工具如
cut和grep主要操作於純文字資料。將其他格式的資料轉換為純文字或CSV,可以更方便地使用這些工具。
使用命令列工具轉換資料格式
本文將示範如何使用curl、pup、xml2json、jq和json2csv等命令列工具,將XML/HTML和JSON資料轉換為CSV格式。
例項:從Wikipedia下載並處理資料
假設我們想要分析Wikipedia上關於各國與地區邊界長度與面積比率的資料。首先,使用curl下載相關的HTML頁面:
$ curl -sL 'http://en.wikipedia.org/wiki/List_of_countries_and_territories_by_border/area_ratio' > wiki.html
接下來,檢視下載的HTML檔案的前10行:
$ < wiki.html trim
<!DOCTYPE html>
<html class="client-nojs" lang="en" dir="ltr">
<head>
<meta charset="UTF-8"/>
<title>List of countries and territories by border/area ratio - Wikipedia</titl...
<script>document.documentElement.className="client-js";RLCONF={"wgBreakFrames":...
"Lists of countries by geography","Lists by area","Border-related lists"],"wgPa...
!1,"wgGELinkRecommendationsFrontendEnabled":!1,"wgWikibaseItemId":"Q6613807"};R...
"ext.navigationTiming","ext.uls.compactlinks","ext.uls.interface","ext.cx.event...
<script>(RLQ=window.RLQ||[]).push(function(){mw.loader.implement("user.options@...
... with 3038 more lines
提取所需資料
使用grep命令查詢包含wikitable類別的表格:
$ grep wikitable -A 21 wiki.html
<table class="wikitable sortable">
<tbody><tr>
<th>Rank</th>
<th>Country or territory</th>
<th>Total length of land borders (km)</th>
<th>Total surface area (km<sup>2</sup>)</th>
<th>Border/area ratio (km/km<sup>2</sup>)
</th></tr>
<tr>
<td>1
使用pup提取HTML元素
使用pup命令提取包含資料的表格內容:
$ < wiki.html pup 'table.wikitable tbody' | tee table.html | trim
<tbody>
<tr>
<th>
Rank
</th>
<th>
Country or territory
</th>
<th>
Total length of land borders (km)
... with 4199 more lines
將HTML轉換為JSON
使用xml2json將提取的HTML轉換為JSON格式:
$ < table.html xml2json > table.json
$ jq . table.json | trim
{
"tbody": {
"tr": [
{
"th": [
{
"$t": "Rank"
},
{
"$t": "Country or territory"
},
{
"$t": "Total length of land borders (km)"
},
內容解密:
curl命令的作用:用於從Wikipedia下載指定的HTML頁面,並將其儲存到本地檔案wiki.html。pup命令的作用:根據CSS選擇器從HTML檔案中提取所需的元素,本例中用於提取包含資料的表格內容。xml2json命令的作用:將XML或HTML格式的資料轉換為JSON格式,以便於進一步處理。jq命令的作用:用於解析和格式化JSON資料,使其更易讀。
使用命令列工具進行資料清洗與專案管理
本章節主要探討如何使用命令列工具進行資料清洗和專案管理。首先,我們將介紹如何將HTML資料轉換為JSON和CSV格式。接著,我們將討論使用make工具進行專案管理的優勢和方法。
資料清洗:從HTML到CSV
在資料分析的過程中,原始資料往往以HTML或其他格式存在。為了更好地處理這些資料,我們需要將其轉換為更易於操作的格式,如CSV。
首先,使用curl命令下載Wikipedia頁面,然後透過html2json工具將HTML轉換為JSON格式。接著,利用jq命令提取JSON資料中的特定部分,並將其重新格式化為CSV檔案。
$ curl -s https://en.wikipedia.org/wiki/List_of_countries_by_border-to-surface-area-ratio | html2json > table.json
$ < table.json jq -r '.tbody.tr[1:][] | [.td[]["$t"]] | @csv' | header -a rank,country,border,surface,ratio > countries.csv
$ csvlook --max-column-width 28 countries.csv
內容解密:
curl命令:用於下載指定的Wikipedia頁面,並將其輸出重定向到html2json命令,以將HTML內容轉換為JSON格式。jq命令:用於解析JSON資料,提取.tbody.tr下的資料,並將其格式化為CSV。.tbody.tr[1:][]:提取表格主體中的行,從第二行開始(跳過標題行)。[.td[]["$t"]]:提取每行中的單元格資料,並選擇$t鍵對應的值。@csv:將提取的資料格式化為CSV。
header命令:為生成的CSV檔案新增列標題。csvlook命令:以表格形式顯示CSV檔案內容。
專案管理:使用make
在進行資料分析時,我們常常需要執行多個步驟,包括資料下載、清洗、轉換等。為了更好地管理這些步驟,我們可以使用make工具。
make是一個命令列工具,允許我們定義工作流程中的依賴關係,並按需執行特定步驟。它有助於提高工作效率,確保結果的可重現性。
使用make的優勢
- 定義工作流程:透過編寫
Makefile,可以清晰地定義專案的工作流程和依賴關係。 - 執行特定步驟:根據需要執行特定的步驟,而不必重新執行整個工作流程。
- 提高效率:避免重複執行不必要的步驟,節省時間。
進一步探索
- 學習
awk:awk是一種強大的文字處理工具,值得深入學習。有許多資源可供參考,如《sed & awk》一書和GNU Awk使用手冊。 - 正規表示式:正規表示式在資料處理中非常有用。推薦閱讀《Regular Expressions Cookbook》以深入瞭解其用法。
- 版本控制:學習使用
git進行版本控制,這對於團隊合作和專案管理至關重要。GitHub是一個流行的線上服務,用於分散式版本控制。
使用Plantuml圖表展示工作流程
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title CSV檔案合併與資料格式轉換技術
package "Linux Shell 操作" {
package "檔案操作" {
component [ls/cd/pwd] as nav
component [cp/mv/rm] as file
component [chmod/chown] as perm
}
package "文字處理" {
component [grep] as grep
component [sed] as sed
component [awk] as awk
component [cut/sort/uniq] as text
}
package "系統管理" {
component [ps/top/htop] as process
component [systemctl] as service
component [cron] as cron
}
package "管線與重導向" {
component [| 管線] as pipe
component [> >> 輸出] as redirect
component [$() 命令替換] as subst
}
}
nav --> file : 檔案管理
file --> perm : 權限設定
grep --> sed : 過濾處理
sed --> awk : 欄位處理
pipe --> redirect : 串接命令
process --> service : 服務管理
note right of pipe
命令1 | 命令2
前者輸出作為後者輸入
end note
@enduml此圖示展示了從下載Wikipedia頁面到使用make管理專案的整個工作流程。透過這個流程,我們可以清晰地瞭解每個步驟之間的依賴關係,以及如何使用不同的命令列工具來完成任務。
使用Make進行專案管理
Make是一個強大的工具,用於組織和管理專案中的命令執行。它圍繞資料及其依賴關係來安排命令的執行順序。透過在一個獨立的文字檔案(即工作流程)中正式定義資料處理步驟,Make能夠自動解析這些步驟之間的依賴關係,並決定哪些命令需要被執行以及執行的順序。
Make的基本概念
- 目標(Target):通常是您希望建立的檔案的名稱,但也可以是更通用的任務名稱。
- 規則(Rule):指定如何構建目標的命令或一系列命令。可以將規則視為一種配方。
- 依賴關係:Make會根據目標和其依賴的檔案之間的關係來決定是否需要重新執行某些命令。
為什麼使用Make?
- 提高效率:Make只會在必要的時候執行命令。例如,如果一個SQL查詢需要10分鐘,那麼只有在結果缺失或查詢發生變化時才會重新執行。
- 便於專案管理和協作:透過正式的工作流程,您可以輕鬆地在幾週後重新接手專案,或與他人協作。
- 節省時間:當您需要重新執行某個步驟時,Make只會重新執行該步驟所依賴的那些步驟。
Make的基礎使用
預設行為:Make預設會在當前目錄下尋找名為
Makefile的組態檔案。您也可以使用其他檔名,但需要透過-f選項指定。目標的構建:當您執行
make命令時,它會構建Makefile中定義的第一個目標。如果您指定了一個目標,如make numbers,則會構建該目標。偽目標(Phony Targets):有時,您希望某些目標無論是否有同名的檔案都能夠被執行。這時,可以使用
.PHONY來宣告這些目標為偽目標。
示例:簡單的Makefile
假設我們有一個名為numbers.make的Makefile,內容如下:
numbers:
seq 7
這裡定義了一個名為numbers的目標,其規則是執行seq 7命令。執行make numbers將輸出1到7的數字序列。
內容解密:
numbers::定義了一個名為numbers的目標。seq 7:這是構建numbers目標的規則。seq 7命令會生成從1到7的數字序列。- 注意縮排:在
seq 7前必須使用一個Tab字元,而不是空格。Make對此非常嚴格,否則會報錯。
使用偽目標的示例
.PHONY: clean publish docker-run
clean:
rm book/2e/book.md book/2e/render*.rds
publish:
(cd www && hugo) && netlify deploy --prod --dir www/public
docker-run:
docker run -it --rm -v $$(pwd)/book/2e/data:/data -p 8000:8000 datasciencetoolbox/dsatcl2e:latest
內容解密:
.PHONY: clean publish docker-run:聲明瞭三個偽目標:clean、publish和docker-run。clean:、publish:、docker-run::定義了這三個偽目標及其對應的規則。clean目標用於清理某些檔案。publish目標用於發布內容到網站。docker-run目標用於執行Docker容器。
透過使用Make,您可以有效地管理和執行專案中的各種任務,提高工作效率並簡化專案管理工作流程。
使用Makefile進行專案管理
在資料科學工作流程中,專案管理是一項重要的任務。make是一個強大的工具,可以幫助我們自動化專案的構建和更新過程。在本章中,我們將探討如何使用Makefile來管理資料科學專案。
建立基本的Makefile
首先,我們來建立一個簡單的Makefile,它可以生成一個名為numbers的檔案,內容是從1到7的數字序列。
numbers:
seq 7 > numbers
執行make numbers指令後,numbers檔案就會被建立。再次執行相同的指令,make會報告numbers是最新的,因此不需要重新構建。
使用自動變數
為了避免重複,我們可以使用自動變數$@,它會被擴充套件為目標的名稱。
numbers:
seq 7 > $@
這樣,無論目標的名稱是什麼,指令都會正確地將輸出寫入對應的檔案。
內容解密:
numbers::定義了一個名為numbers的目標。seq 7 > $@:執行seq 7指令,將輸出重定向到目標檔案。$@代表目標的名稱,在這裡是numbers。- 使用自動變數可以使
Makefile更加靈活和可維護。
新增依賴項
在典型的資料科學工作流程中,很多步驟都依賴於其他步驟。讓我們考慮兩個與Star Wars角色資料集相關的任務。
任務1:計算前10名最高的人類角色
curl -sL 'https://raw.githubusercontent.com/tidyverse/dplyr/master/data-raw/starwars.csv' |
grep Human |
cut -d, -f 1,2 |
sort -t, -k2 -nr |
head
任務2:建立一個箱形圖,顯示每個物種的身高分佈
curl -sL 'https://raw.githubusercontent.com/tidyverse/dplyr/master/data-raw/starwars.csv' |
rush plot --x height --y species --geom boxplot > heights.png
讓我們將這兩個任務放入一個Makefile中。首先,我們來看完整的Makefile,然後逐步解釋語法。
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
URL = "https://raw.githubusercontent.com/tidyverse/dplyr/master/data-raw/starwars.csv"
.PHONY: all top10
all: top10 heights.png
data:
mkdir $@
data/starwars.csv: data
curl -sL $(URL) > $@
內容解密:
SHELL := bash:指定使用的shell為bash。.ONESHELL::指示make在單一shell例項中執行所有指令。.SHELLFLAGS := -eu -o pipefail -c:設定shell的旗標,以啟用錯誤處理和pipefail。URL = ...:定義一個變數,用於儲存Star Wars資料集的URL。.PHONY: all top10:宣告all和top10為偽目標,不對應實際檔案。all: top10 heights.png:定義預設目標all,依賴於top10和heights.png。data:和data/starwars.csv: data:定義規則,用於建立資料目錄和下載Star Wars資料集。