Python
GoogleMapsAPI
形態素解析
さだまさし

さだまさしの歌詞に登場する地名をヒートマップに起こしてみた

More than 3 years have passed since last update.


はじめに

さだまさし氏(まっさん)の歌には、特定の地域・場所を取り上げた歌が数多くあります。


  • 昨日・京・奈良、飛鳥・明後日。

  • 飛梅

  • 長崎小夜曲

どれも素敵な歌ですね(^o^)

特に飛梅は、太宰府天満宮に行くたびに脳内再生されジーンときてしまう超名曲です。

これらを含むまっさんの歌には、どの地域が多く取り上げられているんだろう?

やっぱり長崎?東京?それとも...?の謎を解き明かすべく、キーボードを手に取りコードを書いてみました。

ちなみに「北の国から」は超有名どころですが、これまでのアドベントカレンダーにて何度もフィーチャーされている通り歌詞がないので、今回は調査対象外です。


やったこと(ざっくり)


  • まっさんの歌詞を品詞分解

  • 地域名を抽出してジオコーディング

  • 登場回数と座標をもとにヒートマップを生成


やったこと(詳細)


まっさんの歌詞を品詞分解


歌詞取得

歌詞サイトのスクレイピングを行いました。著作権の問題があるので、ここでは記載しません。


品詞分解

品詞分解には Python の形態素解析ライブラリ janome を使用しました。


tokenize_sample.py

#!/usr/bin/env python

# -* encoding: utf-8 -*

from janome.tokenizer import Tokenizer

def main():
for token in Tokenizer().tokenize('心字池にかかる三つの赤い橋'):
print(token)

if __name__ == '__main__':
main()


上記のようなコードを書いて実行すると...

心字池   名詞,一般,*,*,*,*,心字池,シンジイケ,シンジイケ

に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
かかる 動詞,自立,*,*,五段・ラ行,基本形,かかる,カカル,カカル
三つ 名詞,一般,*,*,*,*,三つ,ミッツ,ミッツ
の 助詞,格助詞,一般,*,*,*,の,ノ,ノ
赤い 形容詞,自立,*,*,形容詞・アウオ段,基本形,赤い,アカイ,アカイ
橋 名詞,一般,*,*,*,*,橋,ハシ,ハシ

おぉぉ!心字池がきちんと認識されている!!

と、テンションが上がります。

janome の詳細な仕組みや歴史については、中の方のスライド『Pyconjp2015 - Python で作って学ぶ形態素解析』がありますのでご参照ください。(2015年生まれなんですね!ありがたや!)


地域名を抽出してジオコーディング


地域を抽出

あらかじめスクレイピングしておいた歌詞群を janome で解析し「地域」に該当する単語を拾い出します。

また、単語の登場回数をヒートマップの濃度に反映させようと思いますので、同時に単語のカウントも取りました。

def count_place():

place_count_dict = {}
with open('sada_lyrics.csv','r') as lyrics:
reader = csv.reader(lyrics)
for row in reader:
t = Tokenizer(udic='sada_dict.csv', udic_enc='utf8')
for token in t.tokenize(row[1]):
if '名詞,固有名詞,地域' in token.part_of_speech:
place_name = token.surface
if place_name in place_count_dict:
place_count_dict[place_name] = place_count_dict[place_name]+1
else:
place_count_dict[place_name] = 1
return place_count_dict

カスタム辞書の読み込みも可能です。

janome ドキュメントによると、辞書フォーマットは Mecab と一緒だそうです。


sada_dict.csv

湯島聖堂,1288,1288,5000,名詞,固有名詞,一般,*,*,*,湯島聖堂,ユシマセイドウ,ユシマセイドウ

スカイツリー,1288,1288,5001,名詞,固有名詞,一般,*,*,*,スカイツリー,スカイツリー,スカイツリー

janome ドキュメントの例には『東京スカイツリー』とありますが、皆さんご存知の「かすてぃら」では『スカイツリー』と歌われていますので、『スカイツリー』での登録推奨です。


