Làm cách nào để tạo tổng số tích lũy đang chạy trong tệp văn bản?


9

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?


1
Trong văn bản của bạn, bạn nói rằng bạn muốn có một bảng tần số . Mẫu đầu ra của bạn là một danh sách. Bạn có thể vui lòng làm rõ điều này?
Wayne_Yux

Thật vậy, đầu ra của bạn không phải là bảng tần số
don.joey

Tôi xin lỗi. Tôi có nghĩa là một bảng tần số tích lũy. Đã sửa đổi câu hỏi. Cảm ơn.

Nó không hay lắm nhưng tôi thường chỉ làm những thứ như thế này vào bảng tính.
John U

@ John tôi thường làm, nhưng tập tin tôi có 1 triệu số.

Câu trả lời:


20

Với awk:

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

$0là 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 totalvà sau đó theo dõi 1là một phím tắt awk - nó in dòng hiện tại cho mọi điều kiện đúng và 1như một điều kiện đánh giá là đúng.


Xin vui lòng giải thích mã của bạn?
George Udosen

Từ này printcũng có thể được sử dụng?
George Udosen

Vâng, print total}thay vì$0 = total}1
muru

1
@George ah, không.
muru

9
Một cách viết kịch bản awk ngắn hơn và có lẽ dễ hiểu hơn sẽ là{print(total += $0)}
Miles

9

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")

Để sử dụng

  • Sao chép tập lệnh vào một tập tin trống, lưu nó dưới dạng 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>
    

Giải trình

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")
    


3
Đây không phải là về hiệu suất ngắn hoặc thời gian (hàng triệu dòng không phải là dữ liệu lớn). Mã trong câu trả lời của bạn không phải là thành ngữ Python. Câu trả lời của tôi chỉ là phiên bản pythonic của bạn.
jfs

8
@JFSebastian nếu phiên bản thành ngữ chậm hơn tại sao mọi người sẽ thích nó? Không có gì đặc biệt về việc "pythonic" đó chỉ là một quy ước giúp các nhà phát triển trăn chia sẻ mã và tiêu chuẩn để dễ đọc. Nếu phiên bản thành ngữ ít hiệu quả hơn (chậm hơn) thì không nên sử dụng trừ khi bạn làm việc trong môi trường mà tiêu chuẩn hóa quan trọng hơn hiệu suất (nghe có vẻ là một ý tưởng khủng khiếp đối với tôi).
terdon

2
@terdon có điều gì đó để nói về tối ưu hóa sớm. Khả năng đọc có thể quan trọng vì khả năng duy trì lâu dài.
muru

4
@muru chắc chắn, nhưng điều này là hoàn toàn có thể đọc được. Đó chỉ là tội ác không phải là "pythonic". Chưa kể rằng chúng ta đang nói về 7 dòng mã, không phải là một dự án khổng lồ. Hy sinh hiệu quả trong tên của các quy ước phong cách có vẻ như cách tiếp cận sai.
terdon

9

Chỉ để cho vui

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

Này hoạt động bằng một ppending +pvới từng ngành nghề của các đầu vào, và sau đó đi qua kết quả đến dcmá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 -e0số đẩy 0lên dcngăn xếp để khởi tạo tổng.


Một cái gì đó như thế này thực sự có thể là nhanh nhất trong một tập dữ liệu lớn
Chấn thương kỹ thuật số

@DigitalTrauma trên 1,3 triệu dòng, thực sự gần như chậm nhất:real 0m4.234s
Jacob Vlijm

niềm vui là tất cả những gì nó cần cho một upvote: D kỳ quặc là quá đủ: D: D
Rinzwind

Hãy giải thích một chút.
AmanicA

8

Trong Bash:

#! /bin/bash

file="YOUR_FILE.txt"

TOTAL=0
while IFS= read -r line
do
    TOTAL=$(( TOTAL + line ))
    echo $TOTAL
done <"$file"

Bash cực kỳ chậm về điều này : real 0m53.116s, gần một phút, trên 1,3 triệu dòng :)
Jacob Vlijm

@JacobVlijm dash nhanh gấp khoảng hai lần, busybox ash và zsh (ở chế độ sh) 1,5 lần, nhưng tất nhiên, ngay cả dash cũng chậm hơn 5 lần so với python.
muru

6

Để 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)

Ví dụ runnable .

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

Ví dụ runnable .

UINTMAX_MAX18446744073709551615.

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')

2
Cũng có thể đáng nói đến accumulate()itertool
David Z

5

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ơn

Ví 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

Tôi thích điều này, việc đưa ra là tốt đẹp :) ...
George Udosen

5

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>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.


1
+1 để phá vỡ câu thần chú và giải thích tất cả các phần. Quá xa hiếm vòng những phần này.
John U

5

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.
Jacob Vlijm

@JacobVlijm đó là một tập tin nhỏ. Tôi đã thêm một thử nghiệm nhỏ với tệp 2,5 triệu dòng. 6,64 giây
Sergiy Kolodyazhnyy

1
Tôi đã chạy 1,3 triệu dòng trên một hệ thống cổ xưa
Jacob Vlijm

3

Một Bash đơn giản:

x=0 ; while read n ; do x=$((x+n)) ; echo $x ; done < INPUT_FILE

xlà tổng tích lũy của tất cả các số từ dòng hiện tại trở lên.
nlà số trong dòng hiện tại.

Chúng ta lặp qua tất cả các dòng ncủa INPUT_FILEvà thêm giá trị số của họ để biến chúng ta xvà 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).


3

Tương tự như câu trả lời của @ Steeldo, nhưng với ít phức tạp hơn bcthay 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 sedbiể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 abc được tự động khởi tạo thành 0. Mỗi dòng tăng a, sau đó in rõ ràng.


real 0m5.642strên 1,3 triệu dòng. sed là rất chậm về điều này.
Jacob Vlijm
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.