У мене є текстовий файл з 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 до кожного рядка введення, а потім передати результат в калькулятор де+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
На 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 дійсно повільний на цьому.