파일의 모든 숫자를 빠르게 합산하려면 어떻게해야합니까?


195

수천 줄의 숫자가 들어있는 파일이 있습니다. 각 줄에는 한 줄이 있습니다.

34
42
11
6
2
99
...

파일의 모든 숫자의 합계를 인쇄하는 스크립트를 작성하려고합니다. 해결책이 있지만 그다지 효율적이지 않습니다. (실행하는 데 몇 분이 걸립니다.) 더 효율적인 솔루션을 찾고 있습니다. 어떤 제안?


5
느린 해결책은 무엇입니까? 어쩌면 느려졌는지 알아낼 수 있습니다. :)
brian d foy

4
@ brian d foy, 게시하기가 너무 창피합니다. 왜 느린 지 알고 있습니다. "cat filename | head -n 1"을 호출하여 최상위 번호를 가져와 누적 합계에 추가하고 "cat filename | tail ..."을 호출하여 다음 반복의 최상위 행을 제거하기 때문입니다 ... I 프로그래밍에 대해 배울 점이 많습니다 !!!
Mark Roberts

6
그것은 ... 매우 체계적입니다. 매우 명확하고 직설적이며 끔찍한 혐오라는 것을 좋아합니다. 처음 시작할 때 알고 있던 도구를 사용하여 만든 것 같습니다.
dmckee --- 전 운영자 고양이


@MarkRoberts 그것을 해결하려면 시간이 오래 걸렸을 것입니다. 그것은 매우 식량 문제 해결 기술이며, 너무 잘못되었습니다. 그것은 과잉 사고의 전형적인 경우처럼 보입니다. Glen Jackman의 여러 솔루션 쉘 스크립팅 솔루션 (및 2 개는 awkand과 같은 것을 사용하지 않는 순수 쉘 bc)입니다. 이것들은 모두 10 초 이내에 백만 개의 숫자를 추가하는 것을 완료했습니다. 그것들을 살펴보고 순수한 쉘에서 어떻게 수행 할 수 있는지보십시오.
David W.

답변:


113

펄 한 줄의 경우,이 같은 일이 기본적으로의 awk용액 아이 만 Hourieh의 대답은 :

 % perl -nle '$sum += $_ } END { print $sum'

Perl one-liners의 기능이 궁금한 경우 다음을 수행하십시오.

 %  perl -MO=Deparse -nle '$sum += $_ } END { print $sum'

그 결과 아무도 더 이상 스스로 작성하지 않는 형태의 프로그램이 더 장황합니다.

BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    $sum += $_;
}
sub END {
    print $sum;
}
-e syntax OK

낄낄 거림을 위해 1,000,000 숫자 (0-9,999 범위)의 파일 로이 작업을 시도했습니다. 내 Mac Pro에서는 거의 즉시 돌아옵니다. 내가 사용하는 mmap것이 정말 빠르기를 바 랐기 때문에 너무 나쁩니다. 그러나 그것은 같은 시간입니다.

use 5.010;
use File::Map qw(map_file);

map_file my $map, $ARGV[0];

$sum += $1 while $map =~ m/(\d+)/g;

say $sum;

4
와우, 그것은 당신이주는 문자열 주위에 어떤 코드가 실제로 랩되는지에 대한 깊은 이해 를 보여줍니다 . 나의 초기 생각은 당신이 술에 취한 상태에서 게시해서는 안된다는 것이었지만, 나는 당신이 누구인지 알고 다른 Perl의 답변 중 일부를 기억했습니다 :-)
paxdiablo

-n 및 -p는 인수 주위에 문자를 -e에두기 만하면 원하는대로 사용할 수 있습니다. 우리는 Effective Perl Programming (선반에 도달하려고 합니다)에서 흥미로운 일을하는 많은 라이너를 가지고 있습니다.
brian d foy

5
이 중대형 중괄호는 무엇입니까?
Frank

