У мене є текстовий файл з 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- це швидкий ярлик - він друкує поточний рядок для кожного справжнього стану і 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 до кожного рядка введення, а потім передати результат в калькулятор де+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стек для ініціалізації суми.
real 0m4.234s
На 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 мільйона ліній :)
Щоб надрукувати часткові суми цілих чисел, наведені на стандартному вхідному одному на рядок:
#!/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.
Код С у кілька разів швидший, ніж команда 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
Ви можете це зробити 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>зберегти та вийти.
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 мільйона рядків. sed дійсно повільний на цьому.