it-swarm-tr.com

Tek bir komutta sayı listesinin min, maks, medyan ve ortalamasını almanın bir yolu var mı?

Her satırda bir tane olan bir dosyadaki numaraların bir listesi var. Minimum, maksimum, medyan ve ortalama değerlerini nasıl alabilirim? Sonuçları bir bash betiğinde kullanmak istiyorum.

Anlık durumum tamsayılar için olsa da, kayan nokta sayıları için bir çözüm satırda yararlı olacaktır, ancak basit bir tamsayı yöntemi iyidir.

101
Peter.O

R programlama dili kullanabilirsiniz.

İşte hızlı ve kirli bir R betiği:

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

"stdin" _ scan, standart girdiden okunacak özel bir dosya adıdır (bu, borulardan veya yönlendirmelerden gelir).

Artık verilerinizi stdin üzerinden R komut dosyasına yönlendirebilirsiniz:

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

Kayan noktalar için de çalışır:

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

Bir R komut dosyası yazmak istemiyorsanız, komut satırında Rscript kullanarak gerçek bir tek satırlık (yalnızca okunabilirlik için linebreak ile) çağırabilirsiniz:

$ 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 adresindeki hassas R kılavuzlarını okuyun.

Ne yazık ki tam referans sadece PDF olarak mevcuttur. Referansı okumanın bir başka yolu ?topicname Etkileşimli R oturumu isteminde.


Tamlık için: İstediğiniz tüm değerleri ve daha fazlasını veren bir R komutu vardır. Ne yazık ki programla ayrıştırılması zor insan dostu bir formatta.

> 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

Aslında sayısal veri tek bir sütun (negatif sayılar dahil) toplamı, veri sayısı, minimum veri, maksimum veri, ortalama ve medyan vermek için biraz awk programı tutmak:

#!/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];
  }
'

Yukarıdaki komut dosyası stdin'den okur ve sekmeyle ayrılmış çıktı sütunlarını tek bir satıra yazdırır.

55
Bruce Ediger

GNU datamash ile:

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

Minimum, maksimum ve ortalama awk ile elde etmek oldukça kolaydır:

% 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

Sayıları sıralamanız ve bir süre hafızaya kaydetmeniz veya iki kez okumanız gerektiğinden medyan hesaplamak biraz daha zordur. Tüm sayıları hafızada saklayan örnek:

% 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

En az:

jq -s min

Maksimum:

jq -s max

Medyan:

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

Ortalama:

jq -s add/length

jq the -s (--Slurp) seçeneği, her satırı JSON olarak veya bu durumda bir sayı olarak ayrıştırdıktan sonra giriş satırları için bir dizi oluşturur.

20
nisetama

pythonpy bu tür şeyler için iyi çalışır:

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

Ve ortanca dahil bir Perl bir- (uzun) astar:

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

Kullanılan özel seçenekler:

  • -0777: Satır satır yerine tüm dosyayı bir kerede okuyun
  • -a: @F dizisine otomatik olarak bölünür

Aynı şeyin daha okunabilir bir komut dosyası sürümü:

#!/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;

Ondalık basamaklar istiyorsanız, %d Yerine %.2f Gibi bir şey ekleyin.

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 cevap:

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

İstatistiksel analizi basitleştirmek için R ortamını kullanır.

6
user48270

Sadece bu sayfada çeşitli seçeneklerin sunulması uğruna, İşte iki yol daha:

1: oktav

  • GNU Octave, öncelikle sayısal hesaplamalar için tasarlanmış, yüksek düzeyde yorumlanmış bir dildir. Doğrusal ve doğrusal olmayan problemlerin sayısal çözümü ve diğer sayısal deneylerin yapılması için yetenekler sağlar.

İşte hızlı bir oktav örneği.

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 + tek amaçlı araçlar .

Bash'ın kayan noktalı sayıları işlemesi için, bu komut dosyası num-utils Paketinden numprocess ve numaverage kullanır.

PS. Ayrıca bc 'a da makul bir şekilde baktım, ancak bu özel iş için awk' ın ötesinde bir şey sunmuyor. Bu ('bc' deki 'c' olarak belirtildiği gibi) bir hesap makinesidir - awk ve bu bash betiği kadar programlama gerektiren bir hesap makinesi ...


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

İkinci olarak lesmana'nın R'yi seçmesi ve ilk R programımı sunacağım. Standart girişte satır başına bir sayı okur ve boşluklarla standart çıkışa ayrılmış dört sayı (min, maks, ortalama, medyan) yazar.

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

num , tam olarak bunu ve daha fazlasını yapan küçük bir awk sarıcıdır, ör.

$ 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

ultra taşınabilir awk ile tekerleği yeniden keşfetmenizi önler. Dokümanlar yukarıda verilmiştir ve doğrudan bağlantı burada (ayrıca GitHub sayfası ).

3
coderofsalvation

Aşağıdaki sort/awk tandem bunu yapar:

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

(değer sayısı çiftse medyanı iki merkezi değerin ortalaması olarak hesaplar)

2
mik

Bruce'un kodundan ipuçları alarak, tüm verileri hafızada tutmayan daha verimli bir uygulama. Soruda belirtildiği gibi, girdi dosyasının satır başına (en fazla) bir sayı olduğunu varsayar. Girdi dosyasında, niteleyici bir sayı içeren satırları sayar ve sayımı, sıralanmış verilerle birlikte (öncesinde) awk komutuna geçirir. Yani, örneğin, dosya içeriyorsa

6.0
4.2
8.3
9.5
1.7

o zaman awk girişi aslında

5
1.7
4.2
6.0
8.3
9.5

Daha sonra awk komut dosyası NR==1 kod bloğu ve orta değeri (ya da orta değeri vermek üzere ortalanan iki orta değeri) gördüklerinde kaydeder.

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 ile:

$ 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 sadece çözüm - boş giriş kanıtı değil!

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

Serin veya zeki olmak yerine yardımcı programla daha fazla ilgileniyorsanız, Perlawk 'den daha kolay bir seçimdir. Genel olarak tutarlı davranışlarla her * nix üzerinde olacak ve pencerelere kurulumu kolay ve ücretsiz. Sanırım awk 'dan daha az şifreli ve kendiniz ve R gibi bir şey yazmak arasında bir ev istiyorsanız, kullanabileceğiniz bazı istatistik modülleri olacak. Oldukça denenmemiş (aslında hatalar olduğunu biliyorum) ama benim amacım için çalışıyor) Perl betiği yazmak yaklaşık bir dakika sürdü ve sanırım tek şifreli kısım while(<>), yani çok kullanışlı steno komut satırı bağımsız değişkenleri olarak iletilen dosya (lar), her seferinde bir satır okuyun ve bu satırı $_. Böylece count.pl adlı bir dosyaya koyabilir ve Perl count.pl myfile. Bunun dışında neler olduğu acı verici bir şekilde açık olmalıdır.

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