単一のコマンドで数値のリストの最小、最大、中央値、平均を取得する方法はありますか?


93

ファイルには、1行に1つずつ番号のリストがあります。最小値、最大値、中央値平均値を取得するにはどうすればよいですか?結果をbashスクリプトで使用したい。

私の当面の状況は整数の場合ですが、浮動小数点数の解決策は将来的には有用ですが、単純な整数法で十分です。


回答:


50

Rプログラミング言語を使用できます。

以下に、すばやくて汚いRスクリプトを示します。

#! /usr/bin/env Rscript
d<-scan("stdin", quiet=TRUE)
cat(min(d), max(d), median(d), mean(d), sep="\n")

は、標準入力(つまり、パイプまたはリダイレクト)から読み取る特別なファイル名"stdin"scanあることに注意してください。

これで、データを標準入力経由でRスクリプトにリダイレクトできます。

$ cat datafile
1
2
4
$ ./mmmm.r < datafile
1
4
2
2.333333

浮動小数点でも動作します:

$ cat datafile2
1.1
2.2
4.4
$ ./mmmm.r < datafile2
1.1
4.4
2.2
2.566667

Rスクリプトファイルを書きたくない場合は、コマンドラインで次のコマンドを使用して、真のワンライナー(読みやすくするために改行のみ)を呼び出すことができますRscript

$ Rscript -e 'd<-scan("stdin", quiet=TRUE)' \
          -e 'cat(min(d), max(d), median(d), mean(d), sep="\n")' < datafile
1
4
2
2.333333

http://cran.r-project.org/manuals.htmlで Rの詳細なマニュアルを読んでください

残念ながら、完全なリファレンスはPDFでのみ入手可能です。参照を読み取る別の方法?topicnameは、対話型Rセッションのプロンプトに入力することです。


完全を期すために、必要なすべての値などを出力するRコマンドがあります。残念ながら、プログラムで解析するのが難しい人間に優しい形式です。

> summary(c(1,2,4))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   1.500   2.000   2.333   3.000   4.000 

1
面白そうです。明日、詳しく見ていきます。ウィキペディアのページに基づいて、「Rは統計学者の間で事実上の標準になりました」...それは重要な称賛です...私は実際にそれをダウンロードしようとしました先日(言及し続けました)、Ubuntuリポジトリで見つけることができませんでした...明日フォローアップします
...-Peter.O

10
ubuntu(およびdebian?)リポジトリでは、パッケージの名前はr-base
レスマナ

おかげで、私はその名前の参照が必要でした:)シナプス検索フィールドでr-を考えていませんでした。Rこの状況では、言語が明らかに私の要件に最適です。Gillesの答えによると、Rscriptスクリプトファイルへのインターフェイスが最も適切です(対R、インタラクティブなインターフェイスです)。 、またはテスト環境(pythonなど)
-Peter.O

(+1)私はRが大好きです。十分に推奨できません。
デイソン

6
または単にcat datafile | Rscript -e 'print(summary(scan("stdin")));'
shabbychef 14

52

私は実際に少しのawkプログラムを保持して、数値データ(負の数を含む)の単一列の合計、データ数、最小データム、最大データム、平均、および中央値を示します。

#!/bin/sh
sort -n | awk '
  BEGIN {
    c = 0;
    sum = 0;
  }
  $1 ~ /^(\-)?[0-9]*(\.[0-9]*)?$/ {
    a[c++] = $1;
    sum += $1;
  }
  END {
    ave = sum / c;
    if( (c % 2) == 1 ) {
      median = a[ int(c/2) ];
    } else {
      median = ( a[c/2] + a[c/2-1] ) / 2;
    }
    OFS="\t";
    print sum, c, ave, median, a[0], a[c-1];
  }
'

上記のスクリプトは標準入力から読み取り、タブで区切られた出力の列を1行に出力します。


