我需要在 .csv 文件中找到 10 个最常见的单词。该文件的结构使得每一行都包含以逗号分隔的单词。如果同一单词在同一行中重复多次,则应计为 1。所以,在下面的例子中:
green,blue,blue,yellow,red,yellow
red,blue,green,green,green,brown
绿色、蓝色和红色应计为 2,黄色和棕色应计为 1
我知道以前也有人问过类似的问题,一种解决方案是:
<file.csv tr -c '[:alnum:]' '[\n*]' | sort|uniq -c|sort -nr|head -10
但这将计算一个单词在同一行中出现的次数,如下所示:
4 green
3 blue
2 yellow
2 red
1 brown
这实际上不是我需要的。有什么帮助吗?另外,我将感谢对该命令的简短解释,以及为什么我在类似问题中发现的命令不能满足我的需要。
答案1
使用 GNUgrep
或兼容:
$ grep -nEo '\w+' file.csv|sort -u|cut -d: -f2-|sort|uniq -c|sort -k1rn|head
2 blue
2 green
2 red
1 brown
1 yellow
答案2
我可能会选择 perl
- 使用模块
uniq
中的List::Util
内容来删除每行的重复项。 - 使用哈希来计算结果的出现次数。
例如
perl -MList::Util=uniq -F, -lnE '
map { $h{$_}++ } uniq @F
}{
foreach $k (sort { $h{$b} <=> $h{$a} } keys %h) {say "$h{$k}: $k"}
' file.csv
2: red
2: green
2: blue
1: yellow
1: brown
sort
如果除了和coreutils之外别无选择uniq
,那么您可以通过添加 shell 循环来实现类似的算法
while IFS=, read -a words; do
printf '%s\n' "${words[@]}" | sort -u
done < file.csv | sort | uniq -c | sort -rn
2 red
2 green
2 blue
1 yellow
1 brown
答案3
您可以使用awk
关联数组和简单的逻辑检查。
awk -F, '
{split("", c); for (i=1; i<=NF; i++)
if (!c[$i]){c[$i]++;wds[$i]++}}
END{for (wd in wds) print wds[wd], wd}' file
输出
1 brown
2 red
1 yellow
2 blue
2 green
演练
将字段分隔符设置为,
awk -F, '
您将计数c
以查看一行中是否出现多个单词,因此请使用 or 确保每行开头的单词计数为空,delete c;
然后split("", c)
迭代字段
{split("", c); for (i=1; i<=NF; i++)
或者
{delete c; for (i=1; i<=NF; i++)
$i
如果您在这一行上还没有看到该单词!c[$i]
,则增加该单词的计数器c[$i]++
(增加到 1,因此如果该单词再次出现在同一行上,则条件测试会失败),然后wds[$i]++
在测试未失败时增加该单词的总体计数
if (!c[$i]){c[$i]++;wds[$i]++}}
文件完成后,只需迭代数组wds
并打印计数wds[wd]
和单词wd
END{for (wd in wds) print wds[wd], wd}' file
只是为了好玩
一种没有awk
关联数组位的黑客方法
awk -F, '{for (i=1; i<=NF; i++) print NR, $i}' file |
sort | uniq | awk '{print $2}'| sort | uniq -c | sort -nr
awk
出字段,以便它们前面带有行号,然后sort | uniq
丢失行重复,awk
再次丢失编号,然后恢复为原始代码。
答案4
有一个脚本可以完成 awk 中主要要求的操作:
awk -F, '
{
i = split( "" , seen ) ;
while( ++i <= NF ) if( ++seen[$i] == 1 ) count[$i]++;
}END{
for( word in count ) print count[word] , word
}' file | sort -rn | head
它的工作原理是:
- 对于输入文件中的每一行:
- 重新初始化
i
为零并清除seen
每个新行的数组i=split("",seen)
。 seen
为每个字段生成数组++seen[$i]
- 第一次(在这一行)看到 a 字段时,对其进行计数。 (
count[$i]++
)。 - 当所有的线都处理完之后
END
, - 对于已计算的每个单词
for( word in count )
, - 打印所有单词及其计数
print count[word] , word
。 - 最后,在 awk 生成输出后,对其进行数字排序
sort -rn
- 并选择前 10 行
head
。
我们可以用一种稍微神秘一点的单行来写:
awk -F, '{i=split("",a);while(++i<=NF)a[$i]++||c[$i]++}END{for(i in c)print c[i],i}' file|sort -rn|head