[英]mawk syntax appropriate >1000 field file for counting non-numeric data column-wise?
以下愚蠢的硬編碼應該是某種循環或並行構造,名義上起作用,但它是糟糕的mawk語法。 我的好mawk語法嘗試都失敗了,在mawk(未顯示)和gnu parallel(未顯示)中使用for循環。
它確實需要從磁盤讀取CSV文件一次,而不是每列一次,因為我有一個非常大的CSV文件(數百萬行,數千列)。 我的原始代碼工作正常(未顯示),但它為每一列再次讀取整個磁盤文件,這需要幾個小時,我在意識到發生了什么后將其殺死。 我有一個使用GPU連接器插槽的快速固態磁盤,因此該設備上的磁盤讀取速度非常快。 因此CPU是這里的瓶頸。 如果我必須硬編碼4000行基本相同的語句(除了列號),代碼愚蠢甚至更成為瓶頸。
代碼是逐列的,可以計算非數字值。 我需要一些循環(for-loop)或並行(首選),因為雖然以下在2列上正常工作,但它不是為數千列編寫mawk代碼的可擴展方法。
tail -n +1 pht.csv | awk -F"," '(($1+0 != $1) && ($1!="")){cnt1++}; (($2+0 != $2) && ($2!="")){cnt2++} END{print cnt1+0; print cnt2+0}'
2
1
如何“第1欄處理;第2欄處理;” 重復代碼減少了嗎? 如何引入循環? 如何引入gnu parallel? 非常感謝。 我是awk的新手。 其他語言並不新鮮。
我一直期待以下一個或多個bash命令的一些聰明的組合能夠輕松地解決這個問題,但是在這里我很多個小時后沒有任何東西要顯示。 我伸出雙手來。 代碼差的施舍?
對不起,我絕對不需要使用像python pandas或R dataframes這樣的CSV特定庫。 我的雙手綁在這里。 抱歉。 謝謝你這么酷。 在這種情況下我只能使用bash命令行。
我的mawk可以處理32000+列,所以NF在這里不是問題,不像我見過的其他awk。 我的列少於32000(但不是那么多)。
Datafile pht.csv包含以下3x2數據集:
cat pht.csv
8,T1,
T13,3,T1
T13,,-6.350818276405334473e-01
沒有訪問mawk
但你可以做一些與此相當的事情
awk -F, 'NR>1 {for(i=1;i<=NF;i++) if($i~/[[:alpha:]]/) a[i]++}
END {for(i=1;i in a;i++) print a[i]}' file
即使是百萬條記錄,也不應該花費超過幾分鍾。
為了識別指數表示法,正則表達式測試不起作用,你需要恢復到評論中提到的$1+0!=$1
測試。 請注意,您不必單獨檢查空字符串。
到目前為止,所有解決方案都沒有並行化 讓我們改變這一點。
假設您有一個串行工作的解決方案,可以從管道中讀取:
doit() {
# This solution gives 5-10 MB/s depending on system
# Changed so it now also treats '' as zero
perl -F, -ane 'for(0..$#F) {
# Perl has no beautiful way of matching scientific notation
$s[$_] += $F[$_] !~ /^-?\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?$/m
}
END { $" = ","; print "@s\n" }';
}
export -f doit
doit() {
# Somewhat faster - but regards empty fields as zero
mawk -F"," '{
for(i=1;i<=NF;i++) { cnt[i]+=(($i+0)!=$i) && ($i!="") }
}
END { for(i=1;i<NF;i++) printf cnt[i]","; print cnt[NF] }';
}
export -f doit
要並行化這個,我們需要將大文件拆分成塊並將每個塊傳遞給串行解決方案:
# This will spawn a process for each core
parallel --pipe-part -a pht.csv --block -1 doit > blocksums
(您需要使用版本20161222或更高版本才能使用'--block -1')。
為了處理標題,我們計算標題的結果,但是我們否定了結果:
head -n1 pht.csv | doit | perl -pe 's/(^|,)/$1-/g' > headersum
現在我們可以簡單總結一下headerum和blocksums:
cat headersum blocksums |
perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] }
END { $" = ",";print "@s\n" }'
或者,如果您更喜歡逐行輸出:
cat headersum blocksums |
perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] }
END { $" = "\n";print "@s\n" }'
這是你想要做的嗎?
$ awk -v RS='[\n,]' '($1+0) != $1' file | sort | uniq -c
1 T1
2 T13
以上使用GNU awk進行多字符RS,並且應該在幾秒鍾內為您描述的輸入文件運行。 如果你沒有GNU awk,你可以這樣做:
$ tr ',' $'\n' < file | awk '($1+0) != $1' | sort | uniq -c
1 T1
2 T13
我避免使用的方法,
作為一個FS從那以后你就不得不使用$i
在一個循環,這將導致AWK為對按時增加每個輸入行做場分裂,但你可以嘗試一下:
$ awk -F, '{for (i=1;i<=NF;i++) if (($i+0) != $i) print $i}' file | sort | uniq -c
1 T1
2 T13
您可以使用非數字值索引的數組在awk中執行唯一計數,但是您可能必須在內存中存儲大量數據(與必要時使用臨時交換文件的sort
不同),因此YMMV采用該方法。
我獨立解決了它。 最后為我做的是以下URL中的動態變量創建示例。 http://cfajohnson.com/shell/cus-faq-2.html#Q24
這是我開發的解決方案。 注意:我添加了另一個列,其中包含一些缺失的數據,以便進行更完整的單元測試 我不一定是最好的解決方案,也就是TBD。 它在我所知道的小csv上正常工作。 最好的解決方案還需要在40 GB的csv文件上運行得非常快(未顯示哈哈)。
$ cat pht.csv
8,T1,
T13,3,T1
T13,,0
$ tail -n +1 pht.csv | awk -F"," '{ for(i=1;i<=NF;i++) { cnt[i]+=(($i+0)!=$i) && ($i!="") } } END { for(i=1;i<=NF;i++) print cnt[i] }'
2
1
1
PS。 老實說,我對自己的答案並不滿意。 他們說過早優化是萬惡之源。 那么這格言在這里並不適用。 我真的,真的希望gnu並行在那里,而不是for循環,如果可能的話,因為我需要速度。
最后說明:下面我將分享順序和並行版本的性能時序,以及最佳可用的單元測試數據集。 特別感謝Ole Tange在這個應用程序中開發代碼以使用他的gnu並行命令。
單元測試數據文件,最終版本:
$ cat pht2.csv
COLA99,COLB,COLC,COLD
8,T1,,T1
T13,3,T1,0.03
T13,,-6.350818276405334473e-01,-0.036
為逐列非數字計數的順序版本定時大數據(未顯示):
ga@ga-HP-Z820:/mnt/fastssd$ time tail -n +2 train_all.csv | awk -F"," '{ for(i=1; i<=NF; i++){ cnt[i]+=(($i+0)!=$i) && ($i!="") } } END { for(i=1;i<=NF;i++) print cnt[i] }' > /dev/null
real 35m37.121s
針對列式非數字計數的並行版本的大數據計時:
# Correctness - 2 1 1 1 is the correct output.
#
# pht2.csv: 2 1 1 1 :GOOD
# train_all.csv:
# real 1m14.253s
doit1() {
perl -F, -ane 'for(0..$#F) {
# Perl has no beautiful way of matching scientific notation
$s[$_] += $F[$_] !~ /^-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?$/m
}
END { $" = ","; print "@s\n" }';
}
# pht2.csv: 2 1 1 1 :GOOD
# train_all.csv:
# real 1m59.960s
doit2() {
mawk -F"," '{
for(i=1;i<=NF;i++) { cnt[i]+=(($i+0)!=$i) && ($i!="") }
}
END { for(i=1;i<NF;i++) printf cnt[i]","; print cnt[NF] }';
}
export -f doit1
parallel --pipe-part -a "$fn" --block -1 doit1 > blocksums
if [ $csvheader -eq 1 ]
then
head -n1 "$fn" | doit1 | perl -pe 's/(^|,)/$1-/g' > headersum
cat headersum blocksums | perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] } END { $" = "\n";print "@s\n" }' > "$outfile"
else
cat blocksums | perl -F, -ane 'for(0..$#F) { $s[$_] += $F[$_] } END { $" = "\n";print "@s\n" }' > "$outfile"
fi
新:這是順序代碼中的ROW方式(不是列式)計數:
tail -n +2 train_all.csv | awk -F"," '{ cnt=0; for(i=1; i<=NF; i++){ cnt+=(($i+0)!=$i) && ($i!="") } print cnt; }' > train_all_cnt_nonnumerics_rowwwise.out.txt
背景:項目是機器學習 。 這是數據探索的一部分。 在使用Samsung 950 Pro SSD存儲的Dual Xeon 32虛擬/ 16物理核心共享內存主機上看到~25倍並行加速 :(32x60)秒連續時間,74秒並行時間。 真棒!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.