17
-n while { }은 프로그램 주위 에 루프를 추가합니다 . } ... {안에 넣으면 while { } ... { }. 악? 약간.
jrockway

5
-MO=Deparse옵션 을 강조하면 큰 보너스 ! 별도의 주제에 있지만.
conny

375

awk를 사용할 수 있습니다 :

awk '{ sum += $1 } END { print sum }' file

4
프로그램 초과 : 최대 필드 크기 수 : 32767
leef

1
으로 -F '\t'선택하여 필드는 공백을 포함 할 경우 탭으로 구분됩니다.
Ethan Furman

5
이것을 최선의 답변으로 표시하십시오. TSV (탭으로 구분 된 값) 파일 내에서 각 행의 첫 번째 값을 합산하려는 경우에도 작동합니다.
Andrea

99

지금까지 어떤 솔루션도 사용하지 않았습니다 paste. 여기 하나가 있습니다 :

paste -sd+ filename | bc

예를 들어, 1 <= n <= 100000 인 Σn을 계산하십시오.

$ seq 100000 | paste -sd+ | bc -l
5000050000

(호기심을 나타 내기 위해 주어진 양의 숫자를 양수로 seq n인쇄합니다 .)1nn


1
아주 좋아요! 기억하기 쉬움
Brendan Maguire

1
seq 100000 | paste -sd+ - | bc -lMac OS X Bash 셸에서. 그리고 이것은 지금까지 가장 달콤하고 가장 해로운 해결책입니다!
Simo A.

1
@SimoA. 가장 섹시한 솔루션은 항상 가장 단순하기 때문에 unixest 대신 unixiest라는 용어를 사용한다고 투표합니다.)
Connor

86

재미로 벤치마킹하자 :

$ for ((i=0; i<1000000; i++)) ; do echo $RANDOM; done > random_numbers

$ time perl -nle '$sum += $_ } END { print $sum' random_numbers
16379866392

real    0m0.226s
user    0m0.219s
sys     0m0.002s

$ time awk '{ sum += $1 } END { print sum }' random_numbers
16379866392

real    0m0.311s
user    0m0.304s
sys     0m0.005s

$ time { { tr "\n" + < random_numbers ; echo 0; } | bc; }
16379866392

real    0m0.445s
user    0m0.438s
sys     0m0.024s

$ time { s=0;while read l; do s=$((s+$l));done<random_numbers;echo $s; }
16379866392

real    0m9.309s
user    0m8.404s
sys     0m0.887s

$ time { s=0;while read l; do ((s+=l));done<random_numbers;echo $s; }
16379866392

real    0m7.191s
user    0m6.402s
sys     0m0.776s

$ time { sed ':a;N;s/\n/+/;ta' random_numbers|bc; }
^C

real    4m53.413s
user    4m52.584s
sys 0m0.052s

5 분 후 sed run을 중단했습니다


나는 다이빙을하고있다 , 그리고 그것은 빠릅니다 :

$ time lua -e 'sum=0; for line in io.lines() do sum=sum+line end; print(sum)' < random_numbers
16388542582.0

real    0m0.362s
user    0m0.313s
sys     0m0.063s

그리고 이것을 업데이트하는 동안 루비 :

$ time ruby -e 'sum = 0; File.foreach(ARGV.shift) {|line| sum+=line.to_i}; puts sum' random_numbers
16388542582

real    0m0.378s
user    0m0.297s
sys     0m0.078s

Ed Morton의 조언에 유의하십시오. $1

$ time awk '{ sum += $1 } END { print sum }' random_numbers
16388542582

real    0m0.421s
user    0m0.359s
sys     0m0.063s

대 사용 $0

$ time awk '{ sum += $0 } END { print sum }' random_numbers
16388542582

real    0m0.302s
user    0m0.234s
sys     0m0.063s

18
+1 : 다양한 솔루션을 개발하고 벤치마킹했습니다.
David W.

시간 고양이 random_numbers | 붙여 넣기 -sd + | bc -l 실제 0m0.317s 사용자 0m0.310s sys 0m0.013s
rafi wiener

그것은 tr솔루션 과 거의 동일해야합니다 .
glenn jackman

