このエントリは Mercurial Advent Calendar 2013 の10日目です。
Mercurialの素晴らしいところの一つは「困ったことがあってもハッシュタグ #mercurialjp をつけてツイートすればすぐに的確なアドバイスをいただける」というコミュニティの強力さだと思ってます。何かあってもすぐにフォローしてもらえる上、内容によっては対応してパッチを本家に投げてもらえるというのは本当に心強い限りです。
そこで『今年の #mercurialjp を振り返る』ということで (「Mercurial Advent CalendarのエントリなのにMercurial自体に触れないなんて、変化球を通り越して敬遠じゃないのか?」という一抹の不安を抱えつつ)、2013年のMercurialトピックを #mercurialjp ツイートを解析*1することで探ってみたいと思います。
まずは前処理から
まずは前処理、元となるデータの作成です。実はこれがデータ解析で一番のキモであり、かつ一番手間のかかるところなのですが、今回は件数もたかが知れているので TwitterのWebページでの検索結果をコピペでテキストファイルに落とし、Emacsの編集機能やキーボードマクロでCSVファイルに加工することにしました。そうやって出来たのがこのファイルです。ちなみに12月に入ってからはAdvent Calendarに関するツイートが多いこともあり、データは1月〜11月で作成しました。これを元に解析していきます。
早速データファイルを R に読み込んで、まずはヒストグラムを作成して月毎のツイート数の推移を見てみましょう。
> data <- read.csv("2013mercurialjp.csv") > colnames(data) [1] "name" "month" "day" "tweet" > nrow(data) [1] 804 > hist(data$month, xlim=c(1,12), breaks=c(0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5,11.5))
総計804ツイート、月によって流量の変化はあるものの、概ね "70ツイート/月" くらいでしょうか。これが今回の材料の全てです。さて、この804ツイートの中にどんなドラマが秘められているのか、探検を始めましょう。
単語の出現頻度を計測する
それではデータを単語に切り分けて*2、出現頻度を集計しましょう。使用するツールは R と、MeCab および RMeCabパッケージ です。名詞、形容詞、動詞で意味を持ちそうな語を抽出し、上位20件を見てみます。
> library(RMeCab) > mercurialjp <- RMeCabFreq("2013mercurialjp.csv") file = 2013mercurialjp.csv length = 4309 > > words <- mercurialjp[((mercurialjp$Info1 == "名詞" | + mercurialjp$Info1 == "形容詞" | + mercurialjp$Info1 == "動詞") & + (mercurialjp$Info2 != "非自立" & + mercurialjp$Info2 != "数")), ] > freq = words[ rev(order(words$Freq)), ] > freq[1:20, ] Term Info1 Info2 Freq 627 "," 名詞 サ変接続 1752 612 " 名詞 サ変接続 1465 708 / 名詞 サ変接続 1400 705 . 名詞 サ変接続 1394 283 する 動詞 自立 992 638 # 名詞 サ変接続 886 755 @ 名詞 サ変接続 798 2073 mercurialjp 名詞 一般 764 1828 flyingfoozy 名詞 一般 600 632 ","@ 名詞 サ変接続 565 1351 Katsunori 名詞 一般 559 1288 FUJIWARA 名詞 一般 559 702 - 名詞 サ変接続 496 720 :// 名詞 サ変接続 444 746 _ 名詞 サ変接続 404 2072 mercurial 名詞 一般 259 255 れる 動詞 接尾 249 1971 jp 名詞 一般 248 1886 hg 名詞 一般 231 1930 http 名詞 一般 200 >
おっと、記号やアクティブユーザのアカウントなどが入ってしまいました……。さてどうやって取り除くかですが、うまい方法が思いつかなないので地道に抽出条件に書き加えました。
> words <- mercurialjp[((mercurialjp$Info1 == "名詞" | + mercurialjp$Info1 == "形容詞" | + mercurialjp$Info1 == "動詞") & + (mercurialjp$Info2 != "非自立" & + mercurialjp$Info2 != "数") & + (mercurialjp$Term != "\",\"" & + mercurialjp$Term != "\"" & + mercurialjp$Term != "/" & + mercurialjp$Term != "." & + mercurialjp$Term != "#" & + mercurialjp$Term != "@" & + mercurialjp$Term != "mercurialjp" & : (snip) : + mercurialjp$Term != ")\"" & + mercurialjp$Term != "?")), ] > freq = words[ rev(order(words$Freq)), ] > freq[1:100,] Term Info1 Info2 Freq 283 する 動詞 自立 992 755 @ 名詞 サ変接続 798 2072 mercurial 名詞 一般 259 255 れる 動詞 接尾 249 1886 hg 名詞 一般 231 300 なる 動詞 自立 194 257 ある 動詞 自立 173 2718 ファイル 名詞 一般 154 1391 Mercurial 名詞 一般 132 3322 場合 名詞 副詞可能 120 1125 設定 名詞 サ変接続 105 294 できる 動詞 自立 105 2805 リビジョン 名詞 一般 94 988 指定 名詞 サ変接続 92 3795 的 名詞 接尾 84 2289 selenic 名詞 一般 83 2055 manual 名詞 一般 79 1105 表示 名詞 サ変接続 78 3643 可能 名詞 形容動詞語幹 74 1513 TortoiseHg 名詞 一般 74 3800 等 名詞 接尾 73 3126 環境 名詞 一般 73 1036 機能 名詞 サ変接続 70 2734 ブランチ 名詞 一般 69 2809 リポジトリ 名詞 一般 66 2577 エクステンション 名詞 一般 64 929 変更 名詞 サ変接続 64 2996 履歴 名詞 一般 63 855 作業 名詞 サ変接続 63 1198 問題 名詞 ナイ形容詞語幹 62 334 使う 動詞 自立 62 2777 マージ 名詞 一般 58 794 コミット 名詞 サ変接続 58 858 使用 名詞 サ変接続 57 4174 無い 形容詞 自立 56 3045 文字 名詞 一般 52 3729 上 名詞 接尾 51 3790 版 名詞 接尾 50 3745 化 名詞 接尾 50 2987 対象 名詞 一般 50 910 参照 名詞 サ変接続 50 3663 必要 名詞 形容動詞語幹 49 3774 時 名詞 接尾 47 2611 コマンド 名詞 一般 47 911 参考 名詞 サ変接続 47 3025 情報 名詞 一般 45 820 リリース 名詞 サ変接続 45 3739 側 名詞 接尾 44 2624 コード 名詞 一般 44 940 実施 名詞 サ変接続 44 419 思う 動詞 自立 44 3767 性 名詞 接尾 42 2896 内容 名詞 一般 42 1864 google 名詞 一般 42 3661 幸い 名詞 形容動詞語幹 41 3088 梱 名詞 一般 41 2233 r 名詞 一般 41 3741 先 名詞 接尾 40 2207 push 名詞 一般 40 2607 ケース 名詞 一般 39 1873 groups 名詞 一般 39 3748 名 名詞 接尾 38 1629 bitbucket 名詞 一般 38 893 利用 名詞 サ変接続 38 4145 ない 形容詞 自立 37 2240 rebase 名詞 一般 37 2102 msg 名詞 一般 37 258 いう 動詞 自立 37 3249 領域 名詞 一般 36 2483 wiki 名詞 一般 36 3398 Mercurial 名詞 固有名詞 35 1021 更新 名詞 サ変接続 35 867 修正 名詞 サ変接続 35 2712 パッチ 名詞 一般 34 1074 確認 名詞 サ変接続 34 1150 追加 名詞 サ変接続 33 3674 有効 名詞 形容動詞語幹 32 2753 ヘッド 名詞 一般 32 3058 日本語 名詞 一般 31 2583 エントリ 名詞 一般 31 1166 選択 名詞 サ変接続 30 1081 管理 名詞 サ変接続 30 3475 hg 名詞 固有名詞 29 3030 手 名詞 一般 29 1163 運用 名詞 サ変接続 29 3120 現状 名詞 一般 28 3116 状態 名詞 一般 28 3093 標準 名詞 一般 28 3040 挙動 名詞 一般 28 2422 twitter 名詞 一般 28 1040 注意 名詞 サ変接続 28 878 共有 名詞 サ変接続 28 3052 方法 名詞 一般 27 2444 update 名詞 一般 27 2381 thg 名詞 一般 27 1893 hgrc 名詞 一般 27 1564 a 名詞 一般 27 2253 repo 名詞 一般 26 3735 付き 名詞 接尾 25 3608 笑 名詞 固有名詞 25 >
上位100件を見みたところ、一般的な単語で占められて特徴的なものは見い出しにくいものの、やはりTortoiseHgが上位にあること (SourceTreeは見当たりませんね) と、あと目に留まるのは rebase あたりでしょうか。
単語の共起関係を調査する
さらなる解析を、と月毎の出現頻度や推移を調べたりなどいろいろやってみたものの、意味のありそうな結果が得られません。元々データの母数が少ない上、ツイッターという字数の少ないメディアなので省略も多いでしょうから、傾向を出すのは無理があるようです。
と、ここで「あれ、そういえば Git って単語が出てこないな」ということに気付きました。早速、出現頻度データフレームから Git という語だけを抜き出して、出現頻度を調べてみましょう。『敬遠と思って安心してたら危険球』になりかけてますが、大丈夫でしょうか? 頭部への危険球は一発退場です。
> freqGit <- subset(freq, subset = (Term == "git" | Term == "Git"), select = c(Term, Info1, Info2, Freq)) > freqGit Term Info1 Info2 Freq 1302 Git 名詞 一般 13 3379 Git 名詞 固有名詞 8 1855 git 名詞 一般 5 3468 git 名詞 固有名詞 2 >
なるほど、トータルでは28回現れていたものの、大文字小文字の表記揺れと、おそらく出現の仕方による構文解析の揺れで、数値が分散して上位には上がって来なかったようです。
では、「#mercurialjp のハッシュタグ付きツイートでは、『Git』という単語はどのような文脈で現れたのか?」を調べてみましょう。
ある単語が別の単語と隣接して現れることを『共起』と言い、RMeCabパッケージでは collocate()関数で求めることができます。collocate()関数の引数はファイルなので、ツイートを一度 tweet.txt というテキストファイルに落としてから喰わせ、出現頻度(Span)順に並べ替えた上で、頻度2以上を見てみます。
> mercurialjp <- read.csv("2013mercurialjp.csv") > write.table(mercurialjp$tweet, "tweet.txt", quote=F, col.names=F) > Git <- collocate("tweet.txt", node="Git") file = tweet.txt length = 4635 > > GitOrderBySpan = Git[ rev(order(Git$Span)), ] > GitOrderBySpan <- subset(GitOrderBySpan, subset = Span > 1, + select = c(Term, Before, After, Span, Total)) > GitOrderBySpan Term Before After Span Total 54 [[TOKENS]] 84 63 147 44084 53 [[MORPHEMS]] 30 22 52 4635 6 Git 21 0 21 21 30 の 3 7 10 1482 37 ブランチ 1 6 7 69 25 と 0 7 7 394 17 『 4 3 7 244 10 mercurialjp 7 0 7 806 1 # 7 0 7 962 31 は 3 3 6 825 15 、 5 1 6 1075 49 的 0 5 5 84 24 で 2 3 5 694 22 だ 0 5 5 590 2 ( 4 1 5 194 46 比較 1 2 3 6 21 する 1 2 3 968 7 Mercurial 0 3 3 167 38 ユーザ 1 1 2 22 35 を 0 2 2 646 16 。 2 0 2 497 3 ) 1 1 2 188 >
どうやら2013年の #mercurial ツイートでは『Git』という単語は『ブランチ』という単語と一緒に語られたことが多かった*3ようです。検索してみるとこんな感じです。
個人的には、ここで先の「rebase」が出てくるのでは?と予測していたので、この結果は意外でした。
それでは出現回数37回を誇る「rebase」は、どんな文脈で現れたのか? 調べてみましょう。
> rebase <- collocate("tweet.txt", node="rebase") file = tweet.txt length = 4635 > rebaseOrderBySpan = rebase[ rev(order(rebase$Span)), ] > rebaseOrderBySpan <- subset(rebaseOrderBySpan, subset = Span > 1, + select = c(Term, Before, After, Span, Total)) > rebaseOrderBySpan Term Before After Span Total 123 [[TOKENS]] 188 141 329 44084 122 [[MORPHEMS]] 60 61 121 4635 35 rebase 47 0 47 47 61 で 5 11 16 694 79 を 1 10 11 646 46 、 9 1 10 1075 2 # 10 0 10 962 7 -- 1 7 8 56 69 の 1 6 7 1482 44 … 1 6 7 474 33 mercurialjp 7 0 7 806 9 / 5 2 7 1389 81 エクステンション 0 6 6 64 75 も 4 2 6 274 56 する 4 2 6 968 28 html 6 0 6 129 8 . 6 0 6 1367 60 て 4 1 5 590 54 が 1 4 5 882 31 keep 2 3 5 12 71 は 3 1 4 825 58 た 3 1 4 449 117 等 0 3 3 76 107 有効 0 3 3 32 74 ます 2 1 3 544 59 だ 1 2 3 590 49 『 1 2 3 244 39 strip 1 2 3 21 29 http 0 3 3 359 24 graft 3 0 3 23 17 MQ 2 1 3 26 16 @ 0 3 3 682 15 :// 0 3 3 444 4 ( 3 0 3 194 121 & 0 2 2 9 113 無し 2 0 2 8 104 改変 1 1 2 13 101 完了 1 1 2 5 100 契機 1 1 2 16 99 場合 1 1 2 120 97 化 1 1 2 50 92 使用 0 2 2 57 76 や 0 2 2 53 70 ので 2 0 2 204 67 なり 1 1 2 10 65 と 1 1 2 394 62 できる 0 2 2 106 53 から 2 0 2 123 47 。 1 1 2 497 43 update 0 2 2 29 26 histedit 1 1 2 16 25 hg 2 0 2 260 20 abort 1 1 2 8 6 ) 1 1 2 188 1 " 1 1 2 128 >
「rebase」は「エクステンション」と併せて語られています。そして「Git」という単語は見当たりません。ということで『Git には rebase があるけど、Mercurial には……』というのは、もう過ぎ去りし日というわけですね。グッバイ・フォーエバー。
結論: ハッシュタグ #mercurialjp ツイートから見る2013年の考察
以上の結果より、2013年の #mercurialjp ツイートからは以下の傾向が見られました。
- やはり TortoiseHg はアクティブ (さすが西原さん*4!)。
- Mercurial の機能では、rebase についての話題が多く見受けられた。
- その場合、Git との比較ではなく、rebaseエクステンションについて語られている。
- Git との比較では、むしろブランチについての話題が上がっていた。
どうなることかと思ったのですが、なんとかそれらしい結果が得られ、また危険球にもならなかったようで胸をなでおろしています。*5
落穂拾いと謝辞と年末のごあいさつ
と、綺麗にまとまったところを混ぜ返してしまいますが、実はこの結論には不備があります。
「#mercurialjp rebase エクステンション」で検索するとわかるのですが、実際には誰かの『mercurial に git の rebase に相当するものってあるのかなあ?』というツイートを拾って『rebase エクステンションが標準で同梱されてますよ』と #mercurialjp ハッシュタグをつけてメンションを投げているケースが少なからずあり、その前段を取りこぼしてるんですね (#mercurialjp ハッシュタグで収集したツイートのみを解析しているので)。冒頭で「前処理がデータ解析で一番のキモ、そして一番手間のかかるところ」と書いた、そのままです……。会話を全て拾って解析できればいいのですが、それは来年への課題*6とさせてください。
なお、今回のデータ解析の手法は Rによるテキストマイニング入門 (伊藤基広 著) を全面的に参考にさせて頂きました。また解析に使用したRMeCabパッケージも同氏による開発です。ありがとうございます。
また、Mercurial日本語ユーザ会の皆様には、今年も大変お世話になりました。ありがとうございます。今年は勉強会(TokyoMercurial)に参加できなかったのが残念ですが、次回はぜひ参加させてください。
来年も、どうぞよろしくお願いします。
*1 このエントリは厳密な技術文書でなく、あくまで『データ解析をネタにしたMercurialに関する面白よみもの』であることを第一義にしていますので、必ずしも内容が正しくないことをご了承ください。意図的に簡略化した箇所につきましては脚注などでフォローを入れましたが、興味がなければ読み飛ばしていただいて結構です。また基本的な理解の誤りがありましたら、ご指摘いただければ幸いです。
*2 正しくは「単語」でなく「形態素」ですが、このエントリでは平易な「単語」という言葉を使用します。ちなみに出現頻度を求める RMeCabFreq()関数は、単純な単語のカウントではなく、活用形を原型に変換した上でカウントします。
*3 本来は「その出現が有意であるか (偶然でなく意味を持っていると見なせるか)」の検証 (統計的検定) が必要ですが、このエントリの趣旨により省略しています。
*4 TortoiseHgのコミッタとしてアクティブに活動されてるのは存じていましたが、まさかここまでとは……。
*5 実はデータ解析で一番大切なことは『その解析結果からPDCAサイクルを構築できるか?』ということです。と偉そうに言ってますが、つい数日前にJapan.R 2013で教わったことだったりします。というわけで2014年へのアクションプランですが……えーっと……えーっと……。
*6 来年もやる気か!? という冷静なツッコミはご遠慮ください ;-)