단일 명령으로 숫자 목록의 최소, 최대, 중앙값 및 평균을 얻는 방법이 있습니까?


93

한 줄에 하나씩 파일에 숫자 목록이 있습니다. 최소값, 최대 값, 중간 값평균값을 어떻게 얻을 수 있습니까? bash 스크립트에서 결과를 사용하고 싶습니다.

필자의 즉각적인 상황은 정수이지만 부동 소수점 숫자에 대한 솔루션은 줄에 유용하지만 간단한 정수 방법이 좋습니다.


답변:


50

R 프로그래밍 언어를 사용할 수 있습니다 .

빠르고 더러운 R 스크립트는 다음과 같습니다.

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

"stdin"scan표준 입력을 판독하는 특별한 이름 (즉 파이프 또는 리디렉션에서 수단).

이제 stdin을 통해 R 스크립트로 데이터를 리디렉션 할 수 있습니다.

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

부동 소수점에도 작동합니다.

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

R 스크립트 파일을 작성하지 않으려면 명령 줄에서 다음을 사용하여 진정한 단일 라이너 (가독성을 위해 줄 바꿈 포함)를 호출 할 수 있습니다 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 에서 훌륭한 R 매뉴얼을 읽으십시오 .

불행히도 전체 참조는 PDF로만 제공됩니다. 참조를 읽는 또 다른 방법 ?topicname은 대화식 R 세션의 프롬프트 에 입력 하는 것입니다.


완전성을 위해 : 원하는 모든 값 등을 출력하는 R 명령이 있습니다. 불행히도 프로그래밍 방식으로 구문 분석하기 어려운 인간 친화적 인 형식입니다.

> 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
흥미로운 것 같습니다 .. 내일 좀 더 자세히 살펴볼 것입니다. 위키피디아의 페이지에 따르면, "R은 통계 학자들 사이에서 사실상의 표준이되었습니다"... 글쎄요. 다른 날에 (나는 그것을 계속 언급했지만) 우분투 레포에서 그것을 찾을 수 없었습니다 ... 나는 내일 그것을 따라갈 것입니다 ...
Peter.O

10
우분투 (및 데비안?) 저장소에서 패키지 이름은 r-base.
lesmana

고마워, 나는 그 이름 참조가 필요했습니다 :) 나는 시냅스 검색 필드에서 r-를 생각하지 않았고 고독한 문자에는 작용하지 않습니다 ... 나는 그것을 시도해 보았고 이상적입니다. RGilles의 답변에 따르면, Rscript스크립트 파일에 대한 인터페이스가 가장 적합합니다 ( R대화 형 인터페이스 인 vs. ). 터미널의 R은 편리한 계산기입니다. 또는 테스트 환경 (python :)
Peter.O

(+1) 나는 R을 좋아한다. 나는 그것을 충분히 추천 할 수 없다.
Dason

6
또는 그냥cat datafile | Rscript -e 'print(summary(scan("stdin")));'
shabbychef

52

실제로 작은 awk 프로그램을 유지하여 숫자 데이터의 단일 열 (음수 포함)의 합계, 데이터 수, 최소 데이텀, 최대 데이텀, 평균 및 중앙값을 제공합니다.

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

위의 스크립트는 stdin에서 읽고 탭으로 구분 된 출력 열을 한 줄에 인쇄합니다.


1
아하! 이 (지금은 당신의 awk 스크립트 : ...이 배열을 정렬 할 때 최소 및 최대를 위해 계속 확인 할 필요가 없습니다 :) 그이 (가) 것을 의미한다 본 적이 분명입니다 NR==1(갈 수를 쓸모없는 사용이 외 ! 경우), 그래서 모든 초기화가 BEGIN 부분에서 (좋은 위치 할 수 있습니다) 최소 / 최대 검사와 함께 ... 의견을 허용하는 것은 좋은 터치가 너무 .. 감사합니다, +1 ...
Peter.O

그냥 생각 ... 어쩌면 단지 수치를 허용하는 의견을 허용하지 않는 것보다 낫다 (그러나 그것은 당신이 사용자의 요구 사항에 따라 다름) ...
Peter.O

