Contents

命令获得数据集的最小,最大,中值和平均值

1. 概述

当我们在 Linux 中管理文件时,我们可能希望从数字列表或具有数字内容的文件中提取统计数据。

在本教程中,我们将专注于借助多种 Linux 工具获取此类数据集的最小值和最大值、中值和均值。

2. 创建我们的示例文件

在我们开始之前,让我们创建一个名为data.txt的文件,我们将使用它来测试我们的策略:

$ cat << eof > data.txt
10
32
4.23
4
223
-53
eof

3.使用awk

awk (GNU/AWK) 是一个值得我们关注的强大工具。它允许我们逐条记录地处理文本并创建报告。 awk的一个优点是它预装在许多 Linux 发行版中。

3.1. 仅使用awk

使用我们对想要实现的目标的了解,让我们创建一个awk脚本并将其保存在名为calculate.awk的文件中:

#!/usr/bin/env awk -f
{ 
    sum += $1
    nums[NR] = $1  # We store the input records
}
END {
    if (NR == 0) exit  #To avoid division by zero
 
    asort(nums)  #  Here, we sort the array that
                 #+ contains the stored input
 
    median = (NR % 2 == 0) ?  #  Let's be carefull with the
                              #+ short-if syntax
        ( nums[NR / 2] + nums[(NR / 2) + 1] ) / 2 \
        :
        nums[int(NR / 2) + 1]
 
    #  We used "(NR / 2) + 1", instead "NR/2 + 1", just for
    #+ the sake of clarity; to be more verbose
 
    mean = sum/NR
 
    #Let's beautify the output
    printf \
        "min = %s, max = %s, median = %s, mean = %s\n",\
        nums[1],\
        nums[NR],\
        median,\
        mean
}

让我们仔细看看代码。

NR变量包含到目前为止看到的输入记录总数的值。我们在END模式之后执行的代码中使用了NR,因为它在所有输入都用完时执行。因此,我们可以确认NR与我们数据集的基数具有相同的值。

在这段代码中,我们使用了这样一个事实,即如果数组nums是一个有限、封闭且有序的集合,那么它的下界和上界分别是最小和最大元素。

我们调用第一个和第n个元素是因为asort函数 对数组数据进行排序,并且该数据将从 1 索引到某个数字n(在我们的例子中n = NR)。

现在,有了awk脚本,让我们运行:

$ awk -f calculate.awk data.txt
min = -53, max = 223, median = 7.115, mean = 36.705

3.2. 结合awksort

在上一节创建的脚本中,我们使用了awkasort 函数,但我们可以不用它。

让我们创建一个名为 calculate2.awk的脚本,它与calculate.awk相同 ,但没有asort函数。现在,我们只需要在使用awk之前 借助sort命令 对元素进行排序:

$ sort -n data.txt | awk -f calculate2.awk
min = -53, max = 223, median = 7.115, mean = 36.705

sort命令中,选项*-n*代表数字排序。

3.3. 拥有我们需要的所有数据

如果我们拥有所需的所有数据怎么办?换句话说,如果我们知道数据集的大小,并且它也已排序(从低到高)怎么办?

让我们创建一个名为calculate3.awkawk脚本,以便进行简单的操作和逻辑决策——不需要排序,也不需要数组来存储值:

#!/usr/bin/env awk -f
BEGIN {
    if (size % 2 == 0) {
        median_position[0] = size/2
        median_position[1] = (size/2) + 1
    }
    else
        median_position[0] = int(size/2) + 1
}
NR == 1 { min = $1 }
NR == median_position[0] { a_median[0] = $1 }
NR == median_position[1] { a_median[1] = $1 }
{ sum += $1 }
END {
    if (NR == 0) exit
    median = (median_position[1]) ? \
        (a_median[0] + a_median[1]) / 2 \
        : \
        a_median[0]
    max = $1
    mean = sum/size
    printf \
        "min = %s, max = %s, median = %s, mean = %s\n",\
        min,\
        max,\
        median,\
        mean
}

让我们分解一下。由于数据已经从低到高排序,第一条记录将是最小值,最后一条记录将是最大值。 这就是为什么当 NR变量等于 1时我们使用min=$1 ,而 当 NR变量在END模式内时我们使用max=$1

最后,我们可以在管道中运行脚本:

$ size=$(wc -l < data.txt); sort -n data.txt | awk -v size=$size -f calculate3.awk
min = -53, max = 223, median = 7.115, mean = 36.705

让我们仔细看看管道:

  • awk -v size=$size中,我们通过知道大小(存储在 size变量中)是奇数还是偶数来传递计算中位数所需的信息
  • 在这里,在wc -l < data.txt中,我们得到文件data.txt的行数

在我们所有的示例中,我们可以看到awk的优势之一是我们可以在流程的每个阶段对文本流进行大量控制。

4. 使用datamash

*datamash *是一个非常强大和简单的工具,可以帮助我们进行命令行计算。

这个工具没有预装在 Linux 发行版中,所以我们需要在开始编写单行代码之前安装它。

如果我们使用基于 Debian 的发行版,我们可以使用:

$ sudo apt-get install -y datamash

或者,如果我们使用yum

$ sudo yum install datamash

对于其他发行版和安装选项,我们可以查阅下载页面 以获取更多说明。

幸运的是,datamash确实具有我们想要应用于数据集的所有操作:

$ datamash min 1 max 1 median 1 mean 1 < data.txt
-53     223     7.115   36.705

在这里,我们指出了每个操作后跟我们要应用它的列。