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'
$0Es 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 1es un atajo awk: imprime la línea actual para cada condición verdadera, y 1como condición se evalúa como verdadera.
printpuede 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.pyEjecú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 +pa cada línea de la entrada, y luego pasando el resultado a la dccalculadora 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 -e0argumento empuja 0a la dcpila 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.116scasi 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_MAXes 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
xes la suma acumulada de todos los números de la línea actual y superior.
nes el número en la línea actual.
Nos bucle sobre todas las líneas nde INPUT_FILEy añadir su valor numérico para la variable xe 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 bclugar:
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 sedexpresión transforma la entrada en:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Esto luego es evaluado por bc. La avariable bc se inicializa automáticamente a 0. Cada línea se incrementa a, luego la imprime explícitamente.
real 0m5.642sen 1.3 millones de líneas. sed es muy lento en esto.