アニメ公式アカウントのTwitterフォロワー数取得
異常なフォロワー上昇をデータから検知する

author: Unadon (見習い飯炊き兵)
動作環境:Mac OS Sierra 10.12.1; R version3.3.1; rstan 2.10.1 2017_WinterAnime_Follower.png
Figure アニメフォロワー数の推移(2017年1月23日~3月27日)


 

はじめに


アニメは鑑賞して楽しむものですが、
観点を変えればビジネスであり町興しです。

有名なアニメ作品、ラブライブ!を中心に見てみますと、もはやそのリーチ力、支出喚起力は有名アイドルにも勝ります。アニメ作品に乗ったWeb広告やTV広告には大きな効果が期待できるといえるでしょう。

ただ、年間100を超える作品が放映されるがゆえに、”どのアニメにターゲットを絞るか”というのは、難しい問題です(たぶん)。途中で急激に人気爆発なんてケースもありますし、人気になったかと思えば放送が終了を迎える、そんなスピード感も難しさの一因でしょう(たぶん)。

「いまこのアニメがアツい!」ということを、
いち早くデータ・ドリブンで検出できれば・・・

今回はそのような課題にチャレンジしてみたいと思います。


本稿の目的


  1. Twitterのアニメ公式アカウントのフォロワーデータを抽出する
  2. フォロワー数の推移と増加量を時系列で可視化する
  3. 異常にフォロワーが上昇しているタイミングを検出する

ライブラリの読み込み


library(RCurl)
library(rlist)
library(rjson)
library(tidyverse)
library(formattable)
library(anytime)
library(RcppRoll)
library(plotly)
library(scales)

1. Twitterのアニメ公式アカウントのフォロワーデータを抽出


アニメ公式アカウントのフォロワー数は、ある素晴らしいAPIから抽出することができます。RでAPIからデータを取得する方法は、いずにゃんの研究日記にまとめられています。

では、実際に、Rでデータを取得してみたいと思います。

現在から2ヶ月ほど遡った時点からの、フォロワー数推移データを引っ張ってきましょう。

初期設定、マスター情報取得、フォロワー数抽出の3ステップでコードを書いていきます。

今回のターゲットは、2017年冬シーズン(2017年1月から3月末頃)に放送されているアニメです(下記図を参照)。

2017Winter.jpg
Figure 2017年冬アニメ一覧(アニメのX アニメ総合情報サイト)


1.1 初期設定

初期設定では、アニメの放送年、シーズン、エンドポイント(いつまでのデータが欲しいか)、エンドポイントから遡ってどれだけのデータが欲しいかを決めておきます。

少しややこしいのですが、APIでは30分に1回フォロワー数を記録していて、デフォルトで100タイムポイントのデータを吐き出します(つまり50時間分)。

samplingsには、エンドポイントから50時間✕何回分のデータが欲しいかを指定しましょう。30で約2ヶ月です。

# 1. 初期設定
#Initial Setting------------------------------
#アニメの放送年:ベクトル(複数指定可)
yearsBC<-c(2017)
#アニメシーズン:ベクトル(1=冬、2=春、3=夏、4=秋: 複数指定可能)
seasons<-c(1)
#現在から何時間前までのデータを取得するか
endTime<-c(1)
#50時間 ✕ 何回分のデータを取得するか
samplings<-c(30)
#---------------------------------------------

1.2 マスター情報の取得

つづいて、2017年冬期のアニメ情報(マスター情報)を取得しましょう。

ここに登録された情報を使って、あとでフォロワー数を抜き出します。

#
#2. Masterデータを取得し、Twitter公式アカウント名とアニメ名を取り出す
#
master<-data.frame(matrix(NA,0,4))
for(i in 1: length(yearsBC)){
    for(j in 1: length(seasons)){
        tmpFull <- paste("http://api.moemoe.tokyo/anime/v1/master",yearsBC[i],seasons[j],sep = "/") %>% 
        sprintf("Array") %>%
            list.load() %>% list.ungroup() 
        N_anime<-length(tmpFull)/15
        for (k in 1:N_anime){
            tmpTwAcName<-unlist(tmpFull[c((k*15)-13)][1])
            tmpAnime<-unlist(tmpFull[c((k*15)-11)][1])
            master[(length(master[,1])+1),]<-c(yearsBC[i],j,tmpTwAcName,tmpAnime)
        }
    }
}
formattable(master)

公式アカウント名とアニメの短縮名が取得されました(最近のアニメはめちゃくちゃ名前が長いものがあるので)。

1.3 フォロワー数を時系列で抽出

では、マスター情報を使って、初期設定に従いながらフォロワー数を取得します。

