たつをの ChangeLog : 2015-06-29

人生において何度も同じようなのを書いていますが、あらためて Perl によるコサイン類似度の計算。他で使っているコサイン類似度計算のコードの動作確認用。

コマンドラインオプション:
- "-c" : 出現頻度をそのまま使う。指定なしだと頻度は全て1。
- "-t" : TF-IDF での頻度補正を行う。

出力:
- 入力された各行ごとに計算した類似度。
- 先頭の二つの数字が入力行番号(1始まり)。

■コード (cossim.pl)
#!/usr/bin/env perl
use strict;
use warnings;
use List::Util qw(sum);
use Getopt::Long;

my $opt_count = 0; # 実際の頻度を使用
my $opt_tfidf = 0; # TF-IDF で頻度補正
GetOptions(
    "count" => \$opt_count,
    "tfidf" => \$opt_tfidf,
);

### 入力
my @ents;
while (<>) {
    chomp;
    next if /^\s*$/;
    next if /^\#/;
    push @ents, [split(/\s+/, $_)];
}

### 出現頻度カウント
my $N = @ents;
my @tf;
my %df;
for (my $i = 0; $i < $N; $i++) {
    if ($opt_count) {
        $tf[$i]{$_}++ for @{$ents[$i]};
    } else {
        $tf[$i]{$_} = 1 for @{$ents[$i]};
    }
    $df{$_}++ for keys %{$tf[$i]};
}
for (my $i = 0; $i < $N; $i++) {
    my @vals = values %{$tf[$i]};
    if ($opt_tfidf) {
        my $n = sum(@vals);
        $tf[$i]{$_} = $tf[$i]{$_}/$n * log($N / $df{$_}) for keys %{$tf[$i]};
    }
    my $len = sqrt(sum(map {$_**2} @vals)); # ベクトルの長さ
    $_ /= $len for values %{$tf[$i]}; # 長さで正規化
}

### 出力
for (my $i = 0; $i < $N; $i++) {
    for (my $j = $i + 1; $j < $N; $j++) {
        my $sim = sum(map {$tf[$i]{$_} * ($tf[$j]{$_}||0)} keys %{$tf[$i]});
        printf "%d %d %.8f\n", $i+1, $j+1, $sim;
    }
}

■実行例
% cat test-1.txt
hoge huga huga foo foo foo
hoge hoge hoge huga
% ./cossim.pl -c test-1.txt
1 2 0.42257713
(ref. [3])

% cat test-2.txt
リンゴ リンゴ バナナ
リンゴ バナナ ミカン
% ./cossim.pl test-2.txt
1 2 0.81649658
(ref. [4])

% cat test-3.txt
日本 今日 今日 今日 高校 高校 国語
日本 日本 明日 大学 数学
% ./cossim.pl -c test-3.txt
1 2 0.19518001
(ref. [5])

% cat cossim-test.txt
六本木 渋谷 恵比寿 目黒 目黒
六本木 渋谷 渋谷 恵比寿
六本木 六本木 渋谷 渋谷 目黒
% ./cossim.pl cossim-test.txt 
1 2 0.86602540
1 3 0.86602540
2 3 0.66666667
% ./cossim.pl -c cossim-test.txt
1 2 0.61721340
1 3 0.75592895
2 3 0.81649658
% ./cossim.pl -t cossim-test.txt
1 2 0.00395490
1 3 0.00395490
2 3 0.00000000
% ./cossim.pl -c -t cossim-test.txt
1 2 0.00126839
1 3 0.00165702
2 3 0.00000000

参考

[1] コサイン類似度
http://www.cse.kyoto-su.ac.jp/~g0846020/keywords/cosinSimilarity.html
[2] IIR C6
http://nlp.stanford.edu/IR-book/pdf/06vect.pdf
[3] perlでコサイン類似度を算出 (end0tknrのkipple - web写経開発)
http://d.hatena.ne.jp/end0tknr/20111021/1319162866
[4] Perlでコサイン類似度を計算する (work.log)
http://worklog.be/archives/3206
[5] コサイン尺度(コサイン類似度)の計算 (Ceekz Logs)
http://private.ceek.jp/archives/003891.html
この記事に言及しているこのブログ内の記事

タイトルの通りツイートIDをキーに Twitter API からツイート情報をゲットする Perl プログラム。Net::Twitter を使うだけ。

■コード (twitterapi_id2tweet.pl)
#!/usr/bin/env perl
use strict;
use warnings;
use JSON;
use Net::Twitter;
use utf8;
binmode STDOUT, ":utf8";
$| = 1;
my $nt = Net::Twitter->new(
    traits => [qw/API::RESTv1_1/],
    ssl => 1,
    consumer_key => 'XXXXXX',
    consumer_secret => 'XXXXXX',
    access_token => 'XXXXXX',
    access_token_secret => 'XXXXXX',
    );
while (<>) {
    chomp;
    next if not /^\d+$/;
    my $rs = eval { $nt->show_status($_) };
    print to_json($rs)."\n" if $rs;
    sleep 6;
};

■実行例
% cat test.txt
614651698812973056
614290454532657152
611666348645552128
611473704011169792
% ./twitterapi_id2tweet.pl test.txt | fold -60
{"retweeted":false,"source":"<a href=\"http://ifttt.com\" re
l=\"nofollow\">IFTTT</a>","favorited":false,"coordinates":nu
ll,"place":null,"retweet_count":0,"possibly_sensitive_appeal
able":false,"entities":{"media":[{"display_url":"pic.twitter
...

memo:

API へのアクセス数の時間当たり上限が決まっているので sleep 入れましょう。

下記のデータのテキスト部分の復元に使えます(が、すでに消えているツイートあり)。
- 場所参照表現タグ付きコーパス Ver 0.1 (2015/05/25)
http://www.cl.ecei.tohoku.ac.jp/~matsuda/LRE_corpus/

ツイートテキストを外部参照とするタグ付きコーパスを作って公開する際には、なるべく利用期間の長いユーザのツイートを選択するのが良いかもしれません。

早朝4時ごろだったか、寝てるとき足を釣りました。久しぶりでしたが、今回は極軽め。部位は左足のふくらはぎともも。ももは膝に近いところのみ。また寝て起きたら痛みは消えていました。

久しぶりに足つる

足つるのはこのブログの記録によれば約4年ぶり[2011-10-13-3]。とはいえ、たぶん記憶に残ってないのもいくつかあったかも知れません。記録しようとしても眠くて忘れちゃったりとか。なんにせよ起きたら痛みが消えてるレベルだったかと。

たつをの ChangeLog
Powered by chalow