資料科學家的命令列技巧

2018-12-13 22:11:00

對於許多資料科學家來說,資料操作從始至終就是 Pandas 或 Tidyverse。從理論上講,這樣做沒有任何問題。畢竟,這就是這些工具存在的原因。然而,對於像分隔符轉換這樣的簡單任務,這些工具是大材小用了。

立志掌握命令列應該在每個開發人員的學習清單上,特別是資料科學家。學習 shell 的來龍去脈將無可否認地提高你的生產力。除此之外,命令列還是計算領域的一個重要歷史課程。例如,awk —— 一種資料驅動的指令碼語言。1977 年,在 Brain Kernighan(即傳奇的 K&R 書中 K)的幫助下,awk 首次出現。今天,大約五十年過去了,awk 仍然活躍在每年新出版的書裡面。因此,可以安全地假設對命令列魔法的付出不會很快貶值。

我們將涵蓋什麼

  • ICONV
  • HEAD
  • TR
  • WC
  • SPLIT
  • SORT & UNIQ
  • CUT
  • PASTE
  • JOIN
  • GREP
  • SED
  • AWK

ICONV

檔案編碼可能會很棘手。現在大部分檔案都是 UTF-8 編碼的。要了解 UTF-8 背後的一些魔力,請檢視這個出色的視訊。儘管如此,有時我們收到的檔案不是這種編碼。這可能引起對改變編碼模式的一些胡亂嘗試。這裡,iconv 是一個拯救者。iconv 是一個簡單的程式,它將獲取採用一種編碼的文字並輸出採用另一種編碼的文字。

# Converting -f (from) latin1 (ISO-8859-1)# -t (to) standard UTF_8iconv -f ISO-8859-1 -t UTF-8 < input.txt > output.txt

實用選項:

  • iconv -l 列出所有已知編碼
  • iconv -c 默默丟棄無法轉換的字元

HEAD

如果你是一個 Pandas 重度使用者,那麼會很熟悉 head。通常在處理新資料時,我們想做的第一件事就是了解其內容。這就得啟動 Pandas,讀取資料然後呼叫 df.head() —— 要說這有點費勁。沒有任何選項的 head 將列印出檔案的前 10 行。head 的真正力量在於乾淨利落的測試操作。例如,如果我們想將檔案的分隔符從逗號更改為管道。一個快速測試將是:head mydata.csv | sed 's/,/|/g'

# Prints out first 10 lineshead filename.csv# Print first 3 lineshead -n 3 filename.csv

實用選項:

  • head -n 列印特定行數
  • head -c 列印特定位元組數

TR

tr 類似於翻譯。這個功能強大的實用程式是檔案基礎清理的主力。理想的用例是替換檔案中的分隔符。

# Converting a tab delimited file into commascat tab_delimited.txt | tr "\t" "," comma_delimited.csv

tr 另一個功能是你可以用內建 [:class:] 變數(POSIX 字元類)發揮威力。這些包括了:

  • [:alnum:] 所有字母和數位
  • [:alpha:] 所有字母
  • [:blank:] 所有水平空白
  • [:cntrl:] 所有控制字元
  • [:digit:] 所有數位
  • [:graph:] 所有可列印字元,但不包括空格
  • [:lower:] 所有小寫字母
  • [:print:] 所有可列印字元,包括空格
  • [:punct:] 所有標點符號
  • [:space:] 所有水平或垂直空白
  • [:upper:] 所有大寫字母
  • [:xdigit:] 所有 16 進位制數位

你可以將這些連線在一起以組成強大的程式。以下是一個基本的字數統計程式,可用於檢查 README 是否被濫用。

cat README.md | tr "[:punct:][:space:]" "\n" | tr "[:upper:]" "[:lower:]" | grep . | sort | uniq -c | sort -nr

另一個使用基本正規表示式的例子:

# Converting all upper case letters to lower casecat filename.csv | tr '[A-Z]' '[a-z]'

實用選項:

  • tr -d 刪除字元
  • tr -s 壓縮字元
  • \b 退格
  • \f 換頁
  • \v 垂直製表符
  • \NNN 八進位制字元

WC

單詞計數。它的價值主要來自其 -l 選項,它會給你提供行數。

# Will return number of lines in CSVwc -l gigantic_comma.csv

這個工具可以方便地確認各種命令的輸出。所以,如果我們在轉換檔案中的分隔符之後執行 wc -l,我們會期待總行數是一樣的,如果不一致,我們就知道有地方出錯了。

實用選項:

  • wc -c 列印位元組數
  • wc -m 列印字元數
  • wc -L 列印最長行的長度
  • wc -w 列印單詞數量

SPLIT

檔案大小的範圍可以很廣。對於有的任務,拆分檔案或許是有好處的,所以使用 split 吧。split 的基本語法是:

# We will split our CSV into new_filename every 500 linessplit -l 500 filename.csv new_filename_# filename.csv# ls output# new_filename_aaa# new_filename_aab# new_filename_aa