(おことわり)

本当は固有名詞の地点を特定した結果もヒートマップに反映させたかったのですが、時間の都合で断念しました。

なので、辞書も使ってないです。話題に挙げておきながら、ごめんなさい。


ジオコーディング

ジオコーディングには、これまた Python の googlemaps ライブラリを使用しました。

Google Maps API を利用することになるので、API キーの指定が必要です。

取得方法は googlemaps の GitHub に記載されています。

import googlemaps

def geocode(place_name):
gmaps = googlemaps.Client(key='write your API key')
geocode_result = gmaps.geocode(place_name)
coord = geocode_result[0]['geometry']['viewport']['northeast']
return coord['lat'], coord['lng']


ここまでまとめ

以下は、ここまでの作業(歌詞を品詞分解〜地域名を抽出〜ジオコーディング)を繋げたコードです。


sada_place_geocoder.py

#!/usr/bin/env python

# -* encoding: utf-8 -*

from janome.tokenizer import Tokenizer
import csv
import googlemaps

def main():
writer = csv.writer(open('sada_places.csv','w'), delimiter=',')
place_count_dict = count_place()
gmaps = googlemaps.Client(key='write your API key')
for place_name, place_count in place_count_dict.items():
lat, lon = geocode(gmaps, place_name)
writer.writerow([place_name, place_count, lat, lon])

def count_place():
place_count_dict = {}
with open('sada_lyrics.csv','r') as lyrics:
reader = csv.reader(lyrics)
for row in reader:
t = Tokenizer()
for token in t.tokenize(row[1]):
if '名詞,固有名詞,地域' in token.part_of_speech:
place_name = token.surface
if place_name in place_count_dict:
place_count_dict[place_name] = place_count_dict[place_name]+1
else:
place_count_dict[place_name] = 1
return place_count_dict

def geocode(gmaps, place_name):
geocode_result = gmaps.geocode(place_name)
coord = geocode_result[0]['geometry']['viewport']['northeast']
return coord['lat'], coord['lng']

if __name__ == '__main__':
main()


結果として、地域名・歌詞上の登場回数・緯度・経度が出力されます。

カスタム辞書使っていないこともあり、ゴミレコードがちらほら見られますが今回は無視して進めます。


sada_places.csv

アメリカ,1,49.38,-66.94

