<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>PCI / Drkcore</title><link>http://blog.kzfmix.com/PCI</link><description>Programming, Music, Snowboarding</description><language>ja</language><lastBuildDate>Tue, 12 Jan 2010 10:52:45 +0919</lastBuildDate><item><title>feedparserとMeCabでエントリから名詞を抜き出す</title><link>http://blog.kzfmix.com/entry/1215346785</link><description>&lt;p&gt;Programming Collective Intelligenceを読み始めた。&lt;/p&gt;
&lt;p&gt;&lt;p&gt;&lt;div class="awsxom"&gt;
    &lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0596529325/ref=nosim/kaerutyuuihou-22"&gt;
    &lt;img src="http://ecx.images-amazon.com/images/I/51A0mD55RaL._SL160_.jpg" align="left" hspace="5" border="0" alt="ProductName" class="image" /&gt;
    &lt;strong&gt;Programming Collective Intelligence: Building Smart Web 2.0 Applications&lt;/strong&gt;&lt;/a&gt;&lt;br /&gt;
    Toby Segaran&lt;br /&gt;
    Oreilly &amp; Associates Inc / 3446円 ( 2007-08 )&lt;br /&gt;
    &lt;br /&gt;
    &lt;br clear="all" /&gt;
    &lt;/div&gt;&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;英語だとスペースで分割すれば単語に分けられるのだけど、日本語は品詞分解できないと類似度を計れないしクラスタリングもできないので、まずMeCabを使えるようにしておく。&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="c"&gt;# -*- coding: utf-8 -*-&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nn"&gt;re&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nn"&gt;feedparser&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;MeCab&lt;/span&gt;

&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feedparser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;http://blog.kzfmix.com/rss/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r&amp;#39;&amp;lt;[^&amp;gt;]+&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summary_detail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MeCab&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tagger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parseToNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;名詞&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;surface&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;RuntimeError:&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</description><pubDate>Tue, 12 Jan 2010 10:52:45 +0919</pubDate><category>Python</category><category>MeCab</category><category>PCI</category></item><item><title>PCI 1章と2章</title><link>http://blog.kzfmix.com/entry/1215434090</link><description>&lt;p&gt;1章はcollective intelligenceの紹介。&lt;/p&gt;
&lt;p&gt;2章は類似性を評価するための距離の計りかた。ユークリッド距離とピアソン距離。あとエクササイズにタニモト距離。&lt;/p&gt;
&lt;p&gt;&lt;p&gt;&lt;div class="awsxom"&gt;
    &lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/0596529325/ref=nosim/kaerutyuuihou-22"&gt;
    &lt;img src="http://ecx.images-amazon.com/images/I/51A0mD55RaL._SL160_.jpg" align="left" hspace="5" border="0" alt="ProductName" class="image" /&gt;
    &lt;strong&gt;Programming Collective Intelligence: Building Smart Web 2.0 Applications&lt;/strong&gt;&lt;/a&gt;&lt;br /&gt;
    Toby Segaran&lt;br /&gt;
    Oreilly &amp; Associates Inc / 3446円 ( 2007-08 )&lt;br /&gt;
    &lt;br /&gt;
    &lt;br clear="all" /&gt;
    &lt;/div&gt;&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;無難に読み流す。&lt;/p&gt;</description><pubDate>Tue, 12 Jan 2010 10:52:25 +0919</pubDate><category>Python</category><category>PCI</category></item><item><title>LDRの購読RSSから単語セットを抽出して永続化</title><link>http://blog.kzfmix.com/entry/1215908791</link><description>&lt;p&gt;LDRで購読しているフィードから単語セットを抽出して遊びたい。
データセットは一度取っておけばいいので、永続化をしておく。入力はLDRから吐き出したOPMLファイル(export.xml)&lt;/p&gt;

