Tech Blog

Information Technology / Machine Learning / Data Analysis / Big Data / System Integration

Twitter4Jでtwitterのキーワード検索データを収集する

目的

Twitterコーパスを収集する。ここでは、キーワードでtweetを検索し、その結果をファイルに保存する。
f:id:tkdmah:20130420100320p:plain

手法

Twitter APIJavaラッパーであるTwitter4Jを用いる。OAuth認証を使ってアクセスする。

exampleを参考にして、以下のようなコードで収集できる。クエリが5秒に1回に制限されているため、sleepで時間を調整している。

/**
 * @author Masaaki TAKADA
 * @since Twitter4J 2.1.7
 */
public class TweetCollector {
  /**
   * Usage: java TweetCollector [path] [query...]
   * 
   * @param args
   */

  final static int QUERY_NUMBER = 100;
  final static int SLEEP_TIME = 6000;

  public static void main(String[] args) {
    if (args.length < 2) {
      System.out.println("java TweetCollector [path] [query...]");
      System.exit(-1);
    }
    for (int i = 1; i < args.length; i++) {
      try {
        File file = new File((args[0] + "/" + args[i]));
        PrintWriter pw = new PrintWriter(new BufferedWriter(
            new FileWriter(file)));
        Twitter twitter = new TwitterFactory().getInstance();
        Query query = new Query(args[i]);
        QueryResult result;
        for (int line = 0; line < QUERY_NUMBER; line++) {
          result = twitter.search(query);
          List<Status> tweets = result.getTweets();
          for (Status tweet : tweets) {
            System.out.println(tweet.getText());
            pw.println(tweet.getText());
          }
          if ((query = result.nextQuery()) == null) {
            break;
          }
          Thread.sleep(SLEEP_TIME);
        }
        pw.close();
      } catch (TwitterException e) {
        e.printStackTrace();
        System.out.println("Failed to search tweets: " + e.getMessage());
      } catch (IOException e) {
        e.printStackTrace();
        System.out.println("Failed to open filewriter:" + e.getMessage());
      } catch (InterruptedException e) {
        e.printStackTrace();
        System.out.println("Failed in thread sleep:" + e.getMessage());
      }
    }
  }
}

Mr.Childrenの歌詞分析(7): どの曲をどんなときに聴くべきか

目的

Mr.Childrenの楽曲は、2013年2月現在で200曲近くあります。恋愛関係の歌、社会問題をテーマにしたもの、己を奮い立たせる歌、失意の曲など、その内容は多種多様です。一方で、楽曲が多いだけに、すべての曲を把握することは難しく、今の気分に合った曲を探し出すのは至難の業です。そこで今回は、Mr.Childrenの楽曲をうまいこと自動分類して、今聞くべき曲をリスト化します。

感情の分類

私の経験を鑑みると、感情は下図のように遷移しているように思います。幸せ・安心といった好調なときが続くと、次第に将来の不安や焦りを感じ始めます。その不安が的中すると、どん底に落とされ、空虚や絶望感に苛まれます。落ちるところまで落ちると、やがて前を向くしかないことに気づいて、困難に立ち向かう勇気や未来への期待が芽生え始めます。そして、それらが達成されると、再び幸福な時間が訪れます。
f:id:tkdmah:20130203024641p:plain

この4分類は「現状の良し/悪し」「将来の展望の良し/悪し」の2×2のマトリクスで言い換えることができると思います。これは、yを充実度(?)として、y>0,y<0とdy/dt>0,dy/dt<0で分類しているとも言えます。また、これらの4分類における感情は、以下のようなキーワードで表すことができそうです。

1. positive = 現状良い×将来良い
キーワード:安心、親近感、快感、優越感、愛しさ
2. minus = 現状良い×将来悪い
キーワード:不安、焦燥、焦り、緊張、嫉妬、切なさ
3. negative = 現状悪い×将来悪い
キーワード:恐怖、不満、嫌悪、恥、罪悪感、劣等感、苦しみ、悲しみ、絶望、憎悪
4. plus = 現状悪い×将来良い
キーワード:憧憬、憧れ、尊敬、意欲、勇気、期待

f:id:tkdmah:20130224123256p:plain

以上の仮説を元に、私はMr.Childrenの楽曲をpositive, minus, negative, plusの4つに分類しました。

手法

それぞれの歌詞に対して、positive, minus, negative, plusのキーワードとの類似度を算出し、もっとも類似しているキーワード群を分類クラスとするようにしました。Rのsmdcパッケージを用いており、N-gram形態素解析、センチメントを組み合わせて分類しています。詳しくは以前の記事をご覧ください。

結果

1. 幸せいっぱいのときに聴きたい曲
(positive: 安心、親近感、快感、優越感、愛しさに類似)

過去と未来と交信する男 / SUNRISE / 僕が僕であるために / 風と星とメビウスの輪 / Everything is made from a dream / 安らげる場所 / my sweet heart / Sign / 蜃気楼 / シーソーゲーム ~勇敢な恋の歌~ / ロードムービー / BLUE / ゆりかごのある丘から / 言わせてみてぇもんだ / Distance / Tomorrow never knows / 雨のち晴れ / 隔たり / my confidence song / ハル / 靴ひも / fanfare / タイムマシーンに乗って / フェイク / ひびき / 君がいた夏 / ありふれた Love Story ~男女問題はいつも面倒だ~ 跳べ / Prism / 花の匂い / Monster / 血の管 / 優しい歌 / 友達のままで / CENTER OF UNIVERSE / 君の事以外は何も考えられない / ヨーイドン / 虜 / everybody goes ~秩序のない現代にドロップキック~ / 潜水

