¿Cómo puedo sumar rápidamente todos los números en un archivo?


Tengo un archivo que contiene varios miles de números, cada uno en su propia línea:

34
42
11
6
2
99
...

Estoy buscando escribir un script que imprima la suma de todos los números en el archivo. Tengo una solución, pero no es muy eficiente. (Se tarda varios minutos en correr.) Estoy buscando una solución más eficiente. Alguna sugerencia?

Author: brian d foy, 2010-04-24

26 answers

Para un Perl one-liner, es básicamente lo mismo que la awk solución en Respuesta de Ayman Hourieh :

 % perl -nle '$sum += $_ } END { print $sum'

Si tienes curiosidad por saber qué hacen los one-liners de Perl, puedes apartarlos:

 %  perl -MO=Deparse -nle '$sum += $_ } END { print $sum'

El resultado es una versión más detallada del programa, en una forma que nadie escribiría por su cuenta:

BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    $sum += $_;
}
sub END {
    print $sum;
}
-e syntax OK

Solo por risitas, probé esto con un archivo que contiene 1,000,000 números (en el rango 0 - 9,999). En mi Mac Pro, regresa virtualmente instantáneamente. Es una lástima, porque esperaba que usar mmap fuera muy rápido, pero es al mismo tiempo:

use 5.010;
use File::Map qw(map_file);

map_file my $map, $ARGV[0];

$sum += $1 while $map =~ m/(\d+)/g;

say $sum;
 92
Author: brian d foy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:20

Puedes usar awk:

awk '{ sum += $1 } END { print sum }' file
 306
Author: Ayman Hourieh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-23 23:39:55

Ninguna de las soluciones hasta ahora utilizar paste. Aquí hay uno:

paste -sd+ filename | bc

Como ejemplo, calcule Σn donde 1

$ seq 100000 | paste -sd+ | bc -l
5000050000

(Para los curiosos, seq n imprimiría una secuencia de números de 1 a n dado un número positivo n.)

 83
Author: devnull,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-27 16:13:55

Solo por diversión, comparémoslo:

$ for ((i=0; i<1000000; i++)) ; do echo $RANDOM; done > random_numbers

$ time perl -nle '$sum += $_ } END { print $sum' random_numbers
16379866392

real    0m0.226s
user    0m0.219s
sys     0m0.002s

$ time awk '{ sum += $1 } END { print sum }' random_numbers
16379866392

real    0m0.311s
user    0m0.304s
sys     0m0.005s

$ time { { tr "\n" + < random_numbers ; echo 0; } | bc; }
16379866392

real    0m0.445s
user    0m0.438s
sys     0m0.024s

$ time { s=0;while read l; do s=$((s+$l));done<random_numbers;echo $s; }
16379866392

real    0m9.309s
user    0m8.404s
sys     0m0.887s

$ time { s=0;while read l; do ((s+=l));done<random_numbers;echo $s; }
16379866392

real    0m7.191s
user    0m6.402s
sys     0m0.776s

$ time { sed ':a;N;s/\n/+/;ta' random_numbers|bc; }
^C

real    4m53.413s
user    4m52.584s
sys 0m0.052s

Aborté el sed run después de 5 minutos

 75
Author: glenn jackman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 13:46:04

Esto funciona:

{ tr '\n' +; echo 0; } < file.txt | bc
 20
Author: Mark L. Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-14 00:46:58

Otra opción es usar jq:

$ seq 10|jq -s add
55

-s (--slurp) lee las líneas de entrada en una matriz.

 14
Author: user4669748,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-06 15:08:25

Esto es Bash recta:

sum=0
while read -r line
do
    (( sum += line ))
done < file
echo $sum
 8
Author: Dennis Williamson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-24 01:04:36

Aquí hay otro one-liner

( echo 0 ; sed 's/$/ +/' foo ; echo p ) | dc

Esto asume que los números son enteros. Si necesita decimales, pruebe

( echo 0 2k ; sed 's/$/ +/' foo ; echo p ) | dc

Ajuste 2 al número de decimales necesarios.

 6
Author: lhf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-26 11:34:12

Prefiero usar GNU datamash para tales tareas porque es más sucinto y legible que perl o awk. Por ejemplo

datamash sum 1 < myfile

Donde 1 denota la primera columna de datos.

 4
Author: hertzsprung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-13 10:34:22
cat nums | perl -ne '$sum += $_ } { print $sum'

(igual que la respuesta de brian d foy, sin 'FIN')

 3
