AWK语言第二版 3.3 数据分组

3.3 数据分组

我们来看看下面这个问题:数据集中有多少不同的值?我们前面已经用了那么多次的 sort 和 uniq -c 的组合,所以现在都可以准确快速地打出来了,不过它们用得太频繁,可能应该写成脚本来用。这里有些关于泰坦尼克数据的“不同值”问题,用泰坦尼克数据为例是因为它数据量较小。

有多少男性和女性乘客?

$ awk --csv '{g[$11]++}
     END {for (i in g) print i, g[i]}' passengers.csv
female 463
sex 1
male 850

看起来是对的——“sex”是表头,而剩下的是男性和女性,正如预期。可用非常相似的程序(只改个域编号)来检查乘客类别,生存状态和年龄。比如,检查年龄可以发现,1313个乘客中有258个没有给出年龄。

如果用下面的程序来统计不同年龄的数量:

$ awk --csv '{g[$5]++}
      END {for (i in g) print i, g[i]}' passengers.csv | sort -n

会得到类似这样的结果:

...
 1 4
1 4
 2 6
2 7
 3 6
3 2
...

大概一半的年龄域都包含一个垃圾的空格!如果不纠正的话,就会很容易影响后续的计算。

更一般地说,排序是用来发现数据异常的强有力技术,因为它将前缀相同(但后面不同)的文本片段都放在了一起。我们看看这个例子,比如想要统计敬语,如Mr 或 Colonel。通过打印名称域的第二个单词就能快速得到一个列表,马上就能找到最明显的这些敬语:

$ awk --csv '{split($4, name, " ")
   print name[2]}' passengers.csv | sort | uniq -c | sort -nr
 728 Mr
 229 Miss
 191 Mrs
  56 Master
  16 Ms
   7 Dr
   6 Rev
   ...
$

当然这个程序还会在末尾产生一长串错的非敬语,但这也提示了程序哪里可以改进;比如把标点去掉,则下面的这些差异也会被去掉:

6 Rev
1 Rev.
1 Mlle.
1 Mlle

这个实验还产生了一个 Colonel 和 一个 Col,大概指的是同一个军衔。

另外有趣的是,在Ms被现代人普遍使用之前,它就已经被用了超过50年,不过我们没法知道当时它所指示的社会状况或条件。

类似的,我们可以问问在啤酒数据集中,有多少酒厂,多少种啤酒风味,多少个评级人:

{ brewery[$2]++; style[$8]++; reviewer[$7]++ }
END { print length(brewery), "breweries," length(style), "styles,"
            length(reviewer), "reviewers" }

会得到:

5744 breweries, 105 styles, 33389 reviewers

函数 length 若用在数组上,则返回数组的元素个数。

代码稍微变动下,就能回答如“不同的啤酒风味受欢迎程度如何”的问题:

{ style[$8]++ }
END { for (i in style) print style[i], i }

会得到:(结合排序,并使用2.2节的只输出头尾的程序)

117586 American IPA
85977 American Double / Imperial IPA
63469 American Pale Ale (APA)
54129 Russian Imperial Stout
50705 American Double / Imperial Stout

...

686 Gose
609 Faro
466 Roggenbier
297 Kvass
241 Happoshu

如果你要做大量“选择一些域并计算其统计值”的这类操作,也许值得写一些短脚本,就像我们在第2章讨论的那样。一个脚本可以用来选择一个特定的域,而另一个脚本用来做排序和去重。

你可能感兴趣的:(AWK,linux,运维,服务器)