4
awk는 필드가 스크립트에 구체적으로 언급되어 있지만 달리 그렇지 않은 경우 awk가 필드 분할 (분명히 시간이 걸리므로) $0대신 사용하면 awk 스크립트가 조금 더 빠르게 실행되어야합니다 $1.
Ed Morton

20

다른 옵션은 다음을 사용하는 것입니다 jq.

$ seq 10|jq -s add
55

-s( --slurp)는 입력 행을 배열로 읽습니다.


1
빠른 작업을위한 멋진 도구입니다. 거의 잊어 버렸습니다. 감사
John


7

여기에 하나의 라이너가 있습니다.

( echo 0 ; sed 's/$/ +/' foo ; echo p ) | dc

이것은 숫자가 정수라고 가정합니다. 소수가 필요한 경우 시도하십시오

( echo 0 2k ; sed 's/$/ +/' foo ; echo p ) | dc

2를 필요한 소수 자릿수로 조정하십시오.


6

나는 GNU 데이터 매쉬를 그러한 작업에 사용하는 것을 선호합니다. perl 또는 awk보다 간결하고 읽기 쉽습니다. 예를 들어

datamash sum 1 < myfile

여기서 1은 데이터의 첫 번째 열을 나타냅니다.


1
우분투 설치에서 보이지 않으므로 표준 구성 요소가 아닌 것 같습니다. 그래도 벤치마킹하고 싶습니다.
스티븐은 쉽게 즐겁게


5

나는 이것을 위해 R을 사용하는 것을 선호한다 :

$ R -e 'sum(scan("filename"))'

나는 다른 응용 프로그램의 R의 팬이지만 이런 식으로 성능에는 좋지 않습니다. 파일 I / O는 중요한 문제입니다. vroom 패키지를 사용하여 속도를 높일 수있는 스크립트에 args를 전달하는 것을 테스트했습니다. 동일한 서버에서 다른 스크립트를 벤치마킹했을 때 자세한 내용을 게시하겠습니다.
톰 켈리

4
cat nums | perl -ne '$sum += $_ } { print $sum'

( '종료'없는 브라이언 디 포이의 대답과 동일)


나는 이것을 좋아하지만 중괄호를 설명 할 수 있습니까? {없이}를 보는 것이 이상하고 그 반대도 마찬가지입니다.
드럼 파이어

1
@drumfire perl -MO=Deparse펄이 프로그램을 파싱하는 방법을 보려면 위의 @brian d foy의 답변 을 참조하십시오. 또는 perlrun에 대한 문서 : perldoc.perl.org/perlrun.html(-n 검색). -l을 사용하면 perl은 코드를 {}로 감싸서 완전한 프로그램이됩니다.
edibleEnergy

4

간결한 내용 :

# Ruby
ruby -e 'puts open("random_numbers").map(&:to_i).reduce(:+)'

# Python
python -c 'print(sum(int(l) for l in open("random_numbers")))'

float로 변환하는 것이 시스템에서 약 두 배 빠릅니다 (320 vs 640 ms). time python -c "print(sum([float(s) for s in open('random_numbers','r')]))"
user12719


3

재미를 위해서 , Perl의 배열 수학 엔진 인 PDL 을 사용해 보자!

perl -MPDL -E 'say rcols(shift)->sum' datafile

rcols열을 행렬 (이 경우 1D)로 읽고 sum(깜짝) 행렬의 모든 요소를 ​​합합니다.


@INC에서 PDL.pm을 찾을 수없는 방법 (PDL 모듈을 설치해야 할 수 있음) (@ INC에는 다음이 포함되어 있음 : / etc / perl /usr/local/lib/x86_64-linux-gnu/perl/5.22.1? )) 재미를 위해 =)
Fortran

1
PDL을 먼저 설치해야합니다. PDL은 기본 모듈이 아닙니다.
Joel Berger 17 년

3

다음은 발전기 표현식과 함께 파이썬을 사용하는 솔루션입니다. 오래된 오래된 노트북에서 백만 개의 숫자로 테스트했습니다.

time python -c "import sys; print sum((float(l) for l in sys.stdin))" < file