1
기술적으로 awk"새"변수가 0이라고 가정하므로이 경우 BEGIN{}섹션이 필요하지 않습니다. 줄 바꿈을 수정했습니다 (줄 바꿈을 벗어날 필요가 없습니다). 또한 라인 OFS="\t"을 정리하고 print@ Peter.O의 두 번째 주석을 구현했습니다. (예, 내 정규 표현식은을 허용 .하지만으로 awk해석 할 0수 있습니다.)
Adam Katz

1
@AdamKatz-이것은 큰 변화이지만, 서있는 것처럼 나는 프로그램을 쓰지 않았습니다. 내 awk스크립트는 이제 크게 다릅니다. 크레딧이 필요한 곳에서 크레딧을 제공하려면 위 프로그램에 대한 크레딧을 받아야한다고 생각합니다.
Bruce Ediger

1
그런데 avg 라는 펄 스크립트를 작성했습니다 .
Adam Katz

47

GNU datamash로 :

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

4
요청에 따라 bash에 대한 가장 간단한 답변
rfabbri

3
brew install datamashHombrew를 설치 한 경우 macOS 용 작업 버전을 제공합니다.
Per Lundberg

19

최소, 최대 및 평균은 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

중앙값을 계산하는 것은 조금 까다 롭습니다. 숫자를 정렬하고 잠시 동안 메모리에 모두 저장하거나 두 번 읽어야하기 때문입니다 (첫 번째는 세고, 두 번째는 중앙값을 얻음). 다음은 모든 숫자를 메모리에 저장하는 예입니다.

% 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

고마워 ... 당신의 예는 나를 위해 awk의 좋은 도입입니다. 나는 그것을 조금 조정하고 두 가지를 함께 모았습니다 (awk의 느낌을 얻음) ... 나는 asort파이프 가 아닌 awk를 사용했습니다. sort, 그리고 그것은 정수와 소수를 올바르게 정렬하는 것 같습니다. 여기 내 결과 버전 paste.ubuntu.com/612674에 대한 링크가 있습니다 ... (그리고 Kim에 대한 메모 : 나는 두 시간 동안 awk를 실험 해왔습니다 개인적인 관심사를 가지고 작업하는 것이 나에게 더 낫다.) 독자들에게주는 일반적인 참고 사항 : 나는 여전히 다른 방법을보고 싶어한다. 더 작을 수록 좋습니다. 잠시만 기다려
주십시오

17

pythonpy 는 이런 종류의 일에 잘 작동합니다.

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

17

최저한의:

jq -s min

최고:

jq -s max

중앙값:

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

평균:

jq -s add/length

에서는 ( 옵션) JSON 각 라인을 파싱 한 후, 입력 라인 어레이를 생성하거나,이 경우의 수로.jq-s--slurp


3
jq 솔루션은 간결하기 때문에 특별한 언급이 필요하며 툴을 명백하지 않은 방식으로 재사용합니다.
jplindstrom

1
아름다운! +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.txt아마 잘 보이지 않을 수도 있습니다cat
malat

6

그리고 중앙값을 포함한 Perl 1- (긴) 라이너 :

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

사용되는 특수 옵션은 다음과 같습니다.

  • -0777 : 전체 파일을 한 줄씩 읽는 대신 한 번에 읽기
  • -a : @F 배열로 자동 분할

같은 것의 더 읽기 쉬운 스크립트 버전은 다음과 같습니다.

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

소수를 원하면 %d다음과 같이 바꾸십시오 %.2f.


6

Simple-r 이 답입니다.

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

R 환경을 사용하여 통계 분석을 단순화합니다.


5

이 페이지에 제시된 다양한 옵션을 위해 두 가지 방법이 더 있습니다.

1 : 옥타브

  • GNU Octave는 주로 수치 계산을위한 고급 해석 언어입니다. 선형 및 비선형 문제의 수치 솔루션과 다른 수치 실험을 수행하는 기능을 제공합니다.