它有兩個奇怪的地方是命名約定和缺少副檔名。字尾約定可以通過 -d 標誌變為數位。要新增副檔名,你需要執行以下 find 命令。它將通過附加 .csv 擴充套件名來更改當前目錄中所有檔案的名稱,所以小心了。

find . -type f -exec mv '{}' '{}'.csv \;# ls output# filename.csv.csv# new_filename_aaa.csv# new_filename_aab.csv# new_filename_aac.csv

實用選項:

  • split -b N 按特定位元組大小分割
  • split -a N 生成長度為 N 的字尾
  • split -x 使用十六進位制字尾

SORT & UNIQ

上面兩個命令很明顯:它們的作用就是字面意思。這兩者結合起來可以提供最強大的衝擊 (例如,唯一單詞的數量)。這是由於 uniq 只作用於重複的相鄰行。這也是在輸出前進行 sort 的原因。一個有趣的事情是 sort -u 會達到和典型的 sort file.txt | uniq 模式一樣的結果。

sort 對資料科學家來說確實具有潛在的有用能力:能夠根據特定列對整個 CSV 進行排序。

# Sorting a CSV file by the second column alphabeticallysort -t"," -k2,2 filename.csv# Numericallysort -t"," -k2n,2 filename.csv# Reverse ordersort -t"," -k2nr,2 filename.csv

這裡的 -t 選項將逗號指定為分隔符,通常假設分隔符是空格或製表符。此外,-k 選項是為了確定我們的鍵。這裡的語法是 -km,nm 作為開始列,n 作為結束列。

實用選項:

  • sort -f 忽略大小寫
  • sort -r 反向排序
  • sort -R 亂序
  • uniq -c 統計出現次數
  • uniq -d 只列印重複行

CUT

cut 用於刪除列。作為演示,如果我們只想刪除第一和第三列。

cut -d, -f 1,3 filename.csv

要選擇除了第一行外的所有行。

cut -d, -f 2- filename.csv

結合其他命令,將 cut 用作過濾器。

# Print first 10 lines of column 1 and 3, where "some_string_value" is presenthead filename.csv | grep "some_string_value" | cut -d, -f 1,3

查出第二列中唯一值的數量。

cat filename.csv | cut -d, -f 2 | sort | uniq | wc -l# Count occurences of unique values, limiting to first 10 resultscat filename.csv | cut -d, -f 2 | sort | uniq -c | head

PASTE

paste 是一個帶有趣味性功能的特定命令。如果你有兩個需要合併的檔案,並且它們已經排序好了,paste 幫你解決了接下來的步驟。

# names.txtadamjohnzach# jobs.txtlawyeryoutuberdeveloper# Join the two into a CSVpaste -d ',' names.txt jobs.txt > person_data.txt# Outputadam,lawyerjohn,youtuberzach,developer

更多 SQL 式變種,見下文。

JOIN

join 是一個簡單的、準切向的quasi-tangential SQL。最大的區別是 join 將返回所有列以及只能在一個欄位上匹配。預設情況下,join 將嘗試使用第一列作為匹配鍵。為了獲得不同結果,必須使用以下語法:

# Join the first file (-1) by the second column# and the second file (-2) by the firstjoin -t "," -1 2 -2 1 first_file.txt second_file.txt

標準的 join 是內連線。然而,外連線通過 -a 選項也是可行的。另一個值得一提的技巧是 -q 標誌,如果發現有缺失的欄位,可用於替換值。

# Outer join, replace blanks with NULL in columns 1 and 2# -o which fields to substitute - 0 is key, 1.1 is first column, etc...join -t"," -1 2 -a 1 -a2 -e ' NULL' -o '0,1.1,2.2' first_file.txt second_file.txt

它不是最使用者友好的命令,而是絕望時刻的絕望措施。

實用選項:

  • join -a 列印不可配對的行
  • join -e 替換丟失的輸入欄位
  • join -j 相當於 -1 FIELD -2 FIELD

GREP

grep用正規表示式全域性搜尋並且列印Global search for a Regular Expression and Print,可能是最有名的命令,並且名副其實。grep 很強大,特別適合在大型程式碼庫中查詢。在資料科學的王國裡,它充當其他命令的提煉機制。雖然它的標準用途也很有價值。

# Recursively search and list all files in directory containing 'word'grep -lr 'word' .# List number of files containing wordgrep -lr 'word' . | wc -l

計算包含單詞或模式的總行數。

grep -c 'some_value' filename.csv# Same thing, but in all files in current directory by file namegrep -c 'some_value' *

對多個值使用“或”運算子: \|

grep "first_value\|second_value" filename.csv

實用選項:

  • alias grep="grep --color=auto" 使 grep 色彩豐富
  • grep -E 使用擴充套件正規表示式
  • grep -w 只匹配整個單詞
  • grep -l 列印匹配的檔名
  • grep -v 非匹配

