web-dev-qa-db-fra.com

Comment générer un total cumulé cumulé des nombres dans un fichier texte?

J'ai un fichier texte avec 2 millions de lignes. Chaque ligne a un entier positif. J'essaie de former une sorte de table de fréquences.

Fichier d'entrée:

3
4
5
8

La sortie devrait être:

3
7
12
20

Comment puis-je faire cela?

8
user110327

Avec awk:

awk '{total += $0; $0 = total}1'

$0 est la ligne en cours. Donc, pour chaque ligne, je l’ajoute à total, la définit à la nouvelle total, puis le 1 suivant est un raccourci awk - il affiche la ligne actuelle pour chaque condition vraie et 1 en tant que condition évaluée à true.

17
muru

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

Utiliser

  • Copiez le script dans un fichier vide, enregistrez-le sous 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>
    

Explication

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)
    
  • Ecrivez le résultat dans le fichier de sortie:

    wr.write(str(n)+"\n")
    
9
Jacob Vlijm

Juste pour le fun

$ sed 'a+p' file | dc -e0 -
3
7
12
20

Cela fonctionne en a en ajoutant +p à chaque ligne de l'entrée, puis en transmettant le résultat à la calculatrice dc

   +      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'argument -e0 pousse 0 dans la pile dc pour initialiser la somme.

9
steeldriver

Dans Bash:

#! /bin/bash

file="YOUR_FILE.txt"

TOTAL=0
while IFS= read -r line
do
    TOTAL=$(( TOTAL + line ))
    echo $TOTAL
done <"$file"
8
Julen Larrucea

Pour imprimer des sommes partielles d'entiers données 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)

Exemple exécutable .

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

Exemple exécutable .

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')
6
jfs

Vous pouvez le faire dans vim. Ouvrez le fichier et tapez les frappes suivantes:

qaqqayiwj@"<C-a>@aq@a:wq<cr>

Notez que <C-a> est en fait ctrl-a et <cr> est 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 lors de la première utilisation. C'est 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 que cette macro récursive est exécutée, nous appelons simplement :wq<cr> pour enregistrer et quitter.

5
DJMcMayhem

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éable

Exemple:
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
5
Wayne_Yux

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
5
Sergiy Kolodyazhnyy

Un simple Bash one-liner:

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 en cours et plus.
n est le numéro de la ligne en cours.

Nous parcourons toutes les lignes n de INPUT_FILE et ajoutons leur valeur numérique à notre variable x et affichons cette somme à chaque itération.

Bash est un peu lent ici, mais vous pouvez vous attendre à environ 20-30 secondes pour un fichier de 2 millions d’entrées, sans imprimer le résultat sur la console (ce qui est encore plus lent, quelle que soit la méthode utilisée).

3
Byte Commander

Semblable à la réponse de @ steeldriver, mais avec le nom un peu moins arcanique bcname__:

sed 's/.*/a+=&;a/' input | bc

La bonne chose à propos de bc(et dcname__) est qu’il s’agit de calculatrices de précision arbitraire, elles ne débordent donc jamais ni ne manquent de précision sur les entiers.

L'expression sedtransforme l'entrée en:

a+=3;a
a+=4;a
a+=5;a
a+=8;a

Ceci est ensuite évalué par bcname__. La variable abc est initialisée automatiquement à 0. Chaque ligne incrémente aname__, puis l’imprime explicitement.

3
Digital Trauma