2. 不安に駆られたときに聴きたい曲
(minus: 不安、焦燥、焦り、緊張、嫉妬、切なさに類似)

常套句 / hypnosis / 旅人 / youthful days / 【es】~Theme of es~ / その向こうへ行こう / Hallelujah / HANABI / and I love you / HERO / Round About ~孤独の肖像~ / マーマレード・キッス / my life / 口笛 / CANDY / Surrender / I'm talking about Lovin' / 未来 / 終末のコンフィデンスソング / 東京 / 365日 / Wake me up! / 幸せのカテゴリー / 終わりなき旅 / PADDLE / Replay / ジェラシー / ロックンロールは生きている / こんな風にひどく蒸し暑い日 / LOVE はじめました / 君が好き / Drawing / 通り雨 / GIFT / ティーンエイジ・ドリーム(I~II) / 掌 / 十二月のセントラルパークブルース / Worlds end / イミテーションの木 / So Let's Get Truth / I'LL BE / innocent world / インマイタウン / 星になれたら / エソラ / 天頂バス / End of the day / Happy Song / #2601 / ポケット カスタネット / グッバイ・マイ・グルーミーデイズ / Door

3. 絶望に陥ったときに聴きたい曲
(negative: 恐怖、不満、嫌悪、恥、罪悪感、劣等感、苦しみ、悲しみ、絶望、憎悪に類似)

NOT FOUND / It's a wonderful world / Everything (It's you) / いつでも微笑みを / 友とコーヒーと嘘と胃袋 / やわらかい風 / UFO / Simple / All by myself / Dear wonderful world / CROSS ROAD / Over / ランニングハイ / ほころび / もっと / ニシエヒガシエ / DISCOVERY / 箒星 / 祈り~涙の軌道 / 旅立ちの唄 / かぞえうた / 逃亡者 / 空風の帰り道 / 花 -Memento-Mori- / 水上バス / Pink~奇妙な夢 / and I close to you / 手紙 / ALIVE / つよがり / ラララ / クラスメイト / Any / 渇いたkiss / ロード・アイ・ミス・ユー / 声 / Heavenly kiss / one two three / ためいきの日曜日 / さよならは夢の中へ / デルモ / 車の中でかくれてキスをしよう / Mirror / 光の射す方へ / 花言葉 / 独り言 / いつの日にか二人で / Brandnew my lover / CHILDREN'S WORLD / Another Mind / 風 ~The wind knows how I feel~ Dance Dance Dance / スロースターター / Mr.Shining Moon / 妄想満月 / I'm sorry

4. 困難に立ち向かうときに聴きたい曲
(plus: 憧憬、憧れ、尊敬、意欲、勇気、期待に類似)

羊、吠える / LOVE / 深海 / 夏が終わる ~夏の日のオマージュ~ / ファスナー / 名もなき詩 / ロザリータ / 蒼 / ラヴ コネクション / 彩り / ロックンロール / Bird Cage / Love is Blindness / Asia(エイジア) / Forever / マシンガンをぶっ放せ / 擬態 / タダダキアッテ / 口がすべって / 抱きしめたい / 僕らの音 / また会えるかな / くるみ / I / 少年 / あんまり覚えてないや / pieces / タガタメ / 1999年、夏、沖縄 / Prelude / アンダーシャツ / 蘇生 / HOWL / シーラカンス / PIANO MAN / 虹の彼方へ / Marshmallow day / 横断歩道を渡る人たち / 傘の下の君に告ぐ / しるし / Image / 思春期の夏 ~君との恋が今も牧場に~ / メインストリートに行こう / ボレロ / Another Story

太字はシングル曲を表し、類似度が高い順に降順に並んでいる。

Rで複数の文書をキーワードごとに分類する ~smdcパッケージの使い方~

目的

文書をキーワードごとに分類する。概略図を以下に示す。
f:id:tkdmah:20130223014956p:plain
文書ファイルとキーワードファイルの類似度を算出し、類似度に基づいて分類を行っている。
類似度の算出方法は以下のとおり。
N-gramによる類似度算出
形態素解析による類似度算出
③センチメント分析による類似度算出

手法

RのRMeCabとsmdcパッケージを用いる。
smdcパッケージをCRANにアップロードするまでの苦労話は以前の記事をご覧ください。

1. データの準備

分類対象ドキュメントと分類キーワード群を用意する。
それぞれを、以下のようなディレクトリ構成で保持する。

C:/LyricsWorkspace
|--Documents #分類対象ドキュメント
|  |--旅立ちの唄.txt
|  |--常套句.txt
|  |--シーソーゲーム.txt
|--KeyWords #分類キーワード
|  |--plus.txt
|  |--negative.txt
|  |--minus.txt
|  |--positive.txt
|--Dictionary #simDicを使ってセンチメント分析等を行う場合のみ必要
|  |--pn_ja.txt #例えば http://www.lr.pi.titech.ac.jp/~takamura/pubs/pn_ja.dic

2. 分類処理

Rで分類処理を行う。

# ライブラリの読込み
install.packages('smdc')
library(smdc)
library(RMeCab)