다음은 빠른 옥타브 예제입니다.

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 + 단일 목적 도구 .

bash는 부동 소수점 숫자를 처리하는 경우,이 스크립트는 사용 numprocessnumaverage패키지 num-utils.

추신. 또한 합리적으로 살펴 bc보았지만이 특정 직업에 대해서는 그 이상을 제공 awk하지 않습니다. 그것은 ( 'bc'상태에서 'c'로) 계산기입니다-계산기 awk와 많은 bash 스크립트 가 필요한 계산기입니다 ...


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

lesmana가 선택한 R을 두 번째로 하고 첫 번째 R 프로그램을 제공 하겠습니다 . 표준 입력에서 한 줄에 하나의 숫자를 읽고 공백으로 구분 된 4 개의 숫자 (최소, 최대, 평균, 중앙값)를 표준 출력에 씁니다.

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

"두 번째"(안정적 임)에 감사드립니다 R. 대화식 인터페이스 인 직선을 인식하지 못하고 Rscript스크립트 파일을 구동하므로 예제가 유용했습니다. , 또는 bash 스크립트 내에서 호출됩니다. 스크립트는 명령 줄 인수 (예 : stackoverflow.com/questions/2045706/… )를 처리 할 수 있으므로 좋아 보입니다 ... 또한 R 표현식은 bash에서 -e...을 통해 사용할 수 있습니다 . 어떻게 궁금해 않는 R비교에 bc...
Peter.O

2

아래 sort/ awk탠덤이 수행합니다.

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

(값 카운트가 짝수이면 두 개의 중앙 값의 평균으로 중앙값을 계산합니다)


2

Bruce의 코드에서 힌트를 얻은 다음은 전체 데이터를 메모리에 보관하지 않는보다 효율적인 구현입니다. 질문에서 언급했듯이 입력 파일에는 줄당 하나의 숫자가 있다고 가정합니다. 규정 된 숫자를 포함하는 입력 파일의 행을 계수 awk하고 정렬 된 데이터와 함께 (앞에) 계수를 명령에 전달합니다 . 예를 들어 파일에

6.0
4.2
8.3
9.5
1.7

입력 awk은 실제로

5
1.7
4.2
6.0
8.3
9.5

그런 다음 awk스크립트는 NR==1코드 블록 에서 데이터 수를 캡처하고 중간 값 (또는 평균을 산출하는 평균 인 두 개의 중간 값)을 볼 때 저장합니다.

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

유닉스 및 리눅스에 오신 것을 환영합니다! 첫 번째 게시물에 잘하셨습니다. (1) 이것이 질문에 대한 답변이 될 수 있지만, 어떻게 / 왜 그렇게 하는지 설명 할 수 있다면 더 나은 답변이 될 것 입니다. 이 사이트의 표준은 지난 4 년 동안 발전해 왔습니다. 2011 년에는 코드 전용 답변이 허용되었지만 이제 더 자세한 설명과 컨텍스트를 제공하는 포괄적 인 답변을 선호합니다. 나는 당신에게 전체 스크립트를 설명하도록 요구하지 않습니다. 변경 한 부분 만 (하지만 전체 스크립트를 설명하려면 괜찮습니다). (BTW, 나는 잘 이해, 나는 우리의 경험이 적은 사용자를 대신 부탁 해요.) ... (계속)
G-남자

(계속)… 의견에 응답하지 마십시오. 명확하고 완전하게 답변을 편집 하십시오. (2) 전체 배열을 메모리에 담을 필요가 없도록 스크립트를 수정하는 것은 좋은 개선이지만, 세 개의 불필요한 cat명령 이있을 때 버전이“보다 효율적”이라고 말하는 것이 적절한 지 확실하지 않습니다 . UUOC를 참조하십시오 . … (계속)
G-Man