real    0m0.619s
user    0m0.512s
sys     0m0.028s

3
명명 된 함수를 사용한 간단한 목록 이해는 다음을위한 유용한 사용 사례입니다 map().map(float, sys.stdin)
sevko

3

난 그냥 지나갈 수 없어 ... 여기 내 하스켈 원 라이너가 있습니다. 실제로 꽤 읽을 수 있습니다.

sum <$> (read <$>) <$> lines <$> getContents

불행히도 ghci -e그것을 실행할 필요가 없으므로 주요 기능, 인쇄 및 컴파일이 필요합니다.

main = (sum <$> (read <$>) <$> lines <$> getContents) >>= print

명확히하기 위해, 우리는 전체 입력 (읽기 getContents하여 분할) lines, read숫자와 같은 sum. <$>이다 fmap운영자 - 확인이 모든 IO에서 발생하기 때문에 우리는 대신에 일반적인 기능이 응용 프로그램의 사용. 또한 목록에 있기 때문에 read추가가 필요 fmap합니다.

$ ghc sum.hs
[1 of 1] Compiling Main             ( sum.hs, sum.o )
Linking sum ...
$ ./sum 
1
2
4
^D
7

수레와 함께 작동하도록 이상한 업그레이드가 있습니다.

main = ((0.0 + ) <$> sum <$> (read <$>) <$> lines <$> getContents) >>= print
$ ./sum 
1.3
2.1
4.2
^D
7.6000000000000005


2

R 스크립트 실행

파일 이름의 인수를 취하고 행을 합치기 위해 R 스크립트를 작성했습니다.

#! /usr/local/bin/R
file=commandArgs(trailingOnly=TRUE)[1]
sum(as.numeric(readLines(file)))

다음과 같이 "data.table"또는 "vroom"패키지를 사용하여 속도를 높일 수 있습니다.

#! /usr/local/bin/R
file=commandArgs(trailingOnly=TRUE)[1]
sum(data.table::fread(file))
#! /usr/local/bin/R
file=commandArgs(trailingOnly=TRUE)[1]
sum(vroom::vroom(file))

벤치마킹

@glenn jackman 과 동일한 벤치마킹 데이터 .

for ((i=0; i<1000000; i++)) ; do echo $RANDOM; done > random_numbers

위의 R 호출과 비교하여 스크립트로 R 3.5.0을 실행하는 것은 동일한 Linux Debian 서버에서 다른 방법과 비교할 수 있습니다.

$ time R -e 'sum(scan("random_numbers"))'  
 0.37s user
 0.04s system
 86% cpu
 0.478 total

readLines가있는 R 스크립트

$ time Rscript sum.R random_numbers
  0.53s user
  0.04s system
  84% cpu
  0.679 total

data.table이있는 R 스크립트

$ time Rscript sum.R random_numbers     
 0.30s user
 0.05s system
 77% cpu
 0.453 total

빗자루와 R 스크립트

$ time Rscript sum.R random_numbers     
  0.54s user 
  0.11s system
  93% cpu
  0.696 total

다른 언어와 비교

동일한 하드웨어에서 제안 된 다른 방법으로 여기를 참조하십시오.

파이썬 2 (2.7.13)

$ time python2 -c "import sys; print sum((float(l) for l in sys.stdin))" < random_numbers 
 0.27s user 0.00s system 89% cpu 0.298 total

파이썬 3 (3.6.8)

$ time python3 -c "import sys; print(sum((float(l) for l in sys.stdin)))" < random_number
0.37s user 0.02s system 98% cpu 0.393 total

루비 (2.3.3)

$  time ruby -e 'sum = 0; File.foreach(ARGV.shift) {|line| sum+=line.to_i}; puts sum' random_numbers
 0.42s user
 0.03s system
 72% cpu
 0.625 total

펄 (5.24.1)

$ time perl -nle '$sum += $_ } END { print $sum' random_numbers
 0.24s user
 0.01s system
 99% cpu
 0.249 total

어크 (4.1.4)