10分くらいの時間かかるのでお覚悟を(ならfor文使うなという話ですが…すみません)

#
# 3. Masterデータの情報からフォロワー数の推移を抽出
#

#endpointの決定
subtract_UNIXtime<-c()
for(t in 1:samplings){subtract_UNIXtime[t]<-c((((60*30*100)*t)-(60*30*100))-3)}
end<-c(as.numeric(Sys.time())-c(endTime*60))
endP<-round(c(end-subtract_UNIXtime)-180000,0)

#Follower Historyの抽出
FollowerHistory<-data.frame(matrix(NA,0,5))
for(i in 1: length(master$X1)){
    for(t in 1: samplings){
        tmpRes <- paste("http://api.moemoe.tokyo/anime/v1/twitter/follower/history?account=",master[i,3],"&end_date=",endP[t],sep="") %>%
            sprintf("account") %>%
            list.load("") %>% list.ungroup()
        for(u in 1 : 100){
            FollowerHistory[(length(FollowerHistory$X1)+1),]<-c(master[i,1],master[i,2],master[i,4],as.numeric(unlist(tmpRes[[2*u]][1])),as.numeric(unlist(tmpRes[[(2*u)-1]][1])))
        }
    }
}

#データの列名や時間情報を整理
TidyAnime<-FollowerHistory %>% 
            dplyr::rename(Year=X1,Season=X2,Anime=X3,UnixTime=X4,Follower=X5)%>%
            dplyr::group_by(Anime)%>%
            tidyr::drop_na()%>%
            ungroup()%>%
            dplyr::mutate(Time=anytime(as.numeric(UnixTime),tz = "Asia/Tokyo"))%>%
            group_by(Anime)%>%
            dplyr::mutate(TimePoint=c(1:length(Anime)))%>%
            dplyr::select(Year,Season,Anime,Time,TimePoint,Follower)

#データの確認
formattable(TidyAnime[c(1:9000),])

時刻とアニメの名前、フォロワー数の入ったデータになりました。一旦、csvか何かに保存しておきます。

#
# 4. Writing csv File
#
csvName<-paste(yearsBC,"Season",seasons,round(((50*samplings)/24),0),"days",sep="_")
FileName<-paste(csvName,"csv",sep=".")
write.csv(TidyAnime,FileName)

# reading csv
TidyDat<-read.csv(FileName)
TidyDat$Time<-as.POSIXct(as.character(TidyDat$Time))
TidyDat$TimePoint<-rep(c(1:(100*samplings)),length(master$X1))
TidyDat$Anime <-as.character(TidyDat$Anime)
TidyDat$Date<-as.Date(TidyDat$Time)

2. フォロワー数の推移と増加量を時系列で可視化


データが取得できたので、可視化していきます。

2.1 累積フォロワー数の推移

まずは、累積的にフォロワー数がどれくらいになっているのかを見てみましょう。

#
# 5. Visualization
#

#フォロワー増加推移の可視化(y軸フリーに注意)
ggFollower<-ggplot(TidyDat) +theme_bw(base_size = 8,base_family = "HiraKakuProN-W6")+
    geom_line(aes(x=Time,y=Follower,colour=Anime),size=0.05)+
    geom_point(aes(x=Time,y=Follower,colour=Anime),size=0.1)+
    facet_wrap(~Anime,scales = "free",ncol = 4)+
    guides(colour=FALSE)

ggFollower

y軸が全てのパネルで異なる点にご注意ください。

2017_WinterAnime_Follower.png

Figure1 アニメフォロワー数の推移(2017年1月23日~3月27日)

必ずしもフォロワー数というのは単調増加しているわけではなく、何かのきっかけがあって急激に増加するパターンもあるようです。

2.2 24時間ごとのフォロワー数の増加量

つづいて、フォロワー数の日毎の増加量を確認してみます。

見にくくなるため、24時間で区切って描画します。

FollewerPerDay.png

Figure2 フォロワー数の24時間ごとの増加量(2017年1月23日~3月27日)

毎日コンスタントに数十から数百のフォロワーが増えているというアニメがほとんどでしょうか。

『けものフレンズ』は何かが起こってますね。

『バンドリ』の増加も特徴的です。後半でブレイクしたのでしょうか。


3. 急激にフォロワー数が上昇しているタイミングを検出する


『バンドリ』のデータが使いやすそうだったので、ここからは『バンドリ』に絞って話を進めます。

ちなみに何のアニメ化知らないので、解釈を間違うかもしれません。たぶん女子高生がバンドする系のやつだと思います。

3.2 アニメ『バンドリ』のフォロワー増加数の推移

バンドリに絞ってデータを再度確認しておきます。

