我有一個非常大的.tsv檔案(80 GB)需要編輯。它由 5 列組成。最后一列代表一個分數。有些職位有多個“分數”條目,我只需要為每個職位保留最高值的行。
例如,這個位置的每個組合都有多個條目:
1 861265 C A 0.071
1 861265 C A 0.148
1 861265 C G 0.001
1 861265 C G 0.108
1 861265 C T 0
1 861265 C T 0.216
2 193456 G A 0.006
2 193456 G A 0.094
2 193456 G C 0.011
2 193456 G C 0.152
2 193456 G T 0.003
2 193456 G T 0.056
所需的輸出如下所示:
1 861265 C A 0.148
1 861265 C G 0.108
1 861265 C T 0.216
2 193456 G A 0.094
2 193456 G C 0.152
2 193456 G T 0.056
由于檔案太大或需要太長時間,因此無法在 python/pandas 中執行此操作。因此,我正在尋找使用的解決方案bash;特別是awk。
Thif 輸入檔案已使用以下命令排序:
sort -t$'\t' -k1 -n -o sorted_file original_file
該命令基本上需要:
- 比較前 4 列的資料
sorted_file - 如果所有這些都相同,則僅應將第 5 列中具有最高值的行列印到輸出檔案中。我對
awk語法不是很熟悉。我在其他論壇上看到過相對類似的問題,但我無法根據我的具體情況調整它。我試圖將其中一種解決方案適應我的情況,如下所示:
awk -F, 'NR==1 {print; next} NR==2 {key=$2; next}$2 != key {print lastval; key = $2} {lastval = $0} END {print lastval}' sorted_files.tsv > filtered_file.tsv
但是,輸出檔案看起來根本不像它應該的那樣。任何幫助將不勝感激。
uj5u.com熱心網友回復:
一種更穩健的方法是對最后一個欄位進行數字排序,然后awk選擇第一個值。如果您的欄位沒有空格,則無需指定分隔符。
$ sort -k1n k5,5nr original_file | awk '!a[$1,$2,$3,$4] ' > max_value_file
正如@Fravadona 評論的那樣,由于它存盤了密鑰,因此如果有許多唯一記錄,它將占用大量記憶體。一個替代方法是委托uniq在重復條目中選擇第一條記錄。
$ sort -k1n k5,5nr original_file |
awk '{print $5,$1,$2,$3,$4}' |
uniq -f1 |
awk '{print $2,$3,$4,$5,$1}'
我們更改欄位的順序以跳過值進行比較,然后再改回來。這不會有任何記憶體占用(除了sort將被管理的)。
如果您不是純粹主義者,這應該與前一個相同
$ sort -k1n k5,5nr original_file | rev | uniq -f1 | rev
uj5u.com熱心網友回復:
這不是 awk,但使用Miller非常簡單有趣
mlr --tsv -N sort -f 1,2,3,4 -n 5 then top -f 5 -g 1,2,3,4 -a input.tsv >output.tsv
你將會擁有
1 861265 C A 1 0.148
1 861265 C G 1 0.108
1 861265 C T 1 0.216
2 193456 G A 1 0.094
2 193456 G C 1 0.152
2 193456 G T 1 0.056
uj5u.com熱心網友回復:
你可以試試這個方法。這也適用于未排序的最后一列,只需對前 4 列進行排序。
% awk 'NR>1&&str!=$1" "$2" "$3" "$4{print line; m=0}
$5>=m{m=$5; line=$0}
{str=$1" "$2" "$3" "$4} END{print line}' file
1 861265 C A 0.148
1 861265 C G 0.108
1 861265 C T 0.216
2 193456 G A 0.094
2 193456 G C 0.152
2 193456 G T 0.056
資料
% cat file
1 861265 C A 0.071
1 861265 C A 0.148
1 861265 C G 0.001
1 861265 C G 0.108
1 861265 C T 0
1 861265 C T 0.216
2 193456 G A 0.006
2 193456 G A 0.094
2 193456 G C 0.011
2 193456 G C 0.152
2 193456 G T 0.003
2 193456 G T 0.056
uj5u.com熱心網友回復:
假設/理解:
- 檔案按第一個欄位排序
- 不保證欄位 #2、#3 和 #4 的順序
- 必須保持當前的行順序(這似乎排除了(重新)對檔案進行排序,因為我們可能會丟失當前的行順序)
- 給定的完整輸出行集
group將適合記憶體(也稱為awk陣列)
總體規劃:
- 我們將欄位 #1 稱為
group欄位;欄位 #1 中具有相同值的所有行都被視為相同的一部分group - 對于給定,我們通過陣列
group跟蹤所有輸出行(索引將是欄位#2、#3、#4 的組合)awkarr[] awk我們還通過陣列跟蹤傳入的行順序order[]arr[]如果我們在欄位 #5 中看到高于前一個值的值,則更新- 當更改將索引
group的當前內容重繪 到標準輸出時arr[]
一個awk想法:
awk '
function flush() { # function to flush current group to stdout
for (i=1; i<=seq; i )
print group,order[i],arr[order[i]]
delete arr # reset arrays
delete order
seq=0 # reset index for order[] array
}
BEGIN { FS=OFS="\t" }
$1!=group { flush()
group=$1
}
{ key=$2 OFS $3 OFS $4
if ( key in arr && $5 <= arr[key] )
next
if ( ! (key in arr) )
order[ seq]=key
arr[key]=$5
}
END { flush() } # flush last group to stdout
' input.dat
這會產生:
1 861265 C A 0.148
1 861265 C G 0.108
1 861265 C T 0.216
2 193456 G A 0.094
2 193456 G C 0.152
2 193456 G T 0.056
uj5u.com熱心網友回復:
更新
手冊摘錄sort:
-k, --key= KEYDEF
KEYDEF表示F[.C][OPTS][,F[.C][OPTS]]開始和停止位置,其中 F 是欄位編號,C 是欄位中的字符位置;兩者都是原點 1,停止位置默認為行的 end。
這意味著通過sort -t$'\t' -k1 -n像您一樣使用,檔案的所有欄位都有助于數字排序。
這可能是使用數字升序排序的最快解決方案: awk
awk '
BEGIN {
FS = "\t"
if ((getline line) > 0) {
split(line, arr)
prev_key = arr[1] FS arr[2] FS arr[4]
prev_line = $0
}
}
{
curr_key = $1 FS $2 FS $4
if (curr_key != prev_key) {
print prev_line
prev_key = curr_key
}
prev_line = $0
}
END {
if (prev_key) print prev_line
}
' file.tsv
注意:當您處理一個大約有 40 億行的檔案時,我試圖將運算元保持在最低限度。例如:
- 只需設定
FS為 即可節省 800 億次"\t"操作。確實,您為什么要允許awk將檔案的每個字符與" "處理 TSV 時進行比較? - 通過處理塊
getline中的第一行來節省 40 億次BEGIN比較。有些人可能會說使用(NR == 1)和/或更安全/更好/更清潔(NR > 1),但這意味著每個輸入行進行 2 次比較,而不是 0 次。
可能值得將此代碼的執行時間與 @EdMorton 的代碼進行比較,該代碼使用相同的演算法而沒有進行這些優化。磁盤速度可能會使差異變平^^
uj5u.com熱心網友回復:
假設您的實際輸入按鍵排序,然后以與您的示例相同的方式升序值是:
$ cat tst.awk
{ key = $1 FS $2 FS $3 FS $4 }
key != prevKey {
if ( NR > 1 ) {
print prevRec
}
prevKey = key
}
{ prevRec = $0 }
END {
print prevRec
}
$ awk -f tst.awk file
1 861265 C A 0.148
1 861265 C G 0.108
1 861265 C T 0.216
2 193456 G A 0.094
2 193456 G C 0.152
2 193456 G T 0.056
如果您的資料尚未排序,則只需使用以下命令對其進行排序:
sort file | awk ..
這種方式只sort需要一次處理整個檔案,并且它被設計為通過使用需求分頁等來做到這一點,因此與將整個檔案讀入 awk 或 python 或任何其他工具相比,記憶體不足的可能性要小得多
uj5u.com熱心網友回復:
使用 sort 和 awk:
sort -t$'\t' -k1,1n -k4,4 -k5,5rn file | awk 'BEGIN{FS=OFS="\t"} !seen[$1,$4] '
印刷:
1 861265 C A 0.148
1 861265 C G 0.108
1 861265 C T 0.216
2 193456 G A 0.094
2 193456 G C 0.152
2 193456 G T 0.056
這假設“組”被定義為第 1 列。
首先按第 1 列分組,然后按第 4 列(每個字母)分組,然后對第 5 列進行反向數字排序。
awk 然后列印第一組,看到的字母將是基于排序的最大值。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/410004.html
標籤:
上一篇:一起列印散列CSV輸出RUBY
下一篇:提高讀取csv檔案C 的速度