&lt;p&gt;&lt;div class="awsxom"&gt;
&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873113644/ref=nosim/kaerutyuuihou-22"&gt;
&lt;img src="http://ecx.images-amazon.com/images/I/51FgSThMzVL._SL160_.jpg" align="left" hspace="5" border="0" alt="ProductName" class="image" /&gt;
&lt;strong&gt;集合知プログラミング&lt;/strong&gt;&lt;/a&gt;&lt;br /&gt;
Toby Segaran &lt;br /&gt;
オライリージャパン / ￥ 3,570 ()&lt;br /&gt;
在庫あり。&lt;br /&gt;
&lt;br clear="all" /&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys,re,feedparser,shelve,MeCab,sgmllib
from xml.dom.minidom import parse, parseString

opmlfile = "/Users/kzfm/export.xml"

def getwordcounts(i,url):
    print "#%d feedparser parse: %s" % (i,url)
    try:
        d = feedparser.parse(url)
    except UnicodeDecodeError,e:
        print "UnicodeDecodeError ", e
        return {}
    except sgmllib.SGMLParseError,e:
        print "sgmllib.SGMLParseError", e
        return {}

    txt = ''
    tage = re.compile(r'&amp;lt;[^&amp;gt;]+&amp;gt;');
    for entry in d.entries:
            if hasattr(entry,"summary_detail") : txt += tage.sub('',entry.summary_detail.value)

    try:
        t = MeCab.Tagger()
        m = t.parseToNode(txt.encode('utf-8'))
        wc = {}
        while m:
            if m.stat &amp;lt; 2:
                if re.match('名詞',m.feature): wc[m.surface] = wc.get(m.surface,0)+1
            m = m.next
        return  wc
    except RuntimeError, e:
        print "RuntimeError:", e;

def parse_opml(file):
    urls = []
    dom = parse(file)
    for outline in dom.getElementsByTagName("outline"):
        url = outline.getAttribute("xmlUrl")
        if url: urls.append(url)
    return urls;

if __name__ == "__main__":
    url_lists = parse_opml(opmlfile)
    tag_data = shelve.open("myldrwordsets")

    for i,url in enumerate(url_lists):
        tag_data[url] = getwordcounts(i,url)

    tag_data.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;1000件ぐらい取ってくるのに30分ぐらいかかったが、ダウンしているサイトをずっと待っているのはよくない。feedparserのタイムアウトの設定ってどうやんのかな。&lt;/li&gt;
&lt;li&gt;今回はshelveを使ってみたがpickleとの使い分けの基準がいまいちわからん&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;できたファイルのサイズは15Mくらいだった。これで類似度を計算したり、クラスタリングするためのデータは揃った。&lt;/p&gt;

&lt;h3&gt;追記 08.07.13&lt;/h3&gt;

&lt;p&gt;shelveだとなぜか辞書の復元がうまくいかなかったのでcPickleに変更した&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if __name__ == "__main__":
    url_lists = parse_opml(opmlfile)
    tag_data = {}

    for i,url in enumerate(url_lists):
        tag_data[url] = getwordcounts(i,url)

    f=file("myldrwordsets","wb")
    pickle.dump(tag_data,f)
    f.close()
&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Tue, 12 Jan 2010 10:51:53 +0919</pubDate><category>Python</category><category>PCI</category></item><item><title>単語の頻度マトリックスを作成</title><link>http://blog.kzfmix.com/entry/1215948646</link><description>&lt;p&gt;&lt;a href="http://blog.kzfmix.com/entry/1215908791"&gt;RSSから単語セットを抽出したら&lt;/a&gt;続いて、頻度のマトリックスを作成する。&lt;/p&gt;

&lt;p&gt;&lt;div class="awsxom"&gt;
&lt;a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873113644/ref=nosim/kaerutyuuihou-22"&gt;
&lt;img src="http://ecx.images-amazon.com/images/I/51FgSThMzVL._SL160_.jpg" align="left" hspace="5" border="0" alt="ProductName" class="image" /&gt;
&lt;strong&gt;集合知プログラミング&lt;/strong&gt;&lt;/a&gt;&lt;br /&gt;
Toby Segaran &lt;br /&gt;
オライリージャパン / ￥ 3,570 ()&lt;br /&gt;
在庫あり。&lt;br /&gt;
&lt;br clear="all" /&gt;
&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;流れとしては、全単語セットからある頻度以上の単語リストを作成（これが行のセット）し、それぞれのURL毎に何個含まれているかを調べてマトリックスにする。この時、あまりにもマイナーな単語とか、逆にあまりにもよく出る単語は類似性をはかるときに情報量ゼロにしかならないので除く。
今回1100件のフィードからなる（かなり多様性の高い）セットだったので、minの頻度はかなり低く設定した。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cPickle,sys