#バンドリだけプロット
ggBanDre<-TidyDat %>% group_by(Anime)%>%
    dplyr::mutate(leadFollower=dplyr::lead(Follower))%>%
    dplyr::mutate(delta=Follower-leadFollower)%>%
    dplyr::mutate(delta2=roll_suml(delta,48))%>%
    ungroup()%>%
    tidyr::drop_na()%>%
    dplyr::filter(Anime=="バンドリ")%>%
    ggplot() +theme_bw(base_size = 8,base_family = "HiraKakuProN-W6")+
    geom_hline(yintercept = 0,size=1,linetype=2,colour="gray50")+
    geom_line(aes(x=Time,y=delta,colour=Anime),size=0.2)+
    geom_point(aes(x=Time,y=delta,colour=Anime),size=0.2)+
    facet_wrap(~Anime,ncol=4)+
    guides(colour=FALSE)+
    scale_x_datetime(breaks = date_breaks("3 days"),labels = date_format())+
    theme(axis.text.x = element_text(angle = 90, hjust = 1))
    
ggBanDre

BanDreData.png

Figure3 『バンドリ』30分ごとのフォロワー増加量(2017年1月23日~3月27日)

最後の方で2回ブレイクがあるように見えますが、よくよく見てみると、序盤と中盤にも30分で100以上のフォロワー増加が認められるタイミングがあります。

上記のデータから、”神回がいつ頃あったのか”を割り出せると楽しいな。

アニメは週に1度放送されますので、そういう周期性を加味したモデルを使う必要がありそうでしょうか。

3.2 {AnomalyDetection}で異常値検出

今回はパッケージ{AnomalyDetection} を使用します。

max_anoms=で異常値が全体の何%までか指定します。

今回は1%。特に理由はありません。

#devtools::install_github("twitter/AnomalyDetection")
library(AnomalyDetection)
BanDreAnom<-TidyDat %>% group_by(Anime)%>%
    dplyr::mutate(leadFollower=dplyr::lead(Follower))%>%
    dplyr::mutate(delta=Follower-leadFollower)%>%
    ungroup()%>%
    filter(Anime=="バンドリ")%>%dplyr::select(Time,delta)
BanDreAnom<-data.frame(BanDreAnom)

#異常値検出
res <- AnomalyDetectionTs(BanDreAnom, max_anoms=0.001, direction='pos', plot=TRUE)

res

実行すると、次のようなリターンとともに、グラフが出力されます。

> res
$anoms
             timestamp anoms
1  2017-03-24 01:00:08    69
2  2017-03-24 00:00:06   170
3  2017-03-23 22:00:06   132
4  2017-03-23 19:00:07   136
5  2017-03-23 18:00:07   130
6  2017-03-23 17:30:06   114
7  2017-03-23 16:30:06   127
8  2017-03-23 16:00:08   211
9  2017-03-23 15:30:06   228
10 2017-03-23 15:00:06   205
11 2017-03-23 14:30:07   318
12 2017-03-23 14:00:06   176
13 2017-03-23 13:30:06   190
14 2017-03-23 13:00:08   292
15 2017-03-23 12:30:07   320
16 2017-03-22 13:00:07   138
17 2017-03-17 00:00:07   170
18 2017-03-16 23:30:06   177
19 2017-03-16 23:00:06   188
20 2017-03-16 22:30:07   174
21 2017-03-16 22:00:07   202
22 2017-03-16 21:30:07   228
23 2017-03-16 21:00:06   200
24 2017-03-16 20:30:07   201
25 2017-03-16 20:00:08   203
26 2017-03-16 19:30:06   284
27 2017-03-16 19:00:06   350
28 2017-03-16 18:30:06   339
29 2017-01-24 13:30:07   209
30 2017-01-23 23:30:07    72

$plot

BanDreAnom.png

Figure3 『バンドリ』フォロワー増加の異常検出(2017年1月23日~3月27日)

お、1月23日、3月16日、3月23日に、何かが起こってますね。「神回」でしょうか。

無事、それらしきものを検出できました。


結語とおまけ


この異常検知の枠組みとアニメデータがあれば、

オンラインで解析して広告の最適化をしたり、

TwitterのBotなんかで「神回あったよ」を告知するシステムが組めそうです。

Pythonでやってみようかな。

ちなみに、バンドリの放送開始日が1月21日でした(今回データは1月23日から取得した)。最初のフォロワー増加も納得。

ですが、3月16日と23日は8話と9話が放送される「2日前」でした。

本編放送前々日のフォロワー増加…これは一体どんな理由が隠れているのでしょうか。

ご存じの方いらっしゃいましたら、教えていただけると嬉しいです。

 

Written on March 28, 2017