(계속)… (3) 코드를 설정 FILENAME하고 설정 한 내용을 알기 때문에 코드가 안전 하지만 일반적으로 타당한 이유가없는 한 항상 쉘 변수를 인용해야합니다. 당신이하고있는 일을 알고 있는지 확인하십시오 . (4) 귀하의 답변과 Bruce는 음수 입력을 무시합니다 (즉,로 시작하는 숫자 -). 질문에 이것이 정확하거나 원하는 행동임을 암시하는 것은 없습니다. 기분 나빠하지 마십시오. 4 년 넘게 지났고, 분명히, 내가 알아 차린 첫 번째 사람입니다.
G-Man

제안에 따라 수정했습니다. cat 명령의 오버 헤드에 대해 몰랐습니다. 항상 단일 파일을 스트리밍하는 데 사용했습니다. UUOC에 대해 알려 주셔서 감사합니다 .....
Rahul Agarwal

좋은. 세 번째를 제거하고 cat설명에 추가했습니다.
G-Man

2

num작은입니다 awk정확히 이것과 더 많은, 예를 들면 않습니다 래퍼

$ 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

휴대 성이 뛰어난 awk에서 휠을 재발 명하는 것을 막아줍니다. 위의 문서와 여기에 직접 링크가 있습니다 ( GitHub 페이지 도 확인 하십시오 ).


사용자 컴퓨터에서 실행되는 가려진 웹 코드에 대한 링크는 나에게 나쁜 생각처럼 보입니다. 코드가 포함

이 "전쟁을 거친"코드 는 4 개월 전에 github 에 설치되기 전에 어디에서 호스팅 되었습니까? 컬 다운로드 명령에서 github에 대한 링크를 벗겨야한다는 것이 매우 의심 스럽습니다. 재정적으로 개발자에게 기부하는 방법을 찾는 것이 훨씬 쉽습니다. 그 코드의 저자는 사람들이 github에 가서 (거의 존재하지 않는) 역사와 통계를 볼 것을 두려워합니다. 돈을 모 으려고 노력하는 것과는 별도로,이 전투를 전혀 테스트하지 않은 이유가 있습니까?
Anthon

@BinaryZeba : 업데이트 됨
coderofsalvation

@Anthon 좋아, 'battletested'부분을 제거했습니다. 나는 이것이 음모 FUD의 장소라고 생각하지 않습니다.
coderofsalvation

2

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/python유일한 해결책- 빈 입력 증명이 아닙니다!

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

당신은 중앙값을
베드로 .O September

@ Peter.O 고정.
ravwojdyla

통계 모듈은 필요 파이썬 버전> = 3.4
Peter.O

@ Peter.O 당신이 맞습니다-그게 문제입니까?
ravwojdyla

적절한 파이썬 버전이 없으면 문제가되지 않습니다. 휴대 성이 떨어집니다.
Peter.O

0

시원하거나 영리하지 않고 유틸리티에 더 관심이 있다면보다 perl쉬운 선택 awk입니다. 대체로 일관된 동작으로 모든 * nix에 있으며, Windows에 쉽고 무료로 설치할 수 있습니다. 나는 또한 그것보다 덜 암호 적이라고 생각하며 awk, 직접 작성하는 것과 R과 같은 것 사이의 중간 집을 원한다면 사용할 수있는 통계 모듈이있을 것입니다. 내 테스트되지 않은 (실제로 버그가 있지만 내 목적으로는 효과가 있음 ) perl스크립트는 작성하는 데 약 1 분이 걸렸습니다. 유일한 암호 부분은 while(<>)매우 유용합니다. 즉, 명령 줄 인수로 전달 된 파일을 가져 와서 한 번에 한 줄씩 읽고 특수 변수의 해당 줄$_. 따라서 이것을 count.pl이라는 파일에 넣고 다음과 같이 실행할 수 있습니다 perl count.pl myfile. 그 외에는 무슨 일이 일어나고 있는지 분명하게 알아야합니다.

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

3
당신은 중앙값을
베드로 .O

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

이 답변은 위의 코드가 질문 에 어떻게 대답 하는지 에 대한 설명이있는 경우 유용합니다 . 예를 들어 Bash (not sh)를 해석기로 사용하고 있다고 말해야합니다 . 파일에서 배열로 데이터를 읽는 방법에도 문제가 있습니다.
Anthony Geoghegan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.