About connecting the dots.

data science related trivial things

マンションポエムで新築マンションをクラスタリング

今回は自然言語処理の話です.それも若干不自然な言語のマンションポエムが対象になります.マンションポエムというのは,工場萌え*1の著者大山さんが提唱している,マンション広告に入っている詩的なコピーのことです*2.具体的にはこんな感じのやつです*3

PLATINUM SHIP ここは、東京の暮らしの新しき起点。

そこは、時空をかける東京。 TOKYO NON DISTANCE


データ

さて,そんなマンションポエムですが,実はデータが公開されておりまして,先述の大山さんがGoogle Mapにまとめていらっしゃいます*4.このデータ,KMLという三次元地理情報を扱うためのXMLベースのマークアップ言語で,Pythonで適当にパースしてあげれば扱いやすいデータに落とし込むことができます.

ということで早速加工してみたんですが,結構データの抜けや欠けがあって,実は割りと地道に手を動かさないといけないことが判明しました.緯度経度とマンションポエムについては,全てのマンションについてあるのですが,1/3くらいは分譲時期や物件価格が入っていません.特に物件価格は注視したいところなので,いちいちググって手で入力するという作業を繰り返しました.マンションの公式サイトは,大半のものが全物件成約した段階で消されてしまうため,過去に分譲された物件は定番のマンションコミュニティのほか,こちらこちらといった,個人で実際の広告や価格表をまとめている方のものを参考に大まかな価格を入れていきました*5*6

あと全然関係ないですが,マンションコミュニティといえばこの覆面座談会,めっちゃ面白いです.

そんなこんなで,おおむね2005年以降に建設されたマンション218件を対象として,マンションポエムを分析しました.

いろいろと前処理

やったこと自体は非常にベーシックで,手作業で作成した辞書を使ってポエムを形態素解析して,名詞/動詞/形容詞だけを取り出して*7ストップワード*8を削除した上で,LDAにかけてトピックを抜き出しました.

形態素解析

定番の形態素解析エンジンMeCabを使って,ざっくりと形態素に分けます.Pythonバインディングを使って,以下のようにざーっと処理を行っていきます.

tagger = MeCab.Tagger()
node = tagger.parseToNode(sentence) # 対象の文字列を読み込んで解析する
words = []
while node:
    features = node.feature.split(",") # 解析結果をカンマで区切って取り出す
    if features[0] in ['名詞', '動詞', '形容詞']: # 名詞と動詞と形容詞だけが対象
        word = node.surface.decode('utf-8') if features[6] == '*' else features[6] # 活用語の場合は基本形を取り出す
        words.append(word.lower())
    node = node.next

辞書

ここが地味に一番だるかったところで,上記の形態素解析をうまく動かすためには,ただしく形態素に分けられないといけないわけです.しかしマンションポエムはかなり妙な日本語を使っており,一般的な日本語辞書だけではうまく分割できない例が多々あります.例えば「駅徒歩圏」ですが,普通に形態素解析してしまうと「駅」「徒歩」「圏」に分かれてしまいます.これは「駅徒歩圏」ではじめて意味をなす単語なので,分割されてしまっては困ります.他にも「庭苑」「緑景」「美邸」「逸邸」といった造語に近いような単語が頻発するので,これを逐一辞書に登録していく必要があります.今回の単語をまとめた辞書は,githubに公開しているので,もし使いたい方がいらしゃればご自由にどうぞ.

同様に「神保町」や「幕張ベイタウン」みたいな地名も,ちゃんと辞書登録してあげないと無残に分割されてしまいますので,これもセコセコと手作業で登録します.地味ですがこれが一番精度に効いてくる気がしています.今回は面倒なので,動詞に関しては辞書登録するのをサボってしまいましたが*9,例えばマンションポエム頻出語として「手に入れる」があって,これが「手」「に」「入れる」に分割されてしまうと,全然ダメだったりします...

ストップワード

上記処理を経ても,意味のない言葉や頻出語は残ってしまうので,これをストップワードとして処理します.辞書と同様にこちらにありますので,興味のある方はご覧ください.

モデル作成とクラスタリング

Latent Dirichlet Allocation (LDA) によるトピックモデルの作成

上記前処理を経て形態素に分けられたマンションポエムを,bug-of-words*10のベクトルに直して,LDAにかけてトピックを抜き出します.このあたりはgensimパッケージを使ってやってしまいました.パッケージ自体の使い方は,ゆうくさんの記事sucroseさんの記事をご覧ください.またLDAの理論的な解説については,例によってあらびきさんの解説を読んでいただくのが良いと思います.

