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?


93

Tôi có một danh sách các số trong một tệp, mỗi dòng trên một dòng. Làm cách nào tôi có thể nhận được các giá trị tối thiểu, tối đa, trung bìnhtrung 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.


Câu trả lời:


50

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"trong scanđó là một 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õ ?topicnamevào dấu nhắc của phiên R tương tác.


Để hoàn thiện: 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 

1
Trông thật thú vị .. Tôi sẽ xem xét kỹ hơn vào ngày mai .. Dựa trên trang của wikipedia, "R đã trở thành một tiêu chuẩn thực tế giữa các nhà thống kê" ... đó cũng là một giải thưởng đáng kể ... Tôi đã cố gắng tải nó xuống vào một ngày khác (tôi vẫn thấy nó được đề cập), nhưng tôi không thể tìm thấy nó trong repo Ubuntu ... Tôi sẽ theo dõi nó vào ngày mai ...
Peter.O

10
trong repo ubfox (và debian?) gói được đặt tên r-base.
lesmana

cảm ơn, tôi cần tham chiếu tên đó :) Tôi không nghĩ đến r- trong trường tìm kiếm synap và nó không hoạt động trên một nhân vật đơn độc ... Tôi đã thử nó ngay bây giờ, và nó có vẻ lý tưởng .. Rngôn ngữ rõ ràng là tốt nhất cho yêu cầu của tôi trong tình huống này .. Theo câu trả lời của Gilles, Rscriptgiao diện cho các tệp script là phù hợp nhất (so với R, đó là giao diện tương tác) ... và R trong thiết bị đầu cuối tạo ra một máy tính tiện dụng hoặc môi trường thử nghiệm (như python :)
Peter.O

(+1) Tôi yêu R. Tôi không thể giới thiệu nó đủ.
Dason

6
hoặc chỉcat datafile | Rscript -e 'print(summary(scan("stdin")));'
shabbychef

52

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.


1
Aha! thật rõ ràng (bây giờ tôi đã thấy tập lệnh awk của bạn :) ... Không cần phải tiếp tục kiểm tra tối thiểu và tối đa khi mảng được sắp xếp :) và điều đó có nghĩa là NR==1có thể đi (sử dụng vô dụng- if) cùng với các kiểm tra tối thiểu / tối đa, vì vậy tất cả việc khởi tạo có thể được đặt trong phần BEGIN (tốt!) ... Cho phép nhận xét cũng là một liên lạc tốt đẹp .. Cảm ơn, +1 ...
Peter.O

Chỉ là một suy nghĩ .. có thể chỉ cho phép số học tốt hơn là không cho phép bình luận (nhưng điều đó phụ thuộc vào yêu cầu của bạn) ..
Peter.O

1
Về mặt kỹ thuật, awksẽ giả sử các biến "mới" bằng 0, vì vậy trong trường hợp này, BEGIN{}phần này là không cần thiết. Tôi đã sửa lỗi gói (không cần thoát khỏi ngắt dòng). Tôi cũng đã từng OFS="\t"dọn dẹp printđường dây và thực hiện bình luận thứ hai của @ Peter.O. (Vâng, regex của tôi cho phép ., nhưng như awkgiải thích rằng 0, điều đó có thể chấp nhận được.)
Adam Katz

1
@AdamKatz - đây là những thay đổi lớn, nhưng vì thế, tôi đã không viết chương trình. awkKịch bản của tôi bây giờ là khác nhau đáng kể. Tôi gần như cảm thấy bạn nên lấy tín dụng cho chương trình trên, để cấp tín dụng khi tín dụng đáo hạn.
Bruce Ediger

1
Tôi đã viết một kịch bản perl được gọi là avg thực hiện điều này và hơn thế nữa.
Adam Katz

47

Với GNU datamash :

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

4
câu trả lời đơn giản nhất cho đến nay cho bash, như đã hỏi
rfabbri

3
brew install datamashcung cấp cho bạn phiên bản làm việc cho macOS, nếu bạn đã cài đặt Hombrew.
Per Lundberg

19

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