Author: edibleEnergy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-23 16:56:45

Solo por diversión, vamos a hacerlo con PDL, el motor de matemáticas de matriz de Perl!

perl -MPDL -E 'say rcols(shift)->sum' datafile

rcols lee columnas en una matriz (1D en este caso) y sum (sorpresa) suma todo el elemento de la matriz.

 3
Author: Joel Berger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-23 19:55:21

Aquí hay una solución usando python con una expresión generadora. Probado con un millón de números en mi viejo portátil cruddy.

time python -c "import sys; print sum((float(l) for l in sys.stdin))" < file

real    0m0.619s
user    0m0.512s
sys     0m0.028s
 3
Author: dwurf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 12:05:19

Prefiero usar R para esto:

$ R -e 'sum(scan("filename"))'
 3
Author: fedorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-04 20:28:16
sed ':a;N;s/\n/+/;ta' file|bc
 2
Author: ghostdog74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-24 02:32:53
$ perl -MList::Util=sum -le 'print sum <>' nums.txt
 2
Author: Zaid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-13 12:59:54

Perl 6

say sum lines
~$ perl6 -e '.say for 0..1000000' > test.in

~$ perl6 -e 'say sum lines' < test.in
500000500000
 2
Author: Brad Gilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-03 18:02:09

Otro para la diversión

sum=0;for i in $(cat file);do sum=$((sum+$i));done;echo $sum

U otro bash solamente

s=0;while read l; do s=$((s+$l));done<file;echo $s

Pero la solución awk es probablemente la mejor ya que es la más compacta.

 1
Author: nickjb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-25 17:06:43

Con Ruby:

ruby -e "File.read('file.txt').split.inject(0){|mem, obj| mem += obj.to_f}"
 1
Author: juanpastas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-11 01:12:07

Más sucinto:

# Ruby
ruby -e 'puts open("random_numbers").map(&:to_i).reduce(:+)'

# Python
python -c 'print(sum(int(l) for l in open("random_numbers")))'
 1
Author: Vidul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-14 13:57:59

No se si puedes obtener algo mucho mejor que esto, considerando que necesitas leer todo el archivo.

$sum = 0;
while(<>){
   $sum += $_;
}
print $sum;
 0
Author: Stefan Kendall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-23 23:38:06

No he probado esto pero debería funcionar:

cat f | tr "\n" "+" | sed 's/+$/\n/' | bc

Es posible que tenga que agregar "\n" a la cadena antes de bc (como a través de echo) si bc no trata EOF y EOL...

 0
Author: DVK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-24 05:19:46

Aquí hay otro:

open(FIL, "a.txt");

my $sum = 0;
foreach( <FIL> ) {chomp; $sum += $_;}

close(FIL);

print "Sum = $sum\n";
 0
Author: ruben2020,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-20 06:24:03

Puede hacerlo con la utilidad de línea de comandos Alacon para la base de datos Alasql.

Funciona con Node.js, por lo que necesita instalar el nodo .js y luego Alasql paquete:

Para calcular la suma del archivo TXT puede usar el siguiente comando:

> node alacon "SELECT VALUE SUM([0]) FROM TXT('mydata.txt')"
 0
Author: agershun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-21 16:45:56

No es más fácil reemplazar todas las líneas nuevas por +, agregar un 0 y enviarlo al intérprete Ruby?

(sed -e "s/$/+/" file; echo 0)|irb

Si no tiene irb, puede enviarlo a bc, pero debe eliminar todas las líneas nuevas excepto la última (de echo). Es mejor usar tr para esto, a menos que tenga un doctorado en sed.

(sed -e "s/$/+/" file|tr -d "\n"; echo 0)|bc
 0
Author: Daniel Porumbel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-15 12:58:01

Solo para ser ridículo:

cat f | tr "\n" "+" | perl -pne chop | R --vanilla --slave
 -1
Author: frankc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-24 01:05:37

C siempre gana por velocidad:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    ssize_t read;
    char *line = NULL;
    size_t len = 0;
    double sum = 0.0;

    while (read = getline(&line, &len, stdin) != -1) {
        sum += atof(line);
    }

    printf("%f", sum);
    return 0;
}

Tiempo para números de 1M (misma máquina/entrada que mi respuesta de python):

$ gcc sum.c -o sum && time ./sum < numbers 
5003371677.000000
real    0m0.188s
user    0m0.180s
sys     0m0.000s
 -1
Author: dwurf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 12:25:29