読者です 読者をやめる 読者になる 読者になる

GIS奮闘記

現役GISエンジニアの技術紹介ブログ。主にPythonを使用。

【PythonとGIS】GIS的視点で高校サッカーを分析してみる

さて、とうとうサッカーネタに手を出してみようと思います。最初は高校サッカーにしてみました。「どの県が最強なのか」というのを分析・視覚化してみようと思います。

※ソースと作成したデータはGitHubで公開しています。https://github.com/sanvarie/MinnanoArcGIS

分析の詳細

  • 各年の選手権の成績において、各校にそれぞれポイントを付与します(以下参照)。

優勝 ・・・10ポイント
準優勝 ・・・5ポイント
ベスト4・・・3ポイント

  • 上記のポイントを県ごとに集計してポイント数で順位付を行います。
  • 集計は戦後からとします。

主な技術的要素

  1. スクレイピング   ・・・成績表から高校名をスクレイピングします。今回はこのサイトを対象にしてみました。全国高校サッカー選手権 歴代優勝校(ベスト4)
  2. ジオコーディング  ・・・スクレイピングした高校名からジオコーディングをしてみます。ジオコーディングについてはこちらをご参照ください。Pythonでジオコーディングをやってみる - GIS奮闘記
  3. ポイントのプロット ・・・ジオコーディングした緯度経度でポイントをプロットします。
  4. 集計        ・・・各県ごとにポイントの集計(上述した「分析の詳細」参照)を行います。

バージョン

ArcGIS10.2、Python2.7

インストール

Pandas、geocoderのインストールをお願いします。また、lxml、html5lib、BeautifulSoup4も必要ですのでインストールをお願いします。

事前準備

  1. 「Soccer.gdb」を作成します。
  2. 前回作成した日本地図を「Soccer.gdb」にコピーします(フィーチャクラス名は「Japan」)。
  3. フィーチャクラス「Japan」に「Point」カラムを追加します(型はLong Integer)。f:id:sanvarie:20160228113422p:plain
  4. 「HighShcool」フィーチャクラスを作成します。

f:id:sanvarie:20160228105537p:plain

f:id:sanvarie:20160228105618p:plain

事前準備完了後のイメージです。「Japan」フィーチャクラスには日本地図が格納されており、「HighShcool」フィーチャクラスは空の状態です。
f:id:sanvarie:20160228105821p:plain

「HighShcool」フィーチャクラスにポイントをプロット

ArcPyを使用して「HighShcool」フィーチャクラスにポイントをプロットします。

サンプルコード

無理矢理感は否めませんが、なんとかポイントをプロットすることができました。

■highSchoolSoccer.py

# -*- coding: utf-8 -*-
import arcpy
import pandas as pd
import geocoder
import datetime

def getCoordinate(location_name):
    try:
        #地名から座標を取得する
        ret = geocoder.google(location_name)
    except KeyError, e:
        print "KeyError(%s)" % str(e)
        return

    return ret

def createPoint(name,points,year):

    #ジオコーディング
    loc = getCoordinate(name)

    if loc.lat is not None:
        point = arcpy.Point()
        point.X = loc.lng
        point.Y = loc.lat
        pointGeometry = arcpy.PointGeometry(point,spatial_reference)

        cur = arcpy.da.InsertCursor(infc, ["SHAPE@","Name","Point","Year"])

        #校名のあとに県などがついている場合、分解する
        if name.find(" ") > 0:
            if len(name.split(" ")) == 2:
                name,ken = name.split(" ")
            if len(name.split(" ")) == 3:
                name,ken,nihon = name.split(" ")
        cur.insertRow((pointGeometry,name,points,year))
        del cur
    else:
        print name + u"の座標がとれない"

        #多々良学園などの座標がとれないのでこれで対応
        if name != "":
            name = name + " " + u"日本"
            #浦和南 埼玉 日本とかだとだめなのでこの場合は南浦和にする
            if len(name.split(" ")) == 4:
                name,ken,nihon,nihon2 = name.split(" ")
                createPoint(name,points,year)
            else:
                createPoint(name,points,year)

#対象のフィーチャクラス
infc = "C:\ArcPySample\Soccer.gdb\HighShcool"
spatial_reference=arcpy.SpatialReference(4612)

#対象サイト
html = 'http://www.tigerkaz.info/highschool/senshuken.html'

#HTMLを読込
dataframes = pd.io.html.read_html(html)

#表の部分を取得
table = pd.DataFrame(dataframes[0])

#カラム作成
table.columns = ['LINK','Time','Year','Champion','Prefecture','Time2','Finalist','Prefecture2','Best4','Prefecture3','Best4_2','Prefecture4']

