Tek bir komutta minimum, maksimum, ortanca ve sayılar listesinin ortalamasını almanın bir yolu var mı?


93

Dosyada, her satıra bir tane olmak üzere bir numara listem var. Minimum, maksimum, ortanca ve ortalama değerleri nasıl alabilirim ? Sonuçları bir bash betiğinde kullanmak istiyorum.

Her ne kadar acil durumum tamsayılar için olsa da, kayan nokta sayıları için bir çözüm, satır boyunca yararlı olacaktır, ancak basit bir tamsayı yöntemi iyidir.


Yanıtlar:


50

R programlama dilini 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")

Not "stdin"in scanstandart giriş okumak için özel dosya adı (yani boru veya yönlendirmeler gelen anlamına gelir).

Artık verilerinizi stdin üzerinden R betiğine yönlendirebilirsiniz:

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

Ayrıca 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 betiği dosyası yazmak istemiyorsanız, aşağıdakileri kullanarak komut satırına gerçek bir tek gömlek (yalnızca okunabilirlik için satır sonu ile) çağırabilirsiniz 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 adresindeki hassas R el kitaplarını okuyun .

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


Bütünlük için: İstediğiniz tüm değerleri ve daha fazlasını veren bir R komutu var. Ne yazık ki programatik olarak ayrıştırılması zor olan 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 

1
İlginç gözüküyor .. Yarın daha yakından bakacağım .. wikipedia sayfasının "R, istatistikçiler arasında fiili bir standart haline geldi" ... iyi bir sürpriz oldu ... tam anlamıyla onu indirmeye çalıştım geçen gün (bahsettiğimi görmeye devam ettim), ama Ubuntu
deposunda bulamadım

10
ubuntu'da (ve debian?) repo paketin adıdır r-base.
lesmana

teşekkürler, bu isim referansına ihtiyacım vardı :) Sinaptik arama alanında r- düşünmedim ve yalnız bir karaktere etki etmedi ... Şimdi denedim ve ideal görünüyor. Rdil Gilles' yanıt başına olarak .. açıkça bu durumda benim gereksinimi için en iyisidir Rscript(genel komut dosyalarını arayüzü en uygun R, interaktif arayüz olan) terminalde ... ve R kullanışlı hesap makinesi yapar , veya test ortamı (python gibi :)
Peter.O

(+1) R'yi seviyorum. Yeterince öneremiyorum.
Dason

6
ya da sadececat datafile | Rscript -e 'print(summary(scan("stdin")));'
shabbychef

52

Aslında tek bir sayısal veri sütununun toplamını, veri sayımını, minimum veriyi, maksimum veriyi, ortalama ve medyanını (negatif sayılar dahil) vermek için biraz garip bir program yapıyorum:

#!/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 stdin'den okur ve sekmeyle ayrılmış çıktı sütunlarını tek bir satıra yazdırır.


1
Aha! apaçıktır (şimdi awk betiğinizi gördüm :) :) Dizi sıralandığında min ve max değerlerini kontrol etmeye gerek yok :) ve bu NR==1gidebildiği anlamına gelir (işe yaramaz bir kullanım ... if) min / max kontrolleri ile birlikte, tüm başlatmalar BEGIN bölümünde bulunabilir (iyi!) ... Yorumlara izin vermek de hoş bir dokunuş ... Teşekkürler, +1 ...
Peter.O

Sadece bir düşünce .. belki sadece nümeriklere izin vermek, yorumların reddedilmesinden daha iyidir (ama bu sizin gereksinimlerinize bağlıdır) ..
Peter.O

1
Teknik olarak, awk"yeni" değişkenlerin sıfır olduğunu varsayar, bu durumda bu BEGIN{}bölüm gereksizdir. Kaydırmayı düzelttim (satır sonlarından kaçmaya gerek yok). Ayrıca çizgiyi OFS="\t"temizlemek için kullanılır printve @ Peter.O'nun ikinci yorumunu uygulardım. (Evet, regex'im izin veriyor ., ancak bunun kabul edilebilir awkolduğu şeklinde yorumluyor 0.)
Adam Katz

1
@AdamKatz - bunlar büyük değişiklikler, ancak olduğu gibi programı yazmadım. Benim awksenaryom şimdi önemli ölçüde farklı. Neredeyse kredinin olduğu yerde kredi vermek için yukarıdaki program için kredi almanız gerektiğini düşünüyorum.
Bruce Ediger,

1
Bu arada, bunu ve daha fazlasını yapan avg adlı bir perl betiği yazdım .
Adam Katz

47

GNU datamash ile :

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

4
sorulduğu gibi, bash için çok basit bir cevap
rfabbri 23

3
brew install datamashHombrew kurulu ise size macOS için çalışan bir versiyonunu verir.
Lundberg Per

19

Min, max ve ortalama awk ile almak 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

