🗂

「大相撲の決まり手って傾向あるの?」をpandasとmatplotlibで調べたよ

2022/07/27に公開

背景

  • データサイエンスを学ぶことにしたので、「どんなことができるのだろうか?」とまずは色々いじってみた感じです。なので分析自体は全然深くないです。
  • 学ぶにあたり、Preferred Networks社のChainerのチュートリアルを一通りやってみました(難しい!!...でもこれ無料、すごい)
  • せっかくなので自分の興味がある領域でまずは色々調べてみようということで、「大相撲の決まり手傾向」をみてみることにしました。

やったこと

  1. 対象データの取得
  2. データの保存
  3. データをいじって傾向をみる

この3点です。
環境はGoogle Colaboratoryを使いました。便利!

内容

対象データの取得

  • 今回は大相撲の直近の結果ということで2022年夏場所(5月)の千秋楽までの15日間、幕内の全力士のデータを取得して見てみることにしました。
  • ということで、早速「日本相撲協会」の取り組みデータを利用させていただくことにしました。

興味のあったpandasでスクレイピングして取得してみる

import pandas as pd
# 取得したい日の取り組みurl
url = "https://www.sumo.or.jp/ResultRikishiDataDaicho/torikumi?day=1&kakuzuke=1&basho_id=611"
df = pd.read_html(url)
df

すると、、、

ValueError: No tables found

いやいや、、、「テーブルありますやん!!」
と早速イラつきながら、他のサイトでの取得を試みます。

ネットサーフィンをした結果、Sportsnaviさんが結果を表示してくれていたので、今度はこちらを参考に取得
すると、、、

