「フリースタイルダンジョン」の勝敗データの分析

http://hip-hop.doorblog.jp/archives/44294573.html に勝敗データがあるのでそこからスクレイピングする

In [1]:
import re
import requests
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd

import matplotlib
import matplotlib.pyplot as plt
matplotlib.style.use('ggplot')
font = {'family':'TakaoPGothic'}
matplotlib.rc('font', **font)

%matplotlib inline

データをダウンロードする

In [2]:
url = 'http://hip-hop.doorblog.jp/archives/44294573.html'
path_txt = '44294573.html'
r = requests.get(url)

with open(path_txt, 'w') as f:
    f.write(r.text)

スクレイピング

In [3]:
with open(path_txt) as f:
    txt = ''.join(f.readlines())

soup = BeautifulSoup(txt, 'lxml')

チャレンジャーのプロフィールをスクレイピング

In [4]:
regexp = re.compile('【チャレンジャー(\d+)】(.+?) \((?:(\d+)歳・)?(.+?)\)【1st BATTLE】')
df_challenger = pd.DataFrame(re.findall(regexp, soup.text), columns=['number', 'name', 'age', 'from'])
df_challenger[['number', 'age']] = df_challenger[['number', 'age']].apply(pd.to_numeric, errors='coerce')
df_challenger.style.highlight_max('age', axis=0, color='yellow').highlight_min('age', axis=0, color='lightblue')
Out[4]:
number name age from
0 1 Dragon One 27.0 神奈川県川崎市
1 2 LEON a.k.a. 獅子 16.0 神奈川県横浜市
2 3 SALVADOR 17.0 東京都大田区
3 4 KOPERU 23.0 大阪府豊中市
4 5 HELL BELL a.k.a.MC妖精 21.0 神奈川県平塚市
5 6 Kiss Shot 18.0 千葉県松戸市
6 7 CHICO CARLITO 21.0 沖縄県那覇市
7 8 MC☆ニガリ a.k.a赤い稲妻 19.0 長野県伊那市
8 9 HIDE 25.0 東京都新宿区
9 10 焚巻 25.0 埼玉県川越市
10 11 Lick-G 16.0 神奈川県逗子市
11 12 BALA a.k.a SHIBAKEN 22.0 神奈川県大和市
12 13 黄猿 26.0 東京区足立区
13 14 ENEMY 17.0 神奈川県藤沢市
14 15 掌幻 26.0 東京都墨田区
15 16 RACK 18.0 京都府八幡市
16 17 TKda黒ぶち 27.0 埼玉県春日部市
17 18 言×THEANSWER 17.0 北海道河西郡
18 19 D.D.S nan 沖縄県那覇市
19 20 DOTAMA 30.0 栃木県佐野市
20 21 GOMESS 21.0 静岡県静岡市
21 22 PONY 30.0 山梨県甲斐市
22 23 GADORO 24.0 宮崎県児湯郡
23 24 Mr.Smile 26.0 千葉県柏市
24 25 輪入道 25.0 千葉県千葉市

勝敗のデータをスクレイピング

In [5]:
regexp = re.compile('\((\d)\)\s+(.+?)\s+VS\s+(.+?)\s+\((\d)\)')
df_match = pd.DataFrame(re.findall(regexp, soup.text),
                        columns=['point_challenger', 'name_challenger', 'name_monster', 'point_monster'])
df_match[['point_challenger', 'point_monster']] = df_match[['point_challenger', 'point_monster']].apply(pd.to_numeric, errors='coerce')
df_match.head()
Out[5]:
point_challenger name_challenger name_monster point_monster
0 4 Dragon One T-PABLOW 1
1 3 Dragon One T-PABLOW 2
2 3 Dragon One サイプレス上野 2
3 2 Dragon One サイプレス上野 3
4 1 Dragon One サイプレス上野 4

簡単な集計

In [6]:
df_match.name_monster.value_counts()
Out[6]:
T-PABLOW          24
サイプレス上野           22
漢 a.k.a. GAMI     17
R指定                8
ACE 【隠れモンスター】      4
Mr.Q 【隠れモンスター】     3
般若                 3
Name: name_monster, dtype: int64
In [7]:
average_points_monster = df_match.pivot_table(index='name_monster', values='point_monster', aggfunc=np.mean).sort_values()
average_points_monster
Out[7]:
name_monster
サイプレス上野           2.181818
漢 a.k.a. GAMI     2.235294
T-PABLOW          2.708333
ACE 【隠れモンスター】     3.000000
Mr.Q 【隠れモンスター】    3.333333
般若                3.666667
R指定               3.750000
Name: point_monster, dtype: float64
In [8]:
average_points_challenger = df_match.pivot_table(index='name_challenger', values='point_challenger', aggfunc=np.mean).sort_values()
average_points_challenger
Out[8]:
name_challenger
PONY                   0.000000
MC☆ニガリ a.k.a赤い稲妻       0.000000
HIDE                   0.000000
HELL BELL              0.000000
ENEMY                  0.000000
Kiss Shot              0.500000
黄猿                     0.750000
BALA a.k.a SHIBAKEN    1.000000
RACK                   1.000000
輪入道                    1.000000
Lick-G                 1.333333
GOMESS                 1.500000
LEON a.k.a. 獅子         2.000000
D.D.S                  2.000000
SALVADOR               2.250000
言×THEANSWER            2.333333
KOPERU                 2.333333
Dragon One             2.600000
TKda黒ぶち                2.750000
DOTAMA                 3.000000
GADORO                 3.000000
焚巻                     3.333333
CHICO CARLITO          3.428571
掌幻                     4.166667
Name: point_challenger, dtype: float64
In [9]:
(df_match
 .assign(critical    = lambda df: df.point_monster == 5)
 .assign(be_critical = lambda df: df.point_monster == 0)
 .pivot_table(index='name_monster', values=['point_monster', 'critical', 'be_critical'], aggfunc=np.mean)
 .rename(columns={'be_critical': '被クリティカル率', 'critical': 'クリティカル率', 'point_monster': '平均得点'})
 .style
 .highlight_max(axis = 0, color='yellow')
 .highlight_min(axis = 0, color='lightblue'))