schoolList = []

for key,row in table.iterrows():

    #年度を保持
    if row.Year.year != -1:
        year = row.Year.year

    #戦後の結果のみを対象とする
    if row.Year.year == datetime.datetime(1945,1,1).year:
        break

    if row.Champion == u"優勝":
        continue

    #両校優勝の場合、変なとこに優勝校の一つが入っているので
    if isinstance(row.Champion, float):
        yusho = row.LINK

        if yusho.find("(") > 0:

            yusho = yusho[0:yusho.find("(")]

        #帝京とかだと中国にジオコーディングされてしまうので
        yusho = yusho  + " " + u"日本"

        schoolList.append([yusho,"","","",year])
    else:
        #校名 + 県名でジオコーディングする。国見とかだと変なとこに飛ぶので。
        #ただし、これをやると高校から微妙に座標がずれる。が、とりあえず近くなのでよしとする。
        yusho = row.Champion   + " " + row.Prefecture

        if yusho.find("(") > 0:
            yusho = yusho[0:yusho.find("(")] + " " + row.Prefecture

        #両校優勝の場合列がずれているので
        if row.Finalist == u"(両校優勝)":
            best4 = row.Prefecture2 + " " + row.Best4
            best4_2 = row.Prefecture3 + " " + row.Best4_2
            schoolList.append([yusho,"",best4,best4_2,row.Year.year])
        else:
            junYusho = row.Finalist + " " + row.Prefecture2
            best4 = row.Best4 + " " + row.Prefecture3
            best4_2 = row.Best4_2 + " " + row.Prefecture4
            schoolList.append([yusho,junYusho,best4,best4_2,row.Year.year])

#リストをデータフレームに変換
schooDf = pd.DataFrame(schoolList)
schooDf.columns = ['Champion','Finalist','Best4','Best4_2','Year']

for key,rowS in schooDf.iterrows():
    #ポイントをプロット
    createPoint(rowS.Champion,10,rowS.Year)
    createPoint(rowS.Finalist,5,rowS.Year)
    createPoint(rowS.Best4,3,rowS.Year)
    createPoint(rowS.Best4_2,3,rowS.Year)

実行後、それっぽいところにポイントがプロットされているのがわかります。ただ、ジオコーディングの関係上(コメント参照)、本当の位置からは若干ずれている気がしますが、県からはずれていないのでよしとします。
f:id:sanvarie:20160228111244p:plain

「Japan」フィーチャクラスに各校の成績を反映

ArcPyを使用して「Japan」フィーチャクラスの「Point」カラムに各校のポイントを集計した値を付与します。

サンプルコード

■updatePoint.py

# -*- coding: utf-8 -*-
import arcpy
import pandas as pd

arcpy.env.workspace = "C:\ArcPySample\Soccer.gdb"

#インターセクト
highShcool = arcpy.Intersect_analysis(["HighShcool", "Japan"],"HighShcool_Japan" ,"", "", "")

field_list = []
fields = arcpy.ListFields(highShcool)
for field in fields:
    if field.type != "Geometry":
        field_list.append(field.name)

#属性テーブルをPandasに格納
column = arcpy.da.FeatureClassToNumPyArray(highShcool,field_list,null_value=-9999)
df = pd.DataFrame(column)

#ポイントの集計
df_group = df.groupby('KEN')['Point'].sum()

#集計値をJapanに格納
for i in df_group.index:
    cursor = arcpy.UpdateCursor("Japan")
    for row in cursor:
        if i == row.KEN:
            row.setValue("Point", df_group.ix[i])
            cursor.updateRow(row)
    del cursor


#コードブロック
codeblock = """
def UpdatePoint(Point):
    if Point is None:
        return 0
    else:
        return Point
"""

# 条件式を設定
expression = "UpdatePoint(!Point!)"

#ポイントが0の県に対してフィールド演算
arcpy.CalculateField_management("Japan", "Point", expression, "PYTHON_9.3", codeblock)

このような結果になれば成功です。各県のポイントが算出できたら何かテンションあがってきました!この後の順位付が楽しみですね♪
f:id:sanvarie:20160228130813p:plain

「Japan」フィーチャクラスの「Point」カラムによるレンダリング

まずは「Japan」フィーチャクラスのシンボル設定を以下のようにします。OKを押すとマップ上から日本地図が消えますが問題ありません。
f:id:sanvarie:20160228115955p:plain

サンプルコード

各県ごとに付与されたポイントを以下レンジでレンダリングを行います。数値が大きいほど、その県が強いということがわかりますね。
0
1~10
11~30
31~50
51~100
101~150
151~200

■rendering.py

# -*- coding: utf-8 -*-
import arcpy