# ディレクトリとファイルを指定
dir1 <- "C:/LyricsWorkspace/KeyWords"
dir2 <- "C:/LyricsWorkspace/Documents"
dict <- "C:/LyricsWorkspace/Dictionary/pn_ja.txt"

# N-gramによる類似度算出
docMatrix1 <- docNgram(dir1, type=0, N=2)
docMatrix2 <- docNgram(dir2, type=0, N=2)
sim1 <- simDoc(docMatrix1,docMatrix2,norm=TRUE,recursive=TRUE)
# ver.0.0.2では sim1 <- simDoc(docMatrix1,docMatrix2,norm=TRUE)
simSum(sim1)

# 形態素解析による類似度算出
docMatrix3 <- docMatrix(dir1,weight="tf3*idf4*norm",pos=c("名詞"))
docMatrix4 <- docMatrix(dir2,weight="tf3*idf4*norm",pos=c("名詞"))
sim2 <- simDoc(docMatrix3,docMatrix4,norm=TRUE,recursive=TRUE)
# ver.0.0.2では sim2 <- simDoc(docMatrix3,docMatrix4,norm=TRUE)
simSum(sim2)

# センチメント分析による類似度算出
docMatrix5 <- docMatrix(dir1,weight="tf3*idf4*norm",pos=c("名詞","動詞","形容詞"))
docMatrix6 <- docMatrix(dir2,weight="tf3*idf4*norm",pos=c("名詞","動詞","形容詞"))
sentiDic <- read.table(dict,sep=":",as.is=TRUE) #因子化を抑制しておく
sentiDic <- sentiDic[order(sentiDic[,1]),] #ソートしておく
sim3 <- simDic(docMatrix5,docMatrix6,sentiDic[,c(1,4)],breaks=seq(-1,1,length=21),norm=TRUE)
simSum(sim3)

# 類似度の統合と結果表示
sim <- simSyn(list(sim1,sim2,sim3),c(0.2,0.3,0.5))
summary <- simSum(sim)
summary

結果

$minus.txt
 [1] "[Mr.Children]常套句.txt"                            "[Mr.Children]hypnosis.txt"                         
 [3] "[Mr.Children]旅人.txt"                              "[Mr.Children]youthful days.txt"                    
 [5] "[Mr.Children]【es】~Theme of es~.txt"             "[Mr.Children]その向こうへ行こう.txt"               
 [7] "[Mr.Children]Hallelujah.txt"                        "[Mr.Children]HANABI.txt"                           
 [9] "[Mr.Children]and I love you.txt"                    "[Mr.Children]HERO.txt"                             
[11] "[Mr.Children]Round About ~孤独の肖像~.txt"        "[Mr.Children]マーマレード・キッス.txt"             
[13] "[Mr.Children]my life.txt"                           "[Mr.Children]口笛.txt"                             
[15] "[Mr.Children]CANDY.txt"                             "[Mr.Children]Surrender.txt"                        
[17] "[Mr.Children]I'm talking about Lovin'.txt"          "[Mr.Children]未来.txt"                             
[19] "[Mr.Children]終末のコンフィデンスソング.txt"        "[Mr.Children]東京.txt"                             
[21] "[Mr.Children]365日.txt"                             "[Mr.Children]Wake me up!.txt"                      
[23] "[Mr.Children]幸せのカテゴリー.txt"                  "[Mr.Children]終わりなき旅.txt"                     
[25] "[Mr.Children]PADDLE.txt"                            "[Mr.Children]Replay.txt"                           
[27] "[Mr.Children]ジェラシー.txt"                        "[Mr.Children]ロックンロールは生きている.txt"       
[29] "[Mr.Children]こんな風にひどく蒸し暑い日.txt"        "[Mr.Children]LOVE はじめました.txt"                
[31] "[Mr.Children]君が好き.txt"                          "[Mr.Children]Drawing.txt"                          
[33] "[Mr.Children]通り雨.txt"                            "[Mr.Children]GIFT.txt"                             
[35] "[Mr.Children]ティーンエイジ・ドリーム(I~II).txt" "[Mr.Children]掌.txt"                               
[37] "[Mr.Children]十二月のセントラルパークブルース.txt"  "[Mr.Children]Worlds end.txt"                       
[39] "[Mr.Children]イミテーションの木.txt"                "[Mr.Children]So Let's Get Truth.txt"               
[41] "[Mr.Children]I'LL BE.txt"                           "[Mr.Children]innocent world.txt"                   
[43] "[Mr.Children]インマイタウン.txt"                    "[Mr.Children]星になれたら.txt"                     
[45] "[Mr.Children]エソラ.txt"                            "[Mr.Children]天頂バス.txt"                         
[47] "[Mr.Children]End of the day.txt"                    "[Mr.Children]Happy Song.txt"                       
[49] "[Mr.Children]#2601.txt"                             "[Mr.Children]ポケット カスタネット.txt"            
[51] "[Mr.Children]グッバイ・マイ・グルーミーデイズ.txt"  "[Mr.Children]Door.txt"                             

