Tengo un archivo de texto con 2 millones de líneas. Cada línea tiene un entero positivo. Estoy tratando de formar un tipo de tabla de frecuencias.
Fichero de entrada:
3
4
5
8
La salida debe ser:
3
7
12
20
¿Cómo voy a hacer esto?
Tengo un archivo de texto con 2 millones de líneas. Cada línea tiene un entero positivo. Estoy tratando de formar un tipo de tabla de frecuencias.
Fichero de entrada:
3
4
5
8
La salida debe ser:
3
7
12
20
¿Cómo voy a hacer esto?
Respuestas:
Con awk
:
awk '{total += $0; $0 = total}1'
$0
Es la línea actual. Entonces, para cada línea, lo agrego a total
, establezco la línea en la nueva total
, y luego el final 1
es un atajo awk: imprime la línea actual para cada condición verdadera, y 1
como condición se evalúa como verdadera.
print
puede usar la palabra también?
print total}
lugar de$0 = total}1
{print(total += $0)}
En un script de 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
Ejecútelo con el archivo de origen y el archivo de salida de destino como argumentos:
python3 /path/to/add_last.py <input_file> <output_file>
El código es bastante legible, pero en detalle:
Abrir archivo de salida para escribir resultados
with open(out, "wt") as wr:
Abrir archivo de entrada para leer por línea
with open(f) as read:
for l in read:
Lea las líneas y agregue el valor de la nueva línea al total:
n = n + int(l)
Escriba el resultado en el archivo de salida:
wr.write(str(n)+"\n")
Solo por diversión
$ sed 'a+p' file | dc -e0 -
3
7
12
20
Esto funciona mediante una pendiente +p
a cada línea de la entrada, y luego pasando el resultado a la dc
calculadora donde
+ 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.
entonces
p Prints the value on the top of the stack, without altering the
stack. A newline is printed after the value.
El -e0
argumento empuja 0
a la dc
pila para inicializar la suma.
real 0m4.234s
En 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
casi un minuto, en 1.3 millones de líneas :)
Para imprimir sumas parciales de enteros dados en la entrada estándar, uno por línea:
#!/usr/bin/env python3
import sys
partial_sum = 0
for n in map(int, sys.stdin):
partial_sum += n
print(partial_sum)
Si por alguna razón el comando es demasiado lento; podrías usar el programa 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;
}
Para construirlo y ejecutarlo, escriba:
$ cc cumsum.c -o cumsum
$ ./cumsum < input > output
UINTMAX_MAX
es 18446744073709551615
.
El código C es varias veces más rápido que el comando awk en mi máquina para el archivo de entrada generado por:
#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
accumulate()
itertool
Probablemente quieras algo como esto:
sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Explicación del comando:
sort -n <filename> | uniq -c
ordena la entrada y devuelve una tabla de frecuencias| awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
convierte la salida en un formato más agradableEjemplo:
archivo de entrada list.txt
:
4
5
3
4
4
2
3
4
5
El comando:
$ 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
Puedes hacer esto en vim. Abra el archivo y escriba las siguientes teclas:
qaqqayiwj@"<C-a>@aq@a:wq<cr>
Tenga en cuenta que en <C-a>
realidad es ctrl-a, y <cr>
es retorno de carro , es decir, el botón enter.
Así es como funciona esto. En primer lugar, queremos borrar el registro 'a' para que no tenga efectos secundarios la primera vez. Esto es simplemente qaq
. Luego hacemos lo siguiente:
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
Después de que esta macro recursiva haya terminado de ejecutarse, simplemente llamamos :wq<cr>
para guardar y salir.
Perl one-liner:
$ perl -lne 'print $sum+=$_' input.txt
3
7
12
20
Con 2.5 millones de líneas de números, el procesamiento tarda aproximadamente 6.6 segundos:
$ 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
, muy agradable.
Un simple Bash de una sola línea:
x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE
x
es la suma acumulada de todos los números de la línea actual y superior.
n
es el número en la línea actual.
Nos bucle sobre todas las líneas n
de INPUT_FILE
y añadir su valor numérico para la variable x
e imprimir esa suma durante cada iteración.
Sin embargo, Bash es un poco lento aquí, puede esperar que esto se ejecute alrededor de 20-30 segundos para un archivo con 2 millones de entradas, sin imprimir el resultado en la consola (que es aún más lento, independientemente del método que utilice).
Similar a la respuesta de @ steeldriver, pero con un poco menos arcano en su bc
lugar:
sed 's/.*/a+=&;a/' input | bc
Lo bueno de bc
(y dc
) es que son calculadoras de precisión arbitrarias, por lo que nunca se desbordarán ni sufrirán falta de precisión sobre los enteros.
La sed
expresión transforma la entrada en:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Esto luego es evaluado por bc
. La a
variable bc se inicializa automáticamente a 0. Cada línea se incrementa a
, luego la imprime explícitamente.
real 0m5.642s
en 1.3 millones de líneas. sed es muy lento en esto.