J'ai un fichier texte avec 2 millions de lignes. Chaque ligne a un entier positif. J'essaie de former une sorte de tableau de fréquences.
Fichier d'entrée:
3
4
5
8
La sortie doit être:
3
7
12
20
Comment dois-je procéder?
J'ai un fichier texte avec 2 millions de lignes. Chaque ligne a un entier positif. J'essaie de former une sorte de tableau de fréquences.
Fichier d'entrée:
3
4
5
8
La sortie doit être:
3
7
12
20
Comment dois-je procéder?
Réponses:
Avec awk
:
awk '{total += $0; $0 = total}1'
$0
est la ligne actuelle. Donc, pour chaque ligne, je l'ajoute à la total
, définit la ligne sur la nouvelle total
, puis la fin 1
est un raccourci awk - il imprime la ligne actuelle pour chaque condition vraie, et 1
comme une condition est évaluée comme vraie.
print
peut-il également être utilisé?
print total}
au lieu de$0 = total}1
{print(total += $0)}
Dans un script python:
#!/usr/bin/env python3
import sys
f = sys.argv[1]; out = sys.argv[2]
n = 0
with open(out, "wt") as wr:
with open(f) as read:
for l in read:
n = n + int(l); wr.write(str(n)+"\n")
add_last.py
Exécutez-le avec le fichier source et le fichier de sortie ciblé comme arguments:
python3 /path/to/add_last.py <input_file> <output_file>
Le code est plutôt lisible, mais en détail:
Ouvrir le fichier de sortie pour écrire les résultats
with open(out, "wt") as wr:
Ouvrir le fichier d'entrée pour la lecture par ligne
with open(f) as read:
for l in read:
Lisez les lignes, en ajoutant la valeur de la nouvelle ligne au total:
n = n + int(l)
Écrivez le résultat dans le fichier de sortie:
wr.write(str(n)+"\n")
Juste pour le fun
$ sed 'a+p' file | dc -e0 -
3
7
12
20
Cela fonctionne par un ppending +p
à chaque ligne de l'entrée, et en faisant passer le résultat à la dc
calculatrice où
+ Pops two values off the stack, adds them, and pushes the result.
The precision of the result is determined only by the values of
the arguments, and is enough to be exact.
puis
p Prints the value on the top of the stack, without altering the
stack. A newline is printed after the value.
L' -e0
argument pousse 0
sur la dc
pile pour initialiser la somme.
real 0m4.234s
Dans Bash:
#! /bin/bash
file="YOUR_FILE.txt"
TOTAL=0
while IFS= read -r line
do
TOTAL=$(( TOTAL + line ))
echo $TOTAL
done <"$file"
real 0m53.116s
près d'une minute, sur 1,3 million de lignes :)
Pour imprimer une somme partielle d'entiers donnée sur l'entrée standard, une par ligne:
#!/usr/bin/env python3
import sys
partial_sum = 0
for n in map(int, sys.stdin):
partial_sum += n
print(partial_sum)
Si pour une raison quelconque, la commande est trop lente; vous pouvez utiliser le programme C:
#include <stdint.h>
#include <ctype.h>
#include <stdio.h>
int main(void)
{
uintmax_t cumsum = 0, n = 0;
for (int c = EOF; (c = getchar()) != EOF; ) {
if (isdigit(c))
n = n * 10 + (c - '0');
else if (n) { // complete number
cumsum += n;
printf("%ju\n", cumsum);
n = 0;
}
}
if (n)
printf("%ju\n", cumsum + n);
return feof(stdin) ? 0 : 1;
}
Pour le construire et l'exécuter, tapez:
$ cc cumsum.c -o cumsum
$ ./cumsum < input > output
UINTMAX_MAX
est 18446744073709551615
.
Le code C est plusieurs fois plus rapide que la commande awk sur ma machine pour le fichier d'entrée généré par:
#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
accumulate()
itertool
Vous voulez probablement quelque chose comme ça:
sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Explication de la commande:
sort -n <filename> | uniq -c
trie l'entrée et renvoie une table de fréquences| awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
transforme la sortie en un format plus agréableExemple:
fichier d'entrée list.txt
:
4
5
3
4
4
2
3
4
5
La commande:
$ sort -n list.txt | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Number Frequency
2 1
3 2
4 4
5 2
Vous pouvez le faire dans vim. Ouvrez le fichier et saisissez les touches suivantes:
qaqqayiwj@"<C-a>@aq@a:wq<cr>
Notez que <C-a>
c'est en fait ctrl-a, et <cr>
est un retour chariot , c'est-à-dire le bouton Entrée.
Voici comment cela fonctionne. Tout d'abord, nous voulons effacer le registre «a» afin qu'il n'ait aucun effet secondaire la première fois. C'est tout simplement qaq
. Ensuite, nous faisons ce qui suit:
qa " Start recording keystrokes into register 'a'
yiw " Yank this current number
j " Move down one line. This will break the loop on the last line
@" " Run the number we yanked as if it was typed, and then
<C-a> " increment the number under the cursor *n* times
@a " Call macro 'a'. While recording this will do nothing
q " Stop recording
@a " Call macro 'a', which will call itself creating a loop
Une fois cette macro récursive exécutée, nous appelons simplement :wq<cr>
à enregistrer et à quitter.
Perl one-liner:
$ perl -lne 'print $sum+=$_' input.txt
3
7
12
20
Avec 2,5 millions de lignes de chiffres, il faut environ 6,6 secondes pour traiter:
$ time perl -lne 'print $sum+=$_' large_input.txt > output.txt
0m06.64s real 0m05.42s user 0m00.09s system
$ wc -l large_input.txt
2500000 large_input.txt
real 0m0.908s
, plutôt sympa.
Une simple doublure Bash:
x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE
x
est la somme cumulée de tous les nombres de la ligne actuelle et au-dessus.
n
est le numéro de la ligne actuelle.
Nous bouclons sur toutes les lignes n
de INPUT_FILE
et ajoutons leur valeur numérique à notre variablex
et imprimons cette somme à chaque itération.
Bash est un peu lent ici cependant, vous pouvez vous attendre à ce que cela dure environ 20-30 secondes pour un fichier avec 2 millions d'entrées, sans imprimer la sortie sur la console (ce qui est encore plus lent, indépendamment de la méthode que vous utilisez).
Semblable à la réponse de @ steeldriver, mais avec un peu moins d'arcane à la bc
place:
sed 's/.*/a+=&;a/' input | bc
La bonne chose à propos de bc
(etdc
) est qu'ils sont des calculateurs de précision arbitraires, donc ils ne déborderont jamais ou ne souffriront pas d'un manque de précision sur les entiers.
L' sed
expression transforme l'entrée en:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Ceci est ensuite évalué par bc
. La a
variable bc est initialisée automatiquement à 0. Chaque ligne s'incrémente a
, puis l'imprime explicitement.
real 0m5.642s
sur 1,3 million de lignes. sed est vraiment lent à ce sujet.