$negative.txt
 [1] "[Mr.Children]NOT FOUND.txt"                        "[Mr.Children]It's a wonderful world.txt"          
 [3] "[Mr.Children]Everything (It's you).txt"            "[Mr.Children]いつでも微笑みを.txt"                
 [5] "[Mr.Children]友とコーヒーと嘘と胃袋.txt"           "[Mr.Children]やわらかい風.txt"                    
 [7] "[Mr.Children]UFO.txt"                              "[Mr.Children]Simple.txt"                          
 [9] "[Mr.Children]All by myself.txt"                    "[Mr.Children]Dear wonderful world.txt"            
[11] "[Mr.Children]CROSS ROAD.txt"                       "[Mr.Children]Over.txt"                            
[13] "[Mr.Children]ランニングハイ.txt"                   "[Mr.Children]ほころび.txt"                        
[15] "[Mr.Children]もっと.txt"                           "[Mr.Children]ニシエヒガシエ.txt"                  
[17] "[Mr.Children]DISCOVERY.txt"                        "[Mr.Children]箒星.txt"                            
[19] "[Mr.Children]祈り~涙の軌道.txt"                   "[Mr.Children]旅立ちの唄.txt"                      
[21] "[Mr.Children]かぞえうた.txt"                       "[Mr.Children]逃亡者.txt"                          
[23] "[Mr.Children]空風の帰り道.txt"                     "[Mr.Children]花 -Memento-Mori-.txt"             
[25] "[Mr.Children]水上バス.txt"                         "[Mr.Children]Pink~奇妙な夢.txt"                  
[27] "[Mr.Children]and I close to you.txt"               "[Mr.Children]手紙.txt"                            
[29] "[Mr.Children]ALIVE.txt"                            "[Mr.Children]つよがり.txt"                        
[31] "[Mr.Children]ラララ.txt"                           "[Mr.Children]クラスメイト.txt"                    
[33] "[Mr.Children]Any.txt"                              "[Mr.Children]渇いたkiss.txt"                      
[35] "[Mr.Children]ロード・アイ・ミス・ユー.txt"         "[Mr.Children]声.txt"                              
[37] "[Mr.Children]Heavenly kiss.txt"                    "[Mr.Children]one two three.txt"                   
[39] "[Mr.Children]ためいきの日曜日.txt"                 "[Mr.Children]さよならは夢の中へ.txt"              
[41] "[Mr.Children]デルモ.txt"                           "[Mr.Children]車の中でかくれてキスをしよう.txt"    
[43] "[Mr.Children]Mirror.txt"                           "[Mr.Children]光の射す方へ.txt"                    
[45] "[Mr.Children]花言葉.txt"                           "[Mr.Children]独り言.txt"                          
[47] "[Mr.Children]いつの日にか二人で.txt"               "[Mr.Children]Brandnew my lover.txt"               
[49] "[Mr.Children]CHILDREN'S WORLD.txt"                 "[Mr.Children]Another Mind.txt"                    
[51] "[Mr.Children]風 ~The wind knows how I feel~.txt" "[Mr.Children]Dance Dance Dance.txt"               
[53] "[Mr.Children]スロースターター.txt"                 "[Mr.Children]Mr.Shining Moon.txt"                 
[55] "[Mr.Children]妄想満月.txt"                         "[Mr.Children]I'm sorry.txt"                       

$plus.txt
 [1] "[Mr.Children]羊、吠える.txt"                          "[Mr.Children]LOVE.txt"                               
 [3] "[Mr.Children]深海.txt"                                "[Mr.Children]夏が終わる ~夏の日のオマージュ~.txt"  
 [5] "[Mr.Children]ファスナー.txt"                          "[Mr.Children]名もなき詩.txt"                         
 [7] "[Mr.Children]ロザリータ.txt"                          "[Mr.Children]蒼.txt"                                 
 [9] "[Mr.Children]ラヴ コネクション.txt"                   "[Mr.Children]彩り.txt"                               
[11] "[Mr.Children]ロックンロール.txt"                      "[Mr.Children]Bird Cage.txt"                          
[13] "[Mr.Children]Love is Blindness.txt"                   "[Mr.Children]Asia(エイジア).txt"                     
[15] "[Mr.Children]Forever.txt"                             "[Mr.Children]マシンガンをぶっ放せ.txt"               
[17] "[Mr.Children]擬態.txt"                                "[Mr.Children]タダダキアッテ.txt"                     
[19] "[Mr.Children]口がすべって.txt"                        "[Mr.Children]抱きしめたい.txt"                       
[21] "[Mr.Children]僕らの音.txt"                            "[Mr.Children]また会えるかな.txt"                     
[23] "[Mr.Children]くるみ.txt"                              "[Mr.Children]I.txt"                                  
[25] "[Mr.Children]少年.txt"                                "[Mr.Children]あんまり覚えてないや.txt"               
[27] "[Mr.Children]pieces.txt"                              "[Mr.Children]タガタメ.txt"                           
[29] "[Mr.Children]1999年、夏、沖縄.txt"                    "[Mr.Children]Prelude.txt"                            
[31] "[Mr.Children]アンダーシャツ.txt"                      "[Mr.Children]蘇生.txt"                               
[33] "[Mr.Children]HOWL.txt"                                "[Mr.Children]シーラカンス.txt"                       
[35] "[Mr.Children]PIANO MAN.txt"                           "[Mr.Children]虹の彼方へ.txt"                         
[37] "[Mr.Children]Marshmallow day.txt"                     "[Mr.Children]横断歩道を渡る人たち.txt"               
[39] "[Mr.Children]傘の下の君に告ぐ.txt"                    "[Mr.Children]しるし.txt"                             
[41] "[Mr.Children]Image.txt"                               "[Mr.Children]思春期の夏 ~君との恋が今も牧場に~.txt"
[43] "[Mr.Children]メインストリートに行こう.txt"            "[Mr.Children]ボレロ.txt"                             
[45] "[Mr.Children]Another Story.txt"                      