[         0         1         2         3         4         5          6  \
 0   初日   2日目   3日目   4日目   5日目   6日目    7日目   
 1  9日目  10日目  11日目  12日目  13日目  14日目  千秋楽   
 
          7  
 0  8日目  
 1      NaN  ,
           東                 東.1 東.2     決まり手  西  \
 0   東横綱  ç
 §ãƒŽå¯Œå£«0勝1敗   ●     押し出し  ○   
 1   東大関     御嶽海1勝0敗   ○     押し出し  ●   
 2     東前2     霧馬山1勝0敗   ○     寄り切り  ●   
 3     西前2     琴ノ若1勝0敗   ○     押し出し  ●   
 4   東関脇     若隆景1勝0敗   ○     寄り切り  â—

「なんじゃこりゃあああ!!!」というレベルの文字化けで怖くて泣いてしまいます。

調べてみると、UTF-8で指定しなさいよということなので

import pandas as pd
# 取得したい日の取り組みurl
url = "https://sports.yahoo.co.jp/sumo/torikumi/stats/?bashoId=202205"
#encodingしないと文字化けしたため
df = pd.read_html(url, encoding = 'utf-8')

すると

[     0     1     2     3     4     5    6    7
 0   初日   2日目   3日目   4日目   5日目   6日目  7日目  8日目
 1  9日目  10日目  11日目  12日目  13日目  14日目  千秋楽  NaN,
        東       東.1.2   決まり手  西       西.1   西.2
 0    東横綱  照ノ富士01敗   ●   押し出し  ○   大栄翔10敗   西小結
 1    東大関   御嶽海10敗   ○   押し出し  ●    高安01敗   東前筆
 2    東前2   霧馬山10敗   ○   寄り切り  ●    正代01敗   西大関
 3    西前2   琴ノ若10敗   ○   押し出し  ●   貴景勝01敗   西大関
 4    東関脇   若隆景10敗   ○   寄り切り  ●  北勝富士01敗   東前3
 5    西前3    玉鷲10敗   ○   突き出し  ●    阿炎01敗   西関脇
・・・
 9   東十10   島津海10敗   ○  突き落とし  ●   武将山01敗  西十10
 10  東十11   平戸海10敗   ○   突き出し  ●   大翔丸01敗  西十11
 11  東十12   松鳳山01敗   ●   寄り切り  ○  熱海富士10敗  西十12
 12  東十13   千代嵐01敗   ●   突き出し  ○    栃丸10敗  西十13
 13  東十14   貴健斗01敗   ●  突き落とし  ○   美ノ海10敗  西十14,
     0                               1
 0 NaN  【スポーツナビ無料公式アプリ】大相撲の最新ニュースを逃さない
 1 NaN  【Yahoo!ニュースアプリ】都道府県別ニュースをまとめ読み]

ずらっと取得することができました。
ここまでですでに一苦労。前途多難。

これだといろんなデータが混ざっているので、幕内のデータだけを取得します。

df[1]

綺麗なDataFrameが取得できました。

	東	東.1.2	決まり手	西	西.1	西.2
0	東横綱	照ノ富士01敗	●	押し出し	○	大栄翔10敗	西小結
1	東大関	御嶽海10敗	○	押し出し	●	高安01敗	東前筆
2	東前2	霧馬山10敗	○	寄り切り	●	正代01敗	西大関
3	西前2	琴ノ若10敗	○	押し出し	●	貴景勝01敗	西大関
4	東関脇	若隆景10敗	○	寄り切り	●	北勝富士01敗	東前3
5	西前3	玉鷲10敗	○	突き出し	●	阿炎01敗	西関脇

これをよく見ると、「うーん、東.1と西.2の列の○勝○敗、邪魔だなあ。あと勝ち負けもわかりやすくしたいなあ」ということで、名前の後ろの勝敗数を削りたいと思います。ちょうど4文字なので後ろから4文字削ります。

df[1]['東.1'] = df[1]['東.1'].str[:-4]
df[1]['西.1'] = df[1]['西.1'].str[:-4]

すると

	東	東.1.2	決まり手	西	西.1	西.2
0	東横綱	照ノ富士	●	押し出し	○	大栄翔	西小結
1	東大関	御嶽海	○	押し出し	●	高安	東前筆
2	東前2	霧馬山	○	寄り切り	●	正代	西大関
3	西前2	琴ノ若	○	押し出し	●	貴景勝	西大関
4	東関脇	若隆景	○	寄り切り	●	北勝富士	東前3
5	西前3	玉鷲	○	突き出し	●	阿炎	西関脇

おおーー、削れてる、削れてるぞお!!
でもここであることに気が付きます。
「あれ?10勝or10敗したら5文字になってまうやん!」
・・・・

仕方ないので面倒臭いですが以下で対応しました。

lendf = len(df[1])

# 二桁の勝ち負けがある力士がいると、これまでの文字列削除ではうまくいかないケースがあるため以下のif文で修正
for i in range(lendf):
  if df[1]['東.1'][i][-5] <= '5': #最大でも15勝あるいは1〜5負のため、5以下で削除
        df[1]['東.1'][i] = df[1]['東.1'][i][:-5]
  else:
        df[1]['東.1'][i] = df[1]['東.1'][i][:-4]

for i in range(lendf):
  if df[1]['西.1'][i][-5] <= '5':
        df[1]['西.1'][i] = df[1]['西.1'][i][:-5]
  else:
        df[1]['西.1'][i] = df[1]['西.1'][i][:-4]
	
# 計算しやすいように東.2の勝ち負けを1,0で表示して代入
dfewl = df[1]['東.2']
lenewl = len(dfewl)

for i in range(lenewl):
    if dfewl[i] == '○':
         dfewl[i] = 1
    else:
         dfewl[i] = 0

# 計算しやすいように西.2の勝ち負けを1,0で表示して代入
dfwwl = df[1]['西'] #east win lose
lenwwl = len(dfwwl)

for i in range(lenwwl): #west win lose
    if dfwwl[i] == '○':
         dfwwl[i] = 1
    else:
         dfwwl[i] = 0

何日目の取り組みかわかるようにDate列を追加しておこう、と思い

# 日付別に比較できるようにdate列を入れる
df[1]['Date'] = 1
# データ表示
df[1]

やっと綺麗なデータになったので、今度はこれを保存します。

データの保存

とりあえず15日分の全データを縦に繋げて、csvに保存しました。
保存先は自分のGoogle Drive

# 全データ結合
dball = pd.concat([df1, df2[1], df3[1], df4[1], df5[1], df6[1], df7[1], df8[1], df9[1], df10[1], df11[1], df12[1], df13[1], df14[1], df15[1]])
print(dball)
# 取得したデータをcsvにしてdatasetに入れる。idexを入れない(取り出す時に使いやすい)
dball.to_csv("/content/drive/MyDrive/sumo/dataset/sumo.csv", index=False)

index=Faleseを入れないとindexごと保存されてしまって、取り出すときに面倒臭い形になるので、ここはFalseで。
データを保存したら、分析用のnotebookを新たに作ってドライブにマウント。
参考:【Google Colaboratory】Google ドライブにマウントし、ファイルへアクセスする方法

データをいじって傾向をみる

まずは保存したcsvの取得です。

import pandas as pd
df = pd.read_csv('/content/drive/MyDrive/sumo/dataset/sumo.csv')
df

これで以下のDataFrameをいじくることができます

	東	東.1.2	決まり手	西	西.1	西.2	Date
0	東横綱	照ノ富士	0	押し出し	1	大栄翔	西小結	1
1	東大関	御嶽海	1	押し出し	0	高安	東前筆	1
2	東前2	霧馬山	1	寄り切り	0	正代	西大関	1
3	西前2	琴ノ若	1	押し出し	0	貴景勝	西大関	1
4	東関脇	若隆景	1	寄り切り	0	北勝富士	東前3	1
...	...	...	...	...	...	...	...	...
294	東前15	東龍	0	足取り	1	照強	西前8	15
295	東前9	琴勝峰	0	はたき込み	1	碧山	東前11	15
296	西前16	翠富士	1	肩透かし	0	栃ノ心	西前9	15
297	東前10	隠岐の海	1	上手投げ	0	明生	西前13	15
298	東前13	千代大龍	0	寄り切り	1	錦木	西前10	15

2022年夏場所の幕内全試合の決まり手傾向

最初に決まり手の数を単純に見てみます。

# 決まり手の技別回数カウント
result = df['決まり手'].value_counts()
print(result)

結果は

押し出し      76
寄り切り      58
突き落とし     28
はたき込み     21
突き出し      17
寄り倒し      11
送り出し      11
押し倒し      10
引き落とし      9
肩透かし       9
小手投げ       8
上手投げ       8
すくい投げ      6
下手投げ       5
上手出し投げ     4
きめ出し       3
不戦勝        2
とったり       2
下手出し投げ     2
きめ倒し       1
徳利投げ       1
つき手        1
切り返し       1
あびせ倒し      1
突き倒し       1
うっちゃり      1
上手ひねり      1
足取り        1

「なるほど、押し出し多いんですねええ!!」
「おおお、足取りってなんですか!!!」

というただのファンの時間が続きます。

せっかくなので棒グラフにします。
ここでmatplotlibを使いたいのでインストールして利用。一応numpyもimportしました。
それと、matplotlibは日本語が文字化けするので、その対応も必要です。

# matplotlibのimport
import matplotlib.pyplot as plt
# numpyのimport
import numpy as np
# 日本語対応
import japanize_matplotlib

# x軸取得
result_name = result.keys()
# y軸取得
result_num = result.values

# グラフのタイトル
plt.title('決まり手比較')
# xラベルの設定
plt.xlabel('決まり手')
# yラベルの設定
plt.ylabel('回数')

# グラフ化
plt.bar(result_name, result_num)
# ラベルサイズ変更
plt.tick_params(axis='x', which='major', labelsize=3)

# 技別決まり手の総数(棒グラフ)
plt.show()

ラベルサイズはtick_paramsで変更できます。
すると

これはわかりやすい、でもx軸の項目、もう小さすぎてわかんないよね。

もうちょっと割合とかも見てみたいな、ということで割合の出し方を調べてみると
先ほどのvalue_countにnormalize=Trueを書いてあげるだけでよさそう。

# 決まり手の技別パーセンテージ
p_result = df['決まり手'].value_counts(normalize=True)

すると

押し出し      0.254181
寄り切り      0.193980
突き落とし     0.093645
はたき込み     0.070234
突き出し      0.056856
寄り倒し      0.036789
送り出し      0.036789
押し倒し      0.033445
引き落とし     0.030100
肩透かし      0.030100
小手投げ      0.026756
上手投げ      0.026756
すくい投げ     0.020067
下手投げ      0.016722
上手出し投げ    0.013378
きめ出し      0.010033
不戦勝       0.006689
とったり      0.006689
下手出し投げ    0.006689
きめ倒し      0.003344
徳利投げ      0.003344
つき手       0.003344
切り返し      0.003344
あびせ倒し     0.003344
突き倒し      0.003344
うっちゃり     0.003344
上手ひねり     0.003344
足取り       0.003344

うーん、これはいいデータ。
せっかくなので円グラフにします。

# 技別決まり手の比率(円グラフ)
print(plt.pie(result_num, labels = result_name))

おお!さっきより断然わかりやすいぞ!ということでフムフムと眺めていると
「あれ?これは押し出し、寄り切り、突き落としで大半決めてるんじゃないの?」ということに気がつきます。

ここまでで得られた結論は

  1. 総試合数は15日で299
  2. 決まり手は押し出し、寄り切りの二つの技で44%を超える。突き落としを入れると53%となり、夏場所は28種の決まり手があったが、その半数以上がこの3つの決まり手で勝敗がついたことになる
  3. 44%を超える押し出し、寄り切りに関しては土俵外に相手側を押し出す決まり手であることから、相手との組手の中で倒すというよりは、土俵外にいかに押し出せるかが、少なくとも夏場所には求められたことがわかる。

ただのファンのくせに最もらしいことを言っています。

全試合の決まり手傾向はなんとなく見えてきたので、上位の力士はどうなのかを調べてみることにしました。

上位力士の決まり手分析

  • ここはかなり手間取ったというか、他に良い方法があったのかもしれませんが、DataFrameを見てみるとわかるとおり、東と西でそれぞれどちらが勝った決まり手なのかが簡単に取り出せません。
    なので、力技で以下の通りデータを整理し直しました。
# Dataframeの長さ
ldf =len(df)
ldf

# 受取用の結果のリスト
res = []

# 元データでは東が勝つパターンと、西が勝つパターンとあるので、勝った方(数値1)の名前と決まり手を一つずつ取ってきて、resリストに返す
for i in range(ldf):
    if df.loc[i]['東.2'] == 1:
      name = df.loc[i]['東.1']
      result = df.loc[i]['決まり手']
      e_res = {
          '力士名':'{}'.format(name),
          '決まり手':'{}'.format(result)
      }
      res.append(e_res)
    else:
      name = df.loc[i]['西.1']
      result = df.loc[i]['決まり手']
      w_res = {
          '力士名':'{}'.format(name),
          '決まり手':'{}'.format(result)
      }
      res.append(w_res)

# resのDataFrame化
r_df = pd.DataFrame(res)

# 力士別勝利数
gname_df = r_df.value_counts('力士名')
print(gname_df)

# 力士別勝ち試合の決まり手
g_df = r_df.groupby(['力士名'])['決まり手'].agg('value_counts')
print(g_df)

groupby機能が結構便利で、これを活用しました。

すると勝利数は以下の通り

力士名
照ノ富士    12
隆の勝     11
大栄翔     11
佐田の海    11
霧馬山     10
宇良      10
・・・
剣翔       1
水戸龍      1
千代丸      1
竜電       1
英乃海      1

「夏場所は結構接戦だったんだな!」 という感想が得られます

力士別は以下のような感じでアウトプットが出てきます

力士名   決まり手  
一山本   突き出し      4
      押し出し      2
      はたき込み     1
      引き落とし     1
佐田の海  押し出し      4
      寄り倒し      3
      寄り切り      3
      すくい投げ     1
剣翔    寄り切り      1
北勝富士  押し出し      3
      寄り切り      1
      引き落とし     1
千代丸   押し出し      1
千代大龍  はたき込み     2
      押し出し      2
      徳利投げ      1
      押し倒し      1
      突き出し      1
      送り出し      1

上のデータから、上位5名の力士は照ノ富士(12)、隆の勝(11)、大栄翔(11)、佐田の海(11)、霧馬山(10)であったことがわかるので、この5人の決まり手を見ていきます。

# 力士別決まり手
gp_df = r_df.groupby(['力士名'])['決まり手'].agg('value_counts',normalize=True)

# トップ5力士分析
print(gp_df['照ノ富士'])
print(plt.pie(gp_df['照ノ富士'], labels = gp_df['照ノ富士'].keys()))

print(gp_df['隆の勝'])
print(plt.pie(gp_df['隆の勝'], labels = gp_df['隆の勝'].keys()))

print(gp_df['大栄翔'])
print(plt.pie(gp_df['大栄翔'], labels = gp_df['大栄翔'].keys()))

print(gp_df['佐田の海'])
print(plt.pie(gp_df['佐田の海'], labels = gp_df['佐田の海'].keys()))

print(gp_df['霧馬山'])
print(plt.pie(gp_df['霧馬山'], labels = gp_df['霧馬山'].keys()))

結果は以下

照ノ富士(横綱)

隆の勝(前頭4枚目)

大栄翔(小結)

佐田の海(前頭12枚目)

霧馬山(前頭2枚目)

「すごい!!・・・すごいぞ!!照ノ富士!!!」 という感想が得られます。

という冗談はさておき、これをみると

1. 照ノ富士(横綱)

  • 寄り切りが25%と一番多く、ついできめ出し(16%)、寄り倒し(16%)と、勝ち試合の半数以上をこの3手で決めている。一方、繰り出した手は8種と他4力士より多い。
  • 寄り切り、きめ出し、寄り倒し、いずれも土俵外へ出す決まり手である

2. 隆の勝(前頭4枚目)

  • 寄り切りが36%と一番多く、ついで押し出し(27%)、突き落とし(18%)と、勝ち試合の80%以上をこの3手決めている
  • 押し出し、寄り倒し、いずれも土俵外へ出す決まり手である

3. 大栄翔(小結)

  • 押し出しが45%と一番多く、ついではたき込み(27%)、引き落とし(18%)、そして突き落とし(9%)とこの4手のみで勝っている。
  • 押し出しで進める一方で、同じくらい駆け引きで相手のバランスを崩すことを得意としているように見える。

4. 佐田の海(前頭12枚目)

  • 押し出しが36%と一番多く、ついで寄り倒し(27%)、寄り切り(27%)、そしてすくい投げ(9%)とこの4手のみで勝っている。
  • 押し出し、寄り倒し、寄り切り、いずれも土俵外へ押し出す決まり手であり、この3手で91%である

5. 霧馬山(前頭2枚目)

  • 寄り切りが30%と一番多く、ついで叩き込み(20%)、押し出し(20%)、と70%の試合をこの3手で勝っている。
  • 押し出し、寄り切り、いずれも土俵外へ押し出す決まり手であり、この2手で50%である

ここまでの結論

  1. 全試合を通して決まり手は土俵外に押し出す手が中心であり、本場所においては、上位5力士にも同じ傾向が見られた
  2. 隆の勝、佐田の海のように土俵外の決まり手を中心とする選手がいる一方で、大栄翔、霧馬山と引き技も組み入れる選手も見られる。
  3. 横綱:照ノ富士はさすがというべきか、バランスよく多様な押しひきの手を決めている。
    土俵外に押し出すフィジカルがベースにあることが大前提であることはあらためて認識できた

感想

  • データを必要な形にすることに思いのほか時間を取られたので、結局ファンの域を超えられない程度の分析にはなったが、客観的にデータを見ることの面白さに気づいた。他の方の分析記事とかみるともっと複雑にさまざまな分析をしているのでもう少し深めたい。
  • スポーツのデータサイエンス、熱い
  • ちょっと話はずれるけれど、初学者でもこの程度なら1週間くらいなので、BizDev,Salesはできた方がやれることの幅が広がりそうだなと思った。

8/5追記:その後、追加の分析をしましたのでまとめました

参考:決まり手八十二手
参考:pandas公式

Discussion