Medyanı hesaplamak biraz daha zordur, çünkü sayıları sıralamanız ve bir süreliğine hepsini hafızaya kaydetmeniz veya iki kez okumanız gerekir (ilk defa sayma, ikinci - medyan değeri elde etmek için). İşte tüm numaraları 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

Teşekkürler ... senin örneklerin awk için iyi bir giriş, benim için .. Ben biraz tweaked ve ikisini bir araya getirdim (awk hissini almak) ... pipk asortyerine awk kullandım sortve tamsayıları ve ondalık sayıları doğru sıralıyor gibi görünüyor .. İşte sonuç versiyonumun bir bağlantısı. paste.ubuntu.com/612674 ... Bir kişisel çıkar örneği ile çalışmak benim için daha iyi bir yol ...) Okuyuculara genel bir not: Diğer yöntemleri görmekle ilgileniyorum. daha kompakt daha iyi. Bir süre bekleyeceğim ...
Peter.O

17

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

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

17

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

Olarak ( ) seçeneği JSON her satırı ayrıştırma sonra giriş hatları için bir dizi oluşturur, ya da bu durumda bir sayı olarak.jq-s--slurp


3
JQ çözümü, özlü olduğu ve aracı açık bir şekilde yeniden kullandığı için özel bir sözü almaya değer.
jplindst,

1
güzel! Keşke +2
RASG 'de

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.txtoldukça doğru görünmüyor, belkicat
malat

6

Ve ortanca dahil bir Perl (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 : tüm dosyayı satır satır yerine tek seferde oku
  • -a : @F dizisine autosplit

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 istiyorsan, bunun %dgibi bir şeyle değiştir %.2f.


6

Simple-r cevaptır:

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.


5

Bu sayfada sunulan çeşitli seçeneklere sahip olmak uğruna, işte iki yol daha:

1: oktav

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

İş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 .

Kayan noktalı sayıları işlemek için bash için bu komut dosyası kullanır numprocessve numaveragepaketten num-utils.

PS. Ayrıca makul bir göz atmıştım bc, ancak bu belirli iş için, bunun ötesinde hiçbir şey awksunmuyor. Bu (bc'deki 'c' gibi) bir hesap makinesidir - bu kadar çok programlama gerektiren bir hesap makinesi awkve bu bash betiği ...


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

İkinci lesmana'nın R'yi seçip ilk R programımı sunacağım. Standart girişte satır başına bir sayı okur ve standart çıktıya boşluklarla ayrılmış dört sayı (en az, en çok, ortalama, ortalama) yazar.

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

"İkincisi" için teşekkürler (güven verici) ... örneğin, Retkileşimli arabirim olduğunu açık bir şekilde fark edemediğimden ve Rscriptörnek hash-bang'ine göre çalıştırılabilen komut dosyalarını çalıştıran örneğiniz yararlı oldu. veya komut komut satırı args (örn. işleyebilir .. bir bash komut dosyası içinden çağrılan stackoverflow.com/questions/2045706/... ) öylesine iyi görünüyor ... Ayrıca ifadeleri aracılığıyla bash kullanılabilir R -e... ama Nasıl Rkarşılaştığını merak ediyorum bc...
Peter.O

2

Aşağıda sort/ awktandem 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}'

(eğer değer sayısı eşitse, ortancayı iki merkezi değerin ortalaması olarak hesaplar)


2

Bruce'un kodundan ipuçları alarak, tüm verileri bellekte tutmayan daha verimli bir uygulama. Soruda belirtildiği gibi, girdi dosyasının her satırda (en fazla) bir numarası olduğunu varsayar. Giriş dosyasındaki bir niteleyici sayı içeren satırları sayar ve sayımı awk, sıralanan verilerle (önceki) birlikte komuta iletir. Yani, örneğin, dosya içeriyorsa

6.0
4.2
8.3
9.5
1.7

o zaman awkaslında girdi

5
1.7
4.2
6.0
8.3
9.5

Daha sonra awkkomut dosyası, NR==1kod bloğundaki veri sayısını toplar ve bunları görünce orta değeri (veya ortanca değeri elde etmek için ortalama olan iki orta değeri) 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
  }
'

Unix ve Linux'a Hoşgeldiniz! İlk gönderi için iyi iş. (1) Bu soruyu cevaplayabilse de, nasıl / neden böyle olduğunu açıklarsanız daha iyi bir cevap olur . Sitenin standartları son dört yılda gelişti; yalnızca kod yanıtları 2011'de kabul edilebilir olsa da, şimdi daha fazla açıklama ve bağlam sağlayan kapsamlı yanıtları tercih ediyoruz. Sizden tüm senaryoyu açıklamanızı istemiyorum; sadece değiştirdiğin kısımları (ancak betiğin tamamını açıklamak istersen, bu da sorun değil). (BTW, ben ince anlamak; bizim daha az deneyimli kullanıcılar adına soruyorum.) ... (devamı)
G-Man