$positive.txt
 [1] "[Mr.Children]過去と未来と交信する男.txt"                           
 [2] "[Mr.Children]SUNRISE.txt"                                          
 [3] "[Mr.Children]僕が僕であるために.txt"                               
 [4] "[Mr.Children]風と星とメビウスの輪.txt"                             
 [5] "[Mr.Children]Everything is made from a dream.txt"                  
 [6] "[Mr.Children]安らげる場所.txt"                                     
 [7] "[Mr.Children]my sweet heart.txt"                                   
 [8] "[Mr.Children]Sign.txt"                                             
 [9] "[Mr.Children]蜃気楼.txt"                                           
[10] "[Mr.Children]シーソーゲーム ~勇敢な恋の歌~.txt"                  
[11] "[Mr.Children]ロードムービー.txt"                                   
[12] "[Mr.Children]BLUE.txt"                                             
[13] "[Mr.Children]ゆりかごのある丘から.txt"                             
[14] "[Mr.Children]言わせてみてぇもんだ.txt"                             
[15] "[Mr.Children]Distance.txt"                                         
[16] "[Mr.Children]Tomorrow never knows.txt"                             
[17] "[Mr.Children]雨のち晴れ.txt"                                       
[18] "[Mr.Children]隔たり.txt"                                           
[19] "[Mr.Children]my confidence song.txt"                               
[20] "[Mr.Children]ハル.txt"                                             
[21] "[Mr.Children]靴ひも.txt"                                           
[22] "[Mr.Children]fanfare.txt"                                          
[23] "[Mr.Children]タイムマシーンに乗って.txt"                           
[24] "[Mr.Children]フェイク.txt"                                         
[25] "[Mr.Children]ひびき.txt"                                           
[26] "[Mr.Children]君がいた夏.txt"                                       
[27] "[Mr.Children]ありふれた Love Story ~男女問題はいつも面倒だ~.txt" 
[28] "[Mr.Children]跳べ.txt"                                             
[29] "[Mr.Children]Prism.txt"                                            
[30] "[Mr.Children]花の匂い.txt"                                         
[31] "[Mr.Children]Monster.txt"                                          
[32] "[Mr.Children]血の管.txt"                                           
[33] "[Mr.Children]優しい歌.txt"                                         
[34] "[Mr.Children]友達のままで.txt"                                     
[35] "[Mr.Children]CENTER OF UNIVERSE.txt"                               
[36] "[Mr.Children]君の事以外は何も考えられない.txt"                     
[37] "[Mr.Children]ヨーイドン.txt"                                       
[38] "[Mr.Children]虜.txt"                                               
[39] "[Mr.Children]everybody goes ~秩序のない現代にドロップキック~.txt"
[40] "[Mr.Children]潜水.txt" 

Rパッケージを作ってCRANで公開する

目的

せっかくRで汎用的な関数を作ったので、他の人にも使ってもらいたい。CRAN(The Comprehensive R Archive Network)には、2013/2/16現在4338個ものRパッケージが公開されている。CRANにアップロードすると、 install.package('パッケージ名') で気軽にインストールできるので便利だ。そこで、パッケージを作成してCRANにアップロードする。

ちなみに、ドキュメント間の類似度を算出する関数をパッケージ化した。作成したパッケージは、以下から閲覧・ダウンロードすることができる。
http://cran.r-project.org/web/packages/smdc/index.html

f:id:tkdmah:20130216133754p:plain

方法

1. スケルトンの生成

Rのコマンドラインから、パッケージのスケルトンを生成する。

> # 関数が定義されていることを確認する。定義されていなければ、source("xxx.R")などで読み込む。
> ls()
 [1] "conv2Freq"  "normalize"  "simDic"  "simDoc"  "simSum"  "simSyn"  "uniform"
> # スケルトンを生成する。
> package.skeleton(list=c("simDoc","simDic","simSyn","simSum","uniform","normalize","conv2Freq"),name="smdc")
Creating directories ...
Creating DESCRIPTION ...
Creating NAMESPACE ...
Creating Read-and-delete-me ...
Saving functions and data ...
Making help files ...
Done.
Further steps are described in './smdc/Read-and-delete-me'.

生成されるスケルトンのディレクトリ構造

smdc/
|--DESCRIPTION
|--NAMESPACE
|--Read-and-delete-me
|--man/
|  |--conv2Freq.Rd
|  |  …
|--R/
|  |--conv2Freq.R
|  |  …

2. ドキュメントの修正

生成されたパッケージ内のドキュメントを修正する。ドキュメントを日本語で書くとCRANのチェックテストが通らないので注意。

  • DESCRIPTIONファイルを編集する。

Package: smdc
Type: Package
Title: Document Similarity
Version: 0.0.2
Date: 2013-02-16
Author: Masaaki TAKADA
Maintainer: Masaaki TAKADA
Description: This package provides similarity among documents.
License: BSD
Depends: proxy,tm

  • NAMESPACEで外部に公開するAPIを定義する。

