Mam plik tekstowy z 2 milionami linii. Każda linia ma dodatnią liczbę całkowitą. Próbuję utworzyć coś w rodzaju tabeli częstotliwości.
Plik wejściowy:
3
4
5
8
Dane wyjściowe powinny wynosić:
3
7
12
20
Jak mam to zrobić?
Mam plik tekstowy z 2 milionami linii. Każda linia ma dodatnią liczbę całkowitą. Próbuję utworzyć coś w rodzaju tabeli częstotliwości.
Plik wejściowy:
3
4
5
8
Dane wyjściowe powinny wynosić:
3
7
12
20
Jak mam to zrobić?
Odpowiedzi:
Z awk
:
awk '{total += $0; $0 = total}1'
$0
jest bieżącą linią. Tak więc dla każdej linii dodaję ją do total
, ustawiam linię na nową total
, a następnie trailing 1
jest skrótem awk - drukuje bieżącą linię dla każdego prawdziwego warunku i 1
jako warunek jest oceniany na prawdziwy.
print
można również użyć tego słowa ?
print total}
zamiast$0 = total}1
{print(total += $0)}
W skrypcie 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
Uruchom go jako plik źródłowy i docelowy plik wyjściowy jako argumenty:
python3 /path/to/add_last.py <input_file> <output_file>
Kod jest raczej czytelny, ale szczegółowo:
Otwórz plik wyjściowy do zapisywania wyników
with open(out, "wt") as wr:
Otwórz plik wejściowy do odczytu według wiersza
with open(f) as read:
for l in read:
Przeczytaj wiersze, dodając wartość nowego wiersza do sumy:
n = n + int(l)
Zapisz wynik w pliku wyjściowym:
wr.write(str(n)+"\n")
Dla żartu
$ sed 'a+p' file | dc -e0 -
3
7
12
20
Działa to przez ppending do każdej linii wejścia, a następnie przekazując wynik do kalkulatora gdzie+p
dc
+ 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.
następnie
p Prints the value on the top of the stack, without altering the
stack. A newline is printed after the value.
W -e0
popycha argumentów 0
na dc
stosie, aby zainicjować sumę.
real 0m4.234s
W 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
prawie minutę, na 1,3 miliona linii :)
Aby wydrukować częściowe sumy liczb całkowitych podane na standardowym wejściu, po jednym w wierszu:
#!/usr/bin/env python3
import sys
partial_sum = 0
for n in map(int, sys.stdin):
partial_sum += n
print(partial_sum)
Przykład możliwy do uruchomienia .
Jeśli z jakiegoś powodu polecenie jest zbyt wolne; możesz użyć programu 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;
}
Aby go zbudować i uruchomić, wpisz:
$ cc cumsum.c -o cumsum
$ ./cumsum < input > output
Przykład możliwy do uruchomienia .
UINTMAX_MAX
jest 18446744073709551615
.
Kod C jest kilka razy szybszy niż polecenie awk na moim komputerze dla pliku wejściowego wygenerowanego przez:
#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
accumulate()
itert
Prawdopodobnie chcesz czegoś takiego:
sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Objaśnienie polecenia:
sort -n <filename> | uniq -c
sortuje dane wejściowe i zwraca tabelę częstotliwości| awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
zamienia wyjście w ładniejszy formatPrzykład:
plik wejściowy list.txt
:
4
5
3
4
4
2
3
4
5
Komenda:
$ 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
Możesz to zrobić w vimie. Otwórz plik i wpisz następujące naciśnięcia klawiszy:
qaqqayiwj@"<C-a>@aq@a:wq<cr>
Zauważ, że <C-a>
tak naprawdę to ctrl-a i <cr>
to powrót karetki , tzn. Przycisk Enter.
Oto jak to działa. Po pierwsze, chcemy wyczyścić rejestr „a”, aby po raz pierwszy nie miał żadnych skutków ubocznych. To jest po prostu qaq
. Następnie wykonujemy następujące czynności:
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
Po zakończeniu działania tego rekurencyjnego makra, po prostu wywołujemy, :wq<cr>
aby zapisać i wyjść.
Perl One-Liner:
$ perl -lne 'print $sum+=$_' input.txt
3
7
12
20
Przy 2,5 milionach linii liczb przetworzenie zajmuje około 6,6 sekundy:
$ 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
, całkiem miły.
Prosta wyściółka Bash:
x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE
x
to skumulowana suma wszystkich liczb z bieżącej linii i powyżej.
n
to liczba w bieżącym wierszu.
Mamy pętla nad wszystkimi liniami n
od INPUT_FILE
i dodać ich wartość numeryczną do naszej zmiennej x
i wydrukować tę sumę podczas każdej iteracji.
Bash jest tu jednak trochę powolny, możesz oczekiwać, że uruchomi się to około 20-30 sekund dla pliku z 2 milionami wpisów, bez drukowania danych wyjściowych na konsoli (co jest jeszcze wolniejsze, niezależnie od używanej metody).
Podobne do odpowiedzi @ steeldriver, ale z nieco mniej tajemnym bc
:
sed 's/.*/a+=&;a/' input | bc
Zaletą bc
(i dc
) jest to, że są one dowolnymi kalkulatorami precyzji, więc nigdy nie przepełnią się ani nie odczują braku precyzji w stosunku do liczb całkowitych.
sed
Wyrażenie przekształca dane wejściowe do:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Jest to następnie oceniane przez bc
. a
Zmienna bc jest automatycznie inicjowany na 0. Każdy przyrosty liniowe a
, a następnie wyraźnie drukuje go.
real 0m5.642s
na 1,3 mln linii. sed jest bardzo powolny.