Як я можу генерувати загальний обсяг чисел у текстовому файлі?


9

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

Вхідний файл:

3
4
5
8

Вихід повинен бути:

3
7
12
20

Як мені це робити?


1
У своєму тексті ви говорите, що ви хочете таблицю частот . Ваш вихідний зразок - це список. Чи можете ви уточнити це?
Wayne_Yux

Дійсно, ваш вихід не є таблицею частот
don.joey

Мені шкода. Я мав на увазі таблицю накопичувальної частоти. Змінили питання. Дякую.

Це не дуже круто, але зазвичай такі речі я роблю в електронну таблицю.
Джон У

@JohnU Я зазвичай це роблю, але у мене файл має 1 мільйон номерів.

Відповіді:


20

З awk:

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

$0- поточний рядок. Отже, для кожного рядка я додаю його до total, встановлюю рядок на новий total, і тоді трейлінг 1- це швидкий ярлик - він друкує поточний рядок для кожного справжнього стану і 1як умова оцінюється як істинний.


Скажіть, будь ласка, свій код?
Джордж Удосен

Чи printможна вживати і слово ?
Джордж Удосен

Так, print total}замість$0 = total}1
muru

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, якщо більш ідіоматична версія повільніше, чому хтось віддасть перевагу? Немає нічого особливого в тому, щоб бути "пітонічним", це лише умова, яка допомагає розробникам пітонів ділитися кодом та стандартами для читабельності. Якщо більш ідіоматична версія є менш ефективною (повільнішою), її не слід використовувати, якщо ви не працюєте в середовищі, де стандартизація важливіша за продуктивність (що мені здається жахливою ідеєю).
тердон

2
@terdon є що сказати про передчасну оптимізацію. Читання може бути важливим через довготривалу ремонтопридатність.
муру

4
@ muru впевнений, але це чудово читабельно. Це лише злочин - це не бути "пітонічним". Не кажучи вже про те, що ми говоримо про 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стек для ініціалізації суми.


Щось подібне може бути насправді найшвидшим за великим набором даних
Digital Trauma

@DigitalTrauma на 1,3 мільйона ліній, насправді майже найповільніший:real 0m4.234s
Яків Влійм

весело - це все, що потрібно для надбавки: достатньо і диваків: D: D
Rinzwind

Будь ласка, поясніть це трохи.
AmanicA

8

На 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майже хвилину, на 1,3 мільйона ліній :)
Яків Влійм

@JacobVlijm дефіс приблизно вдвічі швидший, зайнятий ясень і 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)

Runnable приклад .

Якщо чомусь команда занадто повільна; ви можете використовувати програму 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

Runnable приклад .

UINTMAX_MAXє 18446744073709551615.

Код С у кілька разів швидший, ніж команда awk на моїй машині для вхідного файлу, генерованого:

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

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

Мені подобається, що це місце приємно:) ...
Джордж Удосен

5

Ви можете це зробити in vim. Відкрийте файл і введіть такі натискання клавіш:

qaqqayiwj@"<C-a>@aq@a:wq<cr>

Зауважте, що <C-a>насправді є 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
        <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>зберегти та вийти.


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 мільйонами записів, не друкуючи вихід на консоль (що навіть повільніше, незалежно від методу, який ви використовуєте).


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 мільйона рядків. sed дійсно повільний на цьому.
Яків Влійм
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.