2013年12月10日火曜日

2013年の #mercurialjp ツイートを解析する

このエントリは 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 来年もやる気か!? という冷静なツッコミはご遠慮ください ;-)