バミューダ,1,14.5192371802915,121.0361231302915
秋田,1,39.86527460000001,140.5154199
春日山,1,37.1489639802915,138.2363259802915
ビクトリア,1,48.450518,-123.322346
御宮,1,36.4073904302915,136.4570957
水無瀬,1,34.8791869802915,135.6691649802915
京,3,30.5403905,120.3877692
春の,1,33.8689809,130.8083576
飛鳥,3,38.8972965,139.9375578
日,2,50.68819,5.675110099999999
鎌倉,2,35.3682478,139.5933376
風呂屋,1,34.93531738029149,135.7610285302915
山見,1,36.5698502,136.9701007
エルサレム,1,31.8829601,35.2652869
西の京,1,34.67190798029149,135.7844679802915
加,1,36.5431863,-6.255334599999999
ベルリン,1,52.6754542,13.7611176
喜楽,1,35.1904253,136.7319704
見附,3,37.5933274,139.0009869
ナガサキ,5,35.7377658,139.6976565
浦島,1,35.4839466,139.6447166
愛宕,1,35.9737504,139.6042941
嬉,1,34.4654479,135.5854033
秋篠,1,34.7155978,135.7837222
平安京,1,44.5883529,127.1930004
ホンコン,1,14.4904672802915,121.0242180302915
軽井沢,2,36.4240846,138.6571307
漢,2,32.555258,114.2922103
稲佐,1,32.7592694,129.8647033
京都,1,35.0542,135.8236
武蔵小金井,1,35.70241118029149,139.5080892802915
白萩,1,38.2529733,140.9109412
ちの,3,34.047811,-117.5995851
坂の下,1,35.3120498,139.5356368
ユーコン,1,69.646498,-123.8009179
宅間,1,34.0886418,132.9547384
南京,1,32.3940135,119.050169
フランス,3,51.0891658,9.5597934
実籾,1,35.6882069802915,140.0695889802915
迎,1,37.9205189,112.7839926
音金,1,37.2102144,139.9250478
藪,1,35.3875492,140.1588221
化野,1,35.2016331,135.4969237
海老名,1,35.4774536,139.4364727
れんげ,5,48.02912,8.027220699999999
マゼラン,1,31.8199301,76.95342
みちのく,1,35.5030142302915,139.6870448302915
真珠湾,1,21.3885713,-157.9335744
波止場,1,34.6863148,135.1933421
晴海,1,35.6634906,139.7897775
ニッポン,2,34.6687571,135.5100311
ソフィア,2,42.7877752,23.4569049
アラスカ,4,71.3868712,-129.9945562
揚子江,1,36.4361024802915,139.8532846302915
北前,1,26.3027021,127.7615069
津軽,1,35.0117177302915,135.7573022302915
銀座,1,35.6760255,139.7724941
露,1,51.2964846,22.6735312
合衆国,1,49.38,-66.94
カサブランカ,2,33.6486015,-7.4582757
東京,31,35.817813,139.910202
薬屋,1,35.0155830302915,135.7545184802915
極北,1,12.9797045,15.683687
仏蘭西,1,35.17525588029149,139.6558066802915
五条,1,39.5593820302915,115.7611693
バグダッド,2,33.4350586,44.5558261
観世音寺,1,33.5222913,130.5254343
赤坂,1,35.6782744,139.7459391
仏,4,34.9489952,136.9632495
ニューヨーク,1,40.91525559999999,-73.70027209999999
錦,1,32.2516958,130.9134777
チグリス,1,-15.4044999,-42.8735213
久方,1,35.10910000000001,136.9854947
波打,1,33.9145777,130.8043569
湯島,2,35.711327,139.7724702
平城山,1,34.71184798029149,135.8116589802915
甲子園,1,34.7234607,135.3633836
新宿,2,35.7298963,139.7451654
かすみ,1,24.0234098,82.02101979999999
富士,1,35.3539032,138.8118555
カレー,1,50.9818821,1.9320691
長崎,24,32.9686469,129.9938174
南山手,2,32.7361422,129.8708733
倶知安,1,43.015163,140.9243102
道地,1,36.1243706,139.5655411
坂元,1,37.9298369802915,140.9141139802915
四条,1,35.0044451802915,135.7580809302915
地中海,1,45.7927967,36.215244
あけぼの,1,26.2435843,127.6904124
神楽,1,34.6626033,135.1513682
安曇,2,36.3649943,137.8106765
ヒロシマ,1,31.9163645,131.4305945
峨眉山,1,29.7169085,103.6231299
横浜,1,35.5113,139.674
上野,1,36.1325774,138.8291853
チリ,6,-17.4983293,-66.4169643
用賀,1,35.62797998029149,139.6354899802915
キリマンジャロ,2,-3.0562826,37.3716347
揚羽,1,35.1848028,136.9673238
広島,6,34.4426,132.4865
ナイロビ,1,-1.164744,37.0493746
谷汲,1,35.5452879,136.6135764
練馬,2,35.779946,139.6811359
なんば,1,43.648665,-116.48121
浅草,1,35.7233639,139.8055923
押上,2,35.71155898029149,139.8137769802915
心斎橋,2,30.6801709802915,114.2062109802915
日本,9,45.5227719,145.8175503
都,1,36.0447089,139.3743599
六斗,1,36.0028345,140.1105419
木根,2,36.9430004,137.4747414
立山,1,36.5847934,137.6343407
荒川,1,36.1415564,139.8589857
独,5,41.2296285,141.0143767
君影,1,34.7205324,135.1428907
奈良,3,34.70489999999999,135.8384
上海,2,31.6688967,122.1137989
雲南,4,29.2233272,106.1977228
越,1,36.7995957,138.4063989
祇園,1,34.4529231,132.4693298
信濃,2,36.8707572,138.2803909
東山,1,35.010837,135.7914226
四谷,2,35.6726745,139.4551008
長野,1,36.835842,138.3190722
植木,1,33.6152803,130.5166492