(Devam ediyor)… Lütfen yorumlara cevap vermeyin; cevabınızı daha net ve daha eksiksiz hale getirmek için düzenleyin . (2) Komut dizisini tüm diziyi bellekte tutması gerekmeyecek şekilde düzeltmek iyi bir gelişmedir, ancak üç gereksiz catkomutunuz olduğunda sürümünüzün “daha ​​verimli” olduğunu söylemenin uygun olup olmadığından emin değilim ; UUOC'ye bakınız . … (Devamı)
G-Man

(Devam ediyor)… (3) Kodunuz güvende, çünkü siz ayarladınız FILENAMEve neye ayarladığınızı biliyorsunuzdur, fakat genel olarak, yapmamak için iyi bir nedeniniz olmadıkça, genel olarak kabuk değişkenlerini belirtmelisiniz. Ne yaptığını bildiğinden eminim . (4) Hem cevabınız hem de Bruce’un negatif girişi yok sayması (yani, rakamlarla başlayan -); soruda bunun doğru ya da istenen davranış olduğunu gösteren hiçbir şey yoktur. Kötü hissetme; dört yıldan fazla oldu ve görünüşe göre, ilk fark eden benim.
G-Man

Önerilere göre düzenlemeler yapıldı. Kedi komutanlığının yükünü bilmiyordum. Her zaman tek dosya akışı için kullanılır. Bana UUOC hakkında söylediğin için teşekkürler .....
Rahul Agarwal

İyi. Üçüncüyü ortadan kaldırdım catve açıklamaya ekledim.
G-Man

2

numBir küçücük awktam olarak bu ve daha fazla, mesela yapar sarıcı

$ 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'te tekerleği yeniden icat etmekten kurtarır. Dokümanlar yukarıda ve doğrudan bağlantı burada verilmiştir (ayrıca GitHub sayfasına bakın ).


Kullanıcı bilgisayarında yürütülecek gizlenmiş web koduna olan bağlantılar bana kötü bir fikir gibi geldi. Kod içeren yer burada bulunduğu

Nerede bu "battletested" kod koymak önce ev sahipliği yaptı github tüm 4 ay önce? Github bağlantısının curl download komutundan çıkarılması gerektiğinden şüpheliyim. Geliştiriciye finansal olarak nasıl bağış yapılacağını bulmak çok daha kolaydır. Görünüşe göre bu kodun yazarı insanların github'a gidip (neredeyse varolmayan) tarih ve istatistiklere bakmasından korkuyor. Bu savaşı test etmek için para toplamaya çalışmaktan başka neden var mı?
Anthon,

@BinaryZeba: güncellendi
coderofsalvation 22:16

@Anthon tamam, 'battletested' kısmını çıkardı. Bunun komplo FUD'un yeri olduğunu sanmıyorum.
coderofsalvation

2

İle 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/pythontek çö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))"

Sen olmadığını göstermiştir medyan
Peter.O

@ Peter.O düzeltildi.
ravwojdyla

İstatistik modülü gerektirir piton versiyonunu> = 3.4
Peter.O

@ Peter.O haklısın - bu bir sorun mu?
ravwojdyla 10:15

Uygun python sürümünüz yoksa sorun değil. Sadece daha az taşınabilir hale getirir.
Peter,

0

Eğer zeki veya zeki olmaktansa, kamu hizmeti ile daha fazla ilgileniyorsanız, o zamandan perldaha kolay bir seçimdir awk. Genelde, tutarlı davranışla her * nix'te olacak ve pencerelere kurulumu kolay ve ücretsiz. Sanırım, kendisinden daha az şifreli awk, ve kendin yazıp R gibi bir şey arasında yarıya kadar bir evin olmasını istersen, kullanabileceğin bazı istatistik modülleri olacak. Oldukça denenmemişim (aslında böcekleri olduğunu biliyorum ama amaçlarım için çalışıyor. ) perlsenaryo yazması bir dakika sürdü ve tek şifreli kısmın olacağını tahmin ediyorumwhile(<>) çok kullanışlı , yani anlamı komut satırı argümanları olarak iletilen dosya (lar), her seferinde bir satır oku ve özel değişkende bu satır$_. Böylece bunu count.pl adlı bir dosyaya koyabilir ve çalıştırabilirsiniz perl count.pl myfile. Bunun dışında neler olup bittiği acıyla 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";

3
Sen olmadığını göstermiştir medyan
Peter.O

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

Yukarıdaki cevabın soruyu nasıl cevapladığına dair bir açıklama olsaydı, bu cevap faydalı olurdu , örneğin, shtercüman olarak Bash (değil ) kullandığını söylemelisin . Verilerin dosyadaki diziye nasıl okunduğu konusunda da bir problem var.
Anthony Geoghegan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.