2011/07/26からのアクセス回数 4449
ここで紹介したSageワークシートは、以下のURLからダウンロードできます。
http://www15191ue.sakura.ne.jp:8000/home/pub/19/
また、Sageのサーバを公開しているサイト(http://www.sagenb.org/, http://www15191ue.sakura.ne.jp:8000/)にユーザIDを作成することで、ダウンロードしたワークシートを アップロードし、実行したり、変更していろいろ動きを試すことができます。
この企画は、雑誌や教科書にでているグラフをSageで再現し、 グラフの意味を理解すると共にSageの使い方をマスターすることを目的としています。
今回は、 集合知プログラミング の第10章にできてきますニュースのクラスタリング図10.1「ニュースストーリーのクラスタリングのデンドログラム」 と因子分析したを取り上げます。
3章の例に加えて、NMFの計算を行うnnmf.pyを読み込んでいます。
また、split.sageは、日本語をプログラム内で含むため、split.pyではなくsplit.sageとして保存してあります。
sageへの入力:
# 必要なライブラリを読み込む attach(DATA+'RUtil.py') attach(DATA+'split.sage') attach(DATA+'clusters.py') attach(DATA + 'nnmf.py')
3章と同様にImageFontに日本語のTrueタイプフォントを指定することで デンドログラムの文字化けを回避します。
sageへの入力:
# デンドログラムで日本語を表示するための修正 from PIL import Image,ImageDraw,ImageFont font = ImageFont.truetype('/usr/local/share/Fonts/MS Mincho.ttf', 14)
pythonの問題として、リストや辞書に含まれる日本語が表示できないという問題があります。
そこで、<a href="http://taichino.com/programming/1599">taichino.com永遠のネバーランドさんのページ</a> で紹介されていた方法を使って日本語表示関数ppを使用しました。
sageへの入力:
# http://taichino.com/programming/1599 # から引用させて頂きました import json def pp(obj): if isinstance(obj, list) or isinstance(obj, dict): orig = json.dumps(obj, indent=4) print eval("u'''%s'''" % orig).encode('utf-8') else: print obj
何もしないで、分かち書きspliterの戻り値を表示すると、以下のように16進のコードで 表示されてしまいます。
sageへの入力:
spliter('私の名前は竹本です')
sageの出力:
['\xe7\xa7\x81', '\xe5\x90\x8d\xe5\x89\x8d', '\xe7\xab\xb9\xe6\x9c\xac']
pp関数を使うときれいに日本語の文字列が表示されます。
sageへの入力:
pp(spliter('私の名前は竹本です'))
sageの出力:
[ "私", "名前", "竹本" ]
10章では、ニュースと株価を扱っているため、日経新聞の情報を取得したいと思ったのですが、 日経新聞ではRSSの情報を配信していません。
そこで、 林檎(Mac)好きなぞうさんのメインサイト で提供されている日本経済新聞 RSSを利用させて頂きました。
この他にYahoo、共同通信、Google、ロイター、朝日新聞のRSSを追加し、ニュースのクラスタリングを 行いました。
sageへの入力:
# ニュースフィードとして、日経、Yahoo、共同通信、Google、ロイター、朝日新聞を使用 # 詳しくは以下のコメントを外してください。 # printFile('feedlist.txt')
getwordsは、htmlではなく、ニュースタイトルがそのまま渡されるので、 単にspliterを呼び出すように修正しました。
sageへの入力:
# 日本語対応のgetwordsのプロトタイプ版 def getwords(txt): return spliter(txt)
getwordcountsでは、フィード情報のtitleのみを使用するように修正しました。
sageへの入力:
import feedparser # フィード情報のtitleのみを使用するように修正(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: # Extract a list of words words=spliter(str(e.title)) for word in words: wc.setdefault(word,0) wc[word]+=1 return str(d.feed.title),wc
feedlist.txtに指定されたニュースから単語を抽出してみます。
sageへの入力:
# 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(): junk = apcount.setdefault(word,0) if count>1: apcount[word]+=1 except: print 'Failed to parse feed %s' % feedurl
sageへの入力:
# ニュースのタイトルなので、すべて単語登録する wordlist=[] for w,bc in apcount.items(): frac=float(bc)/len(feedlist) wordlist.append(str(w))
3章と同様に出現頻度マトリックスの作成します。
sageへの入力:
def _word(word, wc): if word in wc: return wc[word] else: return 0
sageへの入力:
# クラスタ分析用に収集したワードカウントを加工 names = [name for name,wc in wordcounts.items()] words = wordlist data = [[_word(word, wc) for word in wordlist] for blog,wc in wordcounts.items()]
単語の出現頻度マトリックスdataをhcluster関数に渡してブログのクラスタ分析をします。
その結果をdrawdegrogram関数に渡してデンドログラムを表示します(newsclust.pngにも保存します)。
3章のブログよりもはっきりとニュースがクラスタリングされているのが、見て取れます。
sageへの入力:
# ニュースのクラスタリング clust=hcluster(data) #printclust(clust,labels=names) drawdendrogram(clust,names,png=DATA + 'newsclust.png') showPNG('newsclust.png', fac=1)
sageへの入力:
# 単語一覧や頻度マトリックスを表示するときにコメントを外して実行してください # pp(wordlist) # print data
クラスタリングの次は、NMFを使ってニュース毎の特徴ベクトルと単語毎の特徴ベクトルを 計算します。
sageへの入力:
# NMFを使って特徴を分解する v = matrix(data) weigths, feat = factorize(v, pc=10, iter=50)
sageの出力:
679316.465853 4686.43669381 4124.64966115 4028.11051799 3999.71989198
おりじなるのshowfeaturesでは日本語が文字化けするため、 pp関数を使って表示するように修正しました。
また、上位6個の単語に対し、上位3位でかつ特徴の値がweightLimit(ここでは1.0をセット) を超えるものだけを出力するようにしました。
sageへの入力:
# 出力する重みの最小値を定義 weightLimit = 1.0
sageへの入力:
# 日本語表示ができるように修正 def showfeatures(w,h,titles,wordvec): pc,wc=shape(h) toppatterns=[[] for i in range(len(titles))] patternnames=[] # Loop over all the features for i in range(pc): slist=[] # Create a list of words and their weights for j in range(wc): slist.append((h[i,j],wordvec[j])) # Reverse sort the word list slist.sort() slist.reverse() # Print the first six elements n=[s[1] for s in slist[0:6]] pp(n) patternnames.append(n) # Create a list of articles for this feature flist=[] for j in range(len(titles)): # Add the article with its weight flist.append((w[j,i],str(titles[j]))) toppatterns[j].append((w[j,i],i,str(titles[j]))) # Reverse sort the list flist.sort() flist.reverse() # Show the top 3 articles for f in flist[0:3]: if f[0] > weightLimit: pp(list(f)) print '' # Return the pattern names for later use return toppatterns,patternnames
2012年7月26日は、ロンドンオリンピックサッカー女子の予選が始まったので、 「サッカー」、「なでしこ」がスポーツ関連のニュースの重要なキーワードになっています。
sageへの入力:
topp, pn = showfeatures(weigths, feat, names, wordlist)
sageの出力:
WARNING: Output truncated! [ "PR", "0", "光", "2", "1", "割" ] [ 13.281899554450645, "社会 - 朝日新聞デジタル" ] [ "場所", "名古屋", "大相撲", "日馬", "千秋楽", "全勝" ] [ 9.4390771861032388, "日本経済新聞 スポーツ:大相撲" ] [ 2.6506193809313454, "日本経済新聞 スポーツ:ゴルフ" ] [ "さん", "1", "戦", "PR", "勝", "2" ] [ 10.992519829496324, "文化 - 朝日新聞デジタル" ] [ "サッカー", "なでしこ", "1", "選手", "女子", "五輪" ] [ 6.1989779542505463, "オリンピック・パラリンピック特集 - 朝日新聞デジタル" ] [ 6.094523098990015, "サッカー日本代表ニュース - 朝日新聞デジタル" ] ... "人", "首相", "大統領", "氏" ] [ 10.165450647673419, "国際 - 朝日新聞デジタル" ] [ 7.1350801060878801, "政治 - 朝日新聞デジタル" ] [ 2.3958580737444528, "日本経済新聞 政治" ] [ "4", "6月", "~", "米", "%", "円" ] [ 11.884816950250961, "日本経済新聞 財務" ] [ 4.4697326638500945, "日本経済新聞 国際" ] [ 3.4524044024389289, "日本経済新聞 インターネット" ] [ "野村", "CEO", "6月", "~", "渡部", "2" ] [ 7.3402444150464516, "日本経済新聞 企業" ] [ 7.0019108669336187, "日本経済新聞 すべて" ] [ 6.6058144393328364, "日本経済新聞 経済" ]
つぎに、showarticlesを使ってニュースフィード単位に、 重要キーワードを出力します。特徴ベクトルの値がweightLimit 以下のキーワードは表示されないようにしました。
sageへの入力:
# 日本語表示ができるように修正 def showarticles(titles,toppatterns,patternnames): # Loop over all the titles for j in range(len(titles)): print titles[j].encode('utf8') + ":" # Get the top features for this article and # reverse sort them toppatterns[j].sort() toppatterns[j].reverse() # Print the top three patterns for i in range(3): # weightがweightLimitより大きなものだけ出力するように修正 if toppatterns[j][i][0] > weightLimit: print str(toppatterns[j][i][0]) # リストの文字列を表示するために、ppを使用 pp(patternnames[toppatterns[j][i][1]]) print # 1行空ける
2012年7月26日の日経新聞のトップは、 「野村、体制刷新で巻き返し図る 渡部CEO辞任へ 」の記事で、 次に 「東証大引け、5日ぶり反発 業績不安薄れ景気敏感株に買い」 の記事が話題になっていました。
以下の出力で、「日本経済新聞 すべて」をみると ["野村", "CEO", "6月", "~", "渡部", "2"]と ["株", "反発", "東証", "小幅", "時", "円"]が きちんと抽出されています。
ニュースフィードのタイトルを抽出するだけの簡単な単語頻度マトリックスと NMF因子分析を使うことによって各ニュースの重要な記事のポイントとなる 単語が抽出できることが分かりました。
sageへの入力:
showarticles(names, topp, pn)
sageの出力:
WARNING: Output truncated! 日本経済新聞 科学 日本経済新聞 国際 4.46973266385 [ "4", "6月", "~", "米", "%", "円" ] 1.56222208197 [ "野村", "CEO", "6月", "~", "渡部", "2" ] Top Stories - Google News 2.25385967974 [ "1", "0", "2", "日", "4", "5" ] 1.28723425534 [ "野村", "CEO", "6月", "~", "渡部", "2" ] Yahoo!ニュース・トピックス - 国内 日本経済新聞 スポーツ:サッカー 2.92494262279 [ "サッカー", "なでしこ", "1", "選手", "女子", "五輪" ] 1.81890385376 [ "1", "0", "2", ... "日", "4", "5" ] 日本経済新聞 政治 4.6277673698 [ "野村", "CEO", "6月", "~", "渡部", "2" ] 2.39585807374 [ "PR", "光", "人", "首相", "大統領", "氏" ] 日本経済新聞 すべて 7.00191086693 [ "野村", "CEO", "6月", "~", "渡部", "2" ] 2.38191543637 [ "株", "反発", "東証", "小幅", "時", "円" ] 日本経済新聞 スポーツ:大相撲 9.4390771861 [ "場所", "名古屋", "大相撲", "日馬", "千秋楽", "全勝" ] Yahoo!ニュース・トピックス - サイエンス Yahoo!ニュース・トピックス - エンターテインメント
sageへの入力:
# 3個以上のカウントのある語のリスト [ s for s in flatten(data) if s > 2]
sageの出力:
[3, 3, 3, 3, 3, 7, 3, 6, 5, 7, 6, 4, 3, 3, 4, 4, 4, 4, 3, 6, 3, 7, 4, 4, 5, 5, 3, 5, 4, 4, 3, 3, 3, 3, 4, 3, 3, 6, 3, 3, 3, 5, 3, 6, 3, 3, 6, 4, 4, 5, 3, 3, 4, 4, 6, 3, 3, 3, 3, 3, 3, 5, 7, 4, 5, 7, 5, 4, 3, 4, 4, 3, 9, 3, 3, 3, 8, 3, 7, 10, 5, 6, 3, 3, 3, 3, 12, 3, 3, 6, 3, 3, 3, 4, 5, 3, 3, 3, 3, 6, 6, 3, 6, 3, 4, 4, 3, 7, 3, 4, 5, 3, 3, 3, 7, 3, 3, 3, 3, 4, 4, 8, 4, 6, 3, 4, 5, 5, 5, 3, 6, 3, 3, 3, 3, 4, 13, 5, 6, 4, 3, 3, 5, 4, 5, 4, 3, 3, 3, 16, 18, 6, 4, 11, 5, 7, 4, 3, 7, 3, 3, 14, 5, 3, 3, 3, 3, 9, 3, 3, 3, 3, 3, 3, 5, 3, 5, 3, 5, 3, 4, 3, 5, 3, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 16, 9, 7, 6, 4, 5, 3, 8, 16, 18, 8, 5, 10, 3, 3, 5, 5, 3, 3, 3, 4, 9, 3, 3, 4, 4, 12, 11, 10, 3, 5, 5, 3, 4, 11, 4, 4, 3, 3, 4, 3, 6, 3, 3, 3, 3, 4, 3, 3, 4, 4, 3, 4, 4, 3, 3, 3, 5, 5, 5, 8, 3, 8, 3, 3, 3, 3, 3, 3, 7, 4, 4]
皆様のご意見、ご希望をお待ちしております。