すのふら

日々の備忘録

【技術編】『ラジカツスターズ!』全102回とコーナー全364問から見えてくるもの

先週書いた『ラジカツスターズ!』の分析で使用したpytonコードをメモとして記載しておく。 snofra.hatenablog.com

実装


ロード部

import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
import numpy as np
import datetime
%pylab inline --no-import-all

#csvファイルのロード
df = pd.read_csv('C:/Users/xxxx/radikatsu_stars_list.csv', index_col=0, parse_dates=[1], engine='python')
df = df.fillna(0)
radikatsu = df.loc[0:] # todo 正解数の抽出できなかったので加工


データ加工部

on_air = radikatsu.index # 放送回数
series_correct_num = radikatsu['正解数'].astype(int) # 正解数全量
series_question_num = radikatsu['出題数'].astype(int) # 出題数全量

# 正解数と出題数をそれぞれ累計
list_cumulative_correct = [] # 正解数
list_cumulative_question = [] # 出題数
list_cumulative_correct_rate = [] # 正解率
cumulative_correct = 0
cumulative_question = 0
cumulative_correct_rate = 0

for x, y in zip(series_correct_num, series_question_num):
    cumulative_correct = cumulative_correct + x
    cumulative_question = cumulative_question + y
    # 暫く0が続くのでその場合は処理を行わない
    if x != 0 and y !=0:
        cumulative_correct_rate = (cumulative_correct / cumulative_question) * 100
    list_cumulative_correct.append(cumulative_correct) # 正解数累計
    list_cumulative_question.append(cumulative_question) # 問題数累計
    list_cumulative_correct_rate.append(cumulative_correct_rate) # 正解率累計

# 各累計数をDataFrameにする
cumulative = pd.DataFrame(
    {'01.on_air': on_air,
     '02.question': list_cumulative_question,
     '03.correct': list_cumulative_correct,
     '04.rate': list_cumulative_correct_rate})
print(cumulative)
cumulative.to_csv("cumulative.csv")

こんな感じで出力 f:id:snofra:20180409003650p:plain


データ加工部(メンバー別)

def member_answer(member):

    #担当回チェック
    list_question = [] #問題数
    list_correct = [] #正解数
    list_date = [] #放送年月
    list_cast = [] #パーソナリティ回数
    bfr_date = None
    question_num = 0
    answer_num = 0
    cast_num = 0
    len_cur = len(radikatsu) 
    # DataFrameの行単位でループ
    for index, i in enumerate(radikatsu.iterrows()):
        # tupleの値部分の取得
        series_row = i[1]

        target = series_row['公開日'].strftime("%Y-%m")

        # パーソナリティ回だった場合
        if series_row[member] == 1:
            # 同年月だった場合累計
            if target == bfr_date:
                question_num = question_num + series_row['出題数']
                answer_num = answer_num + series_row['正解数']
                cast_num = cast_num + 1

        # 1行目もしくは年月が切り替わった場合
        if target != bfr_date and bfr_date is not None:
            list_date.append(bfr_date)
            list_question.append(question_num)
            list_correct.append(answer_num)
            list_cast.append(cast_num)
            # パーソナリティ回だった場合、その年月を設定しなおす。
            if series_row[member] == 1:
                question_num = series_row['出題数']
                answer_num = series_row['正解数']
                cast_num = 1
            else:
                question_num = 0
                answer_num = 0
                cast_num = 0

        bfr_date = target

        # 最終行の追加
        if len_cur == index + 1:
            list_date.append(target)
            list_question.append(question_num)
            list_correct.append(answer_num)
            list_cast.append(cast_num)

    # 年月別の問題/回答数
    member_correct = pd.DataFrame(
        {'01.year': list_date,
         '02.question': list_question,
         '03.correct': list_correct,
         '04.parsonarty': list_cast})
    print(member_correct)
    member_correct.to_csv("member_correct" + member + ".csv")
    
    # 問題数/正解数/正解率の最大値の取得
    question_crr = 0
    answer_crr = 0
    cast_crr = 0
    for x, y, z in zip(list_question, list_correct, list_cast):
        question_crr = question_crr + x
        answer_crr = answer_crr + y
        cast_crr = cast_crr + z
    crr_ans_rate = (answer_crr / question_crr) * 100
   
    # jupiterを日本語対応していないので、適当にタイトルを設定
    if member == "るか":
        title = "Ruka's Question/Correct num"
    elif member == "みき":
        title = "Miki's Question/Correct num"
    elif member == "かな":
        title = "Kana's Question/Correct num"
    elif member == "みほ":
        title = "Miho's Question/Correct num"
    elif member == "ななせ":
        title = "Nanase's Question/Correct num"
    elif member == "せな":
        title = "Sena's Question/Correct num"
    else:
        title = "Rie's Question/Answer num"
        
    # X軸表示用に年月分連番を設定しておく
    serial_no = [index + 1 for index, i in enumerate(list_date)]
    
    # plot
    plt.figure(figsize=(20, 10), dpi=100, linewidth = 100)
    ax = plt.subplot()
    ax.bar(serial_no, list_question, color='#44A5CB', align="center", label="Question num") # 問題数
    ax.bar(serial_no, list_correct, color='#EDAD0B', align="center", label="Correct num") # 正解数
    plt.ylabel("Question/Correct num", fontsize=15)
    ax.legend(loc=2) # 凡例
    plt.title(title, fontsize=15)
    plt.yticks( np.arange(0, 20, 1) )
    plt.xticks(serial_no, list_date, rotation = 90)
    plt.savefig(member + '.png') # グラフのダウンロード
    plt.show()

    return question_crr, answer_crr, crr_ans_rate, cast_crr


メンバー別の表示

list_question_cum = []
list_answer_cum = []
list_crr_ans_rate = []
list_cast_cum =[]

# るか
question_ruka, answer_ruka, rate_ruka, cast_ruka = member_answer('るか')
list_question_cum.append(question_ruka)
list_answer_cum.append(answer_ruka)
list_crr_ans_rate.append(rate_ruka)
list_cast_cum.append(cast_ruka)
# みき
question_miki, answer_miki, rate_miki, cast_miki = member_answer('みき')
list_question_cum.append(question_miki)
list_answer_cum.append(answer_miki)
list_crr_ans_rate.append(rate_miki)
list_cast_cum.append(cast_miki)
# かな
question_kana, answer_kana, rate_kana, cast_kana = member_answer('かな')
list_question_cum.append(question_kana)
list_answer_cum.append(answer_kana)
list_crr_ans_rate.append(rate_kana)
list_cast_cum.append(cast_kana)
# みほ
question_miho, answer_miho, rate_miho, cast_miho = member_answer('みほ')
list_question_cum.append(question_miho)
list_answer_cum.append(answer_miho)
list_crr_ans_rate.append(rate_miho)
list_cast_cum.append(cast_miho)
# ななせ
question_nanase, answer_nanase, rate_nanase, cast_nanase = member_answer('ななせ')
list_question_cum.append(question_nanase)
list_answer_cum.append(answer_nanase)
list_crr_ans_rate.append(rate_nanase)
list_cast_cum.append(cast_nanase)
# せな
question_sena, answer_sena, rate_sena, cast_sena = member_answer('せな')
list_question_cum.append(question_sena)
list_answer_cum.append(answer_sena)
list_crr_ans_rate.append(rate_sena)
list_cast_cum.append(cast_sena)
# りえ
question_rie, answer_rie, rate_rie, cast_rie = member_answer('りえ')
list_question_cum.append(question_rie)
list_answer_cum.append(answer_rie)
list_crr_ans_rate.append(rate_rie)
list_cast_cum.append(cast_rie)

