Tôi có một tệp văn bản với 2 triệu dòng. Mỗi dòng có một số nguyên dương. Tôi đang cố gắng hình thành một loại bảng tần số.
Tập tin đầu vào:
3
4
5
8
Đầu ra phải là:
3
7
12
20
Làm thế nào để tôi đi về làm điều này?
Tôi có một tệp văn bản với 2 triệu dòng. Mỗi dòng có một số nguyên dương. Tôi đang cố gắng hình thành một loại bảng tần số.
Tập tin đầu vào:
3
4
5
8
Đầu ra phải là:
3
7
12
20
Làm thế nào để tôi đi về làm điều này?
Câu trả lời:
Với awk
:
awk '{total += $0; $0 = total}1'
$0
là dòng hiện tại. Vì vậy, đối với mỗi dòng, tôi thêm nó vào total
, đặt dòng thành mới total
và sau đó theo dõi 1
là một phím tắt awk - nó in dòng hiện tại cho mọi điều kiện đúng và 1
như một điều kiện đánh giá là đúng.
print
cũng có thể được sử dụng?
print total}
thay vì$0 = total}1
{print(total += $0)}
Trong một kịch bản 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
Chạy nó với tệp nguồn và tệp đầu ra được nhắm mục tiêu dưới dạng đối số:
python3 /path/to/add_last.py <input_file> <output_file>
Mã này khá dễ đọc, nhưng chi tiết:
Mở tệp đầu ra để viết kết quả
with open(out, "wt") as wr:
Mở tệp đầu vào để đọc trên mỗi dòng
with open(f) as read:
for l in read:
Đọc các dòng, thêm giá trị của dòng mới vào tổng số:
n = n + int(l)
Viết kết quả vào tệp đầu ra:
wr.write(str(n)+"\n")
Chỉ để cho vui
$ sed 'a+p' file | dc -e0 -
3
7
12
20
Này hoạt động bằng một ppending +p
với từng ngành nghề của các đầu vào, và sau đó đi qua kết quả đến dc
máy tính nơi
+ 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.
sau đó
p Prints the value on the top of the stack, without altering the
stack. A newline is printed after the value.
Đối -e0
số đẩy 0
lên dc
ngăn xếp để khởi tạo tổng.
real 0m4.234s
Trong 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
, gần một phút, trên 1,3 triệu dòng :)
Để in một phần số nguyên được đưa ra trên đầu vào tiêu chuẩn trên mỗi dòng:
#!/usr/bin/env python3
import sys
partial_sum = 0
for n in map(int, sys.stdin):
partial_sum += n
print(partial_sum)
Nếu vì lý do nào đó lệnh quá chậm; bạn có thể sử dụng chương trình 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;
}
Để xây dựng nó và chạy, gõ:
$ cc cumsum.c -o cumsum
$ ./cumsum < input > output
UINTMAX_MAX
là 18446744073709551615
.
Mã C nhanh hơn nhiều lần so với lệnh awk trên máy của tôi cho tệp đầu vào được tạo bởi:
#!/usr/bin/env python3
import numpy.random
print(*numpy.random.random_integers(100, size=2000000), sep='\n')
accumulate()
itertool
Bạn có thể muốn một cái gì đó như thế này:
sort -n <filename> | uniq -c | awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
Giải thích về lệnh:
sort -n <filename> | uniq -c
sắp xếp đầu vào và trả về bảng tần số| awk 'BEGIN{print "Number\tFrequency"}{print $2"\t"$1}'
biến ooutput thành một định dạng đẹp hơnVí dụ:
Tệp đầu vào list.txt
:
4
5
3
4
4
2
3
4
5
Lệnh:
$ 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
Bạn có thể làm điều này trong vim. Mở tệp và gõ tổ hợp phím sau:
qaqqayiwj@"<C-a>@aq@a:wq<cr>
Lưu ý rằng đó <C-a>
thực sự là ctrl-a và <cr>
là trả lại vận chuyển , tức là nút enter.
Đây là cách nó hoạt động. Trước hết, chúng tôi muốn xóa đăng ký 'a' để nó không có tác dụng phụ trong lần đầu tiên. Điều này chỉ đơn giản qaq
. Sau đó, chúng tôi làm như sau:
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
Sau khi macro đệ quy này chạy xong, chúng ta chỉ cần gọi :wq<cr>
để lưu và thoát.
Perl một lớp lót:
$ perl -lne 'print $sum+=$_' input.txt
3
7
12
20
Với 2,5 triệu dòng số, mất khoảng 6,6 giây để xử lý:
$ 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
, khá đẹp.
Một Bash đơn giản:
x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE
x
là tổng tích lũy của tất cả các số từ dòng hiện tại trở lên.
n
là số trong dòng hiện tại.
Chúng ta lặp qua tất cả các dòng n
của INPUT_FILE
và thêm giá trị số của họ để biến chúng ta x
và in tổng rằng trong mỗi lần lặp.
Bash hơi chậm ở đây, tuy nhiên, bạn có thể mong đợi nó chạy khoảng 20-30 giây cho một tệp có 2 triệu mục, mà không in đầu ra ra bàn điều khiển (thậm chí còn chậm hơn, độc lập với phương thức bạn sử dụng).
Tương tự như câu trả lời của @ Steeldo, nhưng với ít phức tạp hơn bc
thay vào đó:
sed 's/.*/a+=&;a/' input | bc
Điều hay ho về bc
(và dc
) là chúng là các máy tính chính xác tùy ý, do đó sẽ không bao giờ bị tràn hoặc bị thiếu độ chính xác so với các số nguyên.
Các sed
biểu hiện biến đổi đầu vào:
a+=3;a
a+=4;a
a+=5;a
a+=8;a
Điều này sau đó được đánh giá bởi bc
. Biến a
bc được tự động khởi tạo thành 0. Mỗi dòng tăng a
, sau đó in rõ ràng.
real 0m5.642s
trên 1,3 triệu dòng. sed là rất chậm về điều này.