Ho un file di testo con 2 milioni di righe. Ogni riga ha un numero intero positivo. Sto cercando di formare una specie di tabella delle frequenze.
File di input:
3
4
5
8
L'output dovrebbe essere:
3
7
12
20
Come procedo a fare questo?
Ho un file di testo con 2 milioni di righe. Ogni riga ha un numero intero positivo. Sto cercando di formare una specie di tabella delle frequenze.
File di input:
3
4
5
8
L'output dovrebbe essere:
3
7
12
20
Come procedo a fare questo?
Risposte:
Con awk:
awk '{total += $0; $0 = total}1'
$0è la linea corrente. Quindi, per ogni riga, la aggiungo a total, imposto la riga sulla nuova totale quindi il trailing 1è un collegamento awk: stampa la riga corrente per ogni condizione vera e 1quando una condizione viene valutata vera.
printessere usata anche?
print total}invece di$0 = total}1
{print(total += $0)}
In uno 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.pyEseguilo con il file di origine e il file di output di destinazione come argomenti:
python3 /path/to/add_last.py <input_file> <output_file>
Il codice è piuttosto leggibile, ma in dettaglio:
Apri il file di output per scrivere i risultati
with open(out, "wt") as wr:
Apri il file di input per la lettura per riga
with open(f) as read:
for l in read:
Leggi le righe, aggiungendo il valore della nuova riga al totale:
n = n + int(l)
Scrivi il risultato nel file di output:
wr.write(str(n)+"\n")
Solo per divertimento
$ sed 'a+p' file | dc -e0 -
3
7
12
20
Questo funziona tramite una ppending +psu ciascuna riga dell'input e quindi passando il risultato al dccalcolatore dove
+ 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.
poi
p Prints the value on the top of the stack, without altering the
stack. A newline is printed after the value.
L' -e0argomento viene inserito 0nello dcstack per inizializzare la somma.
real 0m4.234s
In Bash:
#! /bin/bash
file="YOUR_FILE.txt"
TOTAL=0
while IFS= read -r line
do
TOTAL=$(( TOTAL + line ))
echo $TOTAL
done <"$file"
real 0m53.116squasi un minuto, su 1,3 milioni di linee :)
Per stampare somme parziali di numeri interi fornite sull'input standard una per riga:
#!/usr/bin/env python3
import sys
partial_sum = 0
for n in map(int, sys.stdin):
partial_sum += n
print(partial_sum)
Se per qualche motivo il comando è troppo lento; potresti usare il programma 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;
}
Per crearlo ed eseguirlo, digitare:
$ cc cumsum.c -o cumsum
$ ./cumsum < input > output
UINTMAX_MAXlo è 18446744073709551615.
Il codice C è più volte più veloce del comando awk sulla mia macchina per il file di input generato da:
#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
accumulate()Itertool
Probabilmente vuoi qualcosa del genere:
sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Spiegazione del comando:
sort -n <filename> | uniq -c ordina l'ingresso e restituisce una tabella di frequenza| awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}' trasforma l'output in un formato miglioreEsempio:
file di input list.txt:
4
5
3
4
4
2
3
4
5
Il 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
Puoi farlo in vim. Apri il file e digita i seguenti tasti:
qaqqayiwj@"<C-a>@aq@a:wq<cr>
Si noti che in <C-a>realtà è ctrl-a ed <cr>è il ritorno a capo , ovvero il pulsante di invio.
Ecco come funziona. Prima di tutto, vogliamo cancellare il registro 'a' in modo che non abbia effetti collaterali al primo passaggio. Questo è semplicemente qaq. Quindi facciamo quanto segue:
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
Dopo aver eseguito questa macro ricorsiva, chiamiamo semplicemente :wq<cr>per salvare ed uscire.
Perl one-liner:
$ perl -lne 'print $sum+=$_' input.txt
3
7
12
20
Con 2,5 milioni di righe di numeri, sono necessari circa 6,6 secondi per elaborare:
$ 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, piuttosto bella.
Una semplice linea Bash:
x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE
xè la somma cumulata di tutti i numeri dalla riga corrente e superiore.
nè il numero nella riga corrente.
Eseguiamo il ciclo su tutte le righe ndi INPUT_FILEe aggiungiamo il loro valore numerico alla nostra variabile xe stampiamo quella somma durante ogni iterazione.
Bash è un po 'lento qui, tuttavia, ci si può aspettare che funzioni circa 20-30 secondi per un file con 2 milioni di voci, senza stampare l'output sulla console (che è ancora più lento, indipendentemente dal metodo che si utilizza).
Simile alla risposta di @ steeldriver, ma con un po 'meno arcano bcinvece:
sed 's/.*/a+=&;a/' input | bc
La cosa bella di bc(e dc) è che sono calcolatori di precisione arbitrari, quindi non trabocceranno mai o subiranno la mancanza di precisione sugli interi.
L' sedespressione trasforma l'input in:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Questo viene quindi valutato da bc. La avariabile bc viene inizializzata automaticamente su 0. Ogni riga viene incrementata a, quindi la stampa esplicitamente.
real 0m5.642ssu 1,3 milioni di linee. sed è molto lento su questo.