簡體   English   中英

mawk語法適當> 1000字段文件用於按列計數非數字數據?

[英]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命令的一些聰明的組合能夠輕松地解決這個問題,但是在這里我很多個小時后沒有任何東西要顯示。 我伸出雙手來。 代碼差的施舍?

  • seq 1 2(>> 2用於實際CSV文件)
  • tail(根據需要跳過標題或不跳過標題)
  • mawk(漂亮的行列式CSV文件處理,我在我的演示中向您展示了一個方便的語法,可以輕松地在一個巨大尺寸的全數字CSV數據文件中查找非數字文件)
  • tr(刪除換行操作時很方便的換行符)
  • 切(一次抓一列)
  • 並行(快速是好的,我有很多內核需要處理的東西,以及phat RAM)

對不起,我絕對不需要使用像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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM