しりとりチャンネル解析入門
みなさん、「しりとり」やってますか?
なかひこくん さんと他98人くらいの仲間 Discord Server では、12月8日に#しりとりチャンネルが解説されて以来、昼夜を問わずしりとりが続いています。
https://scrapbox.io/files/61b4640b0cf557001dba7515.png
始まりの投稿
また、12月9日(次の日!)にはしりとりの根幹ともいえる単語の重複を検知する Bot が導入されました。
https://scrapbox.io/files/61b464cb65e1e100229587e3.png
最近判定処理が遅くなってきた
さて、そんなしりとりチャンネルですが、この3日間でどのように進んできたのでしょうか。
今回は何となく Python + Pandas + Matplotlib という王道構成で、しりとりチャンネルについてちょっとだけ解析してみました。(Pandas も Matplotlib も初めて使いました)
前準備
まずは環境を用意します。今回は、しりとりチャンネルから投稿を取得するために、Discord.py も入れます。 (入れてから気付きましたが、Discord.py はついこの前に開発終了したみたいです)
code:bash
$ pipenv install discord.py pandas matplotlib
$ # 一応 requirements.txt も吐いておく
$ pipenv lock -r > requirements.txt
code:Pipfile.toml
source
verify_ssl = true
name = "pypi"
"discord.py" = "*"
matplotlib = "*"
pandas = "*"
python_version = "3.9"
こんな感じになりました
では Discord からメッセージを持ってきましょう!
code:app.py
import discord
client = discord.Client()
@client.event
async def on_ready():
# 本来であれば全投稿を取るために複数回呼び出すべきだが、700件ぐらいしか投稿がなかったので一発で取ってくる
messages = await channel.history(limit=1000).flatten()
print(messages)
await client.close()
# 今回は自分のアクセストークンで解析するので bot=False にする
client.run(os.environ'TOKEN', bot=False) これで投稿は取得できました
次に Matplotlib の準備をします
今回は PNG で出力するので日本語を表示できるようにします
あとタイムゾーンの設定もします
code:bash
$ # Arch Linux での場合です
$ sudo pacman -S adobe-source-han-sans-jp-fonts
$ fc-cache -fv
$ # もし既に Matplotlib を使ったことがある場合はフォントキャッシュを削除する
$ rm ~/.cache/matplotlib/fontlist-v330.json
code:app.py
import matplotlib.pyplot as plt
# 後で使うので変数に入れておく
tz = 'Asia/Tokyo'
これで日本語かつ日本時間でグラフを出力する準備ができました
解析
いよいよやっていきます
まずは投稿数のグラフを見てみましょう
適当に関数を作って on_ready() でキックする形にします
code:app.py
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import pandas as pd
def plot_messages_per_day(messages):
# 取得した投稿を Pandas の DataFrame に入れる
# 使うカラムは投稿日(created_at)と投稿数(ダミーで数値を入れたけど date で集計できる気がする…)
"messages": range(0, len(messages))
})
# 投稿日のタイムゾーンを修正して index にする
# Discord.py の datatime は timezone が付与されていないので、まず UTC として設定して JST に変換する
df.index = pd.DatetimeIndex(df.date).tz_localize('UTC').tz_convert(tz)
# 元の date カラムは使わないので削除する
df.drop("date", axis=1, inplace=True)
# 30分単位で投稿数を集計する、今回だとこれが一番良い感じのグラフになった
df = df.resample('30T').count()
# グラフの設定をする、横長になるので横幅多めに取る
fig, ax = plt.subplots(1, 1, figsize=(14, 4))
# X 軸のラベルを設定する、今回は 年-月-日 時:分 にする
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d\n%H:%M"))
# X 軸を日付、Y 軸を投稿数でプロット
ax.plot(df.index, df.messages)
# 適当にラベルを設定(per day ではないだろ…)
plt.ylabel("Number of messages per day")
# 分かりやすいようにグリッドを出す
plt.grid()
# PNG に出力する
fig.savefig("messages_per_day.png")
これで解析できます。やってみましょう
code:bash
$ pipenv run python ./app.py
出ました
https://scrapbox.io/files/61b46a7ff6642b001e1a105b.png
やはりチャンネルが出来た瞬間が一番投稿数が多かったみたいですね
夜中と昼間の12時ごろが投稿数が多いみたいです
昼休みと寝る前の自由時間でしょうか
もう失速気味ですね、早めに記事にしておいてよかったです
次は重複数ランキングを見てみましょう
code:app.py
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import pandas as pd
def plot_duplicates_per_day(messages):
# 投稿を Series に入れる、重複検知の Bot は含めない
# value_counts() で出現数を数える、ついでに名前も設定する
c = s.value_counts().rename("counts")
# 2件以上出現した投稿のみにフィルタリングする
df = pd.DataFrame(cc > 1) # グラフの設定、今回は縦長にする
fig, ax = plt.subplots(1, 1, figsize=(4, 8))
# ラベルが長すぎて入らないので位置を調整
fig.subplots_adjust(left=0.4)
# 横倒しの棒グラフでプロット、表示単位は1ずつ
ax.barh(df.index, df.counts, height=0.8)
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
# 適当にラベルを設定
plt.xlabel("Number of duplicate messages")
# 出力!
fig.savefig("duplicates_per_day.png")
出ました
https://scrapbox.io/files/61b46c323eacad001d4305f1.png
結構被ってますね
3トップが インド 栗 耳、インドは何故被った…
プリパラが被っているのがなかひこくん さんと他98人くらいの仲間 Discord Server っぽいですね
最後に投稿数ランキングを出してみましょう
code:app.py
import matplotlib.pyplot as plt
import pandas as pd
def plot_user_ranking(messages):
# bot 以外の投稿したユーザーを Series に入れる
# value_counts() で出現数を数える、ついでに名前も設定する
c = s.value_counts().rename("counts")
# 集計結果を DataFrame に入れる
df = pd.DataFrame(c)
# グラフの設定、今回はデフォで問題なさそう
fig, ax = plt.subplots(1, 1)
# ラベルが長すぎて入らないので位置を調整
fig.subplots_adjust(left=0.2)
# 横倒しの棒グラフでプロット
ax.barh(df.index, df.counts)
# 例によって適当にラベルを設定
plt.xlabel("User ranking")
# 出力
fig.savefig("user_ranking.png")
出ました、が名前が乗っているので数字だけお見せします
見たい人は自分で集計してみてください
https://scrapbox.io/files/61b46dae1f365f0023ee5081.png
結構偏ってますね
ちょっと $ \{y = \frac{1}{x},\ x > 0\} のグラフっぽいですね
https://scrapbox.io/files/61b46ef9bf1b5f0020a5662a.png
1位は160件を越えそうですね
とまあこんな感じで簡単に解析する事ができました。
頻出するジャンルとかになると難しいですが、辞書データとの突合ができれば実現できそうです。
グラフ機能を重複検知 Bot に載せてみてもおもしろいかもしれません。
なお、今回の解析に使ったコードは GitHub に push しておきました。 では皆さん、楽しいしりとりライフを~