在Linux中,有一个命令行实用程序可以打印数字统计信息。

84
我经常会遇到每行只有一个数字的文件,我最后会将其导入Excel中,以查看中位数、标准差等内容。

在Linux中是否有命令行实用程序可以做同样的事情?我通常需要找到平均值、中位数、最小值、最大值和标准差。


1
这可能是相关的:https://dev59.com/c3VC5IYBdhLWcg3wsTRi。 - Oliver Charlesworth
投票关闭,因为工具推荐。http://stats.stackexchange.com/questions/24934/command-line-tool-to-calculate-basic-statistics-for-stream-of-values || http://serverfault.com/questions/548322/tool-to-do-statistics-in-the-linux-command-line - Ciro Santilli OurBigBook.com
http://unix.stackexchange.com/questions/13731/is-there-a-way-to-get-the-min-max-median-and-average-of-a-list-of-numbers-in - Ciro Santilli OurBigBook.com
对这个问题感兴趣的人可能也会对 jp,一个用于绘制图表的 CLI 工具 感兴趣。 - Matt Parker
https://unix.stackexchange.com/a/202889/44236 - arun
18个回答

64

使用R处理这个文件非常简单。对于像这样的文件:

1
2
3
4
5
6
7
8
9
10
使用这个:
R -q -e "x <- read.csv('nums.txt', header = F); summary(x); sd(x[ , 1])"
为了得到这个:
       V1       
 Min.   : 1.00  
 1st Qu.: 3.25  
 Median : 5.50  
 Mean   : 5.50  
 3rd Qu.: 7.75  
 Max.   :10.00  
[1] 3.02765
  • -q标志会抑制R的启动许可证和帮助输出。
  • -e标志告诉R,您将从终端传递一个表达式。
  • x是一个data.frame - 基本上是一张表格。它是一种结构,适应多个向量/数据列,如果您只读取单个向量,则有点奇怪。这对您可以使用的函数产生影响。
  • 一些函数,例如summary(),自然地适应data.frames。如果x具有多个字段,则summary()将为每个字段提供以上描述性统计信息。
  • 但是,sd()一次只能采用一个向量,这就是为什么我要为该命令索引xx [,1]返回x的第一列)。您可以使用apply(x, MARGIN = 2, FUN = sd)获取所有列的SD。

谢谢。我自从开始使用R以来,认为它是理解数据的绝佳工具。 - MK.
4
为了在Ubuntu上安装R而不需要额外搜索,可以执行以下命令:sudo apt-get install r-base - E-rich
1
R 将来可能会更好用,但我猜它是一个巨大的库安装,所以我考虑安装下面的 st 表格。这与评论无关,但我的 brew install R 在 MacBook Pro Mid 2015 10.12.5 2.5GHz i7 16GB 上花了近一个小时,同时打开了 Chrome、Atom 和其他应用程序。大部分时间都用于使用 Xcode CLT 构建一些 gcc jit patch,但现在我很高兴地使用了这个答案的部分 :) - Pysis
这是一篇关于使用Bash工具进行数据整理的博客文章,可能会对发现这个问题的人感兴趣。文章链接:https://nickcanzoneri.com/bash/command-line/data/2018/03/20/data-analysis-first-aid.html - Matt Parker

49
使用 "st" (https://github.com/nferraz/st)。
$ st numbers.txt
N    min   max   sum   mean  stddev
10   1     10    55    5.5   3.02765

或:

$ st numbers.txt --transpose
N      10
min    1
max    10
sum    55
mean   5.5
stddev 3.02765

(免责声明:我是这个工具的作者 :))


1
任何有关新手安装的信息 - NeDark
3
如果您正在使用Homebrew,安装st非常简单,只需运行“brew install st”即可。 - Jason Axelson
3
请注意,“st”也可能指代“simple terminal”。 - Skippy le Grand Gourou

40

计算平均值、中位数和标准差,您可以使用awk。这通常比R的解决方案更快。例如,以下代码将打印出平均值:

awk '{a+=$1} END{print a/NR}' myfile

