如何使用linux cmd基於列僅取得uniq行?

如何使用linux cmd基於列僅取得uniq行?

這是我的數據集:

col1,col2,col3
a,b,c
a,d,f
d,u,v
f,g,h
d,u,g
x,t,k

預期輸出:

f,g,h
x,t,k

選擇標準:

如果某件事多次發生col1,則所有關聯的行都將被刪除。

sort我可以使用 Linux或uniq其他方式解決這個問題嗎?

答案1

這是一種「非緩衝」(1)兩次方法awk(僅適用於常規檔案)。

awk -F',' 'NR==FNR{cnt[$1]++;next} FNR>1&&cnt[$1]==1' input.csv input.csv 

這將處理該檔案兩次,因此在命令列上將其作為參數聲明兩次。

  • 此參數-F','將欄位分隔符號設為,
  • 在第一遍中,當NR全域行計數器 等於FNR每個文件行計數器時,我們記錄數組中遇到第 1 列中的每個值的頻率cnt(將該值作為「數組索引」),但立即跳到下一行處理。
  • 在第二遍中,我們檢查第一列目前值的出現計數器是否恰好為 1,並且檔案中的行號是否大於 1(以跳過標題)。只有當這是 true 時才會列印目前行。這利用了awk規則區塊之外的表達式的語法,該表達式的計算結果是true指示awk列印當前行。

(1)對我發表的評論的回應非緩衝用引號引起來,因為該解決方案會將檔案中的一些資料暫時儲存在 RAM 中,因此附帶 RAM 使用情況。但是它不會逐字儲存文件內容另外RAM 中任何其他滾動保持資料(其中會考慮實際意義上的「緩衝」)。

答案2

假設該檔案是/tmp/data您可以使用 perl 單行程式碼來完成的:

perl -e 'while(<STDIN>) { /(^\S+?),/; $show->{$1}=$_; $count->{$1}++;}; foreach(keys %$show) {print $show->{$_} if($count->{$_} == 1);}' < /tmp/data

或更具可讀性...:

while(<STDIN>) { #loop through all lines in the input and put the lines in "$_"
  /(^\S+?),/; #Everything before the first "," now ends up in "$1"
  $show->{$1} = $_; #a hash will be created with as keys the "$1" and as values the "$_"
  $count->{$1}++; #In the hash $count the number of occurrences will be increased everytime the same $1 appears
}
foreach(keys %$show) { #loop trough all lines
  print $show->{$_} if($count->{$_} == 1); #only print them if they occur once
}

答案3

awk唯一的解決方案

  1. 不遵守秩序

    awk -F, 'NR>1 { count[$1]++ ; line[$1]=$0 ;} 
       END { for ( c in count) if (count[c] ==1) print line[c]}' data
    
  2. 維持秩序

    awk -F, 'NR>1 { row[a]=$0; col[a]=$1; count[$1]++; ++a; } 
       END { for (i=0; i<a; ++i) if (count[col[i]]==1) print row[i]; }' data
    

在哪裡

  • -F,告訴 awk 要當,分隔符

  • NR>1 第一行之後

  • count[$1]++計算第一列的元素

  • line[$1]=$0 商店線

  • END文件結束後

  • for ( c in count)循環遍歷元素

  • if (count[c] ==1)如果只有一個

  • print line[c]列印行

  • acol[]用於儲存順序保留變體中的行順序。

這可以單行,為了可讀性我折起來

答案4

使用任何版本的強制性POSIX 工具和輸入中的任何字元進行裝飾/排序/使用/取消裝飾(除非您的輸入實際上是一個帶有引號欄位的CSV,其中可以包含逗號和/或換行符,但所有其他答案也會失敗)並且保留輸出的輸入行的順序,並且僅打開輸入一次,因此如果輸入來自管道或文件並且不將整個輸入存儲在內存中,它將起作用:

$ awk 'BEGIN{FS=OFS=","} NR>1{print ++cnt[$1], NR, $0}' file |
    sort -nt, -k1,1r -k2,2 |
    awk -F, '(!seen[$3]++) && ($1==1)' |
    cut -d, -f3-
f,g,h
x,t,k

相關內容