對於許多資料科學家來說,資料操作起始於Pandas或Tidyverse。從理論上看,這個概念沒有錯。畢竟,這是為什麼這些工具首先存在的原因。然而,對於分隔符轉換等簡單任務來說,這些選項通常可能是過於重量級了。
有意掌握命令列應該在每個開發人員的技能鏈上,特別是資料科學家。學習shell中的來龍去脈無可否認地會讓你更高效。除此之外,命令列還在計算方面有一次偉大的歷史記錄。例如,awk – 一種資料驅動的指令碼語言。Awk首次出現於1977年,它是在傳奇的K&R一書中的K,Brian Kernighan的幫助下出現的。在今天,大約50年之後,awk仍然與每年出現的新書保持相關聯! 因此,可以肯定的是,對命令列技術的投入不會很快貶值的。
我們會談及的內容
- ICONV
- HEAD
- TR
- WC
- SPLIT
- SORT & UNIQ
- CUT
- PASTE
- JOIN
- GREP
- SED
- AWK
ICONV
檔案編碼總是棘手的問題。目前大部分檔案都是採用的 UTF-8 編碼。要想了解 UTF-8 的魔力,可以看看這個優秀的視訊。儘管如此,有時候我們還是會收到非 UTF-8 編碼的檔案。這種情況下就需要嘗試轉碼。iconv 就是這種狀況下的救世主。iconv 是一個簡單的程式,可以輸入某種編碼的文字,然後以另一種編碼輸出。
1 2 3 4 |
# Converting -f (from) latin1 (ISO-8859-1) # -t (to) standard UTF_8 iconv -f ISO-8859-1 -t UTF-8 < input.txt > output.txt |
- 常用選項:
- iconv -l 列出所有支援的編碼
- iconv -c 不作提示就丟棄無法轉換的字元
HEAD
如果你是重度Pandas的使用者,那麼你會對head很熟悉。通常在處理新資料時,我們想要做的第一件事就是了解究竟存在那些東西。這會引起Panda啟動,讀取資料,然後呼叫df.head() – 很費勁,至少可以說。head,不需要任何標誌,將輸出檔案的前10行。head真正的能力在於徹查清除操作。 例如,如果我們想將檔案的分隔符從逗號改變為pipe萬用字元。一個快速測試將是:head mydata.csv | sed ‘s/,/|/g’
1 2 3 4 5 6 7 |
# Prints out first 10 lines head filename.csv # Print first 3 lines head -n 3 filename.csv |
- 有用的選項:
- head -n 輸出指定行
- head -c 輸出指定的位元組
TR命令
Tr類似於翻譯,它是基於檔案清理的一個強大使用的工具。一個理想的用法是替換檔案中的分隔符。
1 2 |
#將檔案中的製表符分割轉換成逗號 cat tab_delimited.txt | tr "\t" "," comma_delimited.csv |
Tr的另一個特性是在你的處理中設定上所有的[:class:]變數。包括:
1 2 3 4 5 6 7 8 9 10 11 12 |
[:alnum:] 所有字母和數字 [:alpha:] 所有字母 [:blank:] 所有水平空白 [:cntrl:] 所有控制字元 [:digit:] 所有數字 [:graph:] 所有可列印的字元,不包括空格 [:lower:] 全部小寫字母 [:print:] 所有可列印的字元,包括空格 [:punct:] 所有標點符號 [:space:] 所有的水平或垂直空格 [:upper:] 全部大寫字母 [:xdigit:] 所有十六進位制數字 |
可以將這些多樣化的變數連結在一起,組成一個強大的程式。下面是一個基於字數統計的程式,用來檢查你的README檔案是否使用過度。
1 |
cat README.md | tr "[:punct:][:space:]" "\n" | tr "[:upper:]" "[:lower:]" | grep . | sort | uniq -c | sort -nr |
另外一個例子用於正規表示式
1 2 |
# 將所有的大寫字母轉換成小寫 cat filename.csv | tr '[A-Z]' '[a-z]' |
- 有用的選項:
- tr -d刪除字元
- tr -s壓縮字元
- \b退格
- \f換頁
- \v垂直選項卡
- \NNN八進位制值為NNN的字元
WC
字數統計。它的價值主要體現在使用 -l 引數可以進行行數統計。
1 2 3 |
# Will return number of lines in CSV wc -l gigantic_comma.csv |
個用這個工具來驗證各個命令的輸出實在方便。因此,如果我們要在檔案中轉換分隔符,然後執行 wc -l,驗證總行數是相同的。如果不同,我們就知道一定是哪裡出錯了。
- 常用選項:
- wc -c 列印位元組數
- wc -m 列印字元數
- wc -L 列印最長一行的長度
- wc -w 列印字數
SPLIT命令
檔案大小可以有顯著變化。根據工作的不同,拆分檔案是有益的,就像split。基本用法如下:
1 2 3 4 5 6 7 8 9 |
#我們拆分這個CSV檔案,每500行分割為一個新的檔案new_filename split -l 500 filename.csv new_filename_ # filename.csv # ls output # new_filename_aaa # new_filename_aab # new_filename_aac |
兩個地方很奇怪:一個是命名方式,一個是缺少副檔名。字尾約定可以通過-d標識來數字化。新增副檔名,你需要執行下面這個find命令。他會給當前資料夾下的所有檔案追加.csv字尾,所以需要小心使用。
1 2 3 4 5 6 7 |
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按特定位元組大小拆分
- split -a生成長度為N的字尾
- split -x使用十六進位制字尾分割
SORT & UNIQ
前面的命令是顯而易見的:他們按照自己說的做。這兩者提供了最重要的一擊(即去重單詞計數)。這是由於有uniq,它只處理重複的相鄰行。因此在管道輸出之前進行排序。一個有趣的事情是,sort -u將獲得與sort file.txt | uniq相同的結果。
Sort確實對資料科學家來說是一種很有用的小技巧:能夠根據特定的列對整個CSV進行排序。
1 2 3 4 5 6 7 8 9 10 11 |
# Sorting a CSV file by the second column alphabetically sort -t"," -k2,2 filename.csv # Numerically sort -t"," -k2n,2 filename.csv # Reverse order sort -t"," -k2nr,2 filename.csv |
這裡的-t選項是指定逗號作為分隔符。通常假設是空格或製表符。此外,-k標誌是用來指定我們的鍵的。它的語法是-km,n,m是起始欄位,n是最後一個欄位。
- 有用的選項:
- sort -f 忽略大小寫
- sort -r 逆序
- sort -R 亂序
- uniq -c 計算出現次數
- uniq -d 只列印重複行
CUT命令
cut用於刪除列。舉個例子,如果我們只想要第一列和第三列。
1 |
cut -d, -f 1,3 filename.csv |
選擇除了第一列以外的所有列
1 |
cut -d, -f 2- filename.csv |
與其他的命令組合使用,cut命令作為過濾器
1 2 3 |
#列印存在“some_string_value”的第1列和第3列的前10行 head filename.csv | grep "some_string_value" | cut -d, -f 1,3 |
找出第二列中唯一值的數量。
1 2 3 4 5 |
cat filename.csv | cut -d, -f 2 | sort | uniq | wc -l # 計算唯一值出現的次數,限制輸出前10個結果 cat filename.csv | cut -d, -f 2 | sort | uniq -c | head |
PASTE
paste 是個有趣的小命令。如果你想合併兩個檔案,而這兩個檔案的內容又正好是有序的,那 paste 就可以這樣做。
1 2 3 4 5 6 7 8 9 |
# names.txt adam john zach # jobs.txt lawyer youtuber developer |
1 2 3 |
# Join the two into a CSV paste -d ',' names.txt jobs.txt > person_data.txt |
1 2 3 4 |
# Output adam,lawyer john,youtuber zach,developer |
關於更多 SQL_-esque 變體,請看下面。
JOIN
Join是一種簡單的、準切向的SQL。最大的區別在於Join將返回所有列,匹配可能只發生在一個欄位上。預設情況下,join將嘗試使用第一列作為匹配鍵。對於不同的結果,需要以下語法:
1 2 3 4 |
# Join the first file (-1) by the second column # and the second file (-2) by the first join -t"," -1 2 -2 1 first_file.txt second_file.txt |
標準連線是一個內部連線。然而,外部連線也可以通過-af滯後來實現。另一個值得注意的是-e標誌,如果發現有欄位丟失,它可以用來替換成其他值。
1 2 3 4 |
# 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;可能是最知名的命令,並且有很好的理由。 Grep具有很強的能力,特別是在大型程式碼庫中查詢方法。在資料科學領域,它充當了其他命令的改進機制。但其標準用法也很有用。
1 2 3 4 5 |
# 遞迴搜尋並列出當前目錄下包含'word'的所有檔案 grep -lr 'word' . # 列出包含word的檔案數目 grep -lr 'word' . | wc -l |
對包含word/pattern的行數進行計數
1 2 3 4 5 |
grep -c 'some_value' filename.csv # 同樣的功能,但是按照檔名列出當前目錄下所有包含該關鍵詞的檔案 grep -c 'some_value' * |
Grep使用or運算子- \|來檢索多個值.
1 |
grep "first_value\|second_value" filename.csv |
- 有用的選項
- alias grep=”grep –color=auto” 使grep支援彩色輸出
- grep -E 使用擴充套件正規表示式
- grep -w 僅匹配完整單詞
- grep -l 列印匹配檔案的名稱
- grep -v 倒序匹配
大殺器
Sed和Awk是本文兩個最有用的命令。為了簡潔,我不會討論那些令人費解的細節。相反,我會討論各種各樣的命令來證明他們令人印象深刻的實力。如果你想了解的更多,這本書就可以。
SED
在核心中sed是一個流編輯器。它擅長替換,但是也可以用來重構。
最基本的sed命令包含了s/old/new/g。也就是全域性搜尋舊值,替換新值。沒有/g 我們的命令可能在第一次出現舊值就會終止。
為了儘快瞭解它的能力,我們來看一個例子。在這個情況你會拿到下面的檔案:
1 2 3 |
balance,name $1,000,john $2,000,jack |
我們要做的第一件事就是移除美元符。-i 標識表示就地修改。”就是代表一個零長度檔案擴充套件,因此重寫我們的初始檔案。理想情況下,你會單獨測試這些並輸出到一個新檔案。
1 2 3 4 5 |
sed -i '' 's/\$//g' data.txt # balance,name # 1,000,john # 2,000,jack |
下一步,我們的balance列的逗號。
1 2 3 4 5 |
sed -i '' 's/\([0-9]\),\([0-9]\)/\1\2/g' data.txt # balance,name # 1000,john # 2000,jack |
最終,Jack有一天起來並準備辭職了。所以,再見吧,我的朋友。
1 2 3 4 |
sed -i '' '/jack/d' data.txt # balance,name # 1000,john |
就像你所看到的,sed功能強大,但是樂趣不止於此。
AWK
最好的放最後。Awk不僅是一個簡單的命令:它是一個成熟的語言。在本文中包含的每一個命令中,awk目前是最酷的。如果你發現它令你印象深刻,這有大量的資源- 看這,這,和這。
awk包含的常用案例:
- 文字處理
- 格式化文字報告
- 執行計算操作
- 執行字串操作
Awk在其最初雛形可以與grep平行。
1 |
awk '/word/' filename.csv |
或者多使用一點魔法,讓grep和cut結合。在這,awk對所有行通過word列印了以tab分隔的第三和第四列。-F,只是將分隔符變為逗號。
1 |
awk -F, '/word/ { print $3 "\t" $4 }' filename.csv |
Awk具有大量有用的內建變數。例如, NF -欄位數 – 和NR – 記錄數。為了獲取檔案中這53個記錄:
1 |
awk -F, 'NR == 53' filename.csv |
新增一個小竅門可以基於一個值或者多個值過濾。下面的第一個例子,會列印這些記錄中第一列為string的行數和列。
1 2 3 4 5 |
awk -F, ' $1 == "string" { print NR, $0 } ' filename.csv # Filter based off of numerical value in second column awk -F, ' $2 == 1000 { print NR, $0 } ' filename.csv |
多數值表示式:
1 2 3 4 |
# Print line number and columns where column three greater # than 2005 and column five less than one thousand awk -F, ' $3 >= 2005 && $5 <= 1000 { print NR, $0 } ' filename.csv |
計算第三列之和:
1 |
awk -F, '{ x+=$3 } END { print x }' filename.csv |
計算那些第一列值為“something”的第三列之和。
1 |
awk -F, '$1 == "something" { x+=$3 } END { print x }' filename.csv |
獲取檔案的行數列數:
1 2 3 4 5 |
awk -F, 'END { print NF, NR }' filename.csv # Prettier version awk -F, 'BEGIN { print "COLUMNS", "ROWS" }; END { print NF, NR }' filename.csv |
列印出現過兩次的行:
1 |
awk -F, '++seen[$0] == 2' filename.csv |
移除多行:
1 2 3 4 5 6 7 8 |
# Consecutive lines awk 'a !~ $0; {a=$0}'] # Nonconsecutive lines awk '! a[$0]++' filename.csv # More efficient awk '!($0 in a) {a[$0];print} |
使用內建函式gsub()替換多個值。
1 |
awk '{gsub(/scarlet|ruby|puce/, "red"); print}' |
這個awk命令合併了多個CSV檔案,忽略頭並在結尾追加。
1 |
awk 'FNR==1 && NR!=1{next;}{print}' *.csv > final_file.csv |
需要精簡一個大檔案?好的,awk可以在sed的幫助下完成這件事。具體來說,基於一個行數,這個命令將一個大檔案分為多個小檔案。這個一行檔案也會新增一個副檔名。
1 2 3 4 5 |
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 lines sed '1d;$d' big_data.csv | awk 'NR%100000==1{x="data_"++i".csv";}{print > x}' |
結束前
命令列擁有無窮的力量。本文所涵蓋的命令列知識足以讓你從零基礎到入門。除了這些已涉及的內容外,針對日常資料操作還有需要可考慮的實用程式。Csvkit, xsv和q是其中三個值得關注的。如果你希望進一步深入到命令列的資料科學領域,那麼請看此書。它也可以在此免費獲得!