web-dev-qa-db-ja.com

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

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

私の当面の状況は整数に関するものですが、浮動小数点数の解決策はすぐに役立つでしょうが、単純な整数法で十分です。

101
Peter.O

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

以下は、素早く汚れたRスクリプトです。

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

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

これで、stdinを介してデータを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を使用して、コマンドラインで真の1行(読みやすいように改行のみ)を呼び出すことができます。

$ 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でのみ入手できます。参照を読み取る別の方法は、対話型Rセッションのプロンプトで?topicnameと入力することです。


完全を期すために、必要なすべての値などを出力する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 
54
lesmana

私は実際には、少し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];
  }
'

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

55
Bruce Ediger

GNU datamash の場合:

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

最小値、最大値、平均値は、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
20
gelraen

最小:

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

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

20
nisetama

pythonpy は、この種のものに適しています。

cat file.txt | py --ji -l 'min(l), max(l), numpy.median(l), numpy.mean(l)'
18
RussellStewart

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

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のように置き換えます。

7
mivk
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]};
7
NotANumber

Simple-r が答えです。

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

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

6
user48270

このページにさまざまなオプションを表示するために、さらに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が浮動小数点数を処理するために、このスクリプトはパッケージnum-utilsnumprocessおよびnumaverageを使用します。

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 
5
Peter.O

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

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

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でホイールを作り直す必要がありません。上記のドキュメントと直接リンク hereGitHubページ も確認してください)です。

3

以下の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
mik

ブルースのコードから手がかりを取り、ここにすべてのデータをメモリに保持しないより効率的な実装があります。質問で述べたように、入力ファイルには(最大で)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
  }
'
2
Rahul Agarwal

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
2

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))"
1
ravwojdyla
function median()
{
    declare -a nums=($(cat))
    printf '%s\n' "${nums[@]}" | sort -n | tail -n $((${#nums[@]} / 2 + 1)) | head -n 1
}  
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";
0
iain