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 total
e quindi il trailing 1
è un collegamento awk: stampa la riga corrente per ogni condizione vera e 1
quando una condizione viene valutata vera.
print
essere 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.py
Eseguilo 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 +p
su ciascuna riga dell'input e quindi passando il risultato al dc
calcolatore 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' -e0
argomento viene inserito 0
nello dc
stack 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.116s
quasi 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_MAX
lo è 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 n
di INPUT_FILE
e aggiungiamo il loro valore numerico alla nostra variabile x
e 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 bc
invece:
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' sed
espressione trasforma l'input in:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Questo viene quindi valutato da bc
. La a
variabile bc viene inizializzata automaticamente su 0. Ogni riga viene incrementata a
, quindi la stampa esplicitamente.
real 0m5.642s
su 1,3 milioni di linee. sed è molto lento su questo.