我有一个名为“文件”的文本文件。我需要使用 awk 找到最常见的两个单词序列,而“this is”和“is this”在我们的计数中被认为是相等的。
示例文本:我的名字是,这是我们的文本是这样
预期输出:是这个 3
我使用了这个循环:
awk 'BEGIN{
for(i=1;i<NF;i++)
a[$i OFS $(i+1)]++
}' file
任何人都知道为什么会失败?
答案1
您的代码由于从未读取文件而失败。特殊BEGIN
块被执行前甚至打开命令行上命名的第一个输入文件进行读取(以及END
最后一个之后的块)。您也没有代码的任何输出。
其他人已经给出了自己的解决方案,所以我将采用您的代码并稍微修改一下:
- 在普通块中运行代码以使其针对输入中的每一行执行。
- 使用
END
块查找数组中最常见的单词组合,并在处理完所有输入后进行输出。 - 确保每对中的单词始终按字典顺序使用。
awk '
{
for (i = 1; i < NF; i++)
if ($i < $(i+1)) a[$i OFS $(i+1)]++
else a[$(i+1) OFS $i]++
}
END {
for (words in a)
if (a[words] > a[m]) m = words
print m, a[m]
}' file
根据给定的输入,这将打印is this 3
。
另一种实现是在读取文件时跟踪最大值,然后在末尾打印它:
awk '
{
for (i = 1; i < NF; ++i) {
if ($i < $(i+1)) words = $i OFS $(i+1)
else words = $(i+1) OFS $i
if (++count[words] > count[m]) m = words
}
}
END { print m, count[m] }' file
不用担心数组:
awk '{ for (i = 1; i < NF; ++i) if ($i < $(i+1)) print $i, $(i+1); else print $(i+1), $i }' file | sort | uniq -c | sort -n | tail -n 1
也就是说,只需使用awk
生成单词组合(每行一个),然后对它们进行排序,获取每个出现的次数,排序这些(按计数),然后选出计数最多的那个。虽然在大数据上进行调用sort
会很昂贵,但是在电子邮件等小数据上运行,这就足够了。
答案2
尝试:
gawk '{
for (i=2; i<=NF; i++) {
delete arr; split($(i-1) "\n" $i, arr);
asort(arr); s[arr[1] FS arr[2]]++
};
}
END { for(x in s) print s[x], x }' infile |sort -nr
3 is this
2 my name
1 our text
1 is text
1 is our
1 is name
split()
函数将一对字符串(由空格(制表符/空格)分隔)添加到数组中,arr
但每个字符串都在单独的行中\n
。
asort()
函数对该arr
数组进行排序;然后我们将结果添加到另一个数组中,该数组s
以键作为一对已排序的字符串进行调用,并且如果再次出现,则为每个相同的字符串对增加值。
delete arr
删除数组arr
以供下一对字段的处理使用。
在END
我们循环数组s
元素并打印看到的每对元素的计数以及对本身;sort -nr
对结果进行排序并给出最重复的字符串对。
注意:在结果中,它不会告诉您是否有大多数is this
或this is
全部只有其中之一,这只会告诉您所有排序结果is this
作为本示例的结果。
答案3
正如 @kusalananda 指出的,你的主要错误是将所有代码放在 BEGIN 块中。因此,"this is" and "is this" are considered equal in our count.
您需要执行类似的操作来保证您遇到的每一对单词都以特定(排序)顺序存储,无论它们在输入中出现的相对顺序如何:
awk '
{
for(i=1; i<NF; i++) {
pair = ($i > $(i+1) ? $i OFS $(i+1) : $(i+1) OFS $i)
c = ++cnt[pair]
max = (max > c ? max : c)
}
}
END {
for (pair in cnt) {
c = cnt[pair]
if ( c == max ) {
print pair, c
}
}
}
' file
this is 3