読者です 読者をやめる 読者になる 読者になる

argius note

プログラミング関連

開発しています



英単語の頻度を簡易集計する

Perl Ruby Unix-like

なんとなく思いついたので、やってみました。入力に使ったページはhttp://en.wikipedia.org/wiki/Multiprocessing,http://en.wikipedia.org/wiki/Blogです。マルチバイト文字には対応してません。

shコマンドだけでやってみる

出現回数が多い順に、上位10件を表示します。

$ cat *.htm? | sed "s/<[^>]*>//g" | sed "s/\W/\n/g" | tr [A-Z] [a-z] | \
> grep [a-z] | sort | uniq -c | sort -rn | head
    282 the
    201 of
    166 a
    153 to
    142 in
    139 and
     91 on
     90 blog
     71 blogs
     67 is

コマンドの意味:(cat)HTMLファイルを出力→(sed)タグを削除→(sed)英字以外を全て改行に置換→(tr)大文字を小文字に→(grep)英字のみ抽出→(sort)一意にするため辞書順に並べ替え→(uniq)一意にして出現数を付加→(sort)数値が大きい順に並べ替え→(head)上位10件を表示

Perlで除外リスト付

標準入力からはテキストデータを受け取り、引数からは除外リストを受け取って処理します。ソートと上位10件表示は面倒なのでコマンドにさせます。

use strict;
use warnings;

# 除外リスト生成
my %excludes = ();
for my $file (@ARGV) {
	-f $file or die "file '$file' does not exist";
	open FILE, $file;
	while (<FILE>) {
		s/[\r\n]//g;
		$excludes{$_} = 1;
	}
	close FILE;
}

# 標準入力を処理
my %words = ();
while (<STDIN>) {
	s/<[^>]+>//g;   # タグを削除
	tr [A-Z] [a-z]; # 大文字を小文字に
	my @a = grep { /^[a-z]+$/ } split /\W/;
	                # 英字以外で分割して英字だけ抽出
	for my $word (@a) {
		next if defined $excludes{$word};
		            # 除外リストにある場合はスキップ
		$words{$word} = 0 unless $words{$word};
		++$words{$word};
	}
}

# 結果を出力
while (my ($key, $value) = each %words) {
	printf "%5d %s\n", $value, $key;
}
$ cat *.htm? | perl word_count.pl list.txt | sort -rn | head
   90 blog
   71 blogs
   41 multiprocessing
   40 blogging
   38 retrieved
   32 bloggers
   31 blogger
   28 media
   24 multiple
   23 personal
$

list.txt には簡単な単語1000語程度が含まれています。

Rubyで(以下略)

配列から配列をマイナスってのが面白そうなので、パフォーマンス度外視して使ってみる。

# 除外リスト生成
excludes = []
ARGV.each do |file|
  open(file) do |f|
    f.each do |line|
      excludes << line.chomp.downcase
    end
  end
end

# 単語リスト生成
words = []
STDIN.each do |line|
  line.gsub!(/<[^>]+>/, "") # タグ削除
  words += line.downcase.split(/\W/).grep(/[a-z]/)
                            # 英字以外で分割して英字だけ抽出
end

words -= excludes # 単語リストから除外を取り除く

words.uniq.each do |word|
  printf "%5d ", words.grep(word).size
  puts word
end
$ cat *.htm? | ruby word_count.rb list.txt | sort -rn | head
   90 blog
   71 blogs
   41 multiprocessing
   40 blogging
   38 retrieved
   32 bloggers
   31 blogger
   28 media
   24 multiple
   23 personal
$

結果は同じだけど、段違いに遅い。Perl版に近い実装にすればマシになるかな。