gin*_*ino 15 text-processing sort uniq
我需要在 .csv 文件中找到 10 个最常用的词。该文件的结构使得每一行都包含逗号分隔的单词。如果同一个词在同一行中重复出现多次,则应计为一个。所以,在下面的例子中:
green,blue,blue,yellow,red,yellow
red,blue,green,green,green,brown
Run Code Online (Sandbox Code Playgroud)
绿色、蓝色和红色应计为 2,黄色和棕色应计为 1
我知道以前有人问过类似的问题,一种解决方案是:
<file.csv tr -c '[:alnum:]' '[\n*]' | sort|uniq -c|sort -nr|head -10
Run Code Online (Sandbox Code Playgroud)
但这将计算一个单词出现在同一行中的次数,如下所示:
4 green
3 blue
2 yellow
2 red
1 brown
Run Code Online (Sandbox Code Playgroud)
这实际上不是我需要的。有什么帮助吗?此外,我将感谢对该命令的简短解释,以及为什么我在类似问题中找到的命令不能满足我的需要。
Sté*_*las 16
使用 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
Run Code Online (Sandbox Code Playgroud)
ste*_*ver 11
我可能会使用 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
Run Code Online (Sandbox Code Playgroud)
如果除了sort和uniqcoreutils之外别无选择,那么您可以通过添加一个 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
Run Code Online (Sandbox Code Playgroud)
但是请参阅为什么使用 shell 循环来处理文本被认为是不好的做法?
您可以使用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
Run Code Online (Sandbox Code Playgroud)
输出
1 brown
2 red
1 yellow
2 blue
2 green
Run Code Online (Sandbox Code Playgroud)
演练
将字段分隔符设置为 ,
awk -F, '
Run Code Online (Sandbox Code Playgroud)
您将计算c一行中是否出现多个单词,因此请确保在每行开头的单词计数为空,然后使用delete c;orsplit("", c)遍历字段
{split("", c); for (i=1; i<=NF; i++)
Run Code Online (Sandbox Code Playgroud)
或者
{delete c; for (i=1; i<=NF; i++)
Run Code Online (Sandbox Code Playgroud)
如果你还没有$i在这一行看到这个词,!c[$i]那么增加那个词的计数器c[$i]++(增加到 1,这样条件测试失败,如果它再次出现在同一行),然后wds[$i]++ 当测试没有失败时增加那个词的总计数
if (!c[$i]){c[$i]++;wds[$i]++}}
Run Code Online (Sandbox Code Playgroud)
文件完成后,只需遍历wds数组并打印计数wds[wd]和单词wd
END{for (wd in wds) print wds[wd], wd}' file
Run Code Online (Sandbox Code Playgroud)
只是为了好玩
一个没有awk关联数组位的hacky
awk -F, '{for (i=1; i<=NF; i++) print NR, $i}' file |
sort | uniq | awk '{print $2}'| sort | uniq -c | sort -nr
Run Code Online (Sandbox Code Playgroud)
awk删除字段,以便它们前面是行号,然后sort | uniq丢失行重复,awk再次丢失编号,然后恢复到您的原始代码。
使用awk:
awk -F , '
{
delete seen
for (i = 1; i <= NF; ++i) seen[$i]++ || ++count[$i]
}
END {
for (word in count) printf "%d\t%s\n", count[word], word
}' file |
sort -rn | head
Run Code Online (Sandbox Code Playgroud)
如果在当前行中之前没有出现过,则第一个块中的循环会计算一个单词。END块中的循环输出计数和字数。
对于那些喜欢“one-liners”的人:
awk -F, -v OFS="\t" '{delete s;for(i=1;i<=NF;++i)s[$i]++||++c[$i]} END {for(w in c)print c[w],w}' file | sort -rn | head
Run Code Online (Sandbox Code Playgroud)
zshshell中的类似方法:
awk -F, -v OFS="\t" '{delete s;for(i=1;i<=NF;++i)s[$i]++||++c[$i]} END {for(w in c)print c[w],w}' file | sort -rn | head
Run Code Online (Sandbox Code Playgroud)
这会将每个以逗号分隔的行读入一个数组,words,该数组保持唯一(仅将每个单词的第一个副本添加到数组中)。
对于每行读取,通过增加关联数组中的相应条目来计算唯一字count。
阅读完所有单词后,输出累积的单词及其计数,并根据计数进行排序。使用 将输出截断为 10 行head。
该${(kv)count}膨胀将计算为从键和值的列表count关联数组。这些用于printf将值和键打印为换行符分隔的对。所使用的格式字符串printf第一个挑选出来的值,那么关键,但由于这些都从错误的顺序${(kv)count},我们使用的扩展2$和1$选择参数失灵。
有一个脚本可以执行 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
Run Code Online (Sandbox Code Playgroud)
它的工作原理是:
i为零并seen为每个新行清除数组i=split("",seen)。seen为每个字段生成数组++seen[$i]count[$i]++).END,for( word in count ),print count[word] , word。sort -rnhead。我们可以用稍微更神秘的一行代码来写:
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
Run Code Online (Sandbox Code Playgroud)