Как я могу сгенерировать текущую совокупную сумму чисел в текстовом файле?

У меня есть текстовый файл с 2 миллионами строк. Каждая строка имеет положительное целое число. Я пытаюсь сформировать частотную таблицу.

Входной файл:

3 4 5 8 

Выход должен быть:

 3 7 12 20 

Как мне это сделать?

С awk :

 awk '{total += $0; $0 = total}1' 

$0 – текущая строка. Таким образом, для каждой строки я добавляю ее к total , устанавливаю строку в новую total , а затем завершающий 1 является ярлыком awk – он печатает текущую строку для каждого истинного условия, а 1 как условие оценивает true.

В сценарии 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 для инициализации суммы.

В Баше:

 #! /bin/bash file="YOUR_FILE.txt" TOTAL=0 while IFS= read -r line do TOTAL=$(( TOTAL + line )) echo $TOTAL done <"$file" 

Чтобы напечатать частичные суммы целых чисел, заданные на стандартном входе по одному в строке:

 #!/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_MAX18446744073709551615 .

Код C в несколько раз быстрее, чем команда awk на моей машине для входного файла, сгенерированного:

 #!/usr/bin/env python3 import numpy.random print(*numpy.random.random_integers(100, size=2000000), sep='\n') 

Вы, вероятно, хотите что-то вроде этого:

 sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}' 

Объяснение команды:

  • sort -n <filename> | uniq -c sort -n <filename> | uniq -c сортирует вход и возвращает частотную таблицу
  • | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}' | 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@"<Ca>@aq@a:wq<cr> 

Обратите внимание, что <Ca> на самом деле ctrl-a, а <cr>возврат каретки , т.е. кнопка ввода.

Вот как это работает. Во-первых, мы хотим очистить регистр «а», чтобы он не имел побочных эффектов в первый раз. Это просто 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 <Ca> " 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> для сохранения и завершения работы.

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 

Простой однострочный 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 

Хорошая вещь о bcdc ) заключается в том, что они являются произвольными калькуляторами точности, поэтому никогда не переполнят или не будут испытывать недостаток точности над целыми числами.

Выражение sed преобразует входной сигнал в:

 a+=3;a a+=4;a a+=5;a a+=8;a 

Затем это оценивается с помощью bc . Переменная bc автоматически инициализируется до 0. Каждая строка увеличивает a , затем явно печатает ее.