# メンバー別の問題数/正解数/正解率
crr_ans_rate = pd.DataFrame(
    {'01.member': ['るか', 'みき', 'かな', 'みほ', 'ななせ', 'せな', 'りえ'],
     '02.question': list_question_cum,
     '03.answer': list_answer_cum,
     '04.correct_rate': list_crr_ans_rate,
     '05.parsonaroty': list_cast_cum
    })
print(crr_ans_rate)
crr_ans_rate.to_csv("crr_ans_rate.csv")

メンバー別、月別で問題数と正解数を作ったんだけど。分析するにはいまいち言うこともないし、微妙じゃね?ということで結局載せなかった。

f:id:snofra:20180409004757p:plain f:id:snofra:20180409004841p:plain f:id:snofra:20180409004806p:plain f:id:snofra:20180409004815p:plain f:id:snofra:20180409004823p:plain f:id:snofra:20180409004829p:plain f:id:snofra:20180409004835p:plain


せなとみほの正解数推移比較

# せな
list_sena = []
# DataFrameの行単位でループ
for index, i in enumerate(radikatsu.iterrows()):
    # tupleの値部分の取得
    series_row = i[1]

    target = series_row['公開日'].strftime("%Y-%m")

    # パーソナリティ回だった場合
    if series_row['せな'] == 1:
        list_sena.append(series_row['正解数'])

# みほ
list_miho = []
# DataFrameの行単位でループ
for index, i in enumerate(radikatsu.iterrows()):
    # tupleの値部分の取得
    series_row = i[1]

    target = series_row['公開日'].strftime("%Y-%m")

    # パーソナリティ回だった場合
    if series_row['みほ'] == 1:
        list_miho.append(series_row['正解数'])

# seabornでせなとみほの結果をカーネル密度推計でplot
import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(20, 10), dpi=100, linewidth = 100)
plt.xticks([0,1,2,3,4,5])
plt.tick_params(labelsize=18)
plt.xlabel('Answer', fontsize=18)

sns.distplot(pd.DataFrame({'sena':list_sena}), rug=True, hist=False, color = 'pink', kde_kws={'label':'sena'})

sns.distplot(pd.DataFrame({'sena':list_miho}), rug=True, hist=False, color = 'purple', kde_kws={'label':'miho'})
plt.legend(fontsize=18)
plt.savefig('sena_vs_miho.png')

できた画像を多少加工しているけど、こんな感じのグラフができる。 f:id:snofra:20180403000309p:plain

みほ、せなコンビで今度ライブをやるとのことで、ラジカツ優秀勢きたなって思いました(小並感)


メンバー別の表示

# メンバー別質問数と正解数、正解率
appr_member = [1,2,3,4,5,6,7] # X軸の表示用

# plot
plt.figure(figsize=(20, 10), dpi=100, linewidth = 100)
ax = plt.subplot()
ax.bar(appr_member, list_question_cum, color='#44A5CB', align="center", label="Question num") # 問題数
ax.bar(appr_member, list_answer_cum, color='#EDAD0B', align="center", label="Correct num") # 正解数
ax.legend(loc=2) # 凡例
plt.yticks( np.arange(0, max(list_question_cum)+3, 10) )
plt.xticks(appr_member, ['ruka', 'miki', 'kana', 'miho', 'nanase', 'sena', 'rie'], rotation = 90, fontsize=15)
plt.ylabel("Question num", fontsize=15)

ax2 = ax.twinx()
ax2.plot(appr_member, list_crr_ans_rate, linewidth=5, marker='o', markersize=10, color='#C7243A') # 正解率
plt.ylabel("Correct answer rate", fontsize=15)
plt.savefig('Correct answer rate.png') # グラフのダウンロード
plt.show()

