Comment puis-je regrouper des numéros dans un file

J'ai un file avec des nombres en format flottant.
Je peux les examiner via le sort -rn numbers.txt | less sort -rn numbers.txt | less
J'aimerais bien pouvoir les "grouper". Je vois facilement combien sont dans la même gamme.
Pour donner un exemple du file:

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

Je me fiche de l'exactitude. Donc, je voudrais savoir combien de 25 sont dans le file, par exemple dans ce cas 4 et 30, etc. pour tous les numéros dans le file.
Donc pour cet exemple une sortie comme: 3 pour 30, 1 pour 29, 1 pour 26, 4 pour 25, 3 pour 24.
Y a-t-il un moyen facile de faire ceci?

Que diriez-vous

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

En utilisant vos données d'exemple,

 $ cut -d. -f1 numbers.txt | sort | uniq -c 3 24 4 25 1 26 1 29 3 30 

Avec 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) 

Le -F définit le séparateur de champs ( FS ) sur . , sauf que nous parcourons toutes les lignes avec {COUNTS[$1]++} , en utilisant $1 comme part avant le séparateur décimal ( . ) et en gardant un logging du nombre de fois où nous les rencontrons dans un tableau nommé COUNTS .

À la fin ( END {} ), nous vidons ensuite ce que nous avons trouvé. Comme vous pouvez voir la plus grande partie est la sortie.

Un peu plus lisible dans un file:

 {COUNTS[$1]++} END { for(ct in COUNTS) { printf("%d %d time(s)\n", ct, COUNTS[ct]) } } 

Vous pourriez utiliser awk :

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

Si vous voulez que la sortie soit sortingée, canalisez la sortie pour sort :

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

Pour votre input d'échantillon, cela produirait:

 3 24 4 25 1 26 1 29 3 30 

En perl :

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

modifier

Probablement plus efficace:

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

cut -b-2 choisit les deux premiers caractères, sort -nr sortinge les résultats par la plus haute fréquence en premier

Résultat obtenu:

  4 25 3 30 3 24 1 29 1 26 

Ou en tant que python oneliner, juste pour le plaisir:

 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)]))))' 

Résultat obtenu:

 [(4, '25'), (3, '30'), (3, '24'), (1, '29'), (1, '26')] 

Il semble que votre file a été sortingé, vous pouvez donc faire comme ceci:

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

S'il n'a pas été sortingé:

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

Une approche GNU coreutils + grep:

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

Le drapeau -o indique à grep d'imprimer uniquement la partie correspondante de la ligne et le -P active les expressions régulières compatibles Perl, ce qui nous permet d'utiliser \d pour les nombres. Ainsi, le grep imprimera le plus long tronçon de numbers trouvé au début de la ligne (c.-à-d. Tout jusqu'au premier chiffre, le . ) Puis sortingera la sortie et uniq -c count le nombre d'occurrences de chaque string dans l'input.

Une autre approche Perl:

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

Le $& est la string appariée dans l'opération de correspondance précédente, donc nous la stockons dans un hachage ( %k ) et incrémentons sa valeur d'un. Le bloc END imprimera chaque nombre trouvé ( $_ ) et sa valeur dans le hachage ( $k{$_} ), le nombre de fois où il a été trouvé.

Et une approche de arrays associatifs bash (> = version 4):

 $ while IFS='\.' read -rab; 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 réglé sur . signifie que les lignes d'input sont divisées en loggings sur . de sorte que $a sera les premiers numbers jusqu'à la . . Nous les parcourons et les utilisons comme des keys d'un tableau associatif dont la valeur est augmentée chaque fois que le nombre est trouvé. Ensuite, une fois que le tableau a été rempli, nous parcourons sa list de keys ( ${!ll[@]} ) et imprimons chaque key et sa valeur (le nombre de fois qu'il a été vu).

Utilisation de l'option bin GNU datamash :

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

Sortie:

 24 3 25 4 26 1 29 1 30 3 

Avec le meunier :

 $> 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