def get_tagset(tag_data,min=0.1,max=0.7):
    all_tags = []
    tag_counts = {}
    size = float(len(tag_data))

    for tags in tag_data.values():
        for tag in tags:
            tag_counts[tag] = tag_counts.get(tag,0)+1

    for (tag,count) in tag_counts.items():
        if min &amp;lt; count/size &amp;lt; max: all_tags.append(tag)
    return all_tags

if __name__ == "__main__":
    out=file('blogdata.txt','w')
    f=file('myldrwordsets','rb')
    tag_data = cPickle.load(f)
    wordlist = get_tagset(tag_data,min=0.1,max=0.5)
    totalwords = len(wordlist)
    out.write('Blog')
    for word in wordlist: out.write('\t%s' % word)
    out.write('\n')

    for url,wordcount in tag_data.items():
        out.write(url)
        count = 0
        txt = ""
        for word in wordlist:
            c = wordcount.get(word,0)
            txt += '\t%s' % c
            if (c != 0): count += 1
        if float(count)/totalwords &amp;gt; 0.000000001:
            out.write(txt + '\n')
            print "%s: %2.8f" % (url,float(count)/totalwords)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;実際に作成してみたデータを眺めた。というより、ケモインフォマティクス系はタニモト距離をよく使う関係上、密度(density)には非常に敏感。というわけで、url毎に非0の単語がどのくらいの割合含まれているかをチェック。やたらとビット密度の低いフィードは距離を算出しても仕方ないので除く事にした。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;        if float(count)/totalwords &amp;gt; 0.000000001:
            out.write(txt + '\n')
            print "%s: %2.8f" % (url,float(count)/totalwords)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;結局、915x300くらいのマトリックスが作成された。&lt;/p&gt;

&lt;p&gt;類似度はかるのに尺度はなにを使おうか、、、とりあえず本にある通りにピアソン使おうかな。それとも今回の場合は単語の出現頻度よりも、ある文書に単語リスト中の文字が含まれるかどうかのほうが重要そうだからタニモトのほうがよさそうな気もする。&lt;/p&gt;
</description><pubDate>Tue, 12 Jan 2010 10:51:35 +0919</pubDate><category>Python</category><category>PCI</category></item><item><title>pythonでタニモト係数を計算</title><link>http://blog.kzfmix.com/entry/1216042591</link><description>&lt;p&gt;pythonで01の文字列を10進数に変換するには&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;int('101',2) # 5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;とできるので、あとは、C=(A&amp;amp;B)としてC/(A+B-C)という式で1のビットを数えあげればタニモト係数は求まる。&lt;/p&gt;

&lt;p&gt;ビットの数え上げはシフト演算子を使ってやればよさげ。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; def cb(x):
...   count = 0
...   while(x!=0):
...     count += x &amp;amp; 1
...     x &amp;gt;&amp;gt;= 1
...   return count
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;こんな感じでOKと思うんだけど大きい数字を与えた場合に11LとかLがついてくるのが気持ち悪い。&lt;/p&gt;

&lt;p&gt;タニモト係数はさっきの関数を利用して&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; a = int('10111',2)
&amp;gt;&amp;gt;&amp;gt; b = int('11010',2)
&amp;gt;&amp;gt;&amp;gt; float(cb(a&amp;amp;b))/(cb(a)+cb(b)-cb(a&amp;amp;b))
0.40000000000000002
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;さくっと求まりそうだ。&lt;/p&gt;
</description><pubDate>Tue, 12 Jan 2010 10:51:20 +0919</pubDate><category>Python</category><category>PCI</category></item></channel></rss>