Out[9]:
被クリティカル率 クリティカル率 平均得点
ACE 【隠れモンスター】 0.0 0.25 3.0
Mr.Q 【隠れモンスター】 0.0 0.333333 3.333333
R指定 0.125 0.625 3.75
T-PABLOW 0.166667 0.208333 2.708333
サイプレス上野 0.227273 0.090909 2.181818
漢 a.k.a. GAMI 0.294118 0.176471 2.235294
般若 0.0 0.333333 3.666667

Massey の手法でレーティングを構成

In [10]:
names = np.concatenate((df_match.name_challenger.unique(), df_match.name_monster.unique()))
y = (df_match.point_challenger - df_match.point_monster).values
X = pd.DataFrame(index=df_match.index, columns=names).fillna(0)

for i, row in df_match.T.iteritems():
    X.ix[i, row.name_challenger] =  1
    X.ix[i, row.name_monster]    = -1
    
X = X.values
In [11]:
M = X.T @ X
M[0, :] = 1

p = X.T @ y
p[0] = 0

ratings = np.linalg.solve(M, p)
ratings = pd.DataFrame(ratings, index=names, columns=['ratings'])
In [12]:
average_points = pd.concat((average_points_challenger, average_points_monster))
df_ratings = ratings.merge(pd.DataFrame(data=average_points, index=average_points.index), left_index=True, right_index=True)
df_ratings.columns = ['massey', 'avgpoint']
In [13]:
(df_ratings
 .sort_values(by='massey', ascending=False)
 .style
 .background_gradient(cmap='viridis'))
Out[13]:
massey avgpoint
般若 7.563462 3.666667
Mr.Q 【隠れモンスター】 6.082024 3.333333
R指定 5.331953 3.75
焚巻 5.230129 3.333333
ACE 【隠れモンスター】 4.76701 3.0
DOTAMA 4.550156 3.0
GADORO 4.415358 3.0
掌幻 3.057341 4.166667
CHICO CARLITO 2.043734 3.428571
TKda黒ぶち 1.417573 2.75
D.D.S 0.387413 2.0
ENEMY 0.331953 0.0
MC☆ニガリ a.k.a赤い稲妻 0.331953 0.0
Dragon One 0.168975 2.6
サイプレス上野 0.072566 2.181818
T-PABLOW -0.186413 2.708333
KOPERU -0.519746 2.333333
LEON a.k.a. 獅子 -0.927434 2.0
SALVADOR -1.214132 2.25
漢 a.k.a. GAMI -1.241851 2.235294
言×THEANSWER -1.575185 2.333333
GOMESS -1.927434 1.5
Lick-G -2.519746 1.333333
BALA a.k.a SHIBAKEN -2.927434 1.0
RACK -3.186413 1.0
黄猿 -3.686413 0.75
輪入道 -4.241851 1.0
HELL BELL -4.927434 0.0
PONY -5.186413 0.0
Kiss Shot -5.241851 0.5
HIDE -6.241851 0.0
In [14]:
df = (df_ratings
      .sort_values(by='massey', ascending=True)
      .assign(color = lambda d: np.where(d.massey >= 0, 'yellow', 'lightblue')))

x = range(df.shape[0])

plt.figure(num=None, figsize=(4, 15))
plt.barh(x, width=df.massey.values, color=df.color)
plt.ylim((0, 31))

for i, xi in enumerate(x):
    plt.text(0, xi+0.3, df.index[i], horizontalalignment='center')
In [15]:
%load_ext version_information
%version_information numpy, pandas, bs4, requests
Out[15]:
SoftwareVersion
Python3.5.1 64bit [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)]
IPython4.0.1
OSLinux 3.19.0 49 generic x86_64 with debian jessie sid
numpy1.10.1
pandas0.17.1
bs44.4.1
requests2.8.1
Sat Mar 05 15:56:51 2016 JST