使用 awk 计算字母频率

编写一个 awk 脚本来确定一组单词中最(和最不)常见的字母。
63 位读者喜欢这篇文章。
Typewriter keys

原始照片由 mshipp 拍摄。由 Rikki Endsley 修改。 CC BY-SA 2.0。

我最近开始编写一个游戏,你在其中使用字母方块构建单词。 为了创建这个游戏,我需要知道英语中常用词汇的字母频率,这样我才能提供一套有用的字母方块。 字母频率在很多地方都有讨论,包括维基百科,但我希望自己计算字母频率。

Linux 在 /usr/share/dict/words 文件中提供了一个单词列表,所以我已经有了一个可能使用的单词列表。 words 文件包含了很多我想要的单词,但也有一些我不想要的。 我想要一个所有非复合词(没有连字符或空格)或专有名词(没有大写字母)的列表。 为了获得该列表,我可以运行 grep 命令,仅提取完全由小写字母组成的行

$ grep  '^[a-z]*$' /usr/share/dict/words

这个正则表达式要求 grep 匹配仅为小写字母的模式。 模式中的字符 ^$ 分别表示行的开头和结尾。 [a-z] 分组将仅匹配小写字母 az

这是一个输出的快速示例

$ grep  '^[a-z]*$' /usr/share/dict/words | head
a
aa
aaa
aah
aahed
aahing
aahs
aal
aalii
aaliis

是的,这些都是有效的单词。 例如,“aahed”是“aah”的过去式感叹词,表示放松。 而“aalii”是一种灌木状的热带灌木。

现在我只需要编写一个 gawk 脚本来完成计算每个单词中字母的工作,然后打印它找到的每个字母的相对频率。

计数字母

gawk 中计数字母的一种方法是迭代每个输入行中的每个字符,并计算每个字母 az 的出现次数。 substr 函数将从较大的字符串返回给定长度的子字符串,例如单个字母。 例如,此代码示例将评估输入中的每个字符 c

{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
    }
}

如果我从包含字母表的全局字符串 LETTERS 开始,我可以使用 index 函数来查找字母表中单个字母的位置。 我将扩展 gawk 代码示例,以仅评估输入中的字母 az

BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
 
{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
        ltr = index(LETTERS, c);
    }
}

请注意,index 函数返回字母在 LETTERS 字符串中首次出现的位置,第一个字母从 1 开始,如果未找到则返回零。 如果我有一个 26 个元素的数组,我可以使用该数组来计算每个字母的出现次数。 我将在我的代码示例中添加此内容,以(使用 ++)递增每个字母在输入中出现时的计数

BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
 
{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
        ltr = index(LETTERS, c);
 
        if (ltr > 0) {
            ++count[ltr];
        }
    }
}

打印相对频率

gawk 脚本计算完所有字母后,我想打印它找到的每个字母的频率。 我对输入中每个字母的总数不感兴趣,而是对每个字母的相对频率感兴趣。 相对频率缩放计数,以便将出现次数最少的字母(例如字母 q)设置为 1,而其他字母则相对于此。

我将从字母 a 的计数开始,然后将该值与每个其他字母 bz 的计数进行比较

END {
    min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
        if (count[ltr] < min) {
            min = count[ltr];
        }
    }
}

在该循环结束时,变量 min 包含任何字母的最小计数。 我可以使用它为计数提供比例,以打印每个字母的相对频率。 例如,如果出现次数最少的字母是 q,则 min 将等于 q 计数。

然后我循环遍历每个字母,并打印其相对频率。 我将每个计数除以 min 以打印相对频率,这意味着计数最低的字母将以相对频率 1 打印。 如果另一个字母出现的频率是最低计数的两倍,则该字母的相对频率将为 2。 我只对这里的整数值感兴趣,因此对于我的目的而言,2.1 和 2.9 与 2 相同

END {
    min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
        if (count[ltr] < min) {
            min = count[ltr];
        }
    }
 
    for (ltr = 1; ltr <= 26; ltr++) {
        print substr(LETTERS, ltr, 1), int(count[ltr] / min);
    }
}

整合在一起

现在我有一个 gawk 脚本,可以计算其输入中字母的相对频率

#!/usr/bin/gawk -f
 
# only count a-z, ignore A-Z and any other characters
 
BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
 
{
    len = length($0); for (i = 1; i <= len; i++) {
        c = substr($0, i, 1);
        ltr = index(LETTERS, c);
 
        if (ltr > 0) {
            ++count[ltr];
        }
    }
}
 
# print relative frequency of each letter
    
END {
    min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
        if (count[ltr] < min) {
            min = count[ltr];
        }
    }
 
    for (ltr = 1; ltr <= 26; ltr++) {
        print substr(LETTERS, ltr, 1), int(count[ltr] / min);
    }
}

我将其保存到名为 letter-freq.awk 的文件中,以便我可以更轻松地从命令行使用它。

如果您愿意,您也可以使用 chmod +x 使文件本身可执行。 第一行上的 #!/usr/bin/gawk -f 表示 Linux 将使用 /usr/bin/gawk 程序将其作为脚本运行。 并且由于 gawk 命令行使用 -f 来指示应将其用作脚本的文件,因此您需要悬挂的 -f,以便在 shell 中执行 letter-freq.awk 将被正确解释为运行 /usr/bin/gawk -f letter-freq.awk

我可以使用一些简单的输入来测试脚本。 例如,如果我将字母表输入到我的 gawk 脚本中,则每个字母的相对频率都应为 1

$ echo abcdefghijklmnopqrstuvwxyz | gawk -f letter-freq.awk
a 1
b 1
c 1
d 1
e 1
f 1
g 1
h 1
i 1
j 1
k 1
l 1
m 1
n 1
o 1
p 1
q 1
r 1
s 1
t 1
u 1
v 1
w 1
x 1
y 1
z 1

重复该示例,但添加一个额外的字母 e 实例,将打印字母 e,相对频率为 2,而其他每个字母的相对频率为 1

$ echo abcdeefghijklmnopqrstuvwxyz | gawk -f letter-freq.awk
a 1
b 1
c 1
d 1
e 2
f 1
g 1
h 1
i 1
j 1
k 1
l 1
m 1
n 1
o 1
p 1
q 1
r 1
s 1
t 1
u 1
v 1
w 1
x 1
y 1
z 1

现在我可以迈出重要一步了! 我将结合 grep 命令和 /usr/share/dict/words 文件,并识别完全用小写字母拼写的所有单词的字母频率

$ grep  '^[a-z]*$' /usr/share/dict/words | gawk -f letter-freq.awk
a 53
b 12
c 28
d 21
e 72
f 7
g 15
h 17
i 58
j 1
k 5
l 36
m 19
n 47
o 47
p 21
q 1
r 46
s 48
t 44
u 25
v 6
w 4
x 1
y 13
z 2

/usr/share/dict/words 文件中的所有小写单词中,字母 jqx 出现频率最低。 字母 z 也非常罕见。 毫不奇怪,字母 e 是最常用的。

接下来阅读什么

如何使用 Linux grep 命令

学习在文件中搜索信息的基础知识,然后下载我们的速查表,以快速参考 grep 和正则表达式指南。

(团队,红帽)
2021 年 3 月 18 日
标签
photo of Jim Hall
Jim Hall 是一位开源软件倡导者和开发人员,以 GNOME 中的可用性测试以及 FreeDOS 的创始人兼项目协调员而闻名。

评论已关闭。

© . All rights reserved.