大人物們

sedawk 是本文中最強大的兩個命令。為簡潔起見,我不打算詳細討論這兩個命令。相反,我將介紹各種能證明其令人印象深刻的力量的命令。如果你想了解更多,這兒就有一本書是關於它們的。

SED

sed 本質上是一個流編輯器。它擅長替換,但也可以用於所有輸出重構。

最基本的 sed 命令由 s/old/new/g 組成。它的意思是搜尋 old,全域性替換為 new。 如果沒有 /g,我們的命令將在 old 第一次出現後終止。

為了快速了解它的功能,我們可以深入了解一個例子。 在以下情景中,你已有以下檔案:

balance,name$1,000,john$2,000,jack

我們可能想要做的第一件事是刪除美元符號。-i 標誌表示原位。'' 表示零長度副檔名,從而覆蓋我們的初始檔案。理想情況下,你可以單獨測試,然後輸出到新檔案。

sed -i '' 's/\$//g' data.txt# balance,name# 1,000,john# 2,000,jack

接下來,去除 blance 列的逗號。

sed -i '' 's/\([0-9]\),\([0-9]\)/\1\2/g' data.txt# balance,name# 1000,john# 2000,jack

最後 jack 有一天決定辭職。所以,再見了,我的朋友。

sed -i '' '/jack/d' data.txt# balance,name# 1000,john

正如你所看到的,sed 有很多強大的功能,但樂趣並不止於此。

AWK

最好的留在最後。awk 不僅僅是一個簡單的命令:它是一個成熟的語言。在本文中涉及的所有內容中,awk 是目前為止最酷的。如果你感興趣,這裡有很多很棒的資源 —— 看 這裡這裡這裡

awk 的常見用例包括:

  • 文書處理
  • 格式化文字報告
  • 執行算術運算
  • 執行字串操作

awk 可以以最原生的形式並行 grep

awk '/word/' filename.csv

或者更加神奇:將 grepcut 組合起來。在這裡,對於所有帶我們指定單詞 word 的行,awk 列印第三和第四列,用 tab 分隔。-F, 用於指定切分時的列分隔符為逗號。

awk -F, '/word/ { print $3 "\t" $4 }' filename.csv

awk 內建了許多精巧的變數。比如,NF —— 欄位數,和 NR —— 記錄數。要獲取檔案中的第 53 條記錄:

awk -F, 'NR == 53' filename.csv

更多的花招是其基於一個或多個值進行過濾的能力。下面的第一個範例將列印第一列等於給定字串的行的行號和列。

awk -F, ' $1 == "string" { print NR, $0 } ' filename.csv# Filter based off of numerical value in second columnawk -F, ' $2 == 1000 { print NR, $0 } ' filename.csv

多個數值表示式:

# Print line number and columns where column three greater# than 2005 and column five less than one thousandawk -F, ' $3 >= 2005 && $5 <= 1000 { print NR, $0 } ' filename.csv

求出第三列的總和:

awk -F, '{ x+=$3 } END { print x }' filename.csv

在第一列等於 something 的那些行,求出第三列值的總和。

awk -F, '$1 == "something" { x+=$3 } END { print x }' filename.csv

獲取檔案的行列數:

awk -F, 'END { print NF, NR }' filename.csv# Prettier versionawk -F, 'BEGIN { print "COLUMNS", "ROWS" }; END { print NF, NR }' filename.csv

列印出現了兩次的行:

awk -F, '++seen[$0] == 2' filename.csv

刪除重複的行:

# Consecutive linesawk 'a !~ $0; {a=$0}']# Nonconsecutive linesawk '! a[$0]++' filename.csv# More efficientawk '!($0 in a) {a[$0];print}

使用內建函數 gsub() 替換多個值。

awk '{gsub(/scarlet|ruby|puce/, "red"); print}'

這個 awk 命令將組合多個 CSV 檔案,忽略標題,然後在最後附加它。

awk 'FNR==1 && NR!=1{next;}{print}' *.csv > final_file.csv

需要縮小一個龐大的檔案? awk 可以在 sed 的幫助下處理它。具體來說,該命令根據行數將一個大檔案分成多個較小的檔案。這個一行指令碼將增加一個擴充套件名。

sed '1d;$d' filename.csv | awk 'NR%NUMBER_OF_LINES==1{x="filename-"++i".csv";}{print > x}'# Example: splitting big_data.csv into data_(n).csv every 100,000 linessed '1d;$d' big_data.csv | awk 'NR%100000==1{x="data_"++i".csv";}{print > x}'

結語

命令列擁有無窮無盡的力量。本文中介紹的命令足以將你從一無所知提升到英雄人物。除了涵蓋的內容之外,還有許多實用程式可以考慮用於日常資料操作。Csvkitxsv 還有 q 是需要記住的三個。如果你希望更深入地了解命令列資料科學,檢視這本書。它也可以免費線上獲得!