eスポーツ大会の盛り上がりを可視化してみる


はじめに

先日2020年3月14日にeスポーツチームのCrazy Racoon主催の「第4回 Crazy Raccoon Cup Apex Legends」が生配信サイトMildomで行われました。
(アーカイブ→https://www.mildom.com/playback/10115448/10115448-c16seqdaks9fgfhll5e0)
前回の記事で紹介したMildomのクローラーを使い、大会中の盛り上がり具合を可視化してみました。

ライブラリ準備

pip install websocket-client
pip install mdcrawler

データ収集

from mdcrawler.mildom import Mildom
#Crazy RaccoonのユーザーID
uid = '10115448'

mildom = Mildom()
mildom.observe_room(uid)

配信が始まるタイミングでこちらを実行すると

10115448/1615711502_chats.txt        
10115448/1615711502_viewer_count.txt

が生成されます。
それぞれ

10115448/1615711502_chats.txt ---> 配信中のコメント    
10115448/1615711502_viewer_count.txt ---> 配信中の視聴者数

を表し、上記プログラムを実行中はこれらに取得したデータを追加していきます。
配信終了後、プログラムを終了しました。

可視化

配信中のコメントと視聴者数を取得していました。
盛り上がり具合を見るには、とりあえずコメントの時間窓あたりのコメント数を見ればよさそうです。
そこで、以下のようにデータを整形、可視化しました。

※汚いコードしか書けませんでした、、、

import sys
import pandas as pd
import json
import matplotlib.pyplot as plt
from mdcrawler.mildom import Mildom
import datetime
import numpy as np 
from matplotlib.dates import DateFormatter
from matplotlib.dates import drange
import matplotlib as mpl

def offset_sec_to_min(sec):
    syousu = (sec/60) - int(sec/60)
    return str(int(sec/60)) + ':' + str(int(60*syousu)) 

#配信開始時間を取得。開始時間がわかっていればunix形式で直打ちでもok
mildom = Mildom()
count =1
uid = '10115448'
pb = mildom.get_playbacks_by_uid(uid,count=count)
start_timestamp = int(pb[0].publish_time)
print(start_timestamp)

#前処理
f = open('10115448/1615711502_chats.txt', 'r')
text = f.read()
data = [json.loads(t) for t in text.split('\n') if t!='' ]
df = pd.DataFrame.from_records(data)
df = df[~df.duplicated()]
df['timestamp'] = df['chat_time']
df = df.drop('chat_time',axis=1)
df['timestamp'] = df.timestamp.apply(lambda x: int(x))

#整形
df = pd.DataFrame({
    'timestamp':df.timestamp.value_counts(sort=False).index,
    'cnt':df.timestamp.value_counts(sort=False).values
    })
end_timestamp = df.timestamp.max()
chat_df = pd.DataFrame()
chat_df['timestamp'] = [i for i in range(start_timestamp,end_timestamp+1)]
chat_df = chat_df.merge(df,on='timestamp',how='left')
chat_df = chat_df.fillna(0)
chat_df['cnt'] = chat_df.cnt.apply(lambda x:int(x))
chat_df['cnt_in_5m'] = chat_df.cnt.rolling(window=60*5).sum().shift(-((60*5) -1))
chat_df['offset'] = chat_df.timestamp - start_timestamp
chat_df['offset'] = pd.to_datetime(chat_df.offset,unit='s')
chat_df = chat_df.dropna()
chat_df['offset'] = chat_df["offset"].values.astype(np.datetime64)

#可視化
fig = plt.figure(1, figsize=(12,8))
axes_ = fig.add_subplot(111)
delta = datetime.timedelta(minutes=5)
x = drange(chat_df.offset.min(), chat_df.offset.max(), delta)
axes_.plot(chat_df.offset, chat_df.cnt_in_5m)
axes_.xaxis.set_ticks(x)
axes_.xaxis.set_major_formatter(DateFormatter('%H:%M'))
plt.xticks(rotation=80)
plt.xlabel('timeoffset')
plt.ylabel('chat count')
plt.grid()
fig.savefig('img.png')

すると以下のような結果になります。

x軸が配信開始からの経過時間(アーカイブの再生時間)、y軸が5分時間窓あたりのコメント数です。

2つほど大きなスパイクがあったのでそれぞれ何があったのかアーカイブを見ながら確認します。

赤丸のスパイクは01:05より少し前で起きたようなので、アーカイブの01:04:23~あたりを見てみると...


(https://www.mildom.com/playback/10115448/10115448-c16seqdaks9fgfhll5e0 より)
解説者の方が突然メイド服に着替え登場したシーンでした。

同様に緑丸のスパイクは、順位発表のタイミングで起きていました。

それ以外の小さなスパイクは、バトロワ系ゲームの特徴である最終エリアでの混戦や試合終了のタイミングで起きていることがわかりました。