この処理の意味するところをざっくりとまとめると,たくさんの文書から,文書間に共通するいくつかの潜在的なトピックを抜き出す,というようなことをしています*11.このトピックを使ってクラスタリングを行います.コード的には以下のような感じになります.

# 辞書の作成
dictionary = corpora.Dictionary(words)
dictionary.filter_extremes(no_below=10, no_above=0.4)
# コーパスの作成
corpus = [self._dictionary.doc2bow(w) for w in words] # bag of words
corpus = models.TfidfModel(corpus)[corpus] # apply tf-idf
corpora.MmCorpus.serialize(corpus_path, corpus) # save serialize data
# LDAモデルの作成
model = models.LdaModel(corpus=corpus,
                        num_topics=20,
                        id2word=dictionary)

Gaussian Mixture Model (GMM) によるクラスタリング

上記のLDAによるトピックは,実質的には次元削減と似た効果を持ちます.各ポエムにおける文章の出現数をまとめたベクトル(今回の例だと約1700個の単語があったため,1700次元になります)から20個程度のトピックを抜き出すことは,1700次元を20次元に圧縮することとほぼ同義です.そこで,LDAにより得られた20次元のベクトルを用いて,マンションポエムのクラスタリングを行います.別にk-meansでもいいんですが*12,今回はGMMによるクラスタリングを行いました.GMMの理屈についてはsleipnir002さんの解説をご覧ください.いくつかクラスタ数を試した結果,4つが良さげであることがわかりました*13

可視化

坪単価 × 都心からの距離

ということで,作成した4つのクラスタについて,あとはRにかませてざっと集計&可視化してみました.dplyr使ってパイプっぽく処理して,クラスタごとのサンプル数,平均坪単価,都心からの距離平均(km)*14をまとめたのが以下になります.クラスタ1が最大派閥で90サンプル,価格はクラスタ0が平均坪単価277万でトップ*15,距離もクラスタ0と3が10km程度でした.

> # summarize
> data %>% 
+   group_by(cluster_id) %>% 
+   summarize(n=n(),
+             price.mean=mean(price),
+             distance.mean=mean(distance))
Source: local data frame [4 x 4]

  cluster_id  n price.mean distance.mean
1          0 35   277.0857      10.62453
2          1 90   257.3667      13.93625
3          2 47   228.8511      13.91467
4          3 46   255.6522      10.02251

続いて散布図に全サンプルをプロットしてみました.比較的綺麗に,都心からの距離と坪単価が逆相関しているのがみて取れます.一部に坪単価600万とか800万とかあるのは,いわゆる億ションなので,本当は外れ値にしちゃった方が良いかもですけどね... クラスタごとにみても,そこまではっきりとではないですが,まぁそこそこは綺麗に区分けされているかなぁと思います.

http://f.st-hatena.com/images/fotolife/S/SAM/20141225/20141225195035_original.png

クラスタごとのワードクラウド

続いて,今回の素性ベクトルを作るもととなったマンションポエム形態素を可視化するために,ワードクラウドを作成します.これはTagexedoというサービスを使いました*16.現状日本語で自由な文字列からワードクラウドを作れるのは,このサービスしかないっぽいです.

ということで,各クラスタ形態素をワードクラウド化してみた結果が以下の通りです.

クラスタ0

もっとも平均坪単価が高く,都心からの距離が近いだけあって,「都心」「高台」「空」「空間」などの,都心の閑静な高級住宅地や超高層のタワーマンションなんかを表していそうなワードが並んでいます.他には「世界」「美」「上質」といったいかにもなワードも散りばめられています.
http://f.st-hatena.com/images/fotolife/S/SAM/20141225/20141225200954_original.png

クラスタ1

こちらはもっとも都心から遠いクラスタだけあって,「住」「街」「暮」「生活」といった,そこでの具体的な地に足のついた生活をイメージさせるような単語が並んでいます.他にも「家族」「快適」「自然」「伝統」といったワードが並びます.「伝統」「継承」といった言葉は,おおむね古いランドマークがあるような場所なので(例えば浅草や鎌倉のような場所ですね),都心から離れた高級住宅地も含まれている感じがします.
http://f.st-hatena.com/images/fotolife/S/SAM/20141225/20141225200955_original.png

クラスタ2

最も坪単価が安いだけあって,物件ではなく「家族」が一番大きな言葉となっています.家族生活に適した郊外の住宅地,というイメージでしょうか.トップ3が「家族」「暮らし」「住まい」となっているのが,いかにもという感じです.「レジデンス」については,以下のような文言が典型的な使われ方です.

