Find Máximo de todas as colunas com base na primeira coluna distinta

2

Estou usando o Ubuntu e tenho um arquivo de entrada como este

ifile.dat
1   10  15
3   34  20
1   4   22
3   32  33
5   3   46
2   2   98
4   20  100
3   13  23
4   50  65
1   40  76
2   20  22

Como faço para isso?

ofile.dat
1   40  76
2   20  98
3   34  33
4   50  100
5   3   46

Quero dizer o máximo de cada coluna comparando a primeira coluna. Obrigado.

Aqui está o que eu tentei (em um arquivo de amostra com 13colunas). Mas o valor mais alto não está chegando assim.

cat input.txt | sort -k1,1 -k2,2nr -k3,3nr -k4,4nr -k5,5nr -k6,6nr -k7,7nr -k8,8nr -k9,9nr -k10,10nr -nrk11,11 -nrk12,12 -nrk13,13 | sort -k1,1 -u 

Não funcionou. Então, um cara útil tentou me ajudar com isso abaixo. Mas não importa em mac ou ubuntu com gawk, eu não consegui rodar e ver os erros abaixo

awk 'BEGIN{PROCINFO["sorted_in"] = "@val_num_asc"} {for(i=2;i<=NF;++i) if (a[$1][i]<$i){a[$1][i]=$i}} END{n=asorti(a, asorted); for(col1 in asorted){print col1, a[col1][2], a[col1][3]}}' input.txt 

Erro é

