Twitterの呟きビックデータを感情分析して時系列に可視化する方法
Twitterの呟きビックデータをGolangで取り出して、形態素解析する
GWが終わりを迎え、悲しみにくれる中、「私、能力は平均値でって言ったよね!」と「ぐらんぶる」を1日中見ていました。 Amazon 星3のアニメは最高ですね。可愛い女の子がキャッキャしてるだけで良い。 GWの終わりに感情の起伏が激しいアニメを見るのは辛いのですよ。わかりますか?平坦が良いんですよ。平坦が…
ぐらんぶるは下ネタが大丈夫な人であれば見れると思います。 監獄学園が好きな人は好きなアニメだと思います。
癒やされたところで、寝る前にサクサクとツイートを取得するコードを書いていきましょう。
何をするのか
Twitterの呟きビックデータを取得し、感情コーパスに当てはめ感情の起伏を時系列に見ようと考えてます。
STEP1:Twitterの呟きデータをダウンロード STEP2:ダウンロードした呟きデータをプログラムから読み込み STEP3:呟きデータを形態素解析にかけて字句毎に分割する STEP4:字句毎の感情コーパスに当てはめ感情推定を実施する
STEP2とSTEP3を組み合わせて実行していきます。
STEP2:ダウンロードした呟きデータをプログラム読み取る
tweet.jsをそのまま取り込み、構造体にデータを詰め込んでいきます。 javascriptなので、Json形式で取り込むことができそうですが修正箇所が多そうなので手詰めしていきます。 Json読み込み版は「未確認コード(おまけ)」として置いておきます。
やっていることは、tweet.jsを一行ずつ読み込み ツイート用の構造体にデータを詰め込んで行きます。
どの要素に詰め込むかについては、読み取った行毎に含まれる単語から決めています。
全文
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
)
type Tweet struct {
retweeted bool
source string
entities Entities
urls Urls
display_text_range string
favorite_count string
id_str string
truncated bool
retweet_count string
id string
created_at string
favorited bool
full_text string
lang string
}
type Entities struct {
hashtags string
symbols string
user_mentions Usermentions
}
type Usermentions struct {
name string
screen_name string
indices []int
id_str int
id int
}
type Urls struct {
url string
}
func convertTweet(tweetString string, tweet *Tweet) bool {
tweets := strings.TrimSpace(tweetString)
arrtweet := strings.Split(tweets, ":")
arrtweet[0] = strings.Replace(arrtweet[0], "\"", "", -1)
arrtweet[0] = strings.TrimSpace(arrtweet[0])
if len(arrtweet) > 1 {
switch string(arrtweet[0]) {
case "tweet":
return true
case "retweeted":
tweet.retweeted = false
case "source":
tweet.source = arrtweet[1]
case "hashtags":
tweet.entities.hashtags = arrtweet[1]
case "symbols":
tweet.entities.symbols = arrtweet[1]
// case "user_mentions":
case "urls":
tweet.urls.url = arrtweet[1]
case "display_text_range":
tweet.display_text_range = arrtweet[1]
case "favorite_count":
tweet.favorite_count = arrtweet[1]
case "id_str":
tweet.id_str = arrtweet[1]
case "truncated":
tweet.truncated = false
case "retweet_count":
tweet.retweet_count = arrtweet[1]
case "id":
tweet.id = arrtweet[1]
case "created_at":
tweet.created_at = arrtweet[1] + arrtweet[2] + arrtweet[3]
case "favorited":
tweet.favorited = false
case "full_text":
tweet.full_text = arrtweet[1]
case "lang":
tweet.lang = arrtweet[1]
default:
// fmt.Println("Nonm")
}
}
return false
}
func main() {
data, _ := os.Open("./tweet.js")
defer data.Close()
scanner := bufio.NewScanner(data)
var alltweet []Tweet
var tweet Tweet
for scanner.Scan() {
flag := convertTweet(scanner.Text(), &tweet)
if flag == true { // tweetが来たら次のTweetに移行
alltweet = append(alltweet, tweet)
tweet = Tweet{}
}
}
for _, tweet := range alltweet {
fmt.Println(tweet.created_at, ":", tweet.full_text)
tweetstr := tweet.full_text
cmdstr := "echo " + tweetstr + "|jumanpp.exe --model=jumandic.jppmdl"
stdout, err := exec.Command("sh", "-c", cmdstr).Output()
if err != nil {
fmt.Println(err)
}
fmt.Printf("ls result: \n%s", string(stdout))
}
}
STEP3と組み合わせて使用するコード
各ツイートの内容を構造体から取り出して前の記事のコードと組み合わせます。 Windows10にJUMAN++をインストールし、Golangから実行結果を受け取る方法
for _, tweet := range alltweet {
fmt.Println(tweet.created_at, ":", tweet.full_text)
tweetstr := tweet.full_text
cmdstr := "echo " + tweetstr + "|jumanpp.exe --model=jumandic.jppmdl"
stdout, err := exec.Command("sh", "-c", cmdstr).Output()
if err != nil {
fmt.Println(err)
}
実行結果
各ツイートの内容を形態素解析した結果が得られることが確認できます。
未確認コード(おまけ)
tweet.jsの中身をJSONとして読み込めるように修正します。 冒頭の変数名と「[」末尾の「]」を消去すると良いようです。 ファイル名もtweet.jsonに変えておきましょう。
json.Unmarshalでtweet.jsonを読み取り、構造体に詰め込むコード例を書いておきます。 tweet.jsonに余計な「,」が含まれているので取り込むことはできていないです。
Golangのコードは、多分まぁこんな感じで書けば良いと思います。 ※こんなの乗せるなよって話ですよ。
取得したデータ(tweet.js)を修正せずに、そのまま取り込んで処理できる方が好みです。 もしこの方法で取り込むことができればコメントください。
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
)
type TwitterData struct {
Tweet struct {
Retweeted bool `json:"retweeted"`
Source string `json:"source"`
Entities struct {
Hashtags []struct {
Text string `json:"text"`
Indices []string `json:"indices"`
} `json:"hashtags"`
Symbols []interface{} `json:"symbols"`
UserMentions []struct {
Name string `json:"name"`
ScreenName string `json:"screen_name"`
Indices []string `json:"indices"`
IDStr string `json:"id_str"`
ID string `json:"id"`
} `json:"user_mentions"`
Urls []struct {
URL string `json:"url"`
ExpandedURL string `json:"expanded_url"`
DisplayURL string `json:"display_url"`
Indices []string `json:"indices"`
} `json:"urls"`
Media []struct {
ExpandedURL string `json:"expanded_url"`
SourceStatusID string `json:"source_status_id"`
Indices []string `json:"indices"`
URL string `json:"url"`
MediaURL string `json:"media_url"`
IDStr string `json:"id_str"`
SourceUserID string `json:"source_user_id"`
ID string `json:"id"`
MediaURLHTTPS string `json:"media_url_https"`
SourceUserIDStr string `json:"source_user_id_str"`
Sizes struct {
Thumb struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"thumb"`
Medium struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"medium"`
Small struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"small"`
Large struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"large"`
} `json:"sizes"`
Type string `json:"type"`
SourceStatusIDStr string `json:"source_status_id_str"`
DisplayURL string `json:"display_url"`
} `json:"media"`
} `json:"entities"`
PossiblySensitive bool `json:"possibly_sensitive"`
DisplayTextRange []string `json:"display_text_range"`
FavoriteCount string `json:"favorite_count"`
InReplyToStatusIDStr string `json:"in_reply_to_status_id_str"`
IDStr string `json:"id_str"`
InReplyToUserID string `json:"in_reply_to_user_id"`
Truncated bool `json:"truncated"`
RetweetCount string `json:"retweet_count"`
ID string `json:"id"`
InReplyToStatusID string `json:"in_reply_to_status_id"`
CreatedAt string `json:"created_at"`
Favorited bool `json:"favorited"`
FullText string `json:"full_text"`
Lang string `json:"lang"`
InReplyToScreenName string `json:"in_reply_to_screen_name"`
InReplyToUserIDStr string `json:"in_reply_to_user_id_str"`
ExtendedEntities struct {
Media []struct {
ExpandedURL string `json:"expanded_url"`
Indices []string `json:"indices"`
URL string `json:"url"`
MediaURL string `json:"media_url"`
IDStr string `json:"id_str"`
ID string `json:"id"`
MediaURLHTTPS string `json:"media_url_https"`
Sizes struct {
Thumb struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"thumb"`
Medium struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"medium"`
Small struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"small"`
Large struct {
W string `json:"w"`
H string `json:"h"`
Resize string `json:"resize"`
} `json:"large"`
} `json:"sizes"`
Type string `json:"type"`
DisplayURL string `json:"display_url"`
} `json:"media"`
} `json:"extended_entities"`
} `json:"tweet"`
}
func main() {
raw, err := ioutil.ReadFile("./tweet.json")
if err != nil {
fmt.Println(err.Error())
}
var timeline []TwitterData
if err := json.Unmarshal(raw, &timeline); err != nil {
log.Fatal(err)
}
for _, tweet := range timeline {
fmt.Println(tweet.Tweet.FullText)
}
}
まとめ
とりあえず動くものから始め徐々にレベルアップを図っていきたいですね。 巧遅拙速ですよ。