zoukankan      html  css  js  c++  java
  • shell实现group by聚合操作统计

    在sql中,我们可以方便的使用group by及相应的聚合函数如sum avg count来实现分组统计需求,那当我们面对一个文本,在shell中也可以实现相应的功能吗?

    在shell中,我们主要用awk来实现类似的统计需求,如下我们用例子来解析说明。 

    数据准备

    [root ~]#cat tdata.txt
    apple 20 5.5
    pear 10 4.5
    apple 10 6.5

    我们以空格为分割符,创建一个水果类的售价及数量记录。第一列为水果名name,第二列为数量num,第三列为单价price。

    数据初探,输出各列的数据

    在进行awk操作之前,一般待处理的文本文件行数都比较多,列数也多,而当我们指定不同的分割符时,每个字段究竟输出在第几列有可能数起来也会有点麻烦,所以此时一般先拿第一行做下各字段对应值输出,方便后续的对指定列的处理

    awk的语法格式为:pattern { action },对输入数据一行行处理。只有满足只定的pattern表达式,后面的action才会进行处理,如默认的BEGIN END,还有用户自定义的各种表达式判断条件。

    当未指定分割符时,默认以空白符作为分割。NR NF是awk中的内置参数,NR代表当前文件处理到第几行,NF代表每行分割后的字段数。

    [root ~]#awk 'NR==1{for(i=1;i<=NF;i++){print "$"i,"=", $i}}' tdata.txt
    $1 = apple
    $2 = 20
    $3 = 5.5

    这样我就就知道了$1 $2到$NF对应的输出值,对我们后续要对指定哪列进行分析提供位置参考,当然我们也可以指定另外的分割符看下,-F参数用于指定分割符。

    [root ~]#awk -F'.' 'NR==1{for(i=1;i<=NF;i++){print "$"i,"=", $i}}' tdata.txt
    $1 = apple 20 5
    $2 = 5

    有了对数据的基本感知,我们就可以进一步来处理数据了。

    统计水果数量之和

    [root ~]#awk '{s+=$2}END{print s}' tdata.txt
    40

    awk中  END是在处理完所有行后才会进行的一个操作。在这里,当累加完所有行后,进行了求和输出。s未进行初始化赋值,初始默认为0

    此处相当于sql:  select sum(num) from tdata

    统计apple数量之和

    [root ~]#awk '$1=="apple"{s+=$2}END{print s}' tdata.txt
    30

    由上面讲解的pattern {action}处理模式,我们只需要在上一次中加入pattern表达式即可: $1=="apple"

    此处相当于sql: select sum(num) from tdata where name='apple'

    统计水果的种类

    [root ~]#awk '{s[$1]}END{for(i in s){print i}}' tdata.txt
    apple
    pear

    awk是支持数组的,它提供了更加强大的功能操作,如上为创建了一个叫s的数组,数组的索引值为第一列,因为我们这里只需要用到索引值,所以只写了s[$1],因为未赋值的变量都初始默认为0,所以这里相当于s[$1]=0,而在后面的for(i in s)的循环中,i为s的索引值,所以就遍历出了所有的水果种类

    此处相当于sql: select distinc name from tdata

    统计各种水果的总价格

    [root ~]#awk '{s[$1]+=$2*$3}END{for(i in s){print i,s[i]}}' tdata.txt
    apple 175
    pear 45

    每种分类的总价格等于数量*价格。所以只需要把第二字段和第三字段相乘累加即可

    此处想当于sql: select sum(num*price) from tdata group by name

    统计各种水果的平均值

    [root ~]#awk '{s[$1]+=$2*$3; t[$1]+=$2}END{for(i in s){print i,s[i]/t[i]}}' tdata.txt
    apple 5.83333
    pear 4.5

    由于需要计算均值,故除了上面提到的求总价的数组外,还需要一个数量来存储其总数量,以便做相除,这里我们再引入一个数组t来存储总数量。在最后输出时再做相除即可得出其平均值。

    此处相当于sql: select name,avg(num*price)/sum(num) from tdata group by name

    注意这里的输出小数点位数,输出并不美观,要控制awk的输出,需要用printf函数,如下我们可以保留两位小数点。

    [root ~]#awk '{s[$1]+=$2*$3; t[$1]+=$2}END{for(i in s){printf("%s %0.2f
    ", i, s[i]/t[i]) }}' tdata.txt
    apple 5.83
    pear 4.50

    awk中print与printf的一个区别是print输出默认会带换行,而printf得自己加上换行输出符。当print不符合你的输出格式要求时,就可以用printf。

  • 相关阅读:
    20201231《信息安全专业导论》第一周学习总结
    Python模拟进程状态
    Python gui
    2020-2021-1 20201208《信息安全专业导论》第10周学习总结
    2020-2021-1 20201208 《信息安全专业导论》第九周学习总结
    熟悉编程语言
    俄罗斯方块
    小学四则运算编程实践
    2020-2021-1-1 《信息安全专业导论》第八周学习总结
    2020-2021-1 20201208 《信息安全专业导论》第七周学习总结
  • 原文地址:https://www.cnblogs.com/zejin2008/p/13509318.html
Copyright © 2011-2022 走看看