[[FrontPage]]

#contents

2011/07/26からのアクセス回数 &counter;

ここで紹介したSageワークシートは、以下のURLからダウンロードできます。

http://www15191ue.sakura.ne.jp:8000/home/pub/19/

また、Sageのサーバを公開しているサイト(http://www.sagenb.org/, http://www15191ue.sakura.ne.jp:8000/)にユーザIDを作成することで、ダウンロードしたワークシートを
アップロードし、実行したり、変更していろいろ動きを試すことができます。

* Sageでグラフを再現してみよう:集合知プログラミング第10章 [#t2b09870]

この企画は、雑誌や教科書にでているグラフをSageで再現し、
グラフの意味を理解すると共にSageの使い方をマスターすることを目的としています。

今回は、
[[集合知プログラミング>http://www.amazon.co.jp/dp/4873113644/]]
の第10章にできてきますニュースのクラスタリング図10.1「ニュースストーリーのクラスタリングのデンドログラム」
と因子分析したを取り上げます。

&ref(fig10_1.png);


*** あらかじめ読み込んでおくライブラリ [#sd5442a7]

3章の例に加えて、NMFの計算を行うnnmf.pyを読み込んでいます。 

また、split.sageは、日本語をプログラム内で含むため、split.pyではなくsplit.sageとして保存してあります。

sageへの入力:
#pre{{
# 必要なライブラリを読み込む
attach(DATA+'RUtil.py')
attach(DATA+'split.sage')
attach(DATA+'clusters.py')
attach(DATA + 'nnmf.py')
}}


*** 日本語フォントの指定 [#b5b0285b]

3章と同様にImageFontに日本語のTrueタイプフォントを指定することで
デンドログラムの文字化けを回避します。


sageへの入力:
#pre{{
# デンドログラムで日本語を表示するための修正
from PIL import Image,ImageDraw,ImageFont
font = ImageFont.truetype('/usr/local/share/Fonts/MS Mincho.ttf', 14)
}}


*** リスト、辞書に含まれる日本語の表示 [#rea794d7]

pythonの問題として、リストや辞書に含まれる日本語が表示できないという問題があります。

そこで、<a href="http://taichino.com/programming/1599">taichino.com永遠のネバーランドさんのページ</a>
で紹介されていた方法を使って日本語表示関数ppを使用しました。

sageへの入力:
#pre{{
# 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への入力:
#pre{{
spliter('私の名前は竹本です')
}}
sageの出力:
#pre{{
['\xe7\xa7\x81', '\xe5\x90\x8d\xe5\x89\x8d', '\xe7\xab\xb9\xe6\x9c\xac']
}}


pp関数を使うときれいに日本語の文字列が表示されます。

sageへの入力:
#pre{{
pp(spliter('私の名前は竹本です'))
}}
sageの出力:
#pre{{
[
    "私", 
    "名前", 
    "竹本"
]
}}


** フィードリスト [#h7227388]

10章では、ニュースと株価を扱っているため、日経新聞の情報を取得したいと思ったのですが、
日経新聞ではRSSの情報を配信していません。

そこで、
[[林檎(Mac)好きなぞうさんのメインサイト>http://www.zou3.net/php/index.php?page=rss&sub=nikkei]]
で提供されている日本経済新聞 RSSを利用させて頂きました。

この他にYahoo、共同通信、Google、ロイター、朝日新聞のRSSを追加し、ニュースのクラスタリングを
行いました。


sageへの入力:
#pre{{
# ニュースフィードとして、日経、Yahoo、共同通信、Google、ロイター、朝日新聞を使用
# 詳しくは以下のコメントを外してください。
# printFile('feedlist.txt')
}}



** 日本語関係でオリジナルから修正した部分 [#v1c5c469]

*** getwords [#c728a119]

getwordsは、htmlではなく、ニュースタイトルがそのまま渡されるので、
単にspliterを呼び出すように修正しました。


sageへの入力:
#pre{{
# 日本語対応のgetwordsのプロトタイプ版
def getwords(txt):
    return spliter(txt)
}}



*** getwordcounts [#r161f165]

getwordcountsでは、フィード情報のtitleのみを使用するように修正しました。

sageへの入力:
#pre{{
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
}}



** ニュースから単語の抽出 [#ge0ed725]

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():
      junk = apcount.setdefault(word,0)
      if count>1:
          apcount[word]+=1
  except:
    print 'Failed to parse feed %s' % feedurl
}}



