У меня есть текстовый файл с 2 миллионами строк. Каждая строка имеет положительное целое число. Я пытаюсь сформировать таблицу частот.
Входной файл:
3
4
5
8
Вывод должен быть:
3
7
12
20
Как мне это сделать?
У меня есть текстовый файл с 2 миллионами строк. Каждая строка имеет положительное целое число. Я пытаюсь сформировать таблицу частот.
Входной файл:
3
4
5
8
Вывод должен быть:
3
7
12
20
Как мне это сделать?
Ответы:
С awk
:
awk '{total += $0; $0 = total}1'
$0
текущая строка Таким образом, для каждой строки я добавляю ее в total
, устанавливаю новую строку total
, а затем трейлинг 1
- это ярлык awk - он печатает текущую строку для каждого истинного условия и 1
в качестве условия оценивается как истинное.
print
?
print total}
вместо$0 = total}1
{print(total += $0)}
В скрипте 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
Запустите его с исходным файлом и целевым выходным файлом в качестве аргументов:
python3 /path/to/add_last.py <input_file> <output_file>
Код довольно читабелен, но подробно:
Открыть выходной файл для записи результатов
with open(out, "wt") as wr:
Открыть входной файл для чтения в строке
with open(f) as read:
for l in read:
Прочитайте строки, добавив значение новой строки к итогу:
n = n + int(l)
Запишите результат в выходной файл:
wr.write(str(n)+"\n")
Просто для удовольствия
$ sed 'a+p' file | dc -e0 -
3
7
12
20
Это работает ppending к каждой строке ввода, а затем передать результат в калькулятор где+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.
тогда
p Prints the value on the top of the stack, without altering the
stack. A newline is printed after the value.
В -e0
аргументе толчков 0
на dc
стек для инициализации суммы.
real 0m4.234s
В Баш:
#! /bin/bash
file="YOUR_FILE.txt"
TOTAL=0
while IFS= read -r line
do
TOTAL=$(( TOTAL + line ))
echo $TOTAL
done <"$file"
real 0m53.116s
почти минуту, на 1,3 миллиона строк :)
Чтобы напечатать частичные суммы целых чисел, заданные на стандартном вводе, по одному на строку:
#!/usr/bin/env python3
import sys
partial_sum = 0
for n in map(int, sys.stdin):
partial_sum += n
print(partial_sum)
Если по какой-то причине команда слишком медленная; Вы можете использовать программу на 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;
}
Чтобы собрать его и запустить, введите:
$ cc cumsum.c -o cumsum
$ ./cumsum < input > output
UINTMAX_MAX
есть 18446744073709551615
.
Код C в несколько раз быстрее, чем команда awk на моем компьютере для входного файла, сгенерированного:
#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
accumulate()
itertool
Вы, вероятно, хотите что-то вроде этого:
sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Объяснение команды:
sort -n <filename> | uniq -c
сортирует вход и возвращает таблицу частот| awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
превращает выход в более приятный форматПример:
входной файл list.txt
:
4
5
3
4
4
2
3
4
5
Команда:
$ 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
Вы можете сделать это в VIM. Откройте файл и введите следующие нажатия клавиш:
qaqqayiwj@"<C-a>@aq@a:wq<cr>
Обратите внимание , что <C-a>
на самом деле Ctrl-A, и <cr>
является возврат каретки , т.е. на кнопку ввода.
Вот как это работает. Прежде всего, мы хотим очистить регистр 'a', чтобы он не имел побочных эффектов в первый раз. Это просто qaq
. Затем мы делаем следующее:
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
После завершения работы этого рекурсивного макроса мы просто вызываем :wq<cr>
команду save и завершаем работу.
Perl однострочный:
$ perl -lne 'print $sum+=$_' input.txt
3
7
12
20
С 2,5 миллионами строк чисел обработка занимает около 6,6 секунд:
$ 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
, довольно мило.
Простой Bash с одним вкладышем:
x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE
x
сумма всех чисел из текущей строки и выше.
n
это номер в текущей строке.
Мы перебираем все линии n
от INPUT_FILE
и добавить их числовое значение нашей переменной x
и распечатать эту сумму во время каждой итерации.
Bash здесь немного медленный, но вы можете ожидать, что он будет работать около 20-30 секунд для файла с 2 миллионами записей без вывода вывода на консоль (что даже медленнее, независимо от используемого вами метода).
Похоже на ответ @ steeldriver, но bc
вместо этого немного менее загадочно :
sed 's/.*/a+=&;a/' input | bc
Хорошая вещь о bc
(и dc
) состоит в том, что они являются калькуляторами произвольной точности, поэтому никогда не будут переполнены или будут страдать от недостатка точности по сравнению с целыми числами.
sed
Выражение преобразует входной сигнал в:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Это тогда оценено bc
. a
Переменная Ъс автоматически инициализируются в 0. Каждая строка с шагом a
, затем явно печатает его.
real 0m5.642s
на 1,3 миллиона строк. Сед очень медленно на этом.