AWK语言第二版 3.2啤酒评级

3.2 啤酒评级

我们的下一个数据集是大约160万种啤酒的评级,数据来源于 ratebeer.com,一个啤酒爱好者的网站。数据集太大,通过逐行研究来确认它的特性是不可行的,所以我们要依赖像Awk这样的工具来探索并验证数据。

数据来自Kaggle,一个用于试验机器学习算法的网站。你可以通过链接https://www.kaggle.com/datasets/rdoume/beerreviews 找到原文件;我们感谢RateBeer,Kaggle 和数据集的创建者提供了这么一个有趣的数据集。

首先从一些基本的参数开始:文件有多大,看起来怎么样?做粗略统计的话,没有工具能比 wc 命令更好用:

$ time wc reviews.csv
 1586615 12171013 180174429 reviews.csv
real    0m0.629s
user    0m0.585s
sys     0m0.037s

不出所料,wc 很快,但如我们之前所说,可以很容易用 Awk 写一个与 wc 等效的程序:

$ time awk '{ nc += length($0) + 1; nw += NF }
END { print NR, nw, nc, FILENAME }' reviews.csv
1586615 12170527 179963813 reviews.csv
real    0m9.402s
user    0m9.159s
sys     0m0.125s

在这个特定的测试中Awk慢了一个数量级。在大多数使用场景下Awk是足够快的,不过也有其他程序更适合的时候。有点出人意料的是,Gawk快了5倍,只用了1.9秒。

然而还有更让人意外的:wc 和 Awk 统计出来的单词数和字符数不一样。我们晚点再来研究这个,不过预先说一下,wc 是计算字节的(因此隐含假定输入全是ASCII),而Awk是计算Unicode UTF-8字符的。例如对下面这条评级,两个程序就会得到不同的结果:

95,Löwenbräu AG,1257106630,4,4,3,atis,Munich Helles Lager,4,4,
       Löwenbräu Urtyp,5.4,33038

UTF-8是一种变长编码:ASCII字符占一个字节,而其他语言的字符占2到3个字节。有变音符号的字符在UTF-8中占两个字节。数据集里有些记录还包含亚洲字符,会占3个字节。在此情况下,wc 会比 Awk 多统计出一些字符数。

原始数据有13个属性,而我们这里只用其中的5个:酒厂名称,总体评价,啤酒风格,啤酒名称,酒精度数(酒精所占容量的百分比,简称ABV)。我们提取这五个属性来创建一个新文件,并通过设置输出域的分隔符OFS,把文件格式从CSV转换成TSV。得到如下这些行(太长的行在这里拆成两行来展示,行末有斜杠表示续行,实际文件中是一行)

Amstel Brouwerij B. V.  3.5 Light Lager Amstel Light   3.5
Bluegrass Brewing Co.   4   American Pale Ale (APA) American \
     Pale Ale   5.79
Hoppin' Frog Brewery    2.5 Winter Warmer   Frosted Frog \
     Christmas Ale  8.6

这么做之后文件从180M缩小到 113M,虽然还是很大,但更好处理了。

在上面的样本行中,我们看到酒精度ABV变化范围比较大,这让我们有个疑问:这些啤酒里面最烈的是什么,酒精度有多高?下面这个程序可以轻松解答:

NR > 1 && $5 > maxabv { maxabv = $5; brewery = $1; name = $4 }
END { print maxabv, brewery, name }

得到的结果是【注意:FS要设为\t】

57.7 Schorschbräu Schorschbräu Schorschbock 57%

这数值惊人地高,大概是平常啤酒度数的10倍了,所以表面上看这像是数据错误。但上网查一下,能确认数据没错。这就带来另一个问题:这么高的酒精度是奇异值吗,或者仅仅只是冰山一角?如果我们找下高酒精度,比如大于10%的啤酒:

$5 >= 10 { print $1, $4, $5 }

会得到超过195000条评级记录【实际194359条,没超过】,这暗示着高酒精度的啤酒更流行,至少在上RateBeer网站参与评级的人之中更流行。

当然这会带来更多问题,这回看看低酒精度的啤酒。比如酒精度低于【不高于】0.5%(这是美国——至少美国部分地区——法律定义的无酒精饮料)的啤酒怎么样?

$5 <= 0.5 { print $1, $4, $5 }

结果只有 68808 条评级,这说明低酒精度啤酒远没有那么受欢迎。

评级和酒精度的高低有什么关联吗?

$ awk -F'\t' '$5 >= 10 {rate += $2; nrate++}
    END {print rate/nrate, nrate}' rev.tsv
3.93702 194359

$ awk -F'\t' '$5 <= 0.5 {rate += $2; nrate++}
    END {print rate/nrate, nrate}' rev.tsv
3.61408 68808
$ awk -F'\t' '{rate += $2; nrate++}
    END {print rate/nrate, nrate}' rev.tsv
3.81558 1586615

高酒精度啤酒的平均评级,高于所有啤酒的平均评级,后者又高于低酒精度啤酒的评级。这可能有统计学意义,也可能没有。(不过和三位作者中至少一位的个人偏好是一致的。)

且慢!进一步检查发现,有67800条评级里面根本不带ABV,这个域是空的!我们再加上合适的检查,重跑这个低酒精度评级的计算:

$ awk -F'\t' '$5 != "" && $5 <= 0.5 {rate += $2; nrate++}
    END {print rate/nrate, nrate}' rev.tsv
2.58895 1023

看这个评分,即使不是啤酒爱好者也能猜到,无酒精的啤酒不那么受欢迎,评分也不高。

这些例子告诉我们,还是得仔细看看所有的数据。有多少域是空的,或者明确写出了无用值如 N/A?一个列里值的范围是多少?有哪些不同的值?应该在初始探索过程中回答这些问题,而投入点时间和精力,写一些简单的脚本将这个过程给自动化,会带来更多的回报。

你可能感兴趣的:(AWK,linux,运维,开发语言)