恋するクチコミ分析
ggplot 、pandas、NLTKを使った
ソーシャルメディアのテキストデータ解析
2013 Shizuoka.py #3
自己紹介
♪ オーイシ(@oec014)
♪ webサービス、電子書籍の企画・デザインなど
♪ ソーシャル音楽ランキングサイトの運営
「BeatCaster.net」は…
ソーシャルデータを利用したメディアの構築や
コンテンツ消費に結びつくデータ(分析)の発見
などがテーマの実験的なサービスです。
「恋するフォーチュンクッキー」
AKB選抜総選挙で選ばれたメンバーによる32枚目の
シングル。
シングル選抜総選挙の結果をうけ、初のセンターとなった指原。
HKT活動拠点福岡でのMVのロケーション。フジテレビお台場合衆
国2013テーマソング。公式「踊ってみた」動画…など、バズになり
得るコンテクストがたくさん埋め込まれ、広範囲のインターネット
利用者とコンテンツの接触機会を企て、AKB48のバズプロモーショ
ンの成功事例になった曲………と思われる。
プロモーションの舞台となったSNSでは、
どのような情報がシェアされていたので
しょうか?

Twitterのつぶやきを分析することで
何かわかるかもしれませんよね?
内容
・データのクリーニング
・「恋するフォーチュンクッキー」を含むTWの分布図
・頻出単語を調べる
・頻出単語の分布を調べる
データはtwitterに2013/9/17 10/11の25日間に
つぶやかれた「恋するフォーチュンクッキー」を含む
つぶやきデータ約49,000件。
期間については超適当。
まずはつぶやきの数をggplot for pythonで可視化で
きるようにデータをクリーニングしてみます。
クリーニング用のスクリプト
import csv
import re
#TW本文で入ってしまいがちな改行やカラムの分割をクリーニングします。
with open("test_akb.csv","rb") as infile , open("outfile.csv","wb") as outfile:
reader = csv.reader(infile)
writer = csv.writer(outfile)
#つぶやき部分に出現する分割されたカラムを結合し
#正規表現でアルファベットと数字以外にマッチしたら
#本来先頭のカラムに有るべきtwアカウントではないとざっくり処理
#本当はラインのカラム数でフィルターしたが良いのだけど(実装方法が…)
pat = re.compile("^[a-zA-Z0-9]")
for line in reader:
item = line[0]
if pat.match(item):
newline = line[:4] +['-'.join(line[4:])]
writer.writerow(newline)
クリーニング後のCSV
ggplot for python でつぶやき数のプロット
# -*- coding: utf-8 -*import pandas as pd #pandas!
import numpy as np
import datetime as dt
import csv
from pandas import DataFrame, Series
from ggplot import *
path = 'outfile.csv' #クリーニング済のCSV
df = pd.read_csv(path,skiprows=1,names=['user','userid','date','location','txt'])
tw = pd.DataFrame(df, columns = ['date'])
tw['date'] = pd.to_datetime(tw['date'])
tw['date'] = tw['date'].apply(lambda x: x.date())
tw['tws'] = 1
tc = tw.groupby(['date']).count()
#ggplot for pythonはaes_string未対応でindexの値をそのまま利用できない?
tc['date'] = tc.index
gp = ggplot(aes(x="date",y="tws"),data=tc)
print(gp + geom_line() + scale_x_date(format = "%b-%Y") +xlab("")+ylab("Daily tws"))
plt.show(1)
日ごとのつぶやきの数
単語の出現頻度の分布
#データフレームからTW本文のみ書き出し平文コーパスとして出力しておきます
tws = pd.DataFrame(df, columns = ['txt'])
tws.to_csv('akb482.csv', sep='t', header=False, index=False,
encoding='utf-8')