1
あぁ!それは明らかです(今、私はあなたのawkスクリプトを見ました:)...配列がソートされているときに最小値と最大値をチェックする必要はありません:)そしてそれはNR==1行くことができることを意味しますif)最小/最大チェックとともに、すべての初期化はBEGINセクションに配置できます(良い!)...コメントを許可するのもいい感じです。.ありがとう、+ 1 ...
Peter.O

ただ考え..多分、数字だけを許可する方がコメントを拒否するよりも良いかもしれません(しかしそれはあなたの要件に依存します)
.- Peter.O

1
技術的にawkは、「新しい」変数がゼロであると想定されるため、この場合、このBEGIN{}セクションは不要です。折り返しを修正しました(改行をエスケープする必要もありません)。またOFS="\t"、このprint行を整理して、@ Peter.Oの2番目のコメントを実装しました。(はい、私の正規表現ができます.が、とはawk解釈しているような0、それは許容範囲だ。)
アダム・カッツ

1
@AdamKatz-これらは大きな変更ですが、現状ではプログラムを作成していません。私のawkスクリプトは今ではかなり異なっています。クレジットが支払われるべき場所にクレジットを付与するために、上記のプログラムにクレジットを取るべきだと私はほとんど感じています。
ブルースエディガー

1
ちなみに、これを行うavgというperlスクリプトを作成しました。
アダム・カッツ

47

GNU datamashの場合

$ printf '1\n2\n4\n' | datamash max 1 min 1 mean 1 median 1
4   1   2.3333333333333 2

4
bashの質問に対する最も簡単な答え
-rfabbri

3
brew install datamashHombrewがインストールされている場合、macOSの作業バージョンを提供します。
Per Lundberg

19

awkを使用すると、最小、最大、平均を簡単に取得できます。

% echo -e '6\n2\n4\n3\n1' | awk 'NR == 1 { max=$1; min=$1; sum=0 }
   { if ($1>max) max=$1; if ($1<min) min=$1; sum+=$1;}
   END {printf "Min: %d\tMax: %d\tAverage: %f\n", min, max, sum/NR}'
Min: 1  Max: 6  Average: 3,200000

中央値の計算はもう少し難しいです。数字を並べ替えてしばらくメモリに保存するか、2回読み込む必要があります(最初にカウントしてから、中央値を取得するため)。すべての数値をメモリに保存する例を次に示します。

% echo -e '6\n2\n4\n3\n1' | sort -n | awk '{arr[NR]=$1}
   END { if (NR%2==1) print arr[(NR+1)/2]; else print (arr[NR/2]+arr[NR/2+1])/2}' 
3

ありがとう...あなたの例は私にとってawkの良い導入です..私はそれを少し調整して2つをまとめました(awkの感触を得る)...私はasortパイプではなくawkを使用しましたsort、それは整数と小数を正しくソートしているようです。ここに私の結果のバージョンへのリンクがありますpaste.ubuntu.com/612674 ... 。個人的な興味のある例で作業することは私にとってより良い方法です)...読者への一般的なメモ:私はまだ他の方法を見ることに興味があります。よりコンパクトに、より良いです。しばらくお待ちください
...-Peter.O


17

最小:

jq -s min

最大:

jq -s max

中央値:

sort -n|awk '{a[NR]=$0}END{print(NR%2==1)?a[int(NR/2)+1]:(a[NR/2]+a[NR/2+1])/2}'

平均:

jq -s add/length

()オプションJSONとして各行を解析した後、入力ラインの配列を作成し、又はこの場合の数値として。jq-s--slurp


3
jqソリューションは簡潔であり、ツールを非自明な方法で再利用するため、特別な言及に値します。
jplindstrom

1
綺麗な!+2を与えることができればいいのに
RASG