sageへの入力:
#pre{{
# ニュースのタイトルなので、すべて単語登録する
wordlist=[]
for w,bc in apcount.items():
  frac=float(bc)/len(feedlist)
  wordlist.append(str(w))
}}



*** 単語の出現頻度マトリックスの作成 [#m79f966d]

3章と同様に出現頻度マトリックスの作成します。


sageへの入力:
#pre{{
def _word(word, wc):
    if word in wc:
        return wc[word]
    else:
        return 0
}}



sageへの入力:
#pre{{
# クラスタ分析用に収集したワードカウントを加工
names = [name for name,wc in wordcounts.items()]
words = wordlist
data = [[_word(word, wc) for word in wordlist] for blog,wc in wordcounts.items()]
}}



** ニュースのクラスタ分析 [#fbdca949]

単語の出現頻度マトリックスdataをhcluster関数に渡してブログのクラスタ分析をします。

その結果をdrawdegrogram関数に渡してデンドログラムを表示します(newsclust.pngにも保存します)。

3章のブログよりもはっきりとニュースがクラスタリングされているのが、見て取れます。


sageへの入力:
#pre{{
# ニュースのクラスタリング
clust=hcluster(data)
#printclust(clust,labels=names)
drawdendrogram(clust,names,png=DATA + 'newsclust.png')
showPNG('newsclust.png', fac=1)
}}

&ref(newsclust.png);

sageへの入力:
#pre{{
# 単語一覧や頻度マトリックスを表示するときにコメントを外して実行してください
# pp(wordlist)
# print data
}}



** NMFを使った因子分析 [#ff1ac0de]

クラスタリングの次は、NMFを使ってニュース毎の特徴ベクトルと単語毎の特徴ベクトルを
計算します。

sageへの入力:
#pre{{
# NMFを使って特徴を分解する
v = matrix(data)
weigths, feat = factorize(v, pc=10, iter=50)
}}
sageの出力:
#pre{{
679316.465853
4686.43669381
4124.64966115
4028.11051799
3999.71989198
}}


*** 特徴の出力 [#fc53882c]

おりじなるのshowfeaturesでは日本語が文字化けするため、
pp関数を使って表示するように修正しました。

また、上位6個の単語に対し、上位3位でかつ特徴の値がweightLimit(ここでは1.0をセット)
を超えるものだけを出力するようにしました。

sageへの入力:
#pre{{
# 出力する重みの最小値を定義
weightLimit = 1.0
}}



sageへの入力:
#pre{{
# 日本語表示ができるように修正
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
}}



*** showfeaturesの出力 [#k84af943]

2012年7月26日は、ロンドンオリンピックサッカー女子の予選が始まったので、
「サッカー」、「なでしこ」がスポーツ関連のニュースの重要なキーワードになっています。

sageへの入力:
#pre{{
topp, pn = showfeatures(weigths, feat, names, wordlist)
}}
sageの出力:
#pre{{
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, 
    "日本経済新聞 経済"
]
}}


*** ニュース毎の重要キーワードの表示 [#i6b27ace]

つぎに、showarticlesを使ってニュースフィード単位に、
重要キーワードを出力します。特徴ベクトルの値がweightLimit
以下のキーワードは表示されないようにしました。

sageへの入力:
#pre{{
# 日本語表示ができるように修正
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行空ける
}}



*** 結果の分析 [#x746f6eb]

2012年7月26日の日経新聞のトップは、
「野村、体制刷新で巻き返し図る 渡部CEO辞任へ 」の記事で、
次に
「東証大引け、5日ぶり反発 業績不安薄れ景気敏感株に買い」
の記事が話題になっていました。

以下の出力で、「日本経済新聞 すべて」をみると
["野村", "CEO", "6月", "~", "渡部", "2"]と
["株", "反発", "東証", "小幅", "時", "円"]が
きちんと抽出されています。

ニュースフィードのタイトルを抽出するだけの簡単な単語頻度マトリックスと
NMF因子分析を使うことによって各ニュースの重要な記事のポイントとなる
単語が抽出できることが分かりました。

sageへの入力:
#pre{{
showarticles(names, topp, pn)
}}
sageの出力:
#pre{{
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への入力:
#pre{{
# 3個以上のカウントのある語のリスト
[ s for s in flatten(data) if s > 2]
}}
sageの出力:
#pre{{
[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]
}}


** コメント [#k55fbee1]
#vote(おもしろかった,そうでもない,わかりずらい)
#vote(おもしろかった[1],そうでもない[0],わかりずらい[0])

皆様のご意見、ご希望をお待ちしております。
#comment_kcaptcha

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc