it-swarm-vi.com

Có cách nào để lấy min, max, median và trung bình của một danh sách các số trong một lệnh không?

Tôi có một danh sách các số trong một tập tin, mỗi số một dòng. Làm cách nào tôi có thể nhận các giá trị tối thiểu, tối đa, trung vịtrung bình? Tôi muốn sử dụng các kết quả trong một tập lệnh bash.

Mặc dù tình huống trước mắt của tôi là dành cho số nguyên, một giải pháp cho các số có dấu phẩy động sẽ hữu ích trong dòng, nhưng một phương thức số nguyên đơn giản là ổn.

101
Peter.O

Bạn có thể sử dụng ngôn ngữ lập trình R .

Đây là một kịch bản R nhanh và bẩn:

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

Lưu ý "stdin" in scan là tên tệp đặc biệt để đọc từ đầu vào tiêu chuẩn (có nghĩa là từ các đường ống hoặc chuyển hướng).

Bây giờ bạn có thể chuyển hướng dữ liệu của mình qua stdin sang tập lệnh R:

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

Cũng hoạt động cho các điểm nổi:

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

Nếu bạn không muốn viết tệp tập lệnh R, bạn có thể gọi một tập lệnh thực sự (chỉ ngắt dòng để dễ đọc) trong dòng lệnh bằng cách sử dụng 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

Đọc hướng dẫn sử dụng R tốt tại http://cran.r-project.org/manuals.html .

Thật không may, tài liệu tham khảo đầy đủ chỉ có sẵn trong PDF. Một cách khác để đọc tài liệu tham khảo là bằng cách gõ ?topicname trong Lời nhắc của phiên R tương tác.


Để đầy đủ: có một lệnh R xuất ra tất cả các giá trị bạn muốn và hơn thế nữa. Thật không may trong một định dạng thân thiện với con người mà khó có thể phân tích theo chương trình.

> 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

Tôi thực sự giữ một chương trình awk nhỏ xung quanh để đưa ra tổng, số liệu, số liệu tối thiểu, số liệu tối đa, giá trị trung bình và trung bình của một cột dữ liệu số (bao gồm cả số âm):

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

Kịch bản trên đọc từ stdin và in các cột đầu ra được phân tách bằng tab trên một dòng.

55
Bruce Ediger

Với GNU datamash :

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

Tối thiểu, tối đa và trung bình là khá dễ dàng để có được với 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

Tính toán trung vị khó hơn một chút, vì bạn cần sắp xếp số và lưu trữ tất cả chúng trong bộ nhớ trong một thời gian hoặc đọc chúng hai lần (lần đầu tiên để đếm chúng, lần thứ hai - để có giá trị trung bình). Dưới đây là ví dụ lưu trữ tất cả các số trong bộ nhớ:

% 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

Tối thiểu:

jq -s min

Tối đa:

jq -s max

Trung bình:

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

Trung bình cộng:

jq -s add/length

Trong jq, tùy chọn -s (--Slurp) Tạo một mảng cho các dòng đầu vào sau khi phân tích từng dòng dưới dạng JSON hoặc dưới dạng số trong trường hợp này.

20
nisetama

pythonpy hoạt động tốt cho loại điều này:

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

Và một lớp lót Perl one- (dài), bao gồm cả trung vị:

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

Các tùy chọn đặc biệt được sử dụng là:

  • -0777: đọc toàn bộ tệp cùng một lúc thay vì từng dòng
  • -a: autosplit vào mảng @F

Một phiên bản kịch bản dễ đọc hơn của cùng một thứ sẽ là:

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

Nếu bạn muốn số thập phân, thay thế %d với một cái gì đó như %.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 là câu trả lời:

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

Nó sử dụng môi trường R để đơn giản hóa phân tích thống kê.

6
user48270

Chỉ vì mục đích có nhiều lựa chọn được trình bày trên trang này, Dưới đây là hai cách khác:

1: quãng tám

  • GNU Octave là một ngôn ngữ được giải thích cấp cao, chủ yếu dành cho các tính toán số. Nó cung cấp các khả năng cho giải pháp số của các bài toán tuyến tính và phi tuyến, và để thực hiện các thí nghiệm số khác.

Dưới đây là một ví dụ quãng tám nhanh chóng.

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 + công cụ đơn mục đích .

Để bash xử lý các số dấu phẩy động, tập lệnh này sử dụng numprocessnumaverage từ gói num-utils.

Tái bút Tôi cũng đã có cái nhìn hợp lý về bc, nhưng đối với công việc cụ thể này, nó không cung cấp bất cứ điều gì ngoài những gì awk làm. Đó là (như các trạng thái 'c' trong 'bc') một máy tính. Một máy tính yêu cầu lập trình nhiều như awk và tập lệnh bash này ...


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

Tôi sẽ thứ hai lựa chọn R của lesmana và cung cấp chương trình R đầu tiên của tôi. Nó đọc một số trên mỗi dòng trên đầu vào tiêu chuẩn và ghi bốn số (tối thiểu, tối đa, trung bình, trung bình) được phân tách bằng khoảng trắng thành đầu ra tiêu chuẩn.

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

num là một trình bao bọc awk nhỏ, chính xác thực hiện điều này và hơn thế nữa, ví dụ:.

$ 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

nó giúp bạn tiết kiệm từ việc phát minh lại bánh xe trong awk siêu di động. Các tài liệu được đưa ra ở trên và liên kết trực tiếp tại đây (cũng kiểm tra trang GitHub ).

3
coderofsalvation

Dưới đây sort/awk song song với nó:

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

(nó tính trung bình là giá trị trung bình của hai giá trị trung tâm nếu số giá trị là chẵn)

2
mik

Lấy tín hiệu từ mã của Bruce, đây là một triển khai hiệu quả hơn mà không giữ toàn bộ dữ liệu trong bộ nhớ. Như đã nêu trong câu hỏi, nó giả định rằng tệp đầu vào có (nhiều nhất) một số trên mỗi dòng. Nó đếm các dòng trong tệp đầu vào có chứa một số đủ điều kiện và chuyển số đếm cho lệnh awk cùng với (trước) dữ liệu được sắp xếp. Vì vậy, ví dụ, nếu tập tin chứa

6.0
4.2
8.3
9.5
1.7

sau đó, đầu vào cho awk thực sự là

5
1.7
4.2
6.0
8.3
9.5

Sau đó, tập lệnh awk thu được số liệu trong NR==1 khối mã và lưu giá trị trung bình (hoặc hai giá trị trung bình, được tính trung bình để mang lại giá trị trung bình) khi nhìn thấy chúng.

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

Với 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
Stéphane Chazelas

cat/python chỉ giải pháp - không bằng chứng đầu vào trống!

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
David McLaughlin

Nếu bạn quan tâm đến tiện ích hơn là tuyệt vời hay thông minh, thì Perl là lựa chọn dễ dàng hơn awk. Nhìn chung, nó sẽ xuất hiện trên mọi * nix với hành vi nhất quán và dễ dàng và miễn phí để cài đặt trên windows. Tôi nghĩ rằng nó cũng ít khó hiểu hơn awk, và sẽ có một số mô-đun thống kê bạn có thể sử dụng nếu bạn muốn có một ngôi nhà nửa chừng giữa việc tự viết và một cái gì đó giống như R. Tôi chưa được kiểm chứng (thực tế tôi biết nó có lỗi nhưng nó hoạt động cho mục đích của tôi) Perl script mất khoảng một phút để viết và tôi đoán phần mật mã duy nhất sẽ là while(<>), đó là cách viết tắt rất hữu ích, nghĩa là mất (các) tệp được truyền dưới dạng đối số dòng lệnh, đọc một dòng tại một thời điểm và đặt dòng đó vào biến đặc biệt $_. Vì vậy, bạn có thể đặt tệp này vào một tệp có tên là Count.pl và chạy nó dưới dạng Perl count.pl myfile. Ngoài ra, rõ ràng là những gì đang xảy ra.

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