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

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

评论

stackoverflow.com/questions/3122442 / ...

#1 楼

您可以使用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。阅读参考的另一种方法是在交互式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 


评论


看起来很有意思。明天我会仔细看。根据维基百科的页面,“ R已经成为统计学家的事实上的标准”……嗯,这是一个重大的荣誉……我实际上是在尝试下载它前几天(我一直看到它被提及),但是我在Ubuntu仓库中找不到它...明天我将对其进行跟踪...

– Peter.O
2011年5月25日17:26



在ubuntu(和debian?)仓库中,该软件包名为r-base。

– Lesmana
11年5月25日在17:44

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

– Peter.O
2011年5月26日11:28



(+1)我爱R。我不能推荐它。

–戴森
2012年4月3日,下午2:36

或只是cat数据文件| Rscript -e'print(summary(scan(“ stdin”)));'

–shabbychef
2014年8月11日在22:32



#2 楼

使用GNU datamash:
$ printf '%s\n' 1 2 4 | datamash max 1 min 1 mean 1 median 1
4   1   2.3333333333333 2


评论


如果您安装了Homebrew,brew install datamash为您提供了适用于macOS的工作版本。

– Per Lundberg
18 Mar 5 '18 at 12:26

#3 楼

实际上,我实际上使用了一些awk程序来给出单列数值数据(包括负数)的总和,数据计数,最小数据,最大数据,均值和中位数:

#!/bin/sh
sort -n | awk '
  BEGIN {
    c = 0;
    sum = 0;
  }
   ~ /^(\-)?[0-9]*(\.[0-9]*)?$/ {
    a[c++] = ;
    sum += ;
  }
  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读取,并在一行中打印以制表符分隔的输出列。

评论


啊哈!很明显(现在,我已经看过您的awk脚本了:)... ...在对数组进行排序时,无需继续检查min和max :),这意味着NR == 1可以使用了(无用的-使用-if)以及最小/最大检查,因此所有初始化都可以位于BEGIN部分(好!)...也允许发表评论。.谢谢,+1 ...

– Peter.O
11年5月26日在2:28



只是一个想法..也许只允许数字胜于禁止注释(但这取决于您的要求)。

– Peter.O
2011年5月26日下午6:21

从技术上讲,awk将假定“新”变量为零,因此在这种情况下,BEGIN {}部分是不必要的。我已经解决了换行问题(也无需逃脱换行符)。我还使用OFS =“ \ t”清理了打印行,并实现了@ Peter.O的第二条注释。 (是的,我的正则表达式允许。,但是当awk将其解释为0时,这是可以接受的。)

–亚当·卡兹(Adam Katz)
15年1月15日在21:22

@AdamKatz-这些都是巨大的更改,但就目前而言,我没有编写程序。我的awk脚本现在有了很大的不同。我几乎觉得您应该为上述计划而功劳,以便在应得的学费中给予荣誉。

–布鲁斯·埃迪格(Bruce Ediger)
15年1月15日在22:31

顺便说一句,我写了一个叫avg的perl脚本来完成这个工作。

–亚当·卡兹(Adam Katz)
18年8月1日在17:20

#4 楼

最小值:
 jq -s min
awk 'NR==1||jq -s max
awk 'NR==1||jq -s 'sort|if length%2==1 then.[length/2|floor]else[.[length/2-1,length/2]]|add/2 end'
sort -n|awk '{a[NR]=jq -s add/length
awk '{x+=$ seq 100|jq -s '{minimum:min,maximum:max,average:(add/length),median:(sort|if length%2==1 then.[length/2|floor]else[.[length/2-1,length/2]]|add/2 end)}'
{
  "minimum": 1,
  "maximum": 100,
  "average": 51.5,
  "median": 51.5
}
}END{print x/NR}'
}END{print(NR%2==1)?a[int(NR/2)+1]:(a[NR/2]+a[NR/2+1])/2}'
>x{x=jq}END{print x}'
<x{x=-s}END{print x}'
 

最大值:
 --slurp 

中位数:
 q4312079q 

平均值:
 q4312079q 

与一个命令组合(从注释中修改):
 q4312079q 

在q4312079q,q4312079q(q4312079q)选项在将每行解析为JSON或数字(在这种情况下为数字)之后,为输入行创建一个数组。

评论


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

– jplindstrom
17年5月10日在11:31

美丽!希望我能给+2

– RASG
17年7月19日在21:16

扩展一点:jq -s'{min:min,max:max,sum:add,count:length,avg:(add / length),中位数:(sort |。[length / 2])}'显示输出作为带有标签的对象,漂亮地印有颜色!

–格林
20 Sep 24 '20:14



@Grynn对于中位数来说,这是不正确的。对于奇数列表,回显'[1,2,3]'| jq'sort | 。[length / 2]',您的代码给出的答案为“ null”,对于偶数列表,则给出回显“ [1,2,3,4]” | jq'sort | 。[length / 2]',您的代码选择了第三个元素“ 3”,但应给出答案2.5,即中间两个元素的平均值。

– Lucian Wischik
20年11月12日在18:14

@LucianWischik-好点!解决这个问题的方法可能更好,而不是评论流……而是jq'sort | 。[(长度/ 2)|地板]适用于奇数长度的清单吗?无法想到一种非常紧凑的方式来处理列表

–格林
20-11-14在13:46



#5 楼

使用awk可以很容易地获得最小,最大和平均值:

% echo -e '6\n2\n4\n3\n1' | awk 'NR == 1 { max=; min=; sum=0 }
   { if (>max) max=; if (<min) min=; sum+=;}
   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]=}
   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而不是管道排序,似乎可以正确排序整数和小数。.这是到我生成的版本的链接paste.ubuntu.com/612674 ...(和Kim的注释:我已经在awk上进行了一些实验几个小时了。使用个人兴趣的示例对我来说是更好的方法)...读者的一般注意事项:我仍然对看到其他方法感兴趣。越紧凑越好。我等一会儿...

– Peter.O
11年5月25日在11:06



#6 楼

pythonpy可以很好地用于这种情况:

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


#7 楼

还有一个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

#8 楼

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]};


评论


回声file.txt看起来不太正确,也许是猫

– Malat
2013年12月17日14:14



#9 楼

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

1:八度



GNU八度是一种级别的解释语言,主要用于数值计算。它提供了解决线性和非线性问题的数值方法以及执行其他数值实验的功能。

这里是一个快速的八度音阶示例。

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处理浮点数,此脚本使用numprocess软件包中的numaveragenum-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 


#10 楼

simple-r是答案:

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


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

#11 楼

我将第二次选择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驱动了脚本文件,该文件可以根据您的示例哈希值执行-bang或从bash脚本中调用。这些脚本可以处理命令行args(例如stackoverflow.com/questions/2045706/…),因此看起来不错... R表达式也可以通过-e在bash中使用...但我确实想知道R与bc的比较...

– Peter.O
2011年5月26日下午2:05

#12 楼

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

评论


在我看来,链接到要在用户计算机上执行的模糊Web代码的想法似乎不是一个好主意。包含代码的站点位于此处

–user79743
16年2月12日在6:29

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

–安东
16-2-12在6:58



@BinaryZeba:已更新

–救赎编码器
16 Mar 22 '16 at 6:43

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

–救赎编码器
16-3-22在6:45



#13 楼

下面的sort / awk可以串联:

sort -n | awk '{a[i++]=q4312078q;s+=q4312078q}END{print a[0],a[i-1],(a[int(i/2)]+a[int((i-1)/2)])/2,s/i}'


(如果计数为偶数,则将中位数作为两个中心值的平均值)

#14 楼

借鉴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}  ~ /^[-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 = 
    # 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;}
  }

   ~ /^[-0-9]*(\.[0-9]*)?$/  &&  NR!=1 {
    # setting min value
    if (c==0) {min = ;}
    # middle two values in array
    if (c==med1_loc) {med1_val = ;}
    if (c==med2_loc) {med2_val = ;}
    c++
    sum += 
    max = 
  }
  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说“恢复莫妮卡”
15-10-10在6:18



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

– G-Man说“恢复莫妮卡”
15-10-10在6:19



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

– G-Man说“恢复莫妮卡”
2015年10月10日在6:20



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

–拉胡尔·阿加瓦尔(Rahul Agarwal)
2015年10月10日15:40

好。我消除了第三只猫,并添加了解释。

– G-Man说“恢复莫妮卡”
2015年10月10日17:10

#15 楼

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


#16 楼

使用R型衬里:
R -q -e 'summary(as.numeric(read.table("your_single_col_file")[,1]))'

例如,对于我的文件,我得到这样的输出:
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
  550.4   628.3   733.1   706.5   778.4   832.9


#17 楼

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年9月9日在22:59

@ Peter.O固定。

–rav
2015年9月10日下午0:33

统计模块需要python版本> = 3.4

– Peter.O
2015年9月10日下午13:05

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

–rav
2015年9月10日下午16:17

为什么使用int转换数字。如果数字是浮点数怎么办?此外,您需要从每行中的数字中删除换行符。正确的命令是:cat data.log | python3 -c“将文件输入作为FI导入,将统计信息作为STAT; i = [对于FI.input()中l的float(l.strip()),float(l.strip())]; print('min:',min(i),'max:' ,max(i),“ avg:”,STAT.mean(i),“中位数:”,STAT.median(i))”

– jdhao
20年7月13日在8:11

#18 楼

如果您对实用程序更感兴趣而不是冷酷或聪明,那么perlawk更容易选择。基本上,它将以一致的方式出现在每个* nix上,并且可以轻松,免费地在Windows上安装。
我认为它也比awk的神秘性小,并且如果需要,您可以使用一些统计信息模块
我自己未经测试(实际上我知道它有错误,但可以满足我的需要)。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";


评论


您尚未显示中位数

– Peter.O
2012年3月28日14:31

#19 楼

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


评论


如果对上述代码如何回答该问题做出了解释,则此答案将很有用,例如,您应该说它正在使用Bash(而非sh)作为解释器。如何将数据从文件中读取到数组中也存在问题。

– Anthony Geoghegan
17-10-9在19:19



#20 楼

扩展nisetama的答案:
带有jq的内衬
jq -s '{ min:min, max:max, sum:add, count:length, avg: (add/length), median: (sort|.[(length/2|floor)])
示例:
echo 1 2 3 4 | jq -s '{ min:min, max:max, sum:add, count:length, avg: (add/length), median: (sort|.[(length/2|floor)]) }'
给你:
{
  "min": 1,
  "max": 5,
  "sum": 15,
  "count": 5,
  "avg": 3,
  "median": 3
}

注意:中位数不太正确当项目的数量是偶数但足够接近恕我直言时。