NLTK(Natural Language Toolkit - 自然言語ツールキット)
を使って、以後テキストを自然言語処理していきます。
# -*- coding: utf-8 -*import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import nltk
from nltk.corpus.reader import *
from nltk.corpus.reader.util import *
from nltk.text import Text
平文コーパスからトークン化
#ひらがな/漢字のようにセンテンスの語種から単語に分割
jp_sent_tokenizer = nltk.RegexpTokenizer(u'[^ 「」!?。]*[!?。]')
jp_chartype_tokenizer = nltk.RegexpTokenizer(u'([a-z]+¦[ぁ-んー]+¦[ァ-ンー]+¦
[u4e00-u9FFF]+¦[^ぁ-んァ-ンu4e00-u9FFF]+)')
corpus = PlaintextCorpusReader("", r'akb48.txt',
encoding='utf-8',
para_block_reader=read_line_block,
sent_tokenizer=jp_sent_tokenizer,
word_tokenizer=jp_chartype_tokenizer)
stopwords = nltk.corpus.stopwords.words('english')
symbols = ["'", '"', '`', '.', ',', '-', '!', '?', ':', ';', '(',
')','。','、','「','」','!','-',' ','【','】','。-',';',':','#','[',']']
#単語の出現数をカウントしてみる
text = nltk.Text(corpus.words())
freq = nltk.FreqDist(text)
print "n".join("%st%d" % (w,f) for w,f in freq.items()[:50] if w not in symbols)
#分布図の作図
fdist = nltk.FreqDist(w.lower() for w in text if w not in stopwords + symbols)
fdist.plot(50, cumulative=True)
頻出単語の累積数プロット(平文コーパスから)
MeCabで形態素解析したタグ付きコーパスを出力
# -*- coding: utf-8 -*import sys
import MeCab
mecab = MeCab.Tagger('-Ochasen')
f = open('akb48s1.txt')
sent = f.read()
result = mecab.parse(sent)
fs = open('result1.txt', 'w')
fs.writelines(result)
fs.close()
MeCabのChaSen形式データからトークン化
#ひらがな/漢字のようにセンテンスの語種から単語に分割
ngword = [u"'",u'"',u'クッキー',u'恋する',u'フォーチュ
ン',u'://',u'co',u'Ver',u'48',u'10',u'2013']
jeita = ChasenCorpusReader('', r'result.txt', encoding='utf-8')
corpus = nltk.Text(jeita.words())
clean = []
for w in corpus:
if not w in ngword:
clean.append(w)
tl = []
for w in clean:
if len(w) > 1:
tl.append(w)
#単語の出現数をカウントしてみる
freq = nltk.FreqDist(tl)
print "n".join("%st%d" % (w,f) for w,f in freq.items()[:50] if not w in ngword)
#分布図の作図
fdist = nltk.FreqDist(w.lower() for w in tl)
fdist.plot(50, cumulative=True)
頻出単語の累積数プロット(ChaSenデータから)

Stopwordsの設定しだいの恣意的なデータではありますが
ここから(ひらがなを抜いた)上位の単語を利用します。
単語の出現数のプロット
path = 'outfile.csv'
df = pd.read_csv(path,skiprows=1,names=['user','userid','date','location','txt'])
w = pd.DataFrame(df, columns = ['date','txt'])
w['date'] = pd.to_datetime(w['date'])
words = ['http','握手','公式','youtube','全国','価格','staff']
tw = ['word0','word1','word2','word3','word4','word5','word6']
#wordsの値が'txt'にあるときword0 word4に1を加える
for a in range(7):
w[tw[a]] = 0
for (i,j) in enumerate(w['txt']):
if words[a] in j:
w[tw[a]][i] = 1
w = w.drop('txt',1)
w.index= w['date']
wc = w.resample('D', how='sum') #日ごとの集計
wc['date'] = wc.index
wc_lng =
pd.melt(wc[['date','word0','word1','word2','word3','word4','word5','word6']],id_vars=['date'])
gp = ggplot(aes(x="date",y="value", colour='variable'),data=wc_lng)
print(gp + geom_line() + scale_x_date(format = "%b-%Y") +xlab("")+ylab(""))
plt.show(1)
「恋するフォーチュンクッキー」を含む
つぶやきの中の単語出現数
つぶやき数と重ねて単語頻度をプロットしてみます
w = w.drop('txt',1)
w.index= w['date']
w['tweet'] = 1 #<追加
wc = w.resample('D', how='sum')
wc['date'] = wc.index
# tweet の項目追加
wc_lng = pd.melt(wc[['date','tweet','word0','word1','word2','word3','word4',
'word5','word6']],id_vars=['date'])
gp = ggplot(aes(x="date",y="value", colour='variable'),data=wc_lng)
print(gp + geom_line() + scale_x_date(format = "%b-%Y") +xlab("")+ylab(""))
plt.show(1)
「恋するフォーチュンクッキー」を含む
つぶやき中の単語出現数
一応…簡単に傾向を考える
・つぶやき投稿数のピーク形成時にはテキストに
word0(http-つまりなんらかのリンク)、
word2(公式)という単語が関連している?
・上記の単語の相関が低そうなピークもある。
今後……
・BeatCaster.netにNLPを使った予測機能ややレコメ
ンド機能などを追加していきたい。
・django+EC2+S3に一部サービス構築-移行中

Shizuokapy 3 oec_nlp