ファイル内のすべての数値をすばやく合計するにはどうすればよいですか?


194

数千の数値を含むファイルがあり、それぞれが独自の行にあります。

34
42
11
6
2
99
...

ファイル内のすべての数値の合計を出力するスクリプトを記述しようとしています。解決策はありますが、あまり効率的ではありません。(実行には数分かかります。)より効率的なソリューションを探しています。助言がありますか?


5
あなたの遅い解決策は何でしたか?多分それについてあなたがそれが遅かったものを理解するのを手伝うことができます。:)
ブライアン・ド・フォイ2010

4
@brian d foy、恥ずかしくて投稿できません。なぜそれが遅いのか知っています。これは、「cat filename | head -n 1」を呼び出して上位の数値を取得し、それを現在の合計に追加し、「cat filename | tail ...」を呼び出して次の反復の先頭行を削除するためです... Iプログラミングについて学ぶことがたくさんあります!!!
マークロバーツ

6
それは...非常に体系的です。非常に明確でわかりやすく、恐ろしい忌まわしさであるという点で私はとても気に入っています。あなたが始めたときにあなたが知っていたツールから構築されたと思いますよね?
dmckee ---元モデレーターの子猫2010


@MarkRoberts解決するには長い時間がかかったに違いありません。それは非常に包丁の問題解決テクニックであり、まあまあです。それは、考え過ぎの古典的なケースのように見えます。Glen Jackmanのいくつかソリューションシェルスクリプトソリューション(および2つはawk、およびのようなものを使用しない純粋なシェルですbc)。これらはすべて、10秒未満で100万の数値を追加し終えました。それらを見て、それが純粋なシェルでどのように実行できるかを見てください。
David W.

回答:


113

Perlワンライナーの場合、これは基本的awkAyman Houriehの回答のソリューションと同じです。

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

Perlの1行の処理に興味がある場合は、それらを解析できます。

 %  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
うわー、それはあなたが与えた文字列を実際にラップするコード-nleについての深い理解を示しています。私の最初の考えは、酔っている間は投稿すべきではないということでしたが、私はあなたが誰であるかに気づき、他のPerlの回答のいくつかを思い出しました:-)
paxdiablo

-nと-pは、-eの引数の前後に文字を置くだけなので、これらの文字を好きなように使用できます。私たちは、効果的なPerlプログラミングでこれを使って興味深いことを行う1行のプログラムをたくさん持っています(これは、もうすぐ発売されます)。
ブライアン・ドフォイ2010

5
いいですね、これらの一致しない中括弧は何ですか?
フランク

17
-nは、while { }プログラムの周りにループを追加します。あなたが} ... {中に入れれば、あなたは持っていwhile { } ... { }ます。悪の?少し。
jrockway 2010

5
-MO=Deparseオプションを強調するための大きなボーナス!別のトピックでも。
conny

374

awkを使用できます。

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

3
プログラムを超えました:フィールドサイズの最大数:32767
Leef

1
-F '\t'フィールドにスペースが含まれ、タブで区切られている場合のオプション。
イーサンファーマン2014

5
これをベストアンサーとしてマークしてください。TSV(タブ区切り値)ファイル内の各行の最初の値を合計する場合にも機能します。
Andrea

99

これまでのところ、どのソリューションも使用していませんpaste。ここに一つあります:

paste -sd+ filename | bc

例として、Σnを計算します。ここで、1 <= n <= 100000です。

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

(好奇心旺盛な方は、seq nから1までの一連の数値を印刷してn、正の数値を指定しnます。)


1
非常に素晴らしい!そして覚えやすい
ブレンダンマグワイア2014

1
seq 100000 | paste -sd+ - | bc -lMac OS X Bashシェル。そして、これは最も甘くてunixestなソリューションです!
Simo A.

1
@SimoA。私は、最もセクシーなソリューションが常に最も優れているので、unixestの代わりにunixiestという用語を使用することを投票します;)
コナー

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の実行を中止しました


私はダイビングをしてきました 、それはスピーディーです:

$ 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

エド・モートンのアドバイスに注意: $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.

時間cat random_numbers | paste -sd + | bc -l real 0m0.317s user 0m0.310s sys 0m0.013s
rafi wiener

それはtrソリューションとほぼ同じになるはずです。
グレン・ジャックマン2017

4
awkは、フィールドがスクリプトで具体的に言及されていて、それ以外の場合はそうでない場合、フィールド分割(明らかに時間がかかります)を行うのでは$0なく、使用する場合、awkスクリプトの実行が少し速くなるはず$1です。
Ed Morton