登場回数と座標をもとにヒートマップを生成

先で生成した sada_places.csv に地域ごとの座標・登場回数が記録されているので、この情報を使ってヒートマップに反映させます。

ジオコーディングに Google Maps API を使用したので、ヒートマップも Google Maps API を使ってみました。

スタイルもスクリプトも HTML に書いてしまっていますが、コード量はこんな感じです。

※ヒートマップ表示には Google Maps API の API キーが必要です。下記コードをコピペしても動きませんのでご注意ください。

<!DOCTYPE html>

<html>
<head>
<style>
#map {
width: 1200px;
height: 600px;
}
</style>
<script
src="https://maps.googleapis.com/maps/api/js?key='write your API key'&libraries=geometry,visualization">
</script>
<script>
function initialize() {
var mapCanvas = document.getElementById('map');
var mapOptions = {
center: new google.maps.LatLng(36.83566824724438,138.372802734375),
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(mapCanvas, mapOptions)

var heatmapData = [
//座標の数だけオブジェクトが並ぶ。長いので中略。
//間違いなく外部ファイル化できてる方が良い
{ weight : 2 , location : new google.maps.LatLng(32.7361422,129.8708733) },
{ weight : 4 , location : new google.maps.LatLng(71.3868712,-129.9945562) },
{ weight : 3 , location : new google.maps.LatLng(37.5933274,139.0009869) }
]
var heatmap = new google.maps.visualization.HeatmapLayer({
data: heatmapData,
radius: 50,
map: map
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map"></div>
</body>
</html>

参考にしたページ


結果


日本地図

日本地図

結果を見てみると、予想通りまっさんゆかりの東京・長崎が最も明るいです。

『広島の空』の広島や『昨日・京・奈良、飛鳥・明後日』『修二会』で登場する京都・奈良もそこそこ明るくなっています。

「北海道のやや右上が明るくなっているのはなんだろう?」と思い確認したところ、『日本』のジオコーディング結果がここでした...。


世界地図

世界地図

ズームを引いて世界地図で見てみると、日本以外の地域も数多く歌われている事が分かります。

歌手歴42年大ベテランのはずのまっさん、恐るべきグローバル人材...。

ライブで生歌を聴くたびに涙してしまう『風に立つライオン』のキリマンジャロの白い雪や、

実在した写真家の方をテーマに歌った『極光』『白夜の黄昏の光』で登場するあのアラスカも、ほのかに色付いています。

話逸れますが、『極光』『白夜の黄昏の光』は冬の夜空見ながら聴くととっても泣けるので、「聴いたことないよ」という方はこの機会に是非マスターしてみてください。

「極光」と「白夜の黄昏の光」~さだまさしさんと阿岸充穂さん、そして星野道夫さん

フランス・ドイツあたりも明るくなっていますが、予想以上に明るいようです。

これは品詞分解時に『仏』や『独』という単語が地域として解釈されたことによるものだったので、辞書追加していけば精度向上が見込めるかなと思っています。


さいごに

さだまさし×ITアドベントカレンダー主催者さまが4日目に書かれていた『さだまさしを kuromoji を用いて簡単に品詞分解してみる』の一部内容と思いっきりネタ被り&ヒートマップの結果が薄い内容になってしまって、お恥ずかしい限りです。

実際にやってみて、やっぱりスカイツリーや心字池も表示に反映させたい&Google Maps API でのジオコーディング結果が一部「?」なところもあったので、しっかりと準備と勉強の時間をとって、あらためてヒートマップ作りに再挑戦したいと思いました。

面白い結果が出たら、生さだにハガキ投稿したいなぁ...。