For Your ISHIO Blog

データ分析や機械学習やスクラムや組織とか、色々つぶやくブログです。

Jリーグの移籍情報をスクレイピングしてチーム間の関係性を可視化する

湘南ベルマーレ、ルバン杯優勝おめでとうございます。 私は他チームのサポータですが、湘南を応援していました。理由は、梅崎選手が好きなのと、自分が応援するチームと湘南間での移籍が多いからです。

Jリーグの移籍情報で、チーム間のコネクションの強さがわかるかと思い、移籍情報をスクレイピングして可視化してみました。 コードは汚いですがご了承ください。

スクレイピング先は下記ページです。

www.jsgoal.jp

ページはこんな感じです。2015~2017年の3年間におけるJ1、J2チームの移籍を対象にしました。 f:id:ishitonton:20181028124731p:plain

コードは下記になります。BeautifulSoupを利用しています。 スクレイピングした情報をpandasを利用して、データフレーム形式に構造化しました。

import urllib
from bs4 import BeautifulSoup
from collections import defaultdict
import pandas as pd

urls = ["https://www.jsgoal.jp/transfer/2015/?division=J1",
        "https://www.jsgoal.jp/transfer/2016/?division=J1",
        "https://www.jsgoal.jp/transfer/2017/?division=J1",
        "https://www.jsgoal.jp/transfer/2015/?division=J2",
        "https://www.jsgoal.jp/transfer/2016/?division=J2",
        "https://www.jsgoal.jp/transfer/2017/?division=J2"]

d = defaultdict(list)
for url in urls:
    if "2015" in url:
        year = "2015"
    elif "2016" in url:
        year = "2016"
    else:
        year = "2017"
    res = urllib.request.urlopen(url)
    soup = BeautifulSoup(res, "html.parser")
    clubs = soup.find_all(class_="field")
    for club in clubs:
        club_name = club.h1.span.text
        in_out_info = [club.select("div > div")[0], club.select("div > div")[1]]
        for info in in_out_info:
            name = info.select("div > div > b > a")
            team = info.select("div > div > p")
            for a, t in zip(name, team):
                d['name'].append(a.text)
                d["transfer_team"].append(t.text)
                d['team'].append(club_name)
                d['year'].append(year)
df = pd.DataFrame(d)

df.head()をみると、こんな感じです。

f:id:ishitonton:20181028125121p:plain

前処理として、チームをJ1とJ2チームに絞り、不要な文字列の除去と揺らぎを修正します。 主にpandasstrエクセラを利用して文字列を修正しています。

dic=[("千葉","ジェフユナイテッド千葉"),
     ("清水", "清水エスパルス"),
     ("名古屋", "名古屋グランパス"),
     ("京都", "京都サンガF.C."),
     ("湘南", "湘南ベルマーレ"),
... 
    ("栃木", "栃木SC"),
    ("町田", "FC町田ゼルビア"),
    ("横浜FC", "横浜FC"),
    ("讃岐", "カマタマーレ讃岐"),
    ("金沢", "ツエーゲン金沢"),
    ("徳島", "徳島ヴォルティス")]
df = df[df["transfer_team"].str.contains(u'←')]
df["transfer_team"] = df["transfer_team"].str.replace(u'[ ←', '').str.replace(u' ]', '')
df["transfer_team"] = df["transfer_team"].str.split(u'/').str[0]
for d in dic:
    df['team'] = df['team'].str.replace(d[1].decode('utf-8'), d[0].decode('utf-8'))

もう少しきれいになりました。

f:id:ishitonton:20181028125533p:plain

最後に、移籍元チームをone-hotにして、チーム間での移籍件数を集計し、さらにJ1/J2のチームに絞り込みました。

df = pd.concat([df, pd.get_dummies(df.transfer_team)], axis=1)
df_sum = df.groupby('team').sum()
df = df[df.index.tolist()]

元データとしては、こんな感じです。

f:id:ishitonton:20181028130035p:plain

MDSとかクラスタリングとか、どのように可視化しようか考えたのですが、シンプルにヒートマップで可視化したいと思います。matplotlibseabornを利用します。

%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(10,10),dpi=200)
sns.heatmap(df5, annot=True, cbar=True, cmap='hot_r')
plt.show()

可視化結果はこんな感じ。縦軸横軸はそれぞれチームを示していて、交わっているところの点数が高いほど、チーム間の移籍が多いです。今回、チーム名伏せているのですが、virtualenvの参照先がおかしいのか日本語文字化けが直らんので、面倒なので省略しました。おそらく皆さんの環境であれば文字化けないので、気になる方は試してみてください。(後で画像は更新するかも) f:id:ishitonton:20181028130541p:plain

考察

移籍が多いところを見ると、以下のようなところがここ3年では、関係性が強いことがわかりました。

「広島から浦和に選手が移籍しまくる」話もありましたが、ここ最近はないですね。 もっといい感じにするには、下記を考慮すればよいかと思いました。

  • データが3年分しかなかったので、もう何年か分あればより充実した結果になる。
  • ユースからの移籍とかのデータを今回は省いたので、そういったデータも利用すれば「このチームは内製型」みたいな特徴が可視化・定量化できるはず。
  • 期限付き移籍や完全移籍などのステータスを省いているので、そこらへんを重み付けすると面白いかも。

簡単ですが、以上です。