exportPattern(simDoc,simDic,simSum,simSyn)

  • manフォルダ内のすべての.Rdファイルを編集する。\title{}は必須項目。不要な(書くのが面倒な)項目は削除。

\name{smdc-package}
\alias{smdc-package}
\alias{smdc}
\docType{package}
\title{
Document Similarity
}
\description{
This package provide functions that calculate similarity among documents.
}
\details{
\tabular{ll}{
Package: \tab smdc\cr
Type: \tab Package\cr
Version: \tab 0.0.2\cr
Date: \tab 2013-02-16\cr
License: \tab BSD\cr
}
}
\author{
Masaaki TAKADA

Maintainer: Masaaki TAKADA
}
\keyword{ package }
\examples{

# Load text mining package 'tm' for English.
# 'RMeCab' is available for Japanese.
# install.packages('tm')
library('tm')

# Read corpus data.
crudeDir <- system.file("texts", "crude", package = "tm")
crude <- Corpus(DirSource(crudeDir))
docMatrix1 <- t(as.matrix(DocumentTermMatrix(crude)))
acqDir <- system.file("texts", "acq", package = "tm")
acq <- Corpus(DirSource(acqDir))
docMatrix2 <- t(as.matrix(DocumentTermMatrix(acq)))

# Create score dictionary.
words <- unique(c(rownames(docMatrix1),rownames(docMatrix2)))
scores <- runif(length(words),-1,1)
dict <- data.frame(word=words,score=scores)

# Calculate similarity.
sim1 <- simDoc(docMatrix1,docMatrix2,norm=TRUE)
sim2 <- simDic(docMatrix1,docMatrix2,dict,norm=TRUE)
sim <- simSyn(list(sim1,sim2),c(0.5,0.5))
simSum(sim)

}

  • Read-and-delete-meファイルを読んで削除する。

3. ビルドとチェック

Windowsのコマンドプロンプトでビルドとチェックを行う。WARNINGは1つ残らず消えるまでドキュメントとコードの修正をする。

> :: パッケージのビルド
>R CMD build smdc
* checking for file 'smdc/DESCRIPTION' ... OK
* preparing 'smdc':
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files
* checking for empty or unneeded directories
* building 'smdc_0.0.2.tar.gz'
cygwin warning:
  MS-DOS style path detected: C:/LyricsWorkspace/R/smdc_0
.0.2.tar.gz
  Preferred POSIX equivalent is: /cygdrive/c/LyricsWorksp
ace/R/smdc_0.0.2.tar.gz
  CYGWIN environment variable option "nodosfilewarning" turns off this warning.
  Consult the user's guide for more details about POSIX paths:
    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
> :: パッケージのチェック
> R CMD check --as-cran smdc_0.0.2.tar.gz
* using log directory 'C:/LyricsWorkspace/R/smdc.Rcheck'
* using R version 2.15.2 (2012-10-26)
* using platform: x86_64-w64-mingw32 (64-bit)
* using session charset: CP932
* checking for file 'smdc/DESCRIPTION' ... OK
* checking extension type ... Package
* this is package 'smdc' version '0.0.2'
* checking CRAN incoming feasibility ... NOTE
Maintainer: 'Masaaki TAKADA'
Days since last update: 1
* checking package namespace information ... OK
* checking package dependencies ... OK
* checking if this is a source package ... OK
* checking if there is a namespace ... OK
* checking for executable files ... OK
* checking for hidden files and directories ... OK
* checking whether package 'smdc' can be installed ... OK
* checking installed package size ... OK
* checking package directory ... OK
* checking for portable file names ... OK
* checking DESCRIPTION meta-information ... OK
* checking top-level files ... OK
* checking for left-over files ... OK
* checking index information ... OK
* checking package subdirectories ... OK
* checking R files for non-ASCII characters ... OK
* checking R files for syntax errors ... OK
* checking whether the package can be loaded ... OK
* checking whether the package can be loaded with stated dependencies ... OK
* checking whether the package can be unloaded cleanly ... OK
* checking whether the namespace can be loaded with stated dependencies ... OK
* checking whether the namespace can be unloaded cleanly ... OK
* checking loading without being on the library search path ... OK
* checking for unstated dependencies in R code ... OK
* checking S3 generic/method consistency ... OK
* checking replacement functions ... OK
* checking foreign function calls ... OK
* checking R code for possible problems ... OK
* checking Rd files ... OK
* checking Rd metadata ... OK
* checking Rd cross-references ... OK
* checking for missing documentation entries ... OK
* checking for code/documentation mismatches ... OK
* checking Rd \usage sections ... OK
* checking Rd contents ... OK
* checking for unstated dependencies in examples ... OK
* checking examples ... OK
* checking PDF version of manual ... OK

NOTE: There was 1 note.
See
  'C:/LyricsWorkspace/R/smdc.Rcheck/00check.log'
for details.

4. CRANにアップロード

ビルドしたパッケージ(xxx.tar.gz)をCRANにアップロードする。

5. メールで報告

アップロードしたことをCRAN@R-project.orgにメールする。メールのタイトルは"CRAN submission 'パッケージ名' 'バージョン'"とする。

Title: CRAN submission smdc 0.0.2

Dear Sir/Madam

Hi, I'm Masaaki TAKADA.
I submitted 'smdc' package ver 0.0.2.

I agree to the CRAN policies.

Sincerely,
Masaaki TAKADA

