Imprima se para o mesmo 1º campo há um único valor do 2º campo em todas as linhas

1

Eu tenho um arquivo com duas colunas separadas por um espaço. A segunda coluna terá apenas T ou F . Eu quero ler a carta na primeira coluna, por exemplo: A tem 3 linhas com a mesma letra T na segunda coluna, então eu vou manter A , no entanto, quando misturou letra na segunda coluna como para B e D , serão removidos.

A T
A T
A T
B T
B T
B F
C F
C F
D F
D T
D F

Eu quero imprimir se para o mesmo primeiro campo houver um único valor do segundo campo em todas as linhas. Saída esperada:

A T
A T
A T
C F
C F

Acho que awk pode fazer isso? Obrigado pela sua ajuda!

    
por Ellie 28.02.2017 / 21:54

5 respostas

2

Aqui está uma maneira com awk :

awk 'NR==FNR{if (x[$1]++){if ($2!=t){z[$1]++}} else {t=$2};
next}!($1 in z)' infile infile

Este processo o arquivo duas vezes - 1ª passagem verifica se existem valores diferentes do 2º campo quando o 1º campo é o mesmo valor - se assim for usa $1 como um índice de array então na 2ª passagem ele imprime somente se 1º campo não é um índice da matriz.
Ou, se você não se importa em usar sort com awk :

sort -u infile | awk 'NR==FNR{seen[$1]++;next}seen[$1]==1' - infile

sort -u remove as linhas duplicadas do arquivo e canaliza o resultado para awk , que conta as ocorrências do primeiro campo, e processa o arquivo inteiro novamente, imprimindo as linhas se a contagem for 1 .

    
por 28.02.2017 / 23:32
2
sed -e '
   # this is a do-while loop which collects lines till the time the first
   # field remains the same. We break out of the loop when we see
   # a line whose 1st field != prev lines 1st field **OR** we hit the eof.
  :a
     $bb
     N
  /^\(\S\+\) .\(\n .\)*$/ba

  :b

  # all equal
  # **Action:** Print and quit

  /^\(\S\+ .\)\(\n\)*$/q


  # all same 1st fld, but lines unequal, otw would have matched above
  # **Action:** Drop the whole block as its uninteresting

  /^\(\S\+\) .\(\n .\)*$/d


  # all equal, and trailing line part of next line
  # **Action:** Display upto the last newline and restart 
  # with the trailing portion

  /^\(\(\S\+ .\)\(\n\)*\)\n[^\n]*$/{
     h
     s///p   
     g
  }


  # of same 1st fld but some lines unequal, and trailing portion has
  # next line
  # **Action:** strip till the last newline, and restart over with the
  # trailing part

  s/.*\(\n\)//
  D
' yourfile

Este é um problema muito interessante para "Sed" resolver. No entanto, o que eu acho que falta é um pouco melhor ou devo dizer maior conjunto de insumos fornecidos por OT no SE. O que proponho é que os testes de magnitude e variedade realistas possam ser colocados no site htttp: / pastebin, que é muito útil para tais coisas.

    
por 01.03.2017 / 11:28
1

Se você tiver acesso ao datamash do GNU , poderá recolher os dados da seguinte forma:

datamash -W groupby 1 countunique 2 collapse 2 < file 
A   1   T,T,T
B   2   T,T,F
C   1   F,F
D   2   F,T,F

Isso simplifica o pós-processamento com awk , por exemplo:

datamash -W groupby 1 countunique 2 collapse 2 < file | 
  awk '$2==1 {n = split($3,a,","); for (i=1;i<=n;i++) print $1, a[i]}'
A T
A T
A T
C F
C F
    
por 28.02.2017 / 22:39
1

Apenas para registro, mais uma alternativa com o AWK sem a necessidade de ler o arquivo novamente. Parece funcionar mesmo com arquivos não classificados ou mesmo com entradas encontradas apenas uma vez no arquivo.

$ awk '( a[$1] && (($2!=a[$1]) || ($1 in bad)) ) {bad[$1]++}; \
{a[$1]=$2;f1[FNR]=$1;f2[FNR]=$2}; \
END {for (i in f1) {if (!(f1[i] in bad)) print i,f1[i],f2[i]}}' ./tmp/file12


1 A T
2 A T
3 A T
12 C F
13 C F
21 E F

Em que file12 é como:

$ cat -n ./tmp/file12
 1  A T
 2  A T
 3  A T
 4  B T
 5  B T
 6  B F
 7  B F
 8  B T
 9  B F
10  B F
11  B F
12  C F
13  C F
14  D F
15  D T
16  D F
17  D F
18  D F
19  D F
20  D F
21  E F

Algumas explicações:

( a[$1] && (($2!=a[$1]) || ($1 in bad)) ) {bad[$1]++};
# The first time value $1 is checked,a[$1] will return null/0 since a[$1]
# has never been assigned. Thus a[$1] will be evaluated as false by
# condition check and further condition check and record bad marking will be skipped.
# The second time (second line) a[$1] will have a value and will be evaluated as true . 
# Then we check if $2 is equal to previous a[$1]=$2. If it is equal all ok.  
# Otherwise if current $2 <> previous a[$1] $2value, mark this record as bad.
# Finally there is a last check if record has been already been marked as bad.

{a[$1]=$2;f1[FNR]=$1;f2[FNR]=$2};     #some array delcaration

END {for (i in f1) {if (!(f1[i] in bad)) print i,f1[i],f2[i]}}' ./tmp/file12 
# Printing the lines of file that does not belong in bad boys.
    
por 01.03.2017 / 14:05
0
sed '
    /\n/D
    :1
    $! {
        N
        /^\(\S\+\s\).*\n[^\n]\+$/ b1
    }
    /^\([^\n]\+\n\)\(\)\+[^\n]\+$/! D
    h
    s/\n[^\n]\+$//p
    g
    s/.*\n/\n/
    D
    ' file
    
por 28.02.2017 / 23:38