すでに用意していたのをベースに作業していたんだけど、ここはmatplotlibよりもbokehにしようと思ってた。
だけど、bokehだとグラフを複数使用したときのY軸が左右にでなくて、右のY軸にメモリなきゃ雰囲気グラフやんと思って、結局matplotlibのままでいくことにした。

ちなみにこんなグラフが出る。
f:id:snofra:20180402010014p:plain


番組への貢献度表示

# 番組への貢献度をbokehでプロット
from bokeh.plotting import figure, output_file, show
from bokeh.io import output_notebook
from bokeh.models import ColumnDataSource, LabelSet, Range1d
output_notebook()

source = ColumnDataSource(data=dict(ans=list_crr_ans_rate,
                                    cast=list_cast_cum,
                                    names=['るか', 'みき', 'かな', 'みほ', 'ななせ', 'せな', 'りえ'],
                                    colors=['orange', 'green', '#ef5285', 'purple', 'blue', 'pink', '#00b9f1']))

p = figure(title = "誰が番組に貢献したか", x_range=Range1d(30, 40),y_range=Range1d(10, 40))
p.xaxis[0].axis_label = 'パーソナリティ回数'
p.yaxis[0].axis_label = '正解率(%)'

p.scatter(x='cast', y='ans', size=8, source=source, color='colors')
labels = LabelSet(x='cast', y='ans', text='names', level='glyph',
              x_offset=5, y_offset=5, source=source, render_mode='canvas')
p.add_layout(labels)

show(p)

こんな感じのグラフが出る。 f:id:snofra:20180402005825p:plain

matplotlibとどう違うんだってのも見てみたけど、実装の楽さや見栄え見てもbokehがいいね。
matplotlibはDataFrame使えないし、色をこまめに変えたい場合実装行数が多くなるのが難点。

matplotlibでの散布図実装

お試しでやってみたので併せて載せておく。

# dataFrameの作成
cumulative = pd.DataFrame(
    {'names': ['るか', 'みき', 'かな', 'みほ', 'ななせ', 'せな', 'りえ'],
     'ans': list_crr_ans_rate,
     'cast': list_cast_cum,
     'colors': ['orange', 'green', '#ef5285', 'purple', 'blue', 'pink', '#00b9f1']})

# メンバー別にパージしておく
ruka = cumulative[0:1]
miki = cumulative[1:2]
kana = cumulative[2:3]
miho= cumulative[3:4]
nanase = cumulative[4:5]
sena = cumulative[5:6]
rie = cumulative[6:7]

# plot
fig = plt.figure()
ax = fig.add_subplot(1,1,1)

ax.scatter(ruka["cast"], ruka["ans"], c=ruka["colors"])
ax.annotate("ruka",xy=(ruka["cast"], ruka["ans"]),size=10)

ax.scatter(miki["cast"], miki["ans"], c=miki["colors"])
ax.annotate("miki",xy=(miki["cast"], miki["ans"]),size=10)

ax.scatter(kana["cast"], kana["ans"], c=kana["colors"])
ax.annotate("kana",xy=(kana["cast"], kana["ans"]),size=10)

ax.scatter(miho["cast"], miho["ans"], c=miho["colors"])
ax.annotate("miho",xy=(miho["cast"], miho["ans"]),size=10)

ax.scatter(nanase["cast"], nanase["ans"], c=nanase["colors"])
ax.annotate("nanase",xy=(nanase["cast"], nanase["ans"]),size=10)

ax.scatter(sena["cast"], sena["ans"], c=sena["colors"])
ax.annotate("sena",xy=(sena["cast"], sena["ans"]),size=10)

ax.scatter(rie["cast"], rie["ans"], c=rie["colors"])
ax.annotate("rie",xy=(rie["cast"], rie["ans"]),size=10)
ax.set_xlabel('parsonality num')
ax.set_ylabel('correct per')

plt.savefig('matplotlib.png') # グラフのダウンロード
plt.show()

f:id:snofra:20180409010928p:plain
これだったらbokehでいいかなー