Como obter apenas linhas uniq com base em uma coluna usando linux cmd?

Como obter apenas linhas uniq com base em uma coluna usando linux cmd?

Aqui está meu conjunto de dados:

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

Resultado esperado:

f,g,h
x,t,k

Critério de seleção:

Se algo ocorrer col1várias vezes, todas as linhas associadas serão excluídas.

Posso resolver isso usando Linux sortou uniqqualquer outra coisa?

Responder1

Aqui está uma abordagem de duas passagens "sem buffer" (1)awk (funcionará apenas em arquivos regulares).

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

Isso processará o arquivo duas vezes, portanto, ele será declarado duas vezes como argumento na linha de comando.

  • O argumento -F','define o separador de campos como ,.
  • Na primeira passagem, quando NR, o contador de linhas global, é igual a FNR, o contador de linhas por arquivo, registramos com que frequência cada valor na coluna 1 é encontrado em um array cnt(que assume o valor como "índice do array"), mas pule imediatamente o processamento para a próxima linha.
  • Na segunda passagem, verificamos se o contador de ocorrências do valor atual da primeira coluna é exatamente 1 e se o número da linha dentro do arquivo é maior que 1 (para pular o cabeçalho). Somente se isso for verdade a linha atual será impressa. Isso faz uso da awksintaxe de uma expressão fora dos blocos de regras que avalia para trueinstruir awka imprimir a linha atual.

(1) Em reação a um comentário que fizsem bufferentre aspas, pois como a solução irá armazenar alguns dados do arquivo temporariamente na RAM, elafazvem com uso de RAM. No entanto, não armazenará o conteúdo do arquivo literalmentealém dissopara quaisquer outros dados de manutenção de rolagem na RAM (queEUconsideraria "buffering" no sentido real).

Responder2

Supondo que o arquivo seja, /tmp/datavocê pode fazer isso com uma linha perl:

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

Ou mais legível...:

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
}

Responder3

awkúnica solução

  1. não mantendo a ordem

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

    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
    

onde

  • -F,diga ao awk para usar ,como separador

  • NR>1 depois da primeira linha

  • count[$1]++contar elemento da primeira coluna

  • line[$1]=$0 linha de loja

  • ENDapós o final do arquivo

  • for ( c in count)percorrer o elemento

  • if (count[c] ==1)se apenas um

  • print line[c]linha de impressão

  • ae col[]são usados ​​para armazenar a ordem da linha para preservar a variante.

isso pode ser onelined, eu dobro para facilitar a leitura

Responder4

decorar/classificar/usar/desdecorar usando qualquer versão das ferramentas POSIX obrigatórias e quaisquer caracteres em sua entrada (a menos que sua entrada seja realmente um CSV com campos entre aspas que podem conter vírgulas e/ou novas linhas, mas todas as outras respostas também falhariam) e mantendo a ordem das linhas de entrada para a saída e abrindo a entrada apenas uma vez, para que funcione se a entrada vier de um canal ou arquivo e sem armazenar toda a entrada na memória:

$ 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

informação relacionada