mxd = arcpy.mapping.MapDocument("CURRENT")
for lyr in arcpy.mapping.ListLayers(mxd):
    if lyr.symbologyType == "GRADUATED_COLORS":
        if lyr.symbology.valueField == "":
            lyr.symbology.valueField = "Point"
        lyr.symbology.classBreakValues = [0,10,30,50,100,150,200]
        arcpy.RefreshActiveView()

このような感じになりました。
f:id:sanvarie:20160228132129p:plain

結果の分析

  1. 都市圏が強い。有名私立が多いですし、順当な結果といったところでしょうか。
  2. 雪国の成績は今一歩。やはり雪の影響で冬は外でトレーニングをするのが難しいからでしょうか。
  3. 強豪校がある県が強い。当たり前ですが。国見がある長崎は94ポイントを獲得しています。また、雪国ですが、石川県は星陵だけで21ポイント、秋田県秋田商業だけで34ポイントを獲得していますね。
  4. 2校出場できる東京の順位が思ったより高くない。89ポイントを獲得して全体で7位ですが、有名高校の数、毎年2校出場の割には少ないような気もします。
  5. 沖縄、香川、佐賀、山形、新潟、長野、鳥取は残念ながら0ポイント。確かにこの辺の高校はあまり印象に残っていない気がします。
  6. 最強の県は静岡。やはりサッカー王国と言われるだけあります。しかし、近年の成績はあまりふるわなく、1995年に静岡学園が優勝して以来、優勝からは遠ざかっていますね。おそらく直近20年の分析結果では首位ではないような気がします。ちなみに各県の順位は以下のようになりました。

f:id:sanvarie:20160228134230p:plain

課題

  1. 関東などの地域ごとのポイント集計も行えるようにした方がより分析が面白くなりそうですね。
  2. 優勝、準優勝、ベスト4といったようなおおざっぱなポイント付与ではなく、一勝ごとにポイントを付与するなど、もう少し細かい分析が必要。ただ、そのデータをどこから手に入れるのかという問題がありますが・・・

☆番外編☆

せっかくなので、高校ごとの順位もつけてみようと思います。

テーブルの作成

「HighSchool_Record」テーブルを作成します。
f:id:sanvarie:20160228141940p:plain

作成後、GDBの中身は以下のようになります。「HighShcool_Japan」は「updatePoint.py」でインターセクトした結果です。
f:id:sanvarie:20160228142104p:plain

サンプルコード

高校ごとのポイントを集計した結果をテーブルに格納します。

■highSchoolRecord.py

# -*- coding: utf-8 -*-
import arcpy
import pandas as pd

arcpy.env.workspace = "C:\ArcPySample\Soccer.gdb"

field_list = []
fields = arcpy.ListFields("HighShcool")
for field in fields:
    if field.type != "Geometry":
        field_list.append(field.name)

#属性テーブルをPandasに格納
column = arcpy.da.FeatureClassToNumPyArray("HighShcool",field_list,null_value=-9999)
df = pd.DataFrame(column)

#ポイントの集計
df_group = df.groupby('Name')['Point'].sum()

#ポイントの降順でソート
df_group.sort("Point",ascending=False)

cursor = arcpy.InsertCursor("HighSchool_Record")

#集計値を「HighSchool_Record」に格納
for i in df_group.index:
    row = cursor.newRow()
    row.setValue("Name", i)
    row.setValue("Point", df_group.ix[i])
    cursor.insertRow(row)
del cursor

結果を見てみると、帝京が見事に一位を獲得しました!最近はあまり元気がありませんが、やはり名門ですね。
f:id:sanvarie:20160228143127p:plain

全体の順位は以下のようになっています。有名選手の出身高はこの中でも上位が多い気がします。帝京=田中達也、国見=大久保嘉人藤枝東長谷部誠市立船橋カレン・ロバート浦和市立=ごめんなさい、わからない・・・、鹿児島実業遠藤保仁、韮崎=中田英寿、などなど上位は選手がぱっと浮かぶ高校が多いですね。
f:id:sanvarie:20160228143514p:plain

色々やってみて最後に気づいたことがあります。今年の結果がない(笑)。最新の結果を反映したら少し違った結果になるかと思います。今回は単純に選手権の成績だけでしたが、人口密度や高校数を加味した分析などもおもしろそうですね。また、Jリーグなどでも今回とは違った分析が行えそうだと思います。例えば「Jリーグの順位における対象地域への経済効果の差について」のような感じですかね。それをやるには自分自身もっとスキルを上げる必要があるので、頑張りたいと思います。

以上、今回は「【PythonGISGIS的視点で高校サッカーを分析してみる」でした。