概要です。
これを、Clojure と形態素解析ライブラリの Kuromoji を使って求めます。
試した環境です。
- Mac OSX 10.9.2
- Kuromoji 0.7.7
- Clojure 1.6.0
- Java 1.7.0_45
準備
インストールとプロジェクト作成の様子です。この辺りは、前の記事と同じです。
# Homebrew
$ ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
$ brew update
# Leiningen
$ brew install leiningen
# プロジェクトをつくる
$ lein new acidman
$ cd acidman
project.clj
を編集します。
(defproject acidman "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
;; 以下を編集、追記
:dependencies [[org.clojure/clojure "1.6.0"]
[clj-http "0.9.1"]
[org.atilika.kuromoji/kuromoji "0.7.7"]]
:repositories [["Atilika Open Source repository"
"http://www.atilika.org/nexus/content/repositories/atilika"]])
Kuromoji は、Maven にも Clojars にも登録されていないので、:repositories
でリポジトリを指定しています。
形態素解析
形態素解析やります。形態素解析とは?
形態素解析とは、1.文を形態素という意味の最小単位に分割し、2.各形態素を原型に復元し、3.各形態素に品詞を付与する処理です。
ということです。
$ lein repl
user=> (import [org.atilika.kuromoji Token Tokenizer])
org.atilika.kuromoji.Tokenizer
user=> (def tokenizer (.build (Tokenizer/builder)))
# 'user/tokenizer
user=> (def tokens (.tokenize tokenizer "緩やかな波音赤く染まる頃華やいだ白い砂に影が映る"))
# 'user/tokens
user=> (doseq [t tokens] (println (.getSurfaceForm t) "\t" (.getAllFeatures t)))
緩やか 名詞,形容動詞語幹,*,*,*,*,緩やか,ユルヤカ,ユルヤカ
な 助動詞,*,*,*,特殊・ダ,体言接続,だ,ナ,ナ
波 名詞,一般,*,*,*,*,波,ナミ,ナミ
音 名詞,一般,*,*,*,*,音,オト,オト
赤く 形容詞,自立,*,*,形容詞・アウオ段,連用テ接続,赤い,アカク,アカク
染まる 動詞,自立,*,*,五段・ラ行,基本形,染まる,ソマル,ソマル
頃 名詞,非自立,副詞可能,*,*,*,頃,コロ,コロ
華やい 動詞,自立,*,*,五段・ガ行,連用タ接続,華やぐ,ハナヤイ,ハナヤイ
だ 助動詞,*,*,*,特殊・タ,基本形,だ,ダ,ダ
白い 形容詞,自立,*,*,形容詞・アウオ段,基本形,白い,シロイ,シロイ
砂 名詞,一般,*,*,*,*,砂,スナ,スナ
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
影 名詞,一般,*,*,*,*,影,カゲ,カゲ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
映る 動詞,自立,*,*,五段・ラ行,基本形,映る,ウツル,ウツル
nil
楽しい!(☝ ՞ਊ ՞)☝
ACIDMAN の歌詞によく使われている単語
歌詞の収集
歌詞を J-Lyric.net より拝借します。このサイトでは、アーティスト毎に ID が振られていて、artist/*アーティストのID*/
でアーティストの曲一覧、artist/*アーティストのID*/*曲のID*.html
で歌詞を引くことができます。ACIDMAN の場合、 ID は a00b172 です。
src/acidman/core.clj
を編集します。
(ns acidman
(:require [clojure.java.io :as io])
(:require [clj-http.client :as client])
(:import [org.atilika.kuromoji Token Tokenizer]))
;; アーティストの曲と題名のリストを返す
(defn collect-songs [artist]
(->> (:body (client/get (str "http://j-lyric.net/artist/" artist)))
(re-seq #"<div class='title'><a href='/artist/.+?/(.+?).html'>(.+?)</a> </div>")
(map (partial drop 1))))
;; 曲の歌詞を取得する
(defn get-lyric [artist song]
(as-> (:body (client/get (str "http://j-lyric.net/artist/" artist "/" song ".html"))) x
(re-find #"(?s)<p id='lyricBody'>.+?</p>" x)
(clojure.string/replace x #"<.+?>|\r\n" "")))
;; アーティストの曲をダウンロードしてその曲数を返す
(defn download-songs [artist]
(.mkdir (io/file artist))
(let [songs (collect-songs artist)]
(doseq [[song title] songs]
(spit (str artist "/" title) (get-lyric artist song)))
(count songs)))
$ lein repl
user=> (def ^:dynamic *acidman* "a00b172")
# 'user/*acidman*
user=> (require '[clojure.pprint :only pprint])
nil
user=> (load-file "src/acidman/core.clj")
# 'acidman.core/download-songs
user=> (pprint (acidman/collect-songs *acidman*))
(("l0021c4" "リピート")
("l00ceb5" "金色のカペラ")
("l00ceb6" "街の輪郭")
("l01a1a6" "±0")
("l023548" "レガートの森")
("l02bfc2" "ラストコード")
("l023546" "2145年")
("l009df7" "and world")
("l022828" "ALMA")
("l01a1a1" "Bright & Right")
("l009ee0" "calm")
...
("l023545" "ワンダーランド"))
nil
user=> (acidman/get-lyric *acidman* "l0044aa")
"鮮やかだったあの太陽が ... 今 鐘は響く何度でも息を深く吸い込むのだろう"
user=> (acidman/download-songs *acidman*)
98
これを書いている現在、98曲登録されているみたいです。ただし、
英語詞は歌詞カードの対訳を参照した。なお、「equal」収録の「colors of the wind」は ACIDMAN 作詞ではないので除外。また second line も元の曲と同一とみなし除外した。
というルールがあるので、「COLORS OF THE WIND」と「spaced out(second line)」を削除し、対象は96曲とします。それに加えて、今回は英語詞を考慮しません。
ワードカウント
本題です。使われている単語を使用頻度の多い順にリストアップします。
src/acidman/core.clj
に追記します。
;; ディレクトリにあるファイルのリストを返す
(defn find-files [directory]
(->> (io/file directory) file-seq (filter (memfn isFile))))
;; 文章を形態素解析して含まれる単語とその品詞を返す
(defn morphological-analize [sentence]
(->> (.tokenize (.build (Tokenizer/builder)) sentence)
(map #(vector (.getSurfaceForm %) (.getAllFeatures %)))))
;; 文章に含まれる指定したパターンと品詞を持つ単語を返す
(defn search-words [word class sentence]
(->> (morphological-analize sentence)
(filter (fn [[w f]] (and (re-find (re-pattern word) w)
(re-find (re-pattern (str "^" class ",")) f))))
(map first)))
;; 全ファイルに含まれる単語とそれを含むファイルの個数を返す
;; オプションで単語のパターンと品詞を指定できる
;;(デフォルトでは英単語や平仮名一文字の単語を除く)
(defn count-words
[files & {:keys [word class] :or {word "([^ -~ぁ-ん]|[ぁ-ん][ぁ-ん]+)" class ".*"}}]
(->> files
(mapcat (fn [file] (->> (slurp (.getPath file))
(search-words word class)
(map #(hash-map % (hash-set (.getName file)))))))
(apply merge-with into)
(map (fn [[word titles]] (vector word (count titles))))
(sort #(compare (second %2) (second %1)))))
user=> (load-file "src/acidman/core.clj")
# 'acidman/count-words
user=> (pprint (acidman/find-files *acidman*))
(#<File a00b172/2145年>
#<File a00b172/ALMA>
#<File a00b172/and world>
#<File a00b172/Bright & Right>
#<File a00b172/calm>
...
#<File a00b172/香路>)
nil
user=> (pprint (acidman/morphological-analize "時間は溶けて廻る星いつも通り"))
(["時間" "名詞,副詞可能,*,*,*,*,時間,ジカン,ジカン"]
["は" "助詞,係助詞,*,*,*,*,は,ハ,ワ"]
["溶け" "動詞,自立,*,*,一段,連用形,溶ける,トケ,トケ"]
["て" "助詞,接続助詞,*,*,*,*,て,テ,テ"]
["廻る" "動詞,自立,*,*,五段・ラ行,基本形,廻る,メグル,メグル"]
["星" "名詞,一般,*,*,*,*,星,ホシ,ホシ"]
["いつも" "副詞,一般,*,*,*,*,いつも,イツモ,イツモ"]
["通り" "名詞,一般,*,*,*,*,通り,トオリ,トーリ"])
nil
user=> (pprint (acidman/search-words ".*" "名詞" "時間は溶けて廻る星いつも通り"))
("時間" "星" "通り")
nil
名詞、動詞、形容詞でそれぞれ実行してみます。
user=> (pprint (acidman/count-words (acidman/find-files *acidman*) :class "名詞"))
(["世界" 48]
["音" 43]
["風" 39]
["君" 39]
["今" 37]
["空" 34]
["声" 33]
["何" 32]
["中" 32]
["光" 31]
["全て" 31]
["太陽" 30]
["星" 30]
["よう" 30]
["日" 28]
["日々" 25]
["夢" 24]
["手" 24]
["夜" 23]
["目" 22]
["心" 21]
["まま" 21]
["僕ら" 21]
["色" 21]
["それ" 20]
["一つ" 20]
["達" 20]
["人" 19]
["様" 19]
["事" 19]
...
["染め" 1])
nil
user=> (pprint (acidman/count-words (acidman/find-files *acidman*) :class "動詞"))
(["いる" 29]
["消え" 24]
["ゆく" 24]
["忘れ" 23]
["響く" 22]
["生まれ" 20]
["行く" 18]
["流れ" 16]
["溢れ" 15]
["探し" 14]
["閉じ" 13]
["ある" 12]
["笑っ" 12]
["見上げ" 11]
["なる" 11]
["笑う" 11]
["重ね" 10]
["揺れ" 10]
["繋い" 10]
["似" 10]
["見" 10]
["乗せ" 9]
["描い" 9]
["てる" 9]
["行こ" 9]
["求め" 9]
["変わる" 9]
["触れ" 9]
["混ざり" 8]
["浮かべ" 8]
...
["満ちる" 1])
nil
user=> (pprint (acidman/count-words (acidman/find-files *acidman*) :class "形容詞"))
(["遠く" 14]
["無く" 13]
["ない" 10]
["無い" 8]
["深く" 7]
["遠い" 7]
["強く" 7]
["正し" 7]
["美しく" 6]
["正しい" 6]
["いい" 5]
["欲しい" 5]
["なく" 5]
["良い" 4]
["白い" 4]
["古い" 3]
["遠き" 3]
["美し" 3]
["深い" 3]
["浅い" 3]
["儚き" 3]
["眩し" 3]
["高く" 3]
["儚く" 3]
["美しき" 3]
["悲しき" 2]
["小さく" 2]
["赤い" 2]
["儚" 2]
["優し" 2]
...
["鋭く" 1])
nil
楽しい!(☝ ՞ਊ ՞)☝
終わりに
参考情報です。
普段聞かないので作業中にずっと流していたのだけれど、ACIDMAN 最高っぽい(☝ ՞ਊ ՞)☝