$ time awk '{ sum += $0 } END { print sum }' random_numbers
 0.26s user
 0.01s system
 99% cpu
 0.265 total
$ time awk '{ sum += $1 } END { print sum }' random_numbers
 0.34s user
 0.01s system
 99% cpu
 0.354 total

C (클랑 버전 3.3; gcc (데비안 6.3.0-18) 6.3.0)

 $ gcc sum.c -o sum && time ./sum < random_numbers   
 0.10s user
 0.00s system
 96% cpu
 0.108 total

추가 언어로 업데이트

루아 (5.3.5)

$ time lua -e 'sum=0; for line in io.lines() do sum=sum+line end; print(sum)' < random_numbers 
 0.30s user 
 0.01s system
 98% cpu
 0.312 total

tr (8.26) 은 zsh와 호환되지 않는 bash로 시간을 설정해야합니다.

$time { { tr "\n" + < random_numbers ; echo 0; } | bc; }
real    0m0.494s
user    0m0.488s
sys 0m0.044s

sed (4.4) 는 zsh와 호환되지 않는 bash로 시간을 설정해야합니다.

$  time { head -n 10000 random_numbers | sed ':a;N;s/\n/+/;ta' |bc; }
real    0m0.631s
user    0m0.628s
sys     0m0.008s
$  time { head -n 100000 random_numbers | sed ':a;N;s/\n/+/;ta' |bc; }
real    1m2.593s
user    1m2.588s
sys     0m0.012s

참고 : 사용 가능한 메모리가 더 많은 시스템에서는 sed 통화가 더 빨리 작동하는 것 같습니다 (sed 벤치마킹에 사용되는 더 작은 데이터 집합에 유의하십시오)

줄리아 (0.5.0)

$ time julia -e 'print(sum(readdlm("random_numbers")))'
 3.00s user 
 1.39s system 
 136% cpu 
 3.204 total
$  time julia -e 'print(sum(readtable("random_numbers")))'
 0.63s user 
 0.96s system 
 248% cpu 
 0.638 total

R에서와 같이 파일 I / O 방법의 성능이 다릅니다.


2

C ++ "한 줄짜리":

#include <iostream>
#include <iterator>
#include <numeric>
using namespace std;

int main() {
    cout << accumulate(istream_iterator<int>(cin), istream_iterator<int>(), 0) << endl;
}

1

재미를위한 또 다른

sum=0;for i in $(cat file);do sum=$((sum+$i));done;echo $sum

또는 다른 배쉬 만

s=0;while read l; do s=$((s+$l));done<file;echo $s

그러나 awk 솔루션은 아마도 가장 컴팩트하기 때문에 가장 좋습니다.


1

C는 항상 속도에서 승리합니다.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    ssize_t read;
    char *line = NULL;
    size_t len = 0;
    double sum = 0.0;

    while (read = getline(&line, &len, stdin) != -1) {
        sum += atof(line);
    }

    printf("%f", sum);
    return 0;
}

1M 숫자 타이밍 (파이썬 답변과 동일한 기계 / 입력) :

$ gcc sum.c -o sum && time ./sum < numbers 
5003371677.000000
real    0m0.188s
user    0m0.180s
sys     0m0.000s

1
최고의 답변! 최고 속도)
Fortran

1

루비로 :

ruby -e "File.read('file.txt').split.inject(0){|mem, obj| mem += obj.to_f}"

다른 옵션 (입력이 STDIN에서 온 경우)은 ruby -e'p readlines.map(&:to_f).reduce(:+)'입니다.
nisetama

0

전체 파일을 읽어야한다는 점을 고려할 때 이보다 훨씬 나아질 수 있는지 모르겠습니다.

$sum = 0;
while(<>){
   $sum += $_;
}
print $sum;

1
매우 읽기 쉽다. 펄. 그러나 그래, 그것은 그렇게 될 것입니다 ...
dmckee --- 전 운영자 고양이

$_기본 변수입니다. 라인 입력 연산자, <>기본적으로 거기에 풋 그것의 결과는 당신이 사용하는 경우 <>while.
brian d foy

