ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

Youtube Data APIで取得したデータでSubscriber数の予測モデルを生成する

Youtube Data APIとは、Youtubeで公開されているデータを取得することのできるAPIです。ここでは、APIを用いて膨大なチャンネルのSubscriber数、ビデオ数、登録日を取得した後、ElasticNetを用いて予測モデルを作成します。

データ取得のコード例

#!/usr/bin/python

from apiclient.discovery import build
from apiclient.errors import HttpError
from oauth2client.tools import argparser
import pandas as pd

DEVELOPER_KEY = "YOUR API KEY. REPLACE THIS."
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"

def youtube_search(options):
  youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
                  developerKey=DEVELOPER_KEY)

  search_response = youtube.search().list(
    q=options.q,
    type="channel",
    part="id, snippet",
    maxResults=options.max_results
  ).execute()

  channels = []
  channel_publishedAt = []
  channel_videoCount = []
  channel_subscriberCount = []

  # Add each result to the appropriate list, and then display the lists of
  # matching videos, channels, and playlists.
  for search_result in search_response.get("items", []):
    channels.append("%s" % (search_result["id"]["channelId"]))
  
  for channel in channels:
    channel_data = youtube.channels().list(id=channel, part='snippet, statistics').execute()
    channel_item = channel_data["items"][0]
    channel_publishedAt.append(channel_item["snippet"]["publishedAt"])
    channel_videoCount.append(channel_item["statistics"]["videoCount"])
    channel_subscriberCount.append(channel_item["statistics"]["subscriberCount"])

  df_channels = pd.DataFrame()
  df_channels["id"] = channels
  df_channels["videoCount"] = channel_videoCount
  df_channels["publishedAt"] = channel_publishedAt
  df_channels["subscriberCount"] = channel_subscriberCount
  return(df_channels)

if __name__ == "__main__":
  #argparser.add_argument("--q", help="Search term", default="Google")
  frames = []
  argparser.add_argument("--max-results", help="Max results", default=25)
  hiragana = "あ い う え お か き く け こ さ し す せ そ た ち て と な に ぬ ね の は ひ ふ へ ほ ま み む め も や ゆ よ ら り る れ ろ わ を ん が ぎ ぐ げ ご ざ ず ぜ ぞ だ ぢ づ で ど ば び ぶ べ ぼ ぱ ぴ ぷ ぺ ぽ"
  for q in hiragana.split():
    args = argparser.parse_args()
    args.q = q
    frames.append(youtube_search(args))

  result = pd.concat(frames)
  print(result)
  result.to_csv("output_youtube_1.csv")
$ chmod +x ソースファイル名
$ ./ソースファイル名

Rによるクレンジングと探索的データ分析

youtube_data = read.csv("output_youtube_1.csv", header=T)
elapsed_months <- function(end_date, start_date) {
    ed <- as.POSIXlt(end_date)
    sd <- as.POSIXlt(start_date)
    12 * (ed$year - sd$year) + (ed$mon - sd$mon)
}

youtube_data$passed_month <- elapsed_months(Sys.time(), youtube_data$publishedAt)
youtube_data$videoPerMonth <- youtube_data$videoCount / youtube_data$passed_month
youtube_data$videoPerMonth[youtube_data$videoPerMonth == Inf] <- 0
write.csv(youtube_data, file="R_out_youtube.csv", row.names=F)
hist(youtube_data$videoCount)
hist(log(youtube_data$videoCount))
plot(log(youtube_data$videoCount), log(youtube_data$subscriberCount))

Screenshot from 2017-09-23 20-55-19.png Screenshot from 2017-09-23 21-03-38.png

histでデータをプロットすると、logを使うと正規分布に近づくことがわかります。また、投稿数と登録者数を散布図で表すと正の相関を持っているように見えます。

Jupyter notebookでの実行

それでは、機械学習でモデルを生成してみます。

In[1]:

import pandas as pd
import numpy as np
df = pd.read_csv("R_out_youtube.csv")
X = df[["videoCount", "passed_month", "videoPerMonth"]]
y = df["subscriberCount"].astype(float)
X = np.log(X+1)
y = np.log(y+1)

In[2]:

from sklearn.linear_model import ElasticNet
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
from sklearn.preprocessing import MinMaxScaler, StandardScaler
from sklearn.pipeline import Pipeline, make_pipeline

pipe = Pipeline([('preprocessing', None), ('regressor', None)])

param_grid =[
    {
        'regressor':[ElasticNet()], 
        'preprocessing':[StandardScaler(), None],
        'regressor__alpha':[0.001, 0.01, 0.1, 1, 10],
        'regressor__l1_ratio':[0.001, 0.01, 0.1, 1, 10]
    },
]

grid = GridSearchCV(pipe, param_grid, cv=5, scoring='r2')

X_train, X_test, y_train, y_test = train_test_split(X, y)

grid.fit(X_train, y_train)
best_estimator = grid.best_estimator_
display(best_estimator)
Pipeline(memory=None,
     steps=[('preprocessing', None), ('regressor', ElasticNet(alpha=0.1, copy_X=True, fit_intercept=True, l1_ratio=0.001,
      max_iter=1000, normalize=False, positive=False, precompute=False,
      random_state=None, selection='cyclic', tol=0.0001, warm_start=False))])

In[3]:

grid.score(X_test, y_test)
0.48555882068067691

In[4]:

best_estimator.named_steps['regressor'].coef_
array([ 0.92301469,  0.55609977,  0.19573027])

考察

まず、R2スコアは49%であることから、たった3つの特徴量だけで多く説明できていることがわかります。係数の出力でもっとも重要なのは、videoCountで、動画投稿数です。次いで、投稿月数、月当たりの投稿数となっています。

このことから、「動画投稿数が多いほど、登録者数が増える」という直感的な理解が裏付けられていると言えます。しかし、R2値を改善するためには、もっと多くの特徴量を収集しなければなりません。

参考

  1. https://developers.google.com/youtube/v3/quickstart/python