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


Answers:


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已成为统计学家的事实上的标准”……嗯,这是一个重大的荣誉……我实际上试图下载它前几天(我一直看到它被提及),但是我在Ubuntu
仓库中

10
在ubuntu(和debian?)仓库中,软件包名为r-base
lesmana 2011年

谢谢,我需要该名称参考:)我在突触搜索字段中没有想到r-,它也没有作用于一个孤独的角色...我已经尝试过了,它看起来很理想。R语言显然是在这种情况下最适合我的需求。.根据Gilles的回答,Rscript脚本文件的接口是最合适的(相R对于交互式接口),并且终端中的R使得计算器很方便或测试环境(如python :)
Peter.O 2011年

(+1)我爱R。我不能推荐它。
戴森2012年

6
或只是cat datafile | Rscript -e 'print(summary(scan("stdin")));'
shabbychef 2014年

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脚本了:) ... ...在对数组进行排序时,无需继续检查min和max :),这意味着NR==1可以继续使用(无用的-如果)以及最小/最大检查,那么所有初始化都可以在BEGIN部分中找到(好!)...也允许发表评论。.谢谢,+ 1 ...
Peter.O

只是一个想法..也许只允许NUMERICS比不允许评论更好(但是这取决于你的需求)..
Peter.O

1
从技术上讲,awk将假定“新”变量为零,因此在这种情况下该BEGIN{}部分是不必要的。我已经解决了换行问题(也无需逃脱换行符)。我还经常OFS="\t"清理print线路并实施@ Peter.O的第二条评论。(是的,我的正则表达式允许.,但按照的awk解释0,这是可以接受的。)
Adam Katz 2015年

1
@AdamKatz-这些都是巨大的变化,但就目前而言,我没有编写程序。我的awk脚本现在有了很大的不同。我几乎觉得您应该为上述计划功劳,才能在应得的学费中获得荣誉。
Bruce Ediger 2015年

1
顺便说一句,我写了一个叫做avg的perl脚本来完成这个任务。
亚当·卡兹

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

3
brew install datamash如果已安装Hombrew,则为您提供macOS的工作版本。
Per Lundberg'3

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的感觉)...我使用了awk,asort而不是管道sort,并且似乎正确地对整数和小数进行了排序。.这是我生成的版本的链接paste.ubuntu.com/612674 ...(和Kim的注释:我已经在awk上进行了几个小时的实验(使用个人兴趣的示例对我来说是更好的方法)...读者的一般注意事项:我仍然对看到其他方法感兴趣。越紧凑越好。我会等一会儿……
Peter.O


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

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


3
jq解决方案值得一提,因为它简洁明了,并且以一种非显而易见的方式重新使用了该工具。
jplindstrom's

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一(长)线,包括中位数:

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

答案很简单

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来自package num-utils

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 

4

我将第二次选择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");

感谢您的“第二”(这令人放心)...您的示例很有用,因为我没有意识到直接R就是交互式界面Rscript,而是驱动了脚本文件,该文件可以按照您的示例进行哈希哈希处理,或从bash脚本中调用。。这些脚本可以处理命令行参数(例如stackoverflow.com/questions/2045706/…),因此看起来不错... R表达式也可以通过-e... 在bash中使用我不知道如何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

借鉴布鲁斯的代码,这是一种更有效的实现,它不会将整个数据保留在内存中。如问题中所述,它假定输入文件每行最多有一个数字。它对输入文件中包含合格编号的行进行计数,并将计数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
  }
'

欢迎使用Unix和Linux!做好第一篇文章。(1)虽然这可以回答问题,但如果您可以解释这样做的原因,则将是一个更好的答案。该站点的标准在过去四年中得到了发展。尽管仅代码的答案在2011年是可以接受的,但我们现在更喜欢提供更多解释和上下文的综合答案。我不是要您解释整个脚本;只是更改的部分(但是如果您要解释整个脚本,也可以)。(顺便说一句,很好理解;我代表我们经验不足的用户问。)…(续)
G-Man

(续)…请不要在评论中回复;编辑您的答案,使其更清晰,更完整。(2)修复脚本,使其不需要将整个数组保存在内存中是一个很好的改进,但是我不确定当您有三个不必要的cat命令时,说您的版本“更有效”是否合适;参见UUOC。…(续)
G-Man

(续)…(3)您的代码是安全的,因为您进行设置FILENAME并知道设置的内容,但是通常,除非有充分理由不这样做,否则应始终引用shell变量,并且确保您知道自己在做什么。(4)您的答案和布鲁斯都忽略否定输入(即,以开头的数字-);问题中没有任何迹象表明这是正确的或期望的行为。别难过 已经过去四年了,很明显,我是第一个注意到的人。
G-Man

根据建议进行编辑。是不是不知道猫命令的开销。始终使用它来流式传输单个文件。感谢您告诉我有关UUOC的信息.....
Rahul Agarwal 2015年

好。我删除了第三个,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 page)。


在我看来,链接到要在用户计算机上执行的模糊Web代码的想法不好。包含代码

凡被这“battletested”码上被放在之前托管github上所有的4个月前?我非常怀疑必须从curl下载命令中删除指向github的链接。找出如何向开发商捐款是一件容易的事。该代码的作者似乎担心人们会去github查看(几乎不存在的)历史和统计数据。除了试图筹集资金外,是否有任何理由要求这场战斗经过考验?
Anthon

@BinaryZeba:已更新
coderofsalvation

@Anthon好的,删除了“经过战斗验证”的部分。我认为这不是共谋FUD的地方。
救赎的编码者,2016年

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

您尚未显示中位数
Peter.O 2015年

@ Peter.O固定。
ravwojdyla

所述统计模块需要> = 3.4蟒版本
Peter.O

@ Peter.O您是正确的-这是问题吗?
ravwojdyla

除非您没有适当的python版本,否则这不是问题。它只是使其便携性降低。
Peter.O 2015年

0

如果您对实用程序更感兴趣,而不是冷酷或聪明,那perl是比容易的选择awk。基本上,它将以一致的方式出现在每个* 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";

3
您尚未显示中位数
Peter.O 2012年

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

如果对上述代码如何回答该问题做出了解释,那么此答案将很有用,例如,您应该说它使用Bash(而不是sh)作为解释器。如何将数据从文件读取到数组中也存在问题。
Anthony Geoghegan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.