6. その後

10~20分くらいでメールの返信が来る。しかも人手で。すごい!チェックテストで引っかかるとその旨が記載される。受理されると、ここに掲載される。install.package()で使えるようになるにはもう1日くらいかかるので、ゆっくり待ちましょう。

その他注意事項

  • 当初RMeCabをパッケージ内で動かすプログラムを書いていましたが、RMeCabがCRANに載っていないため、rejectされました。RMeCabのインストール方法など詳しくDESCRIPTIONに書いてみましたが、認められず。CRAN以外のパッケージと強く依存するようなパッケージは、CRANに掲載できないようです。その結果、英語のテキストマイニングパッケージであるtmを勉強する羽目に…。
  • パッケージのmanでexampleを書く際に、使用するデータは誰でもアクセスできる必要があり、何を使ってどう読み込めばよいのか苦労しました。結局他のパッケージに含まれているニュースサイトのデータを使いました。
  • ドキュメントに日本語はご法度です。たとえ日本語解析のパッケージであってもNGです。
  • パッケージをCRANに投稿してから日が経たないうちにアップデートしようとすると怒られます。何やら投稿に伴うテストや掲載などの実作業はボランティアなので、頻繁にアップデートされると手におえないとのこと。"1~2か月ごとより頻繁にならないように"アップデートするのが目安のようです。

補足

パッケージを作成してCRANにあげる人は、以下の公式ページを熟読しましょう。

RMeCabでつまづくの巻

急にRMeCabのdocMatrix()が使えなくなった。
テキストの中身が空で読み込めないと言っているようだが…

文字コードをいろいろ変えて実行してみたけど変わらず。
そもそも以前は同じスクリプト・同じデータでちゃんと動いていたはずなので、明らかにおかしい。

さらにRとRMeCabを再インストールしたけど、やはり使えず。
現在鋭意原因究明中ナリ…

> library(RMeCab)
> a <- docMatrix("C:/LyricsWorkspace/Lyrics/Mr.Children_single", weight = "tf*idf")
file = C:/LyricsWorkspace/Lyrics/Mr.Children_single/[Mr.Children]【es】~Theme of es~.txt
empty results
…
file = C:/LyricsWorkspace/Lyrics/Mr.Children_single/[Mr.Children]箒星.txt
empty results
 以下にエラー terms.formula(formula, data = data) : 
   '.' が式中にありますが, 'data' 引数がありません 


(以下追記)
何日か悩み、MeCabを再インストールしたところ、治りました。
原因はMeCabとRMeCabのバージョンの整合性の問題のようでした。
そういえばPythonMeCabを連携させるためにMeCabのバージョンを変えたような気が…w

というわけで、また研究続行できそうです。(めでたしめでたし)

Pythonで日本語WordNetと英語WordNetを利用して、単語間の類似度を測る

目的

「Mr.Childrenの歌詞分析(4): 単語の意味を考慮した、シングル曲のクラスタリング」では、文書の距離を計測するのに、同一語や同一概念の出現頻度を用いていた。この場合、"似ている"単語は考慮されておらず、クラスタリングの精度もいまひとつであった。

 

この問題を解決するために、単語間の距離をWordNetを用いて計測したい。今回は、2つのテキストファイルを元に、それぞれの単語の類似度を出力するプログラムを作成した。日本語WordNetと英語WordNetを用いて、多義語に対応した単語間の距離を計測している。

f:id:tkdmah:20130122223017p:plain

 

ちなみに、わざわざ英語版WordNetを用いているのは、類似度を測るAPIが日本語WordNet(のPython API)には用意されていないから。また、「Python による日本語自然言語処理 12.1.5 日本語WordNet」 には日本語 WordNet に対するリーダーが掲載されているが、複数のSynset(概念)を持つ多義語に対応できていない。

 

手法

環境設定

前準備として、日本語WordNetと英語WordNetの設定を行う。

  1. 日本語WordNet をダウンロードする。今回はタブ区切りテキスト(Just Japanese Words linked to Princeton WordNet Synsets)を用いる。 
  2. 英語WordNet をダウンロードする。Pythonインタープリタから以下のコマンドでダウンロードできる。
    import nltk
    nltk.download() # ブラウザが表示されるので、WordNetを選択してダウンロード
    # Chromeがデフォルトブラウザだと、ブラウザが表示されない
  3. 今回はEclipseで開発を行った。依存関係や入力補助など便利。 

Pythonスクリプト

単語のリストが書かれた2つのテキストファイルを入力として、その間の類似度を出力するスクリプトは、以下のとおり。

・jwn_driver.py 

