AWK语言第二版 3.4 Unicode数据 3.5 基本图表 3.6 总结

3.4 Unicode数据

啤酒是不分国界的饮料,与之相称的是,啤酒的名字中也使用了很多非ASCII字符。下面的 Awk 程序 charfreq 统计了输入中每个不同的 Unicode 代码点(code point)的出现次数。(代码点通常是一个字符,但有些字符是由多个代码点组成的)

# charfreq - count frequency of characters in input

awk '
{ n = split($0, ch, "")
  for (i = 1; i <= n; i++)
    tab[ch[i]]++
}

END {
  for (i in tab)
    print i "\t" tab[i]
} ' $* | sort -k2 -nr

用空字符串作为域分隔符来拆分每行,把每个字符都存成数组 ch 的一个元素,然后用 tab 来计算这些字符数量;最后以倒序显示累加的数量。

这程序在啤酒数据(见3.2节)上跑得不快,在2015年的 MacBook Air上就花了250秒钟。下面这个版本快了两倍多,正好不超过105秒:

# charfreq2 - alternate version of charfreq

awk '
{ n = length($0)
  for (i = 1; i <= n; i++)
    tab[substr($0, i, 1)]++
}

END {
  for (i in tab)
    print i "\t" tab[i]
} ' $* | sort -k2 -nr

这个版本没有使用 split,而是用 substr 每次提取一个字符。函数 substr(s, m, n) 返回字符串 s 从位置 m 开始(m从1开始)且长度为 n 的子串,若 m 和 n 表示的范围超过了字符串的范围,则返回空字符串。如果不指定 n,则子串一直延续到 s 的末尾。完整的细节见参考手册 A.2.1节。

Gawk,即Awk的GNU版本,再次遥遥领先:第一个版本跑了72秒,第二个版本跑了42秒。

其他语言怎么样?为了比较,我们写了一个简单的Python版本的 charfreq:

# charfreq - count frequency of characters in input

freq = {}
with open('../beer/reviews.csv', encoding='utf-8') as f:
  for ch in f.read():
    if ch == '\n':
      continue
    if ch in freq:
      freq[ch] += 1
    else:
      freq[ch] = 1
for ch in freq:
  print(ch, freq[ch])

Python 版本花了45秒,和Gawk差不多,不过代价是还得写文件处理的代码。(本书作者不是Pythonistas,所以这个程序肯定是还能改进的)

除去每行末尾的换行符之外,啤酒评级文件中有 195 个不同的字符。出现最频繁的是空格,其次是可打印字符:

        10586176
,       19094985
e       12308925
r       8311408
4       7269630
a       7014111
5       6993858
...

还有不少来自欧洲语言的字符,比如德语的变音符号,以及一定量的日语和汉语字符:

ル        1
サ        1
ケ        1
ア        1

山        1

葉        1

黑        229

最后一个字符是黑(hei, black),出现在一种被简单命名为“Black”(对应汉字为 黑)的烈性 Imperial stout 酒名中。

Mikkeller ApS,2, American Double / Imperial Stout, Black (黑), 17.5

3.5 基本图表

可视化是探索性数据分析的重要组成部分,而幸运的是有不少非常不错的绘制库,使制作图表变得相当轻松。对 Python 来说更是如此,它有 Matplotlib 和 Seaborn 等包,不过在 Unix 和 macOS 上用 Gnuplot 进行快速绘图也不错。当然 Excel 和其他电子表格程序也能创建不错的图表。这里我们只会对绘制数据给出最少量的建议,读者们应该自己做实验(来找到自己喜欢的方式)。

在ABV和评分之间存在关联吗?打分人更偏爱高酒精度啤酒吗?可以用散点图来得到一个快速的印象,不过绘制150万个点有困难。我们用Awk取出 0.1%的样本(大概1500个点),并进行绘制:

$ awk -F'\t' 'NR%1000 == 500 {print $2, $5}' rev.tsv >temp
$ gnuplot
plot 'temp'
$

结果见下图 3-1。看来在评分和ABV之间最多存在弱关联。

图3-1

Tukey的可视化箱线图(boxplot)能显示数据集的中位数、四分位数和其他属性。箱线图有时称为箱须图,因为从箱体两段延伸出的两条“晶须”(whiskers)的范围,通常是下四分位数和上四分位数之间范围的1.5倍。晶须之外的点是异常值。

下面这个简短的Python程序生成前面所述样本数据的箱线图。 temp 文件是前面的程序生成的,每行都包含了评分和ABV,中间用空格隔开,且不带表头。

import matplotlib.pyplot as plt
import pandas as pd
df = pd.read_csv('temp', sep=' ', header=None)
plt.boxplot(df[0])
plt.show()

得到图3-2所示的箱线图,显示评分的中位数是4,而一半的评分介于3.5和4.5这两个四分位数之间。晶须最多延伸到四分位数间距的 1.5 倍,另外在 1.5 和 1.0 处存在异常值。

图 3-2

也许通过与大众市场的美国啤酒相比,可以看看任意特定的啤酒或酒厂的表现如何。

$ awk -F'\t' '/Budweiser/ { s += $2; n++ }
      END {print s/n, n }' rev.tsv
3.15159 3958

$ awk -F'\t' '/Coors/ { s += $2; n++ }
      END {print s/n, n }' rev.tsv
3.1044 9291

$ awk -F'\t' '/Hill Farmstead/ { s += $2; n++ }
      END {print s/n, n }' rev.tsv
4.29486 1555

这表明在大规模生产的啤酒与小规模生产的精酿啤酒之间,存在明显的评分差距。

3.6 总结

探索性数据分析的目的是为了在对结果做出假设之前,去了解数据,寻找模式和异常。如 John Tukey所说:

一些数据,结合上对答案的渴望,并不能确保从给定的数据体中提取出合理的答案。

宁可要近似正确,而不要完全错误。

正确问题的近似答案(通常是模糊的)比错误问题的确切答案(总是能做成精确的)要好得多。

作为探索性数据分析的核心工具,Awk是非常值得学习的,因为你能用它来快速地统计、汇总、查找。当然Awk不能做所有的事,但和其他工具,特别是电子表格和绘图库,结合起来,就能非常好地对数据集所包含的内容有一个快速的了解。

其中的很大一部分在于识别异常和怪异。正如很久以前贝尔实验室的一位同事所说:“三分之一的数据是坏的”。尽管他可能为了达到修辞效果而有所夸大,但我们已经看到了很多这样的例子:数据集中很大一部分真是古怪且不可信的。如果你构建了一套用于查看数据的工具和技术,就能更容易找到需要清理(至少是谨慎对待)的地方。

(第三章完)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值