1
@Mark $_는 주제 변수입니다. 'it'처럼 작동합니다. 이 경우 <> 각 줄을 할당합니다. 코드 혼란을 줄이고 원 라이너 작성에 도움을주기 위해 여러 곳에서 사용됩니다. 스크립트는 "합을 0으로 설정하고 각 줄을 읽고 합에 추가 한 다음 합을 인쇄합니다"라고 말합니다.
daotoad

1
@Stefan은 경고 및 제한이 해제 된 상태에서 선언 및 초기화를 건너 뛸 수 있습니다 $sum. 이것은 매우 간단하기 때문에 명령문 수정자를 사용할 수도 있습니다 while.$sum += $_ while <>; print $sum;
daotoad

0

나는 이것을 테스트하지 않았지만 작동해야합니다.

cat f | tr "\n" "+" | sed 's/+$/\n/' | bc

bc가 EOF 및 EOL을 처리하지 않는 경우 bc 전에 문자열에 "\ n"을 추가해야 할 수도 있습니다 (예 : echo를 통해).


2
작동하지 않습니다. bc후행 "+"로 인해 구문 오류가 발생하고 끝에 줄 바꿈이 없습니다. 이것은 작동하며 쓸모없는 사용을 제거합니다 cat. { tr "\n" "+" | sed 's/+$/\n/'| bc; } < numbers2.txt 또는 <numbers2.txt tr "\n" "+" | sed 's/+$/\n/'| bc
추가 통지가있을 때까지 일시 중지되었습니다.

tr "\n" "+" <file | sed 's/+$/\n/' | bc
ghostdog74

0

다른 하나는 다음과 같습니다.

open(FIL, "a.txt");

my $sum = 0;
foreach( <FIL> ) {chomp; $sum += $_;}

close(FIL);

print "Sum = $sum\n";

0

Alacon 데이터베이스 용 명령 줄 유틸리티 인 Alacon을 사용하여이를 수행 할 수 있습니다 .

설치해야하는, 그래서 그것은, Node.js를 작동 Node.js를 다음 Alasql의 패키지를 :

TXT 파일에서 합계를 계산하려면 다음 명령을 사용할 수 있습니다.

> node alacon "SELECT VALUE SUM([0]) FROM TXT('mydata.txt')"

0

모든 새 줄을로 바꾸고 +추가 0하여 Ruby통역사 에게 보내는 것이 더 쉬운 것은 아닙니다 .

(sed -e "s/$/+/" file; echo 0)|irb

이없는 경우 (으)로 irb보낼 수 bc있지만 마지막 개 (개 중 개)를 제외한 모든 개행을 제거해야합니다 echo. tr에 박사 학위가없는 한, 이것을 사용 하는 것이 좋습니다 sed.

(sed -e "s/$/+/" file|tr -d "\n"; echo 0)|bc

0

이동 중 :

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    sum := int64(0)
    for scanner.Scan() {
        v, err := strconv.ParseInt(scanner.Text(), 10, 64)
        if err != nil {
            fmt.Fprintf(os.Stderr, "Not an integer: '%s'\n", scanner.Text())
            os.Exit(1)
        }
        sum += v
    }
    fmt.Println(sum)
}

"64"는 무엇입니까? "10"나는 기본이라고 생각합니까?
피터 K

예, 10이 기본입니다. 64는 비트 수입니다. 결과 int를 해당 비트로 표현할 수 없으면 오류가 반환됩니다. 보다
dwurf

0

배쉬 변형

raw=$(cat file)
echo $(( ${raw//$'\n'/+} ))

$ wc -l file
10000 file

$ time ./test
323390

real    0m3,096s
user    0m3,095s
sys     0m0,000s

0

awk를 사용하는 쉘에서 아래 스크립트를 사용하여 그렇게했습니다.

    #!/bin/bash


total=0;

for i in $( awk '{ print $1; }' <myfile> )
do
 total=$(echo $total+$i | bc )
 ((count++))
done
echo "scale=2; $total " | bc
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.