風と緑のレジデンス「×××」*17駅徒歩8分、賑わいの先に広がる住宅地。駅近ながら住宅地の空気に包まれた、家族の暮らしがここから始まる。都市の利便性を手中にしながら、開放的で落ち着きに満たされたこの地。家族を優しく包み込む、ゆとりを育む、この地ならではのレジデンスを創り上げます。住宅地の最前席に住まう駅徒歩圏では限られた、第一種住居地域。

http://f.st-hatena.com/images/fotolife/S/SAM/20141225/20141225200956_original.png

クラスタ3

最後は「tokyo」「東京」「都心」とひたすら首都をアピールしていますね.「金町」「豊洲」といった地名が並んでいるところからも,都心からの距離が近く,それでいて極端な高値ではないゾーンの物件が中心になっていることが伺えます.臨海副都心や,都心近くで再開発した東京のイメージの薄い場所などが,敢えて東京と強調する,ということなのもしれません.
http://f.st-hatena.com/images/fotolife/S/SAM/20141225/20141225200957_original.png

終わりに

まずは,改めて大山さんにお礼を申し上げます.元となるデータがあることの意義は本当に大きいです*18

そして今回のネタに関連して,いくつか検討したところとか,まだできそうなこととかがあるので,そのうち続きを書こうかなと考えています.このネタを書くために19日から冬休みに入ってて,ようやく今日これが仕上がりました.とはいえ,まだ一般的な冬休みの初めにすらさしかかっていないので,冬休み使ってもう1つ2つできないかなーとか... 本当はこのデータを加工して簡単なWebサービスにしようかと思ってたので,気が向いたらそれやります.あとデベロッパーとか物件の戸数とか階数とかのデータをまとめたら,もう少し面白いことできないかなと思ったり...

*1:私自身,この本を読んで工場夜景クルーズに行ったりもしました.夜の工場いいですよね.FFでいうと7のミッドガルみたいな感じで素敵です.

*2:日経にも取り上げられたりしています.

*3:個人的にマンション広告が好きで,いつの日からかマンション建設予定地に光の柱が立つようになったのを微笑ましくみていたりします.

*4:このデータ作成にかかった多大な労力に,感謝の意を表しつつ利用させていただきます.

*5:物件の広さに影響されるとアレなので,実際にはマンションの価格ではなく,坪単価の形でまとめています.

*6:もちろん同じ物件でも,高層階の東南角地と低層階の形が悪い部屋とでは価格が異なるわけで,複数の坪単価がある場合には,概ね真ん中くらいの数字を使うようにしています.

*7:今回のマンションポエムだと,助詞や副詞がほとんど意味をなさなそうなので無視した,というだけの話で,全ての場合で削除するべきだ,ということではありません.

*8:あまりに一般的すぎるので,実際に自然言語処理を行うときの妨げとなるワードのことです.googleでも,英単語の"a"や"the", "for"なんかは検索の際にストップワードとして無視されています.

*9:動詞や形容詞だと,すべての活用形をちゃんと登録してあげないといけないのですが,これが1語につき10くらいの活用形を登録する必要があって大層面倒なのです.

*10:単語の並び方は考慮せず,単に文章に単語が含まれているかどうかだけをみて,その回数を数字とした形です.

*11:因子分析で,各パラメタの背後にある共通因子を抜き出す,というのと同じイメージかなと思います.まぁモデルの数理的な形は全く違いますが...

*12:とはいえ実際のところはk-meansは精度悪いんで,あんま使いたくはないってことなんですけどね...

*13:5個以上だと似たようなクラスタがいくつもできたり,サンプル数が極端に少ないものができたり,でイマイチだったので...

*14:ここでは都心として皇居の緯度経度(35.6825° N, 139.7521° E)を用いています.ある程度の目安にしかなりませんが,まぁそこは目を瞑ってください.

*15:坪単価が想像できない人のために補足すると,坪単価277万のマンションというのは,親子4人で住むような70平米のマンションで5876万円!! もする価格です.新築マンションってタカイデスネー

*16:利用するためにはMS Silverlightが必要で,しかもChromeだと動きません...

*17:具体的な物件名は特定できない形にしておきます

*18:とはいえ草の根でデータまとめるの本当に大変なので不動産屋さんとかデータ提供してくれたら嬉しいのになぁとか思っています.まぁ企業側に何のメリットもないから無理でしょうけど...