Cảm ơn ... ví dụ của bạn là một sự dẫn dắt tốt cho awk, đối với tôi .. Tôi đã điều chỉnh nó một chút và đặt hai người lại với nhau (cảm giác về awk) ... Tôi đã sử dụng awk asortthay vì đường ống sortvà dường như sắp xếp các số nguyên và số thập phân một cách chính xác .. Đây là liên kết đến phiên bản kết quả của tôi paste.ub Ubuntu.com/612674 ... (Và một lưu ý cho Kim: Tôi đã thử nghiệm với awk vài giờ rồi Làm việc với một ví dụ về sở thích cá nhân là cách tốt hơn đối với tôi) ... Một lưu ý chung cho độc giả: Tôi vẫn thích xem các phương pháp khác. càng nhỏ gọn càng tốt. Tôi sẽ đợi một lúc ...
Peter.O

17

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

17

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 jqcác -s( --slurptùy chọn) tạo ra một mảng cho các dòng đầu vào sau khi phân tích từng dòng như JSON, hoặc là một số trong trường hợp này.


3
Giải pháp jq xứng đáng được đề cập đặc biệt, vì nó ngắn gọn và tái sử dụng công cụ theo cách không rõ ràng.
jplindstrom

1
đẹp! ước gì tôi có thể cho +2
RASG

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.txtcó vẻ không đúng lắm, có lẽcat
malat 17/12/13

6

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ế %dbằng một cái gì đó như %.2f.


6

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ê.


5

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 numprocessnumaveragetừ gói num-utils.

Tái bút Tôi cũng đã có một cái nhìn hợp lý bc, nhưng đối với công việc đặc biệt này, nó không cung cấp bất cứ điều gì ngoài những gì awknó 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 đòi hỏi phải lập trình nhiều awkvà kịch bản 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 

4

Tôi sẽ lựa chọn R thứ hai 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ách nhau bởi khoảng trắng đến đầ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");

Cảm ơn "thứ hai" (nó rất yên tâm) ... ví dụ của bạn rất hữu ích, vì tôi đã không nhận ra ngay đó Rlà giao diện tương tác và điều Rscriptkhiển các tệp được viết theo kịch bản, có thể được thực thi theo hàm băm của ví dụ của bạn hoặc được gọi từ trong tập lệnh bash .. Các tập lệnh có thể xử lý các đối số dòng lệnh (ví dụ: stackoverflow.com/questions/2045706/ trên ) để nó trông ổn ... Ngoài ra, các biểu thức R có thể được sử dụng trong bash thông qua -e... nhưng Tôi tự hỏi làm thế nào Rso sánh với bc...
Peter.O

2

Dưới đây sort/ awksong song làm điều đó:

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

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 awklệnh 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 awkthực sự là

5
1.7
4.2
6.0
8.3
9.5

Sau đó, awktập lệnh nắm bắt số lượng dữ liệu trong NR==1khố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
  }
'

Chào mừng bạn đến với Unix & Linux! Tốt công việc cho một bài viết đầu tiên. (1) Trong khi điều này có thể trả lời câu hỏi, nó sẽ là một câu trả lời tốt hơn nếu bạn có thể giải thích làm thế nào / tại sao nó lại làm như vậy. Các tiêu chuẩn của trang web đã phát triển trong bốn năm qua; trong khi câu trả lời chỉ có mã được chấp nhận trong năm 2011, bây giờ chúng tôi thích câu trả lời toàn diện cung cấp nhiều giải thích và bối cảnh hơn. Tôi không yêu cầu bạn giải thích toàn bộ kịch bản; chỉ những phần bạn đã thay đổi (nhưng nếu bạn muốn giải thích toàn bộ tập lệnh, điều đó cũng ổn). (BTW, tôi hiểu nó tốt; Tôi đang yêu cầu thay mặt cho người dùng ít kinh nghiệm của chúng tôi.) ... (Tiếp theo)
G-Man

