it-swarm.cn

有没有一种方法可以在单个命令中获取数字列表的最小值,最大值,中位数和平均值?

我在文件中有一个数字列表,每行一个。如何获得最小,最大中位数平均值?我想在bash脚本中使用结果。

尽管我目前的情况是整数,但对于浮点数的解决方案在行内很有用,但是简单的整数方法就可以了。

101
Peter.O

您可以使用 R编程语言

这是一个快速而肮脏的R脚本:

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

注意scan中的"stdin",这是一个特殊的文件名,可以从标准输入(即从管道或重定向)中读取。

现在,您可以通过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中可用。读取引用的另一种方法是在交互式R会话的提示中键入?topicname


为了完整起见:有一个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 
54
lesmana

我实际上保留了一些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读取,并在一行中打印制表符分隔的输出列。

55
Bruce Ediger

使用GNU datamash

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

最小值,最大值和平均值很容易通过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
20
gelraen

最低要求:

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

jq中,-s--Slurp)选项在将每一行解析为JSON或本例中的数字之后,为输入行创建一个数组。

20
nisetama

pythonpy 很适合这种事情:

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

Perl一(长)线,包括中位数:

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

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 是答案:

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

它使用R环境简化统计分析。

6
user48270

只是为了在此页面上显示各种选项,这里还有另外两种方法:

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处理浮点数,此脚本使用了num-utils包中的numprocessnumaverage

PS。我对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 
5
Peter.O

我将第二个 lesmana选择的R 并提供我的第一个R程序。它在标准输入上每行读取一个数字,并将以空格分隔的四个数字(最小值,最大值,平均值,中位数)写入标准输出。

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

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页面 )。

3
coderofsalvation

以下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
mik

借鉴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
  }
'
2
Rahul Agarwal

使用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唯一的解决方案- 不是空输入证明!

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

如果您对实用程序更感兴趣,而不是冷酷或聪明,那么Perlawk更容易选择。基本上,它将以一致的方式出现在每个* nix上,并且可以轻松,免费地在Windows上安装。我认为它也比awk的神秘性小,如果您想自己编写它和写R之类的地方,可以使用一些统计信息模块。我相当未经测试(实际上,我知道它有错误)但这是出于我的目的。)Perl脚本花了大约一分钟的时间编写,我猜唯一神秘的部分是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";
0
iain