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

9

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

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

3
4
5
8

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

3
7
12
20

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

Монти Хардер
источник
1
В своем тексте вы говорите, что хотите таблицу частот . Ваш выходной образец является списком. Можете ли вы уточнить это?
Wayne_Yux
На самом деле ваш вывод не таблица частот
don.joey
Мне жаль. Я имел в виду совокупную таблицу частот. Изменили вопрос. Спасибо.
Это не очень круто, но я обычно делаю подобные вещи в электронную таблицу.
Джон U
@JohnU Я обычно делаю, но у меня есть 1 миллион номеров.

Ответы:

20

С awk:

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

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

Мур
источник
Пожалуйста, не могли бы вы объяснить свой код?
Джордж Удосен
Можно ли использовать это слово print?
Джордж Удосен
Да, print total}вместо$0 = total}1
Муру
1
@ Джордж ах, нет.
Муру
9
Более короткий и, возможно, более понятный способ написания сценария на awk{print(total += $0)}
Майлз
9

В скрипте 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")
    
Якоб Влейм
источник
3
Речь не идет о быстродействии или кратковременности (миллион строк - это не большие данные). Код в вашем ответе не является идиоматическим Python. Мой ответ просто более питонная версия вашего.
JFS
8
@JFSebastian, если более идиоматическая версия медленнее, почему бы кто-нибудь предпочел ее? Нет ничего особенного в том, чтобы быть «питоническим», это просто соглашение, которое помогает разработчикам Python делиться кодом и стандартами для удобочитаемости. Если более идиоматическая версия менее эффективна (медленнее), ее не следует использовать, если вы не работаете в среде, где стандартизация важнее производительности (что для меня звучит ужасно).
тердон
2
@terdon есть что сказать о преждевременной оптимизации. Читабельность может быть важна из-за долгосрочной ремонтопригодности.
Муру
4
@ Муру, конечно, но это прекрасно читается. Это только преступление не быть "питоническим". Не говоря уже о том, что мы говорим о 7 строках кода, а не о каком-то гигантском проекте. Приносить в жертву эффективность во имя соглашений о стиле кажется неправильным подходом.
тердон
9

Просто для удовольствия

$ sed 'a+p' file | dc -e0 -
3
7
12
20

Это работает ppending к каждой строке ввода, а затем передать результат в калькулятор где+pdc

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

steeldriver
источник
Нечто подобное может быть самым быстрым за большой набор данных
Digital Trauma
@DigitalTrauma на 1,3 миллиона строк, фактически почти самая медленная:real 0m4.234s
Джейкоб Влейм
веселье - это все, что нужно для подъема: D тоже достаточно причудливо: D: D
Rinzwind
Пожалуйста, объясните это немного.
AmanicA
8

В Баш:

#! /bin/bash

file="YOUR_FILE.txt"

TOTAL=0
while IFS= read -r line
do
    TOTAL=$(( TOTAL + line ))
    echo $TOTAL
done <"$file"
Julen Larrucea
источник
bash на этом очень медленный: real 0m53.116sпочти минуту, на 1,3 миллиона строк :)
Jacob Vlijm
@JacobVlijm тире примерно в два раза быстрее, ashbox busybox и zsh (в режиме sh) в 1,5 раза, но, конечно, даже тире в 5 раз медленнее, чем Python.
Муру
6

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

#!/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')
JFS
источник
2
Также стоит упомянуть accumulate()itertool
David Z
5

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

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
Wayne_Yux
источник
Мне нравится, что выход хороший
:)
5

Вы можете сделать это в 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 и завершаем работу.

Джеймс
источник
1
+1 за разрушение магического заклинания и объяснение всех частей. Слишком редкий вокруг этих частей.
Джон У
5

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, довольно мило.
Джейкоб Влейм
@JacobVlijm это довольно маленький файл. Я добавил небольшой тест с 2,5 миллионами строк файла. 6,64 секунды
Сергей Колодяжный
1
Я пробежал 1,3 миллиона строк по древней системе
Джейкоб Влейм
3

Простой 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 миллионами записей без вывода вывода на консоль (что даже медленнее, независимо от используемого вами метода).

Byte Commander
источник
3

Похоже на ответ @ steeldriver, но bcвместо этого немного менее загадочно :

sed 's/.*/a+=&;a/' input | bc

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

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

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

Это тогда оценено bc. aПеременная Ъс автоматически инициализируются в 0. Каждая строка с шагом a, затем явно печатает его.

Цифровая травма
источник
real 0m5.642sна 1,3 миллиона строк. Сед очень медленно на этом.
Джейкоб Влейм