7
nums=$(<file.txt); 
list=(`for n in $nums; do printf "%015.06f\n" $n; done | sort -n`); 
echo min ${list[0]}; 
echo max ${list[${#list[*]}-1]}; 
echo median ${list[${#list[*]}/2]};

echo file.txt多分cat
malat

6

そして、中央値を含むPerlの1つの(長い)ライナー:

cat numbers.txt \
| perl -M'List::Util qw(sum max min)' -MPOSIX -0777 -a -ne 'printf "%-7s : %d\n"x4, "Min", min(@F), "Max", max(@F), "Average", sum(@F)/@F,  "Median", sum( (sort {$a<=>$b} @F)[ int( $#F/2 ), ceil( $#F/2 ) ] )/2;'

使用される特別なオプションは次のとおりです。

  • -0777 :行ごとではなく、ファイル全体を一度に読み取ります
  • -a :@F配列への自動分割

同じもののより読みやすいスクリプトバージョンは次のようになります。

#!/usr/bin/perl

use List::Util qw(sum max min);
use POSIX;

@F=<>;

printf "%-7s : %d\n" x 4,
    "Min", min(@F),
    "Max", max(@F),
    "Average", sum(@F)/@F,
    "Median", sum( (sort {$a<=>$b} @F)[ int( $#F/2 ), ceil( $#F/2 ) ] )/2;

小数が必要な場合は、の%dようなものに置き換えます%.2f


6

Simple-rが答えです。

r summary file.txt
r -e 'min(d); max(d); median(d); mean(d)' file.txt

R環境を使用して、統計分析を簡素化します。


5

このページにさまざまなオプションを表示するために、さらに2つの方法があります。

1:オクターブ

  • GNU Octaveは高レベルのインタープリター言語であり、主に数値計算を目的としています。線形および非線形問題の数値解法、および他の数値実験を実行する機能を提供します。

簡単なオクターブの例を次に示します。

octave -q --eval 'A=1:10;
  printf ("# %f\t%f\t%f\t%f\n", min(A), max(A), median(A), mean(A));'  
# 1.000000        10.000000       5.500000        5.500000

2:bash +単一目的ツール

bashが浮動小数点数を処理するために、このスクリプトはpackageからnumprocessおよびnumaverageを使用しますnum-utils

PS。また、を合理的に見てきましたbcが、この特定の仕事については、それ以上のことawkは何も提供していません。これは(「bc」状態の「c」のように)電卓awkです。多くのプログラミングとこのbashスクリプトを必要とする電卓です...


arr=($(sort -n "LIST" |tee >(numaverage 2>/dev/null >stats.avg) ))
cnt=${#arr[@]}; ((cnt==0)) && { echo -e "0\t0\t0\t0\t0"; exit; }
mid=$((cnt/2)); 
if [[ ${cnt#${cnt%?}} == [02468] ]] 
   then med=$( echo -n "${arr[mid-1]}" |numprocess /+${arr[mid]},%2/ )
   else med=${arr[mid]}; 
fi     #  count   min       max           median        average
echo -ne "$cnt\t${arr[0]}\t${arr[cnt-1]}\t$med\t"; cat stats.avg 

4

レスマナのRの選択を 2回目にし、最初のRプログラムを提供します。標準入力の行ごとに1つの数値を読み取り、スペースで区切られた4つの数値(最小、最大、平均、中央値)を標準出力に書き込みます。

#!/usr/bin/env Rscript
a <- scan(file("stdin"), c(0), quiet=TRUE);
cat(min(a), max(a), mean(a), median(a), "\n");

「セカンド」(安心)に感謝します... Rインタラクティブなインターフェイスであるストレートを実現しなかったので、あなたのサンプルは役に立ちましたRscript。サンプルのハッシュバンに従って実行可能なスクリプトファイルを駆動します。 、またはbashスクリプト内から呼び出されます。スクリプトはコマンドライン引数(例:stackoverflow.com/questions/2045706/…)を処理できるため、見栄えがよくなります。また、-e...を介してR式をbashで使用できます。私はどのように思うかRを比較しbc...
Peter.O

2

以下sort/ awkタンデムはそれを行います:

sort -n | awk '{a[i++]=$0;s+=$0}END{print a[0],a[i-1],(a[int(i/2)]+a[int((i-1)/2)])/2,s/i}'

(値のカウントが偶数の場合、2つの中央値の平均として中央値を計算します)


2

Bruceのコードからヒントを得て、ここにデータ全体をメモリに保持しないより効率的な実装を示します。質問で述べたように、入力ファイルには(最大で)1行に1つの番号があると想定しています。適格な数値を含む入力ファイル内の行をカウントし、そのカウントをawkソート済みデータ(前)とともにコマンドに渡します。したがって、たとえば、ファイルに

6.0
4.2
8.3
9.5
1.7

の入力awkは実際には

5
1.7
4.2
6.0
8.3
9.5

次に、awkスクリプトはNR==1コードブロックのデータカウントをキャプチャし、中央値(または中央値を生成するために平均化される2つの中央値)を保存します。

FILENAME="Salaries.csv"

(awk 'BEGIN {c=0} $1 ~ /^[-0-9]*(\.[0-9]*)?$/ {c=c+1;} END {print c;}' "$FILENAME"; \
        sort -n "$FILENAME") | awk '
  BEGIN {
    c = 0
    sum = 0
    med1_loc = 0
    med2_loc = 0
    med1_val = 0
    med2_val = 0
    min = 0
    max = 0
  }

  NR==1 {
    LINES = $1
    # We check whether numlines is even or odd so that we keep only
    # the locations in the array where the median might be.
    if (LINES%2==0) {med1_loc = LINES/2-1; med2_loc = med1_loc+1;}
    if (LINES%2!=0) {med1_loc = med2_loc = (LINES-1)/2;}
  }

  $1 ~ /^[-0-9]*(\.[0-9]*)?$/  &&  NR!=1 {
    # setting min value
    if (c==0) {min = $1;}
    # middle two values in array
    if (c==med1_loc) {med1_val = $1;}
    if (c==med2_loc) {med2_val = $1;}
    c++
    sum += $1
    max = $1
  }
  END {
    ave = sum / c
    median = (med1_val + med2_val ) / 2
    print "sum:" sum
    print "count:" c
    print "mean:" ave
    print "median:" median
    print "min:" min
    print "max:" max
  }
'

Unix&Linuxへようこそ!最初の投稿に適しています。(1)これは質問に答える可能性がありますが、どのように/なぜそうするのかを説明できれば、より良い答えになるでしょう。サイトの標準は過去4年間で進化しました。2011年にはコードのみの回答が受け入れられましたが、現在ではより多くの説明とコンテキストを提供する包括的な回答が好まれています。スクリプト全体を説明するようにお願いしているわけではありません。変更した部分だけです(ただし、スクリプト全体を説明したい場合でも大丈夫です)。(ところで、私は細かいことを理解し、私たちの経験の少ないユーザーの代わりに求めている。)...(続き)
G-マン

(続き)…コメントに返信しないでください。回答を編集して、より明確で完全なものにします。(2)配列全体をメモリに保持する必要がないようにスクリプトを修正するのは良い改善ですが、3つの不要なcatコマンドがある場合、バージョンが「より効率的」であると言うのが適切かどうかわかりません。UUOCを参照してください。…(続き)
Gマン

(続き)…(3)設定しFILENAME、何に設定するかを知っているため、コードは安全ですが、一般的に、正当な理由がない限り、シェル変数を常に引用する必要があります。あなたが何をしているを知っていることを確認してください。(4)あなたの答えとブルースの負の入力(つまり、で始まる数字-)は無視されます。これが正しいまたは望ましい動作であることを示唆する質問には何もありません。気分を悪くしないでください。それは4年以上経ちました、そして、どうやら、私は気づいた最初の人です。
Gマン

提案に従って編集しました。キャットコマンドのオーバーヘッドについては知りませんでした。常に単一ファイルのストリーミングに使用されます。UUOCについての私に言ってくれてありがとう.....
ラーフルAgarwalさん

良い。3番目を削除catし、説明に追加しました。
Gマン

2

num小さなでawk、例えばまさにこれを行い、より多くのラッパー

$ echo "1 2 3 4 5 6 7 8 9" | num max
9
$ echo "1 2 3 4 5 6 7 8 9" | num min max median mean
..and so on

超ポータブルなawkで車輪を再発明する必要がありません。ドキュメントは上記にあり、直接リンクはこちらGitHubページも確認してください)。


ユーザーのコンピューターで実行される不明瞭なWebコードへのリンクは、悪い考えのように思えます。コードが含まれているサイトはここに常駐

どこでこの「battletested」のコードは、上に置くされる前にホストされたgithubのすべての4ヶ月前に?githubへのリンクをcurlダウンロードコマンドから削除する必要があることは非常に疑わしいと思います。開発者に財政的に寄付する方法を見つけるのははるかに簡単です。そのコードの作者は、人々がgithubに行って(ほとんど存在しない)履歴と統計を見るのを恐れているように見えます。お金を集めようとする以外に、この戦いをテスト済みと呼ぶ理由はありますか?
アントン

@BinaryZeba:更新
coderofsalvation

@Anthon OK、「バトルテスト済み」の部分を削除しました。これは陰謀のFUDの場所だとは思わない。
coderofsalvation

2

perl

$ printf '%s\n' 1 2 4 |
   perl -MList::Util=min,max -MStatistics::Basic=mean,median -w -le '
     chomp(@l = <>); print for min(@l), max(@l), mean(@l), median(@l)'
1
4
2.33
2

1

cat/python唯一の解決策- 空入力証明ではありません!

cat data |  python3 -c "import fileinput as FI,statistics as STAT; i = [int(l) for l in FI.input()]; print('min:', min(i), ' max: ', max(i), ' avg: ', STAT.mean(i), ' median: ', STAT.median(i))"

中央値を
Peter.O

@ Peter.Oが修正されました。
ravwojdyla

統計モジュールが必要とPythonのバージョン> = 3.4
Peter.O

@ Peter.Oあなたは正しいです-それは問題ですか?
ravwojdyla

適切なpythonバージョンをお持ちでない限り、これは問題ではありません。移植性が低くなります。
Peter.O

0

クールで賢いというよりもユーティリティに興味perlがある場合は、を選択するよりも簡単ですawk。概して、それは一貫した振る舞いを持つすべての* nixにあり、Windowsに簡単かつ無料でインストールできます。私はそれよりもわかりにくいと思うしawk、あなたがそれを自分で書いてRのようなものとの中間の家が欲しいならあなたが使うことができるいくつかの統計モジュールがあるだろう。私のかなりテストされていない)perlスクリプトの記述には約1分かかりましたが、唯一の不可解な部分はであると思いますwhile(<>)。これは非常に便利な速記です。つまり、コマンドライン引数として渡されたファイルを取り、一度に1行ずつ読み取り、特殊変数のその行$_。したがって、これをcount.plというファイルに入れて、として実行できますperl count.pl myfile。それとは別に、何が起こっているのかが痛々しいほど明白であるはずです。

$max = 0;
while (<>) {
 $sum = $sum + $_;
 $max = $_ if ($_ > $max);
 $count++;
}
$avg=$sum/$count;
print "$count numbers total=$sum max=$max mean=$avg\n";

3
中央値を
Peter.O

0
function median()
{
    declare -a nums=($(cat))
    printf '%s\n' "${nums[@]}" | sort -n | tail -n $((${#nums[@]} / 2 + 1)) | head -n 1
}  

上記のコードが質問にどのように回答するの説明があれば、この回答は役に立ちます。例えば、shインタプリタとしてBash(ではなく)を使用していると言う必要があります。また、ファイルからデータを配列に読み込む方法にも問題があります。
アンソニー・ジョゲガン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.