読者です 読者をやめる 読者になる 読者になる

clojureで自分のツイートを触ってみた

このごろclojureを始めたのでその練習に自分のTwitterのつぶやきをいろいろなライブラリを使っていじくってみました.
つかったライブラリはkuromojiとかincantereです.
とりあえずデータはTwitterのアカウント->ユーザ情報の下の方に全ツイート履歴というのがあるのでそこから取ってくる.

今回やったことは以下の3つです

  • 月別のツイート
  • 時間別のツイート
  • よくツイートしている単語

準備

プロジェクトを作成する

lein new twitter
cd twitter

./project.cljを以下のように書き換える

(defproject twitter "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.5.1"]
                 [incanter "1.5.4"]
                 [clj-time "0.6.0"]
                 [org.atilika.kuromoji/kuromoji "0.7.7"]]
  :repositories [["Atilika Open Source repository"
                  "http://www.atilika.org/nexus/content/repositories/atilika"]]
  :main twitter.core
  :aot :all)

依存しているライブラリをよむ

lien deps

./twitter/core.cljに以下を追加

(ns twitter.core
  (:require [clojure.pprint]
            [net.cgrand.enlive-html :as en]
            [clojure.string :as str]
            [clojure.test :as te]
            [clj-time [core :as ct] [format :as ft]])
  (:import [org.atilika.kuromoji Token Tokenizer])
  (:use (incanter core charts stats io datasets pdf))
  (:gen-class))

;データ
(def resource (read-dataset "tweets.csv" :header true))

(read-dataset)incanterというライブラリの関数でcsvを読んでくれる.

月別ツイート

./twitter/core.cljに以下を追加

(defn remove-day
  "年月日から日を取り除く"
  [ymd]
  (let [[y m _] (str/split ymd #"-")]
    (str y m)))

(defn year-month-day
  "年月日を取り出す"
  [timestamp]
  (let [[ymd _ _] (str/split timestamp #" ")]
    ymd))

(defn year-month
  "年月を取り出す"
  [timestamp]
  ((comp remove-day year-month-day) timestamp))

; (year-month "2013-02-06 15:31:33 +0000") ;=> 201302


;月別のツイート数
(let [data (->> ($ :timestamp resource) (map year-month) frequencies sort)]
    (view (bar-chart
           :time :count
           :data (dataset [:time :count] data))))

Twitterから撮ってきたcsvのタイムスタンプが2013-02-06 15:31:33 +0000こんな感じ.
そこで,year-monthを定義して年月を取り出しfrequenciesでそれぞれの頻度を求めて最後にソートする.
そしてincanterのbar-chartを使用してグラフにする.
x軸の他多くの要素があると文字が潰れてしまうのですが直し方がわからず放置しました.

f:id:ganmacs:20131220170702p:plain

時間別ツイート

./twitter/core.cljに以下を追加

(defn get-time
  "時分秒を取り出す"
  [timestamp]
  (let [[_ time _] (str/split timestamp #" ")]
    time))

(defn hour
  "時分秒から時を取り出す"
  [time]
  (let [[hour _ _] (str/split (get-time time) #":")]
    hour))

; (hour "2013-02-06 15:31:33 +0000") ;=> 15

;時間ごとのツイート回数表示
(let [data (->> ($ :timestamp resource) (map hour) frequencies sort)] ;データ作成
  (view (bar-chart "time(h)" "count"
         :data (dataset ["time(h)" "count"] data)))) ;表示

こちらはhourを定義して時間を取り出しfrequenciesでそれぞれの頻度を求めて最後にソートする.
最後にincanterの関数bar-chartで結果を表示.

f:id:ganmacs:20131220170707p:plain

よくツイートしている単語

./twitter/core.cljに以下を追加 この部分はClojure/kuromojiでテキストマイニング入門 ~形態素解析からワードカウントまで~を見てやったのでそっちのほうがわかりやす位と思います.

(defn get-tokenizer "受け取った文字列のtokenizeをかえす"
  [str]
  (let [^Tokenizer get-tokenizer (.build (Tokenizer/builder))]
    (-> get-tokenizer (.tokenize str))))

(defn noun [str]
  (letfn [(noun? [str] (= "名詞" str))
          (split-first [str del] (first (str/split str del)))]
    (for [toke (get-tokenizer str)
          :when (noun? (-> toke .getAllFeatures (split-first #",")))]
      (.getSurfaceForm toke))))

(defn disuse? [str]
  (let [dic #{"Y" "ん" "の" "/" "\\" "(" ")" "." "^"
              "これ" "こと" "あと" "#" "-" "ー" "30" "それ"
              "\\\"" "さ" "thou" "\""
              "1" "2" "3" "4" "5" "6" "7" "8" "9"}]
    (not (contains? dic str))))

; ツイート頻度の高い単語の上位N件をとる
(view (letfn [(url-reply [s] (str/replace s #"@\w+|https?://[\w/:%#\$&\?\(\)~\.=\+\-]+|@\w+" ""))]
        (->> ($ :text resource)
             (map url-reply)
             (map noun)
             flatten
             (filter disuse?)
             frequencies
             (sort-by last)
             reverse
             (take 30))))

リプライを消すために正規表現を書いたり,明らかにおかしいものを弾くように辞書っぽいものを作ったりしました. 一人称が"俺"で"ww"と”笑”と"www"などが笑いの表現が統一されていないということくらいしかわからないというつまらない結果になりました.

f:id:ganmacs:20131220170645p:plain