20

別のオプションは使用することjqです:

$ seq 10|jq -s add
55

-s--slurp)入力行を配列に読み込みます。


1
それはそのような迅速なタスクのための素晴らしいツールですが、ほとんど忘れてしまいました。ありがとう
ジョン


7

これが別のワンライナーです

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

これは、数値が整数であることを前提としています。小数が必要な場合は、

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

必要な小数点以下の桁数に2を調整します。


6

perlやawkよりも簡潔で読みやすいため、このようなタスクにはGNU datamashを使用することを好みます。例えば

datamash sum 1 < myfile

ここで、1はデータの最初の列を示します。


1
Ubuntuのインストールでは表示されないため、これは標準コンポーネントではないようです。しかし、それをベンチマークしてもらいたいと思います。
Steven the Easily Amused


5

私はこれにRを使用することを好みます:

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

私は他のアプリケーションのRのファンですが、この方法でのパフォーマンスは良くありません。ファイルI / Oは大きな問題です。vroomパッケージを使用して高速化できるスクリプトに引数を渡すことをテストしました。同じサーバーで他のスクリプトをベンチマークしたときに、詳細を投稿します。
トム・ケリー

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

( 'END'なしのbrian d foyの回答と同じ)


私はこれが好きですが、中かっこを説明できますか?{なしで}を見るのはおかしいですし、逆も同様です。
ドラムファイア

1
@drumfire perl -MO=Deparseは、perlがプログラムをどのように解析するかを確認するために、上記の@brian d foyの回答を参照してください。またはperlrunのドキュメント:perldoc.perl.org/perlrun.html(-nを検索)。-nを使用すると、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")))'

フロートへの変換は、私のシステムでは約2倍高速です(320対640ミリ秒)。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をインストールする必要があります。これはPerlネイティブモジュールではありません。
Joel Berger

3

これは、ジェネレータ式でpythonを使用するソリューションです。古い古いノートパソコンで100万の数値をテストしました。

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

私はただ通り過ぎることができなかった...これが私のHaskellのワンライナーです。実際にはかなり読みやすいです:

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

残念ながらghci -eそれを実行するだけでは何もないので、メイン機能である印刷とコンパイルが必要です。

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

明確にするために、我々は、入力全体を(読んgetContentsで、それを分割し、) linesread数字としてsum<$>is fmapoperator-これはすべて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

vroomを使用したRスクリプト

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

他の言語との比較

同じハードウェアで提案されている他のいくつかの方法としての参照用

Python 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

Python 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

Ruby(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

Perl(5.24.1)

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

awk(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(clangバージョン3.3; gcc(Debian 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)はbashでタイミングをとる必要があり、zshと互換性がありません

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

sed(4.4)はbashでタイミングをとる必要があり、zshと互換性がありません

$  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

または別のbashのみ

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の数値のタイミング(私のPythonの回答と同じマシン/入力):

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

1
ベストアンサー!最高速度)
Fortranは

1

Rubyの場合:

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
とても読みやすい。perlの場合。しかし、そうです、それはそのようなものでなければなりません...
dmckee ---元モデレーターの子猫

$_デフォルトの変数です。行入力演算子は、<>で使用すると<>、デフォルトで結果をそこに配置しますwhile
ブライアン・ドフォイ2010

1
@Mark $_はトピック変数です。「it」のように機能します。この場合<> 、各行がそれに割り当てられます。コードの乱雑さを減らし、ワンライナーの作成を支援するために、さまざまな場所で使用されます。スクリプトは、「合計を0に設定し、各行を読み取って合計に追加してから、合計を出力します」と述べています。
daotoad 2010

1
@Stefan、警告と制限をオフにして、宣言と初期化をスキップできます$sum。これはとても簡単ですので、あなたも、文の修飾子を使用することができますwhile$sum += $_ while <>; print $sum;
daotoad

0

私はこれをテストしていませんが、うまくいくはずです:

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

bcがEOFとEOLを処理しない場合は、bcの前の文字列に「echoを介して」「\ n」を追加する必要がある場合があります...


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 2010

0

ここに別のものがあります:

open(FIL, "a.txt");

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

close(FIL);

print "Sum = $sum\n";

0

Alacon - Alasqlデータベースのコマンドラインユーティリティを使用して実行できます。

あなたがインストールする必要があるので、それは、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」だと思いますか?
Peter K

はい、10がベースです。64はビット数です。結果のintがそのビット数で表現できない場合、エラーが返されます。golang.org/pkg/strconv/#ParseIntを
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.