[[FrontPage]] #contents 2011/07/15からのアクセス回数 &counter; ここで紹介したSageワークシートは、以下のURLからダウンロードできます。 http://www15191ue.sakura.ne.jp:8000/home/pub/18/ また、Sageのサーバを公開しているサイト(http://www.sagenb.org/, http://www15191ue.sakura.ne.jp:8000/)にユーザIDを作成することで、ダウンロードしたワークシートを アップロードし、実行したり、変更していろいろ動きを試すことができます。 * Sageでグラフを再現してみよう:集合知プログラミング第3章 [#b17b6bb9] この企画は、雑誌や教科書にでているグラフをSageで再現し、 グラフの意味を理解すると共にSageの使い方をマスターすることを目的としています。 今回は、 [[集合知プログラミング>http://www.amazon.co.jp/dp/4873113644/]] の第3章にできてきます似たブログをグルーピイングした図3.3「ブログのクラスタのデンドログラム」 を取り上げます。 &ref(fig3_3.png); sageへの入力: #pre{{ attach(DATA+'RUtil.py') }} ** MeCabを使った単語切り分け [#w9b738ee] 以前chasenを使って同じグラフの表示を行った [[python/集合知3章]] では、chasenが英語の単語を扱えないため、トリッキーな処理を行いましたが、 MeCabではそのまま英語の単語が扱えるので、すっきりした形になります。 MeCabのインストール方法については、 [[MeCab]] を参照してください。 日本語のテキストから分かち書きの単語の内、partsに指定した形容詞、形容動詞、副詞、連体詞、名詞、動詞、未知語を 日本語の単語として抽出することにしました。また、minlenより小さな単語は取り除くようにしました。 pythonようのMeCabは、Taggerメソッドで生成し、parseメソッドで解析するというのが通常ですが、 ここでは単語の品詞と動詞の原形を使用したいので、parseToNodeメソッドでノードに分割した結果を 使って単語抽出を行いました。 sageへの入力: #pre{{ import sys import MeCab import re # splitの定義 def split(txt, minlen=2): m = MeCab.Tagger ("-Ochasen") n = m.parseToNode(txt) parts = ['^形容詞.*', '^形容動詞.*', '^副詞.*', '^連体詞.*', '^名詞.*', '^動詞.*', '^未知語.*'] ret = [] n = n.next while n.next != None: features = n.feature.split(',') if len(features) > 7: surface = features[6] else: surface = n.surface feature = features[0] n = n.next if len(surface) >= minlen: for part in parts: pat = re.compile(part) if pat.match(str(feature)): #print surface, feature ret.append(surface) return ret }} *** 単語区切りのテスト [#g65e54ea] まずは、簡単なテキストを入力して正しく単語が抽出できているか確認してみます。 splitの結果は、単語で区切られた文字列のリストを返すのですが、そのままだと \xnnの形式で表示されてしまうので、1単語ずつ表示してみました。 sageへの入力: #pre{{ # テスト用のコード txt = 'オリジナルのソースは、原書著者TobesのページからPCI_Code.zipと してダウンロードできます。' #print split(txt) for s in split(txt): print s }} #pre{{ オリジナル ソース 原書 著者 Tobes ページ PCI Code zip する ダウンロード できる }} ** Sageのpythonを日本語対応する方法 [#vb0ca2ed] Sageはそのままではunicodeから文字列への変換でエラーが発生します。 #pre{{ UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-10: ordinal not in range(128) }} これに対する対応としては、Sageのpythonのsite-packagesにsitecustomize.pyを以下のように 作成します。私の場合には、/usr/local/sage/local/lib/python/site-packages/に配置しました。 #pre{{ import sys sys.setdefaultencoding('utf-8') }} 配置が完了したら、sageを再起動してください。 ** 日本語対応のgetwords [#g5803f91] splitができたので、htmlから単語のリストを返す getwordsはHTMLからタグをのぞいてsplitを呼び出すだけの処理となります。 sageへの入力: #pre{{ # 日本語対応のgetwordsのプロトタイプ版 def getwords(html): ret = [] txt=re.compile(r'<[^>]+>').sub('',html) return split(txt) }} ** フィードリスト [#w53bc4ab] フィードリストは、ワークシートのdataに含まれていますので、 ワークシートの上部のDataプルダウンからfeedlist.txtを選択して 編集することができます。 Sageのワークシートは実行可能なドキュメントですので、 自分の興味のあるブログリストをfeedlistにセットしてその結果を表示することが できます。 sageへの入力: #pre{{ # 使用するフィードリスト # printFile("feedlist.txt") }} ** 日本語関係でオリジナルから修正した部分 [#b0f89877] *** getwordcounts [#r626e3cf] Sageのpythonのデフォルト言語がasciiであるため、そのままでは 日本語のテキストを処理しようとするとエラーになります。 getwords関数を呼び出している部分でstr関数を使ってunicodeを 文字列に変換しています。 sageへの入力: #pre{{ import feedparser # strを使ってunicodeから文字列に変換するように修正(Hiroshi TAKEMOTO) # Returns title and dictionary of word counts for an RSS feed def getwordcounts(url): # Parse the feed d=feedparser.parse(url) wc={} # Loop over all the entries for e in d.entries: if 'summary' in e: summary=e.summary else: summary=e.description # Extract a list of words words=getwords(str(e.title+' '+summary)) for word in words: wc.setdefault(word,0) wc[word]+=1 return str(d.feed.title),wc }} *** 日本語フォントの指定 [#dd02f7f0] PILライブラリは、そのままでは日本語を表示できませんが、 ImageFontに日本語のTrueタイプフォントを指定することで 文字化けなしに表示できるようになります。 また、日本語を表示する部分では、unicodeに変換してからtextメソッド に渡す必要があります。 #pre{{ utxt = unicode(labels[clust.id],'utf-8') draw.text((x+5,y-7),utxt,(0,0,0),font=font) }} sageへの入力: #pre{{ # デンドログラムで日本語を表示するためのトリック from PIL import Image,ImageDraw,ImageFont font = ImageFont.truetype('/usr/local/share/Fonts/MS Mincho.ttf', 14) }} ** ブログから単語の抽出 [#r0f71f05] 集合知3章の例題の通り、feedlist.txtに指定されたブログから 単語を抽出してみます。 sageへの入力: #pre{{ # feedlistにあるブログの単語を抽出し、出現一覧を作成する apcount={} wordcounts={} feedlist=[line for line in file(DATA+'feedlist.txt')] for feedurl in feedlist: try: title,wc=getwordcounts(feedurl) wordcounts[title]=wc for word,count in wc.items(): apcount.setdefault(word,0) if count>1: apcount[word]+=1 except: print 'Failed to parse feed %s' % feedurl wordlist=[] for w,bc in apcount.items(): frac=float(bc)/len(feedlist) if frac>0.1 and frac<0.5: wordlist.append(str(w)) }} *** 単語の出現頻度マトリックスの作成 [#ke6b00c1] wordlistにはすべてのブログに出現した単語のリストが入っているため、 各ブログの単語の出現頻度を計算する場合、ブログに指定単語が含まれる かをチェックする必要があります。_word関数を以下のように定義して クラスタ分析に渡す、出現頻度マトリックスの作成します。 sageへの入力: #pre{{ def _word(word, wc): if word in wc: return wc[word] else: return 0 }} sageへの入力: #pre{{ # クラスタ分析用に収集したワードカウントを加工 blognames = [blog for blog,wc in wordcounts.items()] words = wordlist data = [[_word(word, wc) for word in wordlist] for blog,wc in wordcounts.items()] }} ** ブログのクラスタ分析 [#y4198e16] 単語の出現頻度マトリックスdataをhcluster関数に渡してブログのクラスタ分析をします。 その結果をdrawdegrogram関数に渡してデンドログラムを表示します(blogclust.pngにも保存します)。 アメブロの芸能人のブログが固まっていたり、ちょっとお固そうなブログが集まっていたりとそれらしい分類が されているのが見て取れます。 sageへの入力: #pre{{ attach(DATA+'clusters.py') }} sageへの入力: #pre{{ # ブログのクラスタリング clust=hcluster(data) #printclust(clust,labels=blognames) drawdendrogram(clust,blognames,png=DATA + 'blogclust.png') showPNG('blogclust.png') }} &ref(out1.png); ** コメント [#m8116ae9] #vote(おもしろかった,そうでもない,わかりずらい) 皆様のご意見、ご希望をお待ちしております。 #comment_kcaptcha