如何找到 .csv 文件中最常见的单词,忽略每行上的重复项?

如何找到 .csv 文件中最常见的单词,忽略每行上的重复项?

我需要在 .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

不过请参考为什么使用 shell 循环处理文本被认为是不好的做法?

答案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

相关内容