Como posso agrupar números em um arquivo

4

Eu tenho um arquivo com números no formato float.
Posso revisá-los via sort -rn numbers.txt | less
Eu gostaria de poder "agrupá-los". Ou seja veja facilmente quantos estão no mesmo intervalo.
Para dar um exemplo do arquivo:

30.9695041179657  
30.8851490020752  
30.2127060890198  
29.1361880302429  
26.4587681293488   
25.8535399436951   
25.7361891269684   
25.7305450439453   
25.1068568229675   
24.7598769664764   
24.3106801509857   
24.0782940387726   

Eu não me importo com precisão. Então eu gostaria de saber quantos 25's estão no arquivo, por exemplo neste caso, 4 e 30, etc, para todos os números no arquivo.
Portanto, para este exemplo, uma saída como: 3 para 30, 1 para 29, 1 para 26, 4 para 25, 3 para 24.
Existe uma maneira fácil de fazer isso?

    
por Jim 02.05.2014 / 14:50

9 respostas

13

Que tal

cut -d. -f1 numbers.txt | sort | uniq -c

Usando seus dados de exemplo,

$ cut -d. -f1 numbers.txt | sort | uniq -c
      3 24
      4 25
      1 26
      1 29
      3 30
    
por 02.05.2014 / 14:56
8

com awk ( mawk ):

$ awk -F . '{COUNTS[$1]++} END{for(ct in COUNTS) {printf("%d %d time(s)\n", ct, COUNTS[ct])}}' test.txt
30 3 time(s)
24 3 time(s)
25 4 time(s)
26 1 time(s)
29 1 time(s)

O -F define o separador de campo ( FS ) como . , além de percorrer todas as linhas com {COUNTS[$1]++} , usando $1 como a parte antes do separador decimal ( . ) e manter um registro de quantas vezes os encontramos em uma matriz chamada COUNTS .

No final ( END {} ), despejamos o que encontramos. Como você pode ver, a maior parte é a saída.

Um pouco mais legível em um arquivo:

{COUNTS[$1]++}
END {
  for(ct in COUNTS)
  {
    printf("%d %d time(s)\n", ct, COUNTS[ct])
  }
}
    
por 02.05.2014 / 15:00
7

Você pode usar awk :

awk '{a[int($1)]++}END{for (i in a) {print a[i], i}}' inputfile

Se você quiser que a saída seja classificada, canalize a saída para sort :

awk '{a[int($1)]++}END{for (i in a) {print a[i], i}}' inputfile | sort -k2

Para sua entrada de amostra, isso produziria:

3 24
4 25
1 26
1 29
3 30
    
por 02.05.2014 / 14:59
4

Em perl :

perl -lan -F'\.' -e '$count{$F[0]}++;
    END{ 
        print "$_ --> $count{$_}" for sort {$a <=> $b} keys %count
    }' your_file

Editar

Provavelmente mais eficiente:

perl -ne '
    $count{int()}++;
    END{ print "$_ --> $count{$_}" for sort {$a <=> $b} keys %count }'
your_file
    
por 02.05.2014 / 15:01
2
cut -b-2 numbers.txt | sort -n | uniq -c | sort -nr

cut -b-2 seleciona os dois primeiros caracteres, sort -nr classifica os resultados pela maior frequência primeiro

Resultado resultante:

  4 25
  3 30
  3 24
  1 29
  1 26

Ou como um oneliner python, só para o inferno:

python -c 'l = [x[:2] for x in open("numbers.txt").readlines()];print(list(reversed(sorted([(l.count(x),x) for x in set(l)]))))'

Resultado resultante:

[(4, '25'), (3, '30'), (3, '24'), (1, '29'), (1, '26')]
    
por 03.05.2014 / 15:25
1

Parece que seu arquivo foi classificado, então você pode fazer assim:

$ uniq -c <(perl -pe 's/\.\d*//' file)
      3 30
      1 29
      1 26
      4 25
      3 24

Se não tiver sido ordenado:

$ uniq -c <(perl -pe 's/\.\d*//' file | sort -rn)
      3 30
      1 29
      1 26
      4 25
      3 24
    
por 03.05.2014 / 07:46
1

Uma abordagem GNU coreutils + grep:

$ grep -oP '^\d+' file | sort | uniq -c
      3 24
      4 25
      1 26
      1 29
      3 30

O -o sinalizador diz grep para imprimir apenas a parte correspondente da linha e o -P ativa Perl Compatible Regular Expressions que nos permite usar \d para números. Assim, o grep imprimirá o maior número de dígitos encontrados no início da linha (ou seja, até o primeiro não dígito, o . ) e, em seguida, sort classificará a saída e uniq -c contará o número. número de ocorrências de cada string na entrada.

Outra abordagem de Perl:

$ perl -lne '/^\d+/ && $k{$&}++; END{print "$k{$_} : $_" for sort keys %k}' file 
3 : 24
4 : 25
1 : 26
1 : 29
3 : 30

O $& é a string correspondida na operação de correspondência anterior, portanto, armazenamos em um hash ( %k ) e incrementamos seu valor em um. O bloco END imprimirá cada número encontrado ( $_ ) e o valor que tiver no hash ( $k{$_} ), o número de vezes que foi encontrado.

E uma abordagem de matrizes associativas bash (> = versão 4):

$ while IFS='\.' read -r a b; do (( ll[$a]++ )); done <  file; 
    for i in ${!ll[@]} ; do echo ${ll[$i]} : $i; done
3 : 24
4 : 25
1 : 26
1 : 29
3 : 30

IFS definido como . significa que as linhas de entrada são divididas em registros em . , de modo que $a serão os primeiros dígitos até . . Nós os iteramos e os usamos como chaves para um array associativo cujo valor é aumentado toda vez que o número é encontrado. Então, uma vez que a matriz foi preenchida, nós iteramos através de sua lista de chaves ( ${!ll[@]} ) e imprimimos cada chave e seu valor (o número de vezes que foi visto).

    
por 03.05.2014 / 15:32
0

Usando a GNU datamash bin opção:

datamash -s  bin:1 1 < num | datamash -s -g 1 count 1

Saída:

24  3
25  4
26  1
29  1
30  3
    
por 22.10.2016 / 05:05
0

Com o moleiro :

$> mlr --from data.txt --ocsv put '$1=int(ceil($1))' then count-distinct -f 1
1,count
31,3
30,1
27,1
26,4
25,3
    
por 22.10.2016 / 08:00