'''
メインモジュール
"""
fin1 = 'TagExamples.txt' #入力ファイル1
fin2 = 'WordExamples.txt' #入力ファイル2
fout = 'Output.txt' #出力ファイル
 
import sim
wordLists = sim.makeWordLists(fin1,fin2) #単語リストを作成
synLists = sim.convWords2Synsets(wordLists[0], wordLists[1]) #概念リストを作成
simMatrix = sim.calcSim(synLists[0], synLists[1]) #類似度行列を作成
sim.writeSim(wordLists[0],wordLists[1],simMatrix,fout) #ファイルへの書き込み

・sim.py 

# -*- coding: utf-8 -*-
'''
英語WordNetから類似度を算出するモジュール
""" 
import codecs
 
def makeWordLists(fin1,fin2):
  """ ファイル名を2つ受け取って単語リストのリストを返す """
  fins = [fin1, fin2]
  wordLists = [[ ], [ ]]
  for i in [0,1]:
    f=codecs.open(fins[i], encoding="utf-8")
    for line in f:
      wordLists[i].append(line.strip("\r\n").strip("\n"))
    f.close()
  return wordLists
 
def convWords2Synsets(wordList1, wordList2):
  """ 単語リストを2つ受け取って概念リストのリストを返す """
  import jwn_corpusreader
  jwn = jwn_corpusreader.JapaneseWordNetCorpusReader('C:/LyricsWorkspace/nltk_data/corpora/wordnet', 'C:/LyricsWorkspace/WordNet/wnjpn-ok.tab') #英語WordNetと日本語WordNetを指定する
  synLists = [[ ],[ ]]
  wordLists = [wordList1, wordList2]
  for i in [0,1]:
    for j in range(len(wordLists[i])):
      synLists[i].append(jwn.synsets(wordLists[i][j]))
  return synLists
 
def calcSim(synList1,synList2):
  """ 概念リストを2つ受け取って類似度の行列を返す """
  import numpy as np
  simMatrix = np.zeros( (len(synList1), len(synList2)))
  for i in range(len(synList1)):
    for j in range(len(synList2)):
      sims = [ ]
      for syn1 in synList1[i]:
        for syn2 in synList2[j]:
          sims.append(syn1.path_similarity(syn2))
      simMatrix[i,j] = max(sims)
  return simMatrix
 
def writeSim(wordList1, wordList2, simMatrix,fout):
  """ 単語リストを2つと類似度行列とファイル名を受け取ってファイルに出力する """
  f = codecs.open(fout,'w', encoding="utf-8")
  for i in range(len(wordList1)):
    for j in range(len(wordList2)):
      f.write(wordList1[i] + "-" + wordList2[j] +": " + str(simMatrix[i][j])+"\r\n")
  f.close()

・jwn_corpusreader.py(WordNetCorpusReaderを継承)

# -*- coding: utf-8 -*-
"""
日本語リーダー
""" 
from nltk.corpus.reader.wordnet import WordNetCorpusReader
class JapaneseWordNetCorpusReader(WordNetCorpusReader):
    def __init__(self, root, filename):
        WordNetCorpusReader.__init__(self, root)
        import codecs
        f=codecs.open(filename, encoding="utf-8")
        self._jword2offset = {}
        for line in f:
            _cells = line.strip().split('\t')
            _offset_pos = _cells[0]
            _word = _cells[1]
            if len(_cells)>2: _tag = _cells[2]
            _offset, _pos = _offset_pos.split('-')
            try:
              self._jword2offset[_word].append({'offset': int(_offset), 'pos': _pos})
            except:
              self._jword2offset[_word]=[{'offset': int(_offset), 'pos': _pos}]
    def synsets(self, word):
        if word in self._jword2offset:
            results = [ ]
            for offset in (self._jword2offset[word]):
                results.append(WordNetCorpusReader._synset_from_pos_and_offset(
                self, offset['pos'], offset['offset']
                ))
            return results
        else:
            return None

 

結果

以下のような出力が得られた。多義語に対して最も類似度の高い類似度を採用した結果がsim_max.txt, 類似度の平均値を採用した結果がsim_average.txtである。

f:id:tkdmah:20130122231449p:plain

類似度の高い組み合わせとして、「楽しむ-好き」「楽しむ-失望」「楽しむ-空」が挙がっている。なぜか「楽しむ-失望」が類似している・・・これはいただけない・・・。また、「別れる」と類似しているのは「楽しむ」だと・・・。

 

そもそもWordNetの類似度(path_similarity)は、共通の上位語階層の概念への最短経路の長さを表す。つまり、名詞-動詞間の類似度や、系統的でない関係を持つ単語間の類似度は、人間が感じる心理的な類似度と乖離してしまっている。考えてみると当然と言えるかもしれない。これを解決するには、WebテキストやTwitterから共起を検出するしかないのかなぁ。

 

Mr.Childrenの歌詞分析(6): 感情に基づくマッピング

主結果

Mr.Childrenの歌詞の感情(センチメント)に基づいて、横軸に感情の平均値、縦軸に感情のばらつきをとって、楽曲をプロットしました。

f:id:tkdmah:20130114225726p:plain

f:id:tkdmah:20130114225737p:plain

考察

全曲だと曲数が多すぎるので、分かりやすいようにシングル曲のグラフを見てみます。センチメントが低いクラスタには、代表的な失恋ソングである「旅立ちの唄」「君がいた夏」「CROSS ROAD」「innocent world」や、失意や絶望の中の「HANABI」「くるみ」「終わりなき旅」が入っています。また、センチメントが高いクラスタには、「君が好き」「抱きしめたい」をはじめとして、多くの恋愛ソングが入っています。全体としての傾向はセンチメントに基づいてうまくマッピングされているようです。

手法

Mr.Childrenの歌詞をRMeCabで形態素解析し、「単語感情極性対応表」(高村大也, 乾孝司, 奥村学, "スピンモデルによる単語の感情極性抽出", 情報処理学会論文誌ジャーナル, Vol.47 No.02 pp. 627--637, 2006. )に基づき、センチメントを算出しました。詳細は前回の記事をご覧ください。