it-swarm-id.com

Apakah ada cara untuk mendapatkan min, maks, median, dan rata-rata daftar angka dalam satu perintah?

Saya memiliki daftar nomor dalam file, satu per baris. Bagaimana saya bisa mendapatkan nilai minimum, maksimum, median dan rata-rata? Saya ingin menggunakan hasil dalam skrip bash.

Meskipun situasi langsung saya adalah untuk integer, solusi untuk angka floating-point akan berguna di telepon, tetapi metode integer sederhana baik-baik saja.

101
Peter.O

Anda dapat menggunakan bahasa pemrograman R .

Berikut ini adalah skrip R cepat dan kotor:

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

Catat "stdin" dalam scan yang merupakan nama file khusus untuk dibaca dari input standar (artinya dari pipa atau pengalihan).

Sekarang Anda dapat mengarahkan data Anda melalui stdin ke skrip R:

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

Juga berfungsi untuk floating point:

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

Jika Anda tidak ingin menulis file skrip R, Anda dapat memanggil true-liner (dengan linebreak hanya untuk keterbacaan) di baris perintah menggunakan 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

Baca manual R halus di http://cran.r-project.org/manuals.html .

Sayangnya referensi lengkap hanya tersedia dalam PDF. Cara lain untuk membaca referensi adalah dengan mengetik ?topicname dalam Konfirmasi sesi R interaktif.


Untuk kelengkapan: ada perintah R yang menampilkan semua nilai yang Anda inginkan dan banyak lagi. Sayangnya dalam format ramah manusia yang sulit diurai secara terprogram.

> 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

Saya sebenarnya menyimpan sedikit program awk untuk memberikan jumlah, jumlah data, datum minimum, datum maksimum, rata-rata dan median dari satu kolom data numerik (termasuk angka negatif):

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

Script di atas membaca dari stdin, dan mencetak kolom-kolom output yang dipisahkan tab pada satu baris.

55
Bruce Ediger

Dengan GNU datamash :

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

Min, maks dan rata-rata cukup mudah didapat dengan 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

Menghitung median sedikit lebih rumit, karena Anda perlu mengurutkan angka dan menyimpannya dalam memori untuk sementara waktu atau membacanya dua kali (pertama kali menghitungnya, kedua - untuk mendapatkan nilai median). Berikut adalah contoh yang menyimpan semua angka dalam memori:

% 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

Minimum:

jq -s min

Maksimum:

jq -s max

Median:

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

Rata-rata:

jq -s add/length

Dalam jq the -s (--Slurp) opsi membuat array untuk jalur input setelah mengurai setiap baris sebagai JSON, atau sebagai nomor dalam kasus ini.

20
nisetama

pythonpy berfungsi dengan baik untuk hal seperti ini:

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

Dan liner satu (panjang) Perl, termasuk median:

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

Opsi khusus yang digunakan adalah:

  • -0777: baca seluruh file sekaligus, bukan baris demi baris
  • -a: autosplit ke dalam array @F

Versi skrip yang lebih mudah dibaca dari hal yang sama adalah:

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

Jika Anda ingin desimal, ganti %d dengan sesuatu seperti %.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 adalah jawabannya:

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

Ia menggunakan lingkungan R untuk menyederhanakan analisis statistik.

6
user48270

Hanya demi memiliki berbagai opsi yang disajikan pada halaman ini, Berikut adalah dua cara lagi:

1: oktaf

  • GNU Octave adalah bahasa interpretasi tingkat tinggi, terutama ditujukan untuk perhitungan numerik. Ini memberikan kemampuan untuk solusi numerik masalah linear dan nonlinier, dan untuk melakukan eksperimen numerik lainnya.

Ini adalah contoh oktaf cepat.

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 + alat tujuan tunggal .

Agar bash menangani angka floating-point, skrip ini menggunakan numprocess dan numaverage dari paket num-utils.

PS. Saya juga memiliki pandangan yang masuk akal pada bc, tetapi untuk pekerjaan khusus ini, ia tidak menawarkan apa pun di luar apa yang awk lakukan. Ini adalah (seperti 'c' dalam 'bc' menyatakan) kalkulator — kalkulator yang membutuhkan banyak pemrograman sebagai awk dan skrip bash ini ...


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

Saya akan kedua pilihan lesmana tentang R dan menawarkan program R pertama saya. Bunyinya satu angka per baris pada input standar dan menulis empat angka (min, maks, rata-rata, median) dipisahkan oleh spasi ke output standar.

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

num adalah pembungkus kecil awk yang melakukan hal ini dan banyak lagi, mis.

$ 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

itu menyelamatkan Anda dari menciptakan kembali roda di awk ultra-portabel. Dokumen diberikan di atas, dan tautan langsung di sini (periksa juga halaman GitHub ).

3
coderofsalvation

Di bawah ini sort/awk tandem yang melakukannya:

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

(Ini menghitung median sebagai rata-rata dari dua nilai pusat jika nilai hitung genap)

2
mik

Mengambil petunjuk dari kode Bruce, berikut ini adalah implementasi yang lebih efisien yang tidak menyimpan seluruh data dalam memori. Seperti yang dinyatakan dalam pertanyaan, diasumsikan bahwa file input memiliki (paling banyak) satu nomor per baris. Itu menghitung baris dalam file input yang berisi angka yang memenuhi syarat dan melewati hitungan ke perintah awk bersama dengan (sebelumnya) data yang diurutkan. Jadi, misalnya, jika file tersebut berisi

6.0
4.2
8.3
9.5
1.7

maka input ke awk sebenarnya

5
1.7
4.2
6.0
8.3
9.5

Kemudian skrip awk menangkap jumlah data dalam NR==1 blok kode dan menyimpan nilai tengah (atau dua nilai tengah, yang dirata-rata untuk menghasilkan median) ketika melihatnya.

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

Dengan 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 satu-satunya solusi - bukan bukti masukan kosong!

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

Jika Anda lebih tertarik pada utilitas daripada menjadi keren atau pintar, maka Perl adalah pilihan yang lebih mudah daripada awk. Pada umumnya ini akan ada di setiap * nix dengan perilaku konsisten, dan mudah dan gratis untuk menginstal di windows. Saya pikir itu juga kurang samar daripada awk, dan akan ada beberapa modul statistik yang dapat Anda gunakan jika Anda ingin setengah jalan antara menulis sendiri dan sesuatu seperti R. Saya cukup belum diuji (sebenarnya saya tahu itu memiliki bug tetapi berfungsi untuk tujuan saya) Perl skrip membutuhkan waktu sekitar satu menit untuk menulis, dan saya kira satu-satunya bagian samar adalah while(<>), yang merupakan singkatan yang sangat berguna, artinya mengambil file dilewatkan sebagai argumen baris perintah, baca satu baris sekaligus dan masukkan baris itu ke dalam variabel khusus $_. Jadi Anda bisa meletakkan ini dalam file bernama count.pl dan menjalankannya sebagai Perl count.pl myfile. Terlepas dari itu, jelas sekali menyakitkan apa yang terjadi.

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