(Tiếp theo) Xin vui lòng không trả lời trong các bình luận; chỉnh sửa câu trả lời của bạn để làm cho nó rõ ràng và đầy đủ hơn. (2) Sửa tập lệnh để nó không cần giữ toàn bộ mảng trong bộ nhớ là một cải tiến tốt, nhưng tôi không chắc liệu phiên bản của bạn có hiệu quả hơn hay không khi bạn có ba catlệnh không cần thiết ; xem UUOC . Tiết (Cont'd)
G-Man

(Tiếp theo) ... (3) Mã của bạn là an toàn, vì bạn thiết lập FILENAMEvà bạn biết những gì bạn đặt nó vào, nhưng nói chung, bạn nên luôn luôn biến vỏ quote trừ khi bạn có lý do chính đáng không, và bạn chắc chắn bạn biết những gì bạn đang làm. (4) Cả câu trả lời của bạn và Bruce đều bỏ qua đầu vào phủ định (nghĩa là các số bắt đầu bằng -); không có gì trong câu hỏi cho thấy đây là hành vi đúng hay mong muốn. Đừng cảm thấy tồi tệ; đã hơn bốn năm và rõ ràng, tôi là người đầu tiên nhận thấy.
G-Man

Thực hiện chỉnh sửa theo đề xuất. Didn, t biết về chi phí của lệnh mèo. Luôn luôn sử dụng nó để truyền phát các tập tin duy nhất. Cảm ơn vì đã cho tôi biết về UUOC .....
Rahul Agarwal

Tốt Tôi loại bỏ thứ ba catvà thêm vào lời giải thích.
G-Man

2

Đây numlà một awktrình bao bọc nhỏ , chính xác thực hiện điều này và hơn thế nữa, vd

$ 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 ở đây (cũng kiểm tra trang GitHub ).


Liên kết đến mã web bị che khuất sẽ được thực thi trong máy tính người dùng đối với tôi có vẻ như là một ý tưởng tồi. Các trang web có chứa mã cư trú ở đây

Mã "battletested" này được lưu trữ ở đâu trước khi được đưa vào github tất cả 4 tháng trước? Tôi thấy cực kỳ nghi ngờ rằng liên kết đến github phải được bóc từ lệnh tải xuống curl. Sẽ dễ dàng hơn nhiều để tìm ra cách quyên góp tài chính cho nhà phát triển. Có vẻ như tác giả của mã đó sợ mọi người có thể vào github và xem lịch sử và số liệu thống kê (gần như không tồn tại). Có bất kỳ lý do để gọi cuộc chiến này được thử nghiệm ở tất cả, ngoài việc cố gắng để kiếm tiền?
Anthon

@BinaryZeba: đã cập nhật
coderofsalvation 22/03/2016

@Anthon ok, xóa phần 'battletested'. Tôi không nghĩ đây là nơi dành cho âm mưu FUD.
coderofsalvation 22/03/2016

2

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

1

cat/pythongiải pháp duy nhất - không phải 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))"

Bạn đã không hiển thị trung vị
Peter.O

@ Peter.O đã sửa.
ravwojdyla

Các thống kê mô-đun đòi hỏi phiên bản python> = 3,4
Peter.O

@ Peter.O bạn đúng - đó có phải là vấn đề không?
ravwojdyla

Nó không phải là vấn đề trừ khi bạn không có phiên bản python thích hợp. Nó chỉ làm cho nó ít di động hơn.
Peter.O

0

Nếu bạn quan tâm đến tiện ích hơn là mát mẻ hay thông minh, thì đó perllà một 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ĩ 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 ) perlkịch bản mất khoảng một phút để viết và tôi đoán phần mật mã duy nhất sẽ while(<>)là phần tốc ký rất hữu ích, nghĩa là lấy (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 đó trong biến đặc biệt$_. Vì vậy, bạn có thể đặt nó trong 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";

3
Bạn đã không hiển thị trung vị
Peter.O

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

Câu trả lời này sẽ hữu ích nếu có một lời giải thích về cách đoạn mã trên trả lời câu hỏi, ví dụ, bạn nên nói rằng nó sử dụng Bash (không phải sh) làm trình thông dịch. Cũng có một vấn đề với cách dữ liệu được đọc vào mảng từ tệp.
Anthony Geoghegan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.