awk: syntax error at source line 1
 context is
    BEGIN{PROCINFO["sorted_in"] = "@val_num_asc"} {for(i=2;i<=NF;++i) if >>>  (a[$1][ <<< 
awk: illegal statement at source line 1
awk: illegal statement at source line 1

Eu tentei remover a instrução BEGIN e brincar com o loop for, mas não consegui encontrar sorte. Obrigado.

P.S .: Eu recebi essa resposta do stackoverflow. Então estou postando aqui porque este é um fórum especial unix / linux.

    
por j smith 05.07.2017 / 22:48

6 respostas

4

solução awk para qualquer número de colunas (você mencionou um arquivo de amostra com 13 colunas ):

Digamos que temos o arquivo de amostra estendido:

1   10  15  10  99
3   34  20  20  111
1   4   22  22  33
3   32  33  12  5
5   3   46  44  9
2   2   98  55  55 
4   20  100 11  33
3   13  23  77  23
4   50  65  33  66
1   40  76  78  16
2   20  22  98  93
awk '{ for(i=2;i<=NF;i++) { if (!($1 in a) || $i > a[$1][i]) a[$1][i]=$i }}
     END{ r=""; for(i in a) { r=i; for(j in a[i]) r=r OFS a[i][j]; print r } 
     }' OFS='\t' file

A saída:

1   40  76  78  99
2   20  98  98  93
3   34  33  77  111
4   50  100 33  66
5   3   46  44  9
    
por 05.07.2017 / 23:14
7

O datamash do GNU é bom para coisas como esta:

$ datamash -sW groupby 1 max 2,3 < ifile.dat 
1   40  76
2   20  98
3   34  33
4   50  100
5   3   46

Para lidar com um número maior de colunas, você pode especificar um intervalo , por exemplo,

datamash -sW groupby 1 max 2-13 < ifile.dat 
    
por 05.07.2017 / 23:12
4

Aqui está uma maneira no awk:

$ awk '{ 
        if($2 > a[$1][2]){
            a[$1][2] = $2
        } 
        if($3 > a[$1][3]){
            a[$1][3] = $3
        }
       }
  END{
        for(i in a){
            printf "%s ", i; 
            for(c=1; c<=maxFields; c++){
              if(c in a[i]){
                 printf "%s ",a[i][c]
              }
            }
            print ""
        }' ifile.dat 
1 40 76
2 20 98
3 34 33
4 50 100
5 3 46

O script simplesmente usa a matriz bidimensional a para armazenar o valor máximo para cada uma das duas colunas. Para cada valor i da primeira coluna, a[i][2] manterá o valor máximo encontrado para i na segunda coluna e a[i][3] o máximo para a terceira coluna. Depois que o arquivo inteiro tiver sido processado, imprimiremos os valores máximos para cada valor de i .

Se você tem mais de 3 colunas, você pode usar:

awk '{ 
        for(c=2; c<=NF; c++){
            if($c > a[$1][c]){
                a[$1][c] = $c; 
            }
        }
       } 
       END{
            for(i in a){
                printf "%s: ", i; 
                for(c in a[i]){
                    printf "%s ",a[i][c]
                }
                print ""
            }
        }' ifile.dat 

Observe que a solução acima não funcionará corretamente com valores negativos, ou se você pode ter 0 etc e pode errar a ordem dos campos, pois awk não necessariamente percorre os arrays em ordem. Uma abordagem mais robusta é:

awk '{ 
        for(c=2; c<=NF; c++){
            if(!(c in a) || $c > a[$1][c]){
                a[$1][c] = $c; 
            }
        }
      } 
      END{
            for(i in a){
                printf "%s ", i; 
                for(c in a[i]){
                    printf "%s ",a[i][c]
                }
                print ""
            }
         }' ifile.dat 
    
por 05.07.2017 / 23:11
2
perl -lane '
   $F[$_] > $A[$F[0]-1][$_] and $A[$F[0]-1][$_] = $F[$_] for 1 .. $#F}{
   print 1+$_, "@{$A[$_]}" for grep defined $A[$_], 0 .. $#A
' ifile.dat

Resultados

1 40 76
2 20 98
3 34 33
4 50 100
5 3 46

Trabalhando

Data structure involved is an 'LoL' (list of lists) assuming that the
column 1 data is nonnegative.

@A = (
   [column_2_max_for_idx1, column_3_max_for_idx1, column_4_max_for_idx1, ...],
   [........],
);
    
por 06.07.2017 / 07:45
2

Uso da classificação como a principal ferramenta:

sort             ifile.dat -k1,1 -k2,2nr | sort -uk1,1 | awk '{print $1,$2}' \
| paste - <(sort ifile.dat -k1,1 -k3,3nr | sort -uk1,1 | awk '{print $3}')
    
por 05.07.2017 / 23:12
2

Script do Python 3

#!/usr/bin/env python3
import sys
from collections import OrderedDict as od

# read data in the file first, create data dictionary of column lists
data = od()
with open(sys.argv[1]) as f:
     for line in f:
          columns = line.strip().split()
          how_many = len(columns)-1
          if columns[0] not in data.keys():
              data[ columns[0] ] = [ [] for i in range(how_many) ]
          for index in range(how_many):
              data[ columns[0] ][index].append( int(columns[index+1]) )

# post process all the created lists of lists by applying max() on each
for item in sorted(data.keys()):
    print(item,end=" ") 
    for array in data[item]:
        print(max(array),end=" ")
    print("")

Execução de teste

Com o exemplo de entrada fornecido pelo OP:

$ ./columns_max.py input.txt                                                                                                                         
1 40 76 
2 20 98 
3 34 33 
4 50 100 
5 3 46 

Com um exemplo estendido na resposta de Roman Perekhrest:

$ ./columns_max.py input.txt                                                                                                                         
1 40 76 78 99 
2 20 98 98 93 
3 34 33 77 111 
4 50 100 33 66 
5 3 46 44 9 

Como isso funciona:

A idéia essencial é que criamos um dicionário de itens da primeira coluna. Portanto, no dicionário, teremos as chaves 1,2,3,4 e 5. Cada valor correspondente ao item do dicionário é uma lista de listas, onde cada sub-lista corresponde a uma coluna. Portanto, para a chave 1, teríamos uma lista com duas listas, em que a primeira lista é para todos os itens da coluna 2 e a segunda lista para todos os itens da coluna 3. Basicamente, isso:

('1', [ ['10', '4', '40'], ['15', '22', '76']] )

Agora, há uma função muito legal chamada max() , que nos permite obter uma lista numérica e extrair o maior item dela. Tudo o que temos a fazer é iterar cada chave, eliminar todas as listas e aplicar a função max() a elas.

    
por 06.07.2017 / 05:29