(NRawk 变量,表示记录数,$1 表示行的第一个(以空格分隔的)参数($0 表示整行,虽然在这里也可以使用,但原则上不太安全,尽管对于计算来说,它可能只需要第一个参数),END 表示在处理整个文件后执行以下命令(也可以在 BEGIN{a=0} 语句中初始化 a0)。

这是一个简单的 awk 脚本,提供更详细的统计信息(接受 CSV 文件作为输入,否则请更改 FS):

#!/usr/bin/awk -f

BEGIN {
    FS=",";
}
{
   a += $1;
   b[++i] = $1;
}
END {
    m = a/NR; # mean
    for (i in b)
    {
        d += (b[i]-m)^2;
        e += (b[i]-m)^3;
        f += (b[i]-m)^4;
    }
    va = d/NR; # variance
    sd = sqrt(va); # standard deviation
    sk = (e/NR)/sd^3; # skewness
    ku = (f/NR)/sd^4-3; # standardized kurtosis
    print "N,sum,mean,variance,std,SEM,skewness,kurtosis"
    print NR "," a "," m "," va "," sd "," sd/sqrt(NR) "," sk "," ku
}

将min/max添加到此脚本非常简单,但使用管道sorthead/tail同样容易:

sort -n myfile | head -n1
sort -n myfile | tail -n1

26

另一个可以用来计算统计数据和在ASCII模式下查看分布的工具是ministat。它是来自FreeBSD的一个工具,但也被打包到像Debian/Ubuntu这样的流行Linux发行版中。或者您可以从源代码中简单地下载并构建它 - 它只需要一个C编译器和C标准库。

使用示例:

$ cat test.log 
Handled 1000000 packets.Time elapsed: 7.575278
Handled 1000000 packets.Time elapsed: 7.569267
Handled 1000000 packets.Time elapsed: 7.540344
Handled 1000000 packets.Time elapsed: 7.547680
Handled 1000000 packets.Time elapsed: 7.692373
Handled 1000000 packets.Time elapsed: 7.390200
Handled 1000000 packets.Time elapsed: 7.391308
Handled 1000000 packets.Time elapsed: 7.388075

$ cat test.log| awk '{print $5}' | ministat -w 74
x <stdin>
+--------------------------------------------------------------------------+
| x                                                                        |
|xx                                   xx    x x                           x|
|   |__________________________A_______M_________________|                 |
+--------------------------------------------------------------------------+
    N           Min           Max        Median           Avg        Stddev
x   8      7.388075      7.692373       7.54768     7.5118156    0.11126122

20

是的,它叫做Perl
这里有一个简洁的一行代码:

perl -e 'use List::Util qw(max min sum); @a=();while(<>){$sqsum+=$_*$_; push(@a,$_)}; $n=@a;$s=sum(@a);$a=$s/@a;$m=max(@a);$mm=min(@a);$std=sqrt($sqsum/$n-($s/$n)*($s/$n));$mid=int @a/2;@srtd=sort @a;if(@a%2){$med=$srtd[$mid];}else{$med=($srtd[$mid-1]+$srtd[$mid])/2;};print "records:$n\nsum:$s\navg:$a\nstd:$std\nmed:$med\max:$m\min:$mm";'

例子

$ cat tt
1
3
4
5
6.5
7.
2
3
4

而这个命令

cat tt | perl -e 'use List::Util qw(max min sum); @a=();while(<>){$sqsum+=$_*$_; push(@a,$_)}; $n=@a;$s=sum(@a);$a=$s/@a;$m=max(@a);$mm=min(@a);$std=sqrt($sqsum/$n-($s/$n)*($s/$n));$mid=int @a/2;@srtd=sort @a;if(@a%2){$med=$srtd[$mid];}else{$med=($srtd[$mid-1]+$srtd[$mid])/2;};print "records:$n\nsum:$s\navg:$a\nstd:$std\nmed:$med\max:$m\min:$mm";'
records:9
sum:35.5
avg:3.94444444444444
std:1.86256162380447
med:4
max:7.
min:1

23
我相信那个方法有效,但是把所有内容都写在一行让我眼睛很难受。为什么不编写一个脚本来代替这种可怕的写法呢? - Oliver Charlesworth
20
我很确定“函数式语言”不等于“把所有东西都写在一行上尽可能简洁地表达”。 - Oliver Charlesworth
13
仅仅因为你可以在一行里做某件事情,并不意味着你应该这么做。 - Mike Monkiewicz
10
一句话简介绝不适合阅读。但是,它们非常适合复制粘贴到我的putty中,并从apache日志中获得一些数字统计数据……所以bua roxx。 - Vajk Hermecz
3
命令行(至少bash)也支持多行字符串。只需在文字串中使用换行符即可。 - Paŭlo Ebermann
显示剩余4条评论

18

均值:

awk '{sum += $1} END {print "mean = " sum/NR}' filename

中位数:

gawk -v max=128 '

    function median(c,v,    j) { 
       asort(v,j) 
       if (c % 2) return j[(c+1)/2]
       else return (j[c/2+1]+j[c/2])/2.0
    }

    { 
       count++
       values[count]=$1
       if (count >= max) { 
         print  median(count,values); count=0
       } 
    } 

    END { 
       print  "median = " median(count,values)
    }
    ' filename

模式:

awk '{c[$1]++} END {for (i in count) {if (c[i]>max) {max=i}} print "mode = " max}' filename

这种模式计算需要偶数个样本,但您可以看到它是如何工作的...

标准差:

awk '{sum+=$1; sumsq+=$1*$1} END {print "stdev = " sqrt(sumsq/NR - (sum/NR)**2)}' filename

2
干得好 - 而且使用的工具在每个Linux发行版上都有。 - rbellamy
1
@rbellamy - 谢谢!不仅仅是Linux - 我管理FreeBSD系统,其中awkgawk之间的区别很重要(因为在BSD上的Plain Old Awk不包括asort())。 - ghoti
太好了,我不需要安装任何额外的工具! - janechii

18

又一个工具:https://www.gnu.org/software/datamash/

# Example: calculate the sum and mean of values 1 to 10:
$ seq 10 | datamash sum 1 mean 1
55 5.5

可能更常见地打包(至少我在 nix 中找到的第一个预打包的工具)


9

如果需要的话,可以使用datastat这个简单的 Linux 程序来从命令行计算简单统计数据。例如:

cat file.dat | datastat

将输出file.dat文件每列的平均值。如果您需要知道标准差、最小值和最大值,可以分别添加 --dev--min--max 选项。

datastat 可以根据一个或多个“关键”列的值对行进行聚合。例如,

cat file.dat | datastat -k 1

对于第一列(“键”)找到的每个不同值,将所有其他列值的平均值作为在具有相同键值的所有行中聚合的结果产生。您可以使用更多列作为关键字段(例如,-k 1-3,-k 2,4等)。

它用C++编写,运行速度快,占用内存小,并且可以与其他工具(如cutgrepsedsortawk等)很好地配合使用。


对于“无用的cat使用”而言,这只是一种风格上的小问题。 - tripleee
好像缺少作者声明。 - Benjamin W.

8

我发现自己想在shell管道中执行此操作,并且花费了一些时间来获取R的所有正确参数。以下是我的解决方案:

seq 10 | R --slave -e 'x <- scan(file="stdin",quiet=TRUE); summary(x)'
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00    3.25    5.50    5.50    7.75   10.00

--slave选项"使R尽可能地安静运行...它隐含了--quiet和--no-save。"-e选项告诉R将以下字符串视为R代码。第一个语句从标准输入读取,并将读取的内容存储在名为"x"的变量中。scan函数的quiet=TRUE选项抑制了一行显示读取了多少个项目的输出。第二个语句将summary函数应用于x,产生输出。


8
你也可以考虑使用 clistats。它是一个高度可配置的命令行界面工具,用于计算输入数字流的统计信息。

I/O 选项

  • 输入数据可以来自文件、标准输入或管道
  • 输出可以写入文件、标准输出或管道
  • 输出使用以“#”开头的标题,以便将其通过管道传递给 gnuplot

解析选项

  • 基于信号、文件结束符或空白行进行检测以停止处理
  • 可以设置注释和分隔符字符
  • 可以从处理中过滤掉列
  • 可以基于数值约束从处理中过滤掉行
  • 可以基于字符串约束从处理中过滤掉行
  • 可以跳过初始标题行
  • 可以处理固定数量的行
  • 可以忽略重复的分隔符
  • 可以将行重构为列
  • 严格强制仅处理相同大小的行
  • 可以使用包含列标题的行来命名输出统计信息

统计选项

  • 汇总统计信息(计数、最小值、平均值、最大值、标准偏差)
  • 协方差
  • 相关性
  • 最小二乘偏移
  • 最小二乘斜率
  • 直方图
  • 过滤后的原始数据

注意:我是作者。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接