目次


NBAにおけるソーシャル・メディアの力、影響、実績, 第 2 回

個々の NBA プレイヤーを調査する

Python、pandas、そして少々の R

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: NBAにおけるソーシャル・メディアの力、影響、実績, 第 2 回

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:NBAにおけるソーシャル・メディアの力、影響、実績, 第 2 回

このシリーズの続きに乞うご期待。

はじめに

この連載の第 1 回では、データ・サイエンスと機械学習の基礎について学び、Jupyter Notebook、pandas、scikit-learn を使用して NBA チームとその評価額との間の関係を探りました。今回は NBA プレイヤーについて、ソーシャル・メディア、年俸、試合出場中の成績の間にある関係を調べます。

統合データ・フレームを作成する (警告: 大変な作業が待っています!)

まず始めに、新しい Jupyter Notebook を作成して、nba_player_power_influence_performance という名前を付けます。

次に、プレイヤーに関するすべてのデータをロードして、それらのデータを 1 つの統合データ・フレームにマージします。

複数のデータ・フレームを処理するという作業は、データ・サイエンスでの作業の 80 パーセントを占める重労働のカテゴリーに分類されます。リスト 1 とリスト 2 に、basketball-reference データ・フレームをコピーして、列名のいくつかを変更する方法を示します。

リスト 1. Jupyter Notebook をセットアップしてデータ・フレームをロードする

import pandas as pd
import numpy as np
import statsmodels.api as sm
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans
color = sns.color_palette()
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))
%matplotlib inline
attendance_valuation_elo_df = pd.read_csv("../data/nba_2017_att_val_elo.csv")
salary_df = pd.read_csv("../data/nba_2017_salary.csv")
pie_df = pd.read_csv("../data/nba_2017_pie.csv")
plus_minus_df = pd.read_csv("../data/nba_2017_real_plus_minus.csv")
br_stats_df = pd.read_csv("../data/nba_2017_br.csv")

リスト 2. plus minus データ・フレーム内の列に含まれる不良データを修正する

plus_minus_df.rename(columns={"NAME":"PLAYER"}, inplace=True)
players = []
for player in plus_minus_df["PLAYER"]:
    plyr, _ = player.split(",")
    players.append(plyr)
plus_minus_df.drop(["PLAYER"], inplace=True, axis=1)
plus_minus_df["PLAYER"] = players
plus_minus_df.head()

以下に、NAME 列を PLAYER 列に名前変更するコマンドの出力を記載します。この出力では、余分な列もドロップされています。inplace=TRUE と、既存のデータ・フレームに適用するドロップ処理に留意してください。

図 1. NBA データ・セットのロードと記述
コマンドの出力を示す画像
コマンドの出力を示す画像

次のステップでは、Basketball Reference からの統計の大半を保持する中核的データ・フレームを、名前を変更してマージします。それには、リスト 3 とリスト 4 に記載するコードを使用します。

リスト 3. basketball reference データ・フレームを、名前を変更してマージする

nba_players_df = br_stats_df.copy()
nba_players_df.rename(columns={'Player': 'PLAYER','Pos':'POSITION', 'Tm': "TEAM", 'Age': 'AGE'}, inplace=True)
nba_players_df.drop(["G", "GS", "TEAM"], inplace=True, axis=1)
nba_players_df = nba_players_df.merge(plus_minus_df, how="inner", on="PLAYER")
nba_players_df.head()

リスト 4. PIE フィールドをクリーンアップしてマージする

pie_df_subset = pie_df[["PLAYER", "PIE", "PACE"]].copy()
nba_players_df = nba_players_df.merge(pie_df_subset, how="inner", on="PLAYER")
nba_players_df.head()

図 2 に示す出力では、列が 2 つに分割されて作成し直されています。列の分割と再作成は一般的な作業であり、データ・サイエンスの問題を解決する際にデータを処理する時間の大半は、この作業が占めます。

図 2. マージされた PIE データ・フレーム
5 行×37 列のデータを示す画像
5 行×37 列のデータを示す画像

ここまでのところ、データ処理のほとんどは比較的単純でわかりやすいものでしたが、これから先は難しくなってきます。それは、欠落しているレコードがあるためです。111 件のレコードが欠落しているため、リスト 5 では、この問題に対処する 1 つの方法は、欠落している行をドロップするマージ処理を行うことです。欠けているデータに対処する手法はさまざまです。この例に示してあるように、欠落している行を単にドロップするという手法が常に最善であるとは限りません。このリンク先の「Titanic: Machine Learning from Disaster」に欠けているデータに対処する数多くの例が紹介されているので、いくつかのサンプル・ノートブックを調べてみる価値は大いにあります。

リスト 5. 年俸データをクリーンアップする

salary_df.rename(columns={'NAME': 'PLAYER'}, inplace=True)
salary_df.drop(["POSITION","TEAM"], inplace=True, axis=1)
salary_df.head()

リスト 6 に、データが欠けている行の数を計算するためのセットを作成する方法が示されています。この便利な手法は、2 つのデータ・フレーム間の違いを判断する際に非常に有益です。この手法で使用している Python の組み込み関数 len() は、通常の Python プログラミング内でリストの長さを調べる目的でも一般的に使用されています。

リスト 6. 欠けているレコードを検出してマージする

diff = list(set(nba_players_df["PLAYER"].values.tolist()) - set(salary_df["PLAYER"].values.tolist()))
len(diff)

Out[45]:  111
nba_players_with_salary_df = nba_players_df.merge(salary_df)

上記のコードによる出力は以下のとおりです。

図 3. データ・フレーム間の違い
データ・フレーム間の違いを示す画像
データ・フレーム間の違いを示す画像

データ・フレームのマージが完了したら、次は相関マップを作成して、どの特徴が互いに関連しているかを調べます。以下に記載するヒートマップには、35 の列と 342 の行の相関関係を結合した出力が示されています。このヒートマップですぐに気付くのは、年俸は獲得ポイントと WINS_RPM の両方に密接に関連しているという点です (WINS_RPM は、プレイヤーが試合に出場することでチームが獲得すると推定される勝利を計算する高度な統計です)。

別の興味深い相関関係としては、Wikipedia のページ閲覧数が Twitter のお気に入りの数と密接に関係していることも挙げられます。Wikipedia と Twitter はどちらも NBA プレイヤーへのファンのエンゲージメントとプレイヤーの人気度の指標であることから、この相関関係は直感的に納得がいきます。これは、機械学習モデルに取り込む特徴を突き止めるのに役立つ視覚化の一例です。

図 4. NBA プレイヤーの相関ヒートマップ: 2016 年~ 2017 年のシーズン (統計および年俸)
ヒートマップの画像
ヒートマップの画像

手始めとして相互に関係する特徴をいくつか見つけたところで、次は、Seaborn 内でグラフを描画して、データに潜む関係をさらに見つけ出します。このグラフを描画するために実行するコマンドは、以下のとおりです。

リスト 7. Seaborn lmpot による年俸と WINS_RPM のグラフ描画

sns.lmplot(x="SALARY_MILLIONS", y="WINS_RPM", data=nba_players_with_salary_df)

以下に示す描画の出力から、年俸と WINS_RPM の間には強力な線形関係があることがわかります。この関係をさらに詳しく調べるために、線形回帰を実行します。

図 5. Seaborn lmplot による年俸と勝利の Real Plus-Minus 統計グラフ
年俸 (100 万ドル単位) の x 軸と勝利の y 軸を示す画像
年俸 (100 万ドル単位) の x 軸と勝利の y 軸を示す画像

以下に、勝利に対する 2 つの線形回帰の出力を示します。とりわけ興味深い調査結果のうちの 1 つは、勝利は得点よりも WINS_RPM によって説明されることです。得点の R-squared (適合度) は 0.200 であるのに対し、WINS_RPM は 0.324 となっています。WINS_RPM は、特定のプレイヤーを要因とする勝利のそれぞれを説明する統計です。このようにディフェンスとオフェンスの統計と、試合に出場した時間を考慮した高度な統計のほうが、単なるオフェンスの統計よりも正確な予測となるのは当然のことです。

高度な統計に基づく予測が実際にどのような形で表われるのかを説明する例として、シュート率はかなり低いが獲得ポイントが高いプレイヤーがいるとします。シュート率が高いチームメイトではなく、彼が何度もシュートしたとすると、彼は勝利を逃していたでしょう。この例は 2015 年~ 2016 年のシーズンに現実になりました。当時、Los Angeles Lakers で現役最後の年を迎えていた Kobe Bryant は、シーズンあたりのポイントとして 17.6 を獲得しましたが、ツー・ポイントのシュート率は 41 パーセントでした。結局、チームの勝利数は 17 試合にとどまり、WINS_RPM 統計は 0.66 にすぎませんでした (彼はシーズン中に勝利の半分にしか貢献しませんでした)。

図 6. 勝利データの線形回帰
勝利データの線形回帰を示す画像
勝利データの線形回帰を示す画像

リスト 8. 勝利と獲得ポイントの回帰

results = smf.ols('W ~POINTS', data=nba_players_with_salary_df).fit()
print(results.summary())

この関係をグラフで表すには、Python の ggplot を使用するという方法もあります。リスト 9 に一例として、グラフの描画をセットアップする方法を示します。Python で作成された ggplot ライブラリーは、R 言語での ggplot を直接移植したものであり、現在も活発に開発が進められています。このチュートリアルを作成している時点では、通常の R ggplot ほど使いやすくはありませんが、多数の便利な機能が用意されています。以下に、ggplot を使用して描画したグラフを示します。
注: 便利な機能の 1 つとして、別の連続変数の列を色で表すことができます。

リスト 9. Python ggplot

from ggplot import *
p = ggplot(nba_players_with_salary_df,aes(x="POINTS", y="WINS_RPM", color="SALARY_MILLIONS")) + geom_point(size=200)
p + xlab("POINTS/GAME") + ylab("WINS/RPM") + ggtitle("NBA Players 2016-2017:  POINTS/GAME, WINS REAL PLUS MINUS and SALARY")
図 7. Python ggplot による年俸とポイントの Real Plus-Minus 統計グラフ
勝利数/RPM の x 軸とポイント数/試合の y 軸を示す画像
勝利数/RPM の x 軸とポイント数/試合の y 軸を示す画像

NBA プレイヤーの Wikipedia ページ閲覧数を取得する

次のタスクは、通常は雑多なデータの集合となっている Wikipedia ページの閲覧数の情報を収集する方法を考え出すことです。問題には以下が含まれます。

  1. Wikipedia (または何らかの Web サイト) からデータを取得する方法を解明する
  2. プログラムによって Wikipedia ハンドルを生成する方法を解明する
  3. 取得したデータをデータ・フレームに書き込んで、他のデータに結合する

以下のコードは、このチュートリアル用の GitHub リポジトリー内にあります。以降のセクション全体をとおして、このコードに関するコメントを説明します。

リスト 10 に、JSON レスポンスを返す Wikipedia URL を構成するコードを記載します。第 1 回で、docstring に構成体へのルートが示されています。この URL をコードが呼び出して、ページの閲覧データを取得します。

リスト 10. Wikipedia、パート 1

"""
Example Route To Construct:

https://wikimedia.org/api/rest_v1/ +
metrics/pageviews/per-article/ +
en.wikipedia/all-access/user/ +
LeBron_James/daily/2015070100/2017070500 +

"""
import requests
import pandas as pd
import time
import wikipedia

BASE_URL =\
 "https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/en.wikipedia/all-access/user"

def construct_url(handle, period, start, end):
    """Constructs a URL based on arguments

    Should construct the following URL:
    /LeBron_James/daily/2015070100/2017070500
    """


    urls  = [BASE_URL, handle, period, start, end]
    constructed = str.join('/', urls)
    return constructed

def query_wikipedia_pageviews(url):

    res = requests.get(url)
    return res.json()

def wikipedia_pageviews(handle, period, start, end):
    """Returns JSON"""

    constructed_url = construct_url(handle, period, start,end)
    pageviews = query_wikipedia_pageviews(url=constructed_url)
    return pageviews

リスト 10 のパート 2 では、Wikipedia ハンドルを作成するために、まず、名と姓はプレイヤーの名前であると推測してハンドルを作成します。それでエラーとなった場合は、URL の末尾に "(basketball)" を付加してハンドルの作成を試みます。これにより、ほとんどのケースが解決されて、欠けている名前/ハンドルはわずかな数だけになります。最初の推測では、例えば「LeBron」が名、「James」が姓であるといった具合に推測します。なぜ、まずこのように推測するのかというと、この名前のパターンは Wikipedia ページの 80 パーセント近くに一致し、URL を一つひとつ見つける時間を節約できるためです。このパターンに一致しない残りの 20 パーセントの名前については、そのうちの 80 パーセントに一致させる別の方法があります (以下を参照)。

Wikipedia はバスケットボールの分野で有名な名前と他の名前とを区別するために、"(basketball)" を追加するため、この表記を使うことによって、一致しなかった名前の大部分を捕捉できます。リスト 10 のパート 2 に、残りの名前を見つけるための最後の手段を示します。

リスト 10. Wikipedia、パート 2

def wikipedia_2016(handle,sleep=0):
    """Retrieve pageviews for 2016"""

    print("SLEEP: {sleep}".format(sleep=sleep))
    time.sleep(sleep)
    pageviews = wikipedia_pageviews(handle=handle,
            period="daily", start="2016010100", end="2016123100")
    if not 'items' in pageviews:
        print("NO PAGEVIEWS: {handle}".format(handle=handle))
        return None
    return pageviews

def create_wikipedia_df(handles):
    """Creates a Dataframe of Pageviews"""

    pageviews = []
    timestamps = []
    names = []
    wikipedia_handles = []
    for name, handle in handles.items():
        pageviews_record = wikipedia_2016(handle)
        if pageviews_record is None:
            continue
        for record in pageviews_record['items']:
            pageviews.append(record['views'])
            timestamps.append(record['timestamp'])
            names.append(name)
            wikipedia_handles.append(handle)
    data = {
        "names": names,
        "wikipedia_handles": wikipedia_handles,
        "pageviews": pageviews,
        "timestamps": timestamps
    }
    df = pd.DataFrame(data)
    return df


def create_wikipedia_handle(raw_handle):
    """Takes a raw handle and converts it to a wikipedia handle"""

    wikipedia_handle = raw_handle.replace(" ", "_")
    return wikipedia_handle

def create_wikipedia_nba_handle(name):
    """Appends basketball to link"""

    url = " ".join([name, "(basketball)"])
    return url

リスト 10 のパート 3 では、プレイヤーの登録名簿にアクセスできるようにすることによって、ハンドルの推測を容易にしています。コードのこの部分は、上記に示した突き合わせコードを、この記事で前に収集した NBA 登録名簿全体に対して実行します。

リスト 10. Wikipedia、パート 3

def wikipedia_current_nba_roster():
    """Gets all links on wikipedia current roster page"""

    links = {}
    nba = wikipedia.page("List_of_current_NBA_team_rosters")
    for link in nba.links:
        links[link] = create_wikipedia_handle(link)
    return links

def guess_wikipedia_nba_handle(data="data/nba_2017_br.csv"):
    """Attempt to get the correct wikipedia handle"""

    links = wikipedia_current_nba_roster()
    nba = pd.read_csv(data)
    count = 0
    verified = {}
    guesses = {}
    for player in nba["Player"].values:
        if player in links:
            print("Player: {player}, Link: {link} ".format(player=player,
                 link=links[player]))
            print(count)
            count += 1
            verified[player] = links[player] #add wikipedia link
        else:
            print("NO MATCH: {player}".format(player=player))
            guesses[player] = create_wikipedia_handle(player)
    return verified, guesses

リスト 10 のパート 4 では、このスクリプト全体が、CSV ファイルを入力として使用し、別の CSV ファイルに出力としての役割を持たせて動作します。Wikipedia Python ライブラリーを使用してページを調べ、最終的な一致結果の中から「NBA」という単語を検索することに注目してください。これは、それまでの推測手法で見つからなかったページを見つけるための最終チェックです。NBA プレイヤーの Wikipedia ハンドルを取得するには、これらのヒューリスティックな手法のすべてを使用することが、比較的信頼性の高い方法になります。ご想像どおり。他のスポーツにも、これと同じような手法を適用できます。

リスト 10. Wikipedia、パート 4

def validate_wikipedia_guesses(guesses):
    """Validate guessed wikipedia accounts"""

    verified = {}
    wrong = {}
    for name, link in guesses.items():
        try:
            page = wikipedia.page(link)
        except (wikipedia.DisambiguationError, wikipedia.PageError) as error:
            #try basketball suffix
            nba_handle = create_wikipedia_nba_handle(name)
            try:
                page = wikipedia.page(nba_handle)
                print("Initial wikipedia URL Failed: {error}".format(error=error))
            except (wikipedia.DisambiguationError, wikipedia.PageError) as error:
                print("Second Match Failure: {error}".format(error=error))
                wrong[name] = link
                continue
        if "NBA" in page.summary:
            verified[name] = link
        else:
            print("NO GUESS MATCH: {name}".format(name=name))
            wrong[name] = link
    return verified, wrong

def clean_wikipedia_handles(data="data/nba_2017_br.csv"):
    """Clean Handles"""

    verified, guesses = guess_wikipedia_nba_handle(data=data)
    verified_cleaned, wrong = validate_wikipedia_guesses(guesses)
    print("WRONG Matches: {wrong}".format(wrong=wrong))
    handles = {**verified, **verified_cleaned}
    return handles

def nba_wikipedia_dataframe(data="data/nba_2017_br.csv"):
    handles = clean_wikipedia_handles(data=data)
    df = create_wikipedia_df(handles)
    return df

def create_wikipedia_csv(data="data/nba_2017_br.csv"):
    df = nba_wikipedia_dataframe(data=data)
    df.to_csv("data/wikipedia_nba.csv")


if __name__ == "__main__":
    create_wikipedia_csv()

NBA プレイヤーに関する Twitter エンゲージメント情報を取得する

NBA プレイヤーに関するツイートをダウンロードするには、Twitter ライブラリーが必要になります。リスト 11 のパート 1 に、このコードを使用する API が示されています。以下の単純なスクリプトに比べ、Twitter API はより高度な内容になっています。これが、長年かけて開発されたサード・パーティー製ライブラリーを使用するメリットの 1 つです。

リスト 11. Twitter のメタデータ抽出、パート 1

"""
Get status on Twitter

df = stats_df(user="KingJames")
In [34]: df.describe()
Out[34]:
       favorite_count  retweet_count
count      200.000000     200.000000
mean     11680.670000    4970.585000
std      20694.982228    9230.301069
min          0.000000      39.000000
25%       1589.500000     419.750000
50%       4659.500000    1157.500000
75%      13217.750000    4881.000000
max     128614.000000   70601.000000

In [35]: df.corr()
Out[35]:
                favorite_count  retweet_count
favorite_count        1.000000       0.904623
retweet_count         0.904623       1.000000

"""

import time

import twitter
from . import config
import pandas as pd
import numpy as np
from twitter.error import TwitterError

def api_handler():
    """Creates connection to Twitter API"""

    api = twitter.Api(consumer_key=config.CONSUMER_KEY,
    consumer_secret=config.CONSUMER_SECRET,
    access_token_key=config.ACCESS_TOKEN_KEY,
    access_token_secret=config.ACCESS_TOKEN_SECRET)
    return api

def tweets_by_user(api, user, count=200):
    """Grabs the "n" number of tweets.  Defaults to 200"""

    tweets = api.GetUserTimeline(screen_name=user, count=count)
    return tweets

以下に示す次のセクションでは、ツイートを抽出して pandas データ・フレームに変換し、すべての値の中央値を保管します。データのサイズを小さくするには、対象とする値 (データ・セットの中央値) だけを保管するという手法が極めて有効です。外れ値に大きく左右されない中央値は、有効なメトリックとなります。

リスト 11. Twitter のメタデータ抽出、パート 2

def stats_to_df(tweets):
    """Takes twitter stats and converts them to a dataframe"""

    records = []
    for tweet in tweets:
        records.append({"created_at":tweet.created_at,
            "screen_name":tweet.user.screen_name,
            "retweet_count":tweet.retweet_count,
            "favorite_count":tweet.favorite_count})
    df = pd.DataFrame(data=records)
    return df

def stats_df(user):
    """Returns a dataframe of stats"""

    api = api_handler()
    tweets = tweets_by_user(api, user)
    df = stats_to_df(tweets)
    return df

def twitter_handles(sleep=.5,data="data/twitter_nba_combined.csv"):
    """yield handles"""

    nba = pd.read_csv(data)
    for handle in nba["twitter_handle"]:
        time.sleep(sleep) #Avoid throttling in twitter api
        try:
            df = stats_df(handle)
        except TwitterError as error:
            print("Error {handle} and error msg {error}".format(
                handle=handle,error=error))
            df = None
        yield df

def median_engagement(data="data/twitter_nba_combined.csv"):
    """Median engagement on twitter"""

    favorite_count = []
    retweet_count = []
    nba = pd.read_csv(data)
    for record in twitter_handles(data=data):
        print(record)
        #None records stored as Nan value
        if record is None:
            print("NO RECORD: {record}".format(record=record))
            favorite_count.append(np.nan)
            retweet_count.append(np.nan)
            continue
        try:
            favorite_count.append(record['favorite_count'].median())
            retweet_count.append(record["retweet_count"].median())
        except KeyError as error:
            print("No values found to append {error}".format(error=error))
            favorite_count.append(np.nan)
            retweet_count.append(np.nan)

    print("Creating DF")
    nba['twitter_favorite_count'] = favorite_count
    nba['twitter_retweet_count'] = retweet_count
    return nba

def create_twitter_csv(data="data/nba_2016_2017_wikipedia.csv"):
    nba = median_engagement(data)
    nba.to_csv("data/nba_2016_2017_wikipedia_twitter.csv")

高度な視覚化を作成する

ソーシャル・メディアのデータも追加されたので、他の洞察も提供する、さらに高度なグラフを作成できます。図 8 に示されているのは、ヒートマップと呼ばれる高度なグラフです。ヒートマップは、主要な特徴をひとまとめにして相互の関係を明らかにします。これらの特徴は、クラスタリング (このシリーズの第 1 回を参照) などの機械学習を行うには絶好のビルティング・ブロックになります。このデータを使用して、皆さん自身でさまざまなクラスタリング構成を試してみる価値はあるはずです。

図 8. NBA プレイヤーのスポンサー収入、ソーシャル・メディアの影響、試合出場中の成績、チームの評価額の相関ヒートマップ: 2016 年~ 2017 年のシーズン
ヒートマップの画像
ヒートマップの画像

リスト 12 に、この相関ヒートマップを作成するコードを記載します。

リスト 12. 相関ヒートマップ

endorsements = pd.read_csv("../data/nba_2017_endorsement_full_stats.csv")
plt.subplots(figsize=(20,15))
ax = plt.axes()
ax.set_title("NBA Player Endorsement, Social Power, On-Court Performance, Team Valuation Correlation Heatmap:  2016-2017 Season")
corr = endorsements.corr()
sns.heatmap(corr,
            xticklabels=corr.columns.values,
            yticklabels=corr.columns.values, cmap="copper")

リスト 13 に示す色分けされたヒートマップでは、特殊なカラー・マップと併せてログ・スケールが使用されています。セル間の違いを明確にするには、この手法が非常に効果的です。ログ・スケールは、相対的な差と実際の差を形にして表します。値の間に大規模な差 (例えば、10 と 100 万など) がある場合、グラフを作成する際は、一般的にこの手法が使用されています。相対変動と実際の差を示すことで、一層明確なグラフになります。一般に、グラフは線形スケール (直線) で描画されます。対数スケール (測程線) が描画されると、グラフ化時のべき乗数が減ります (つまり、平坦化されます)。

リスト 13. 相関ヒートマップ拡張版

from matplotlib.colors import LogNorm
plt.subplots(figsize=(20,15))
pd.set_option('display.float_format', lambda x: '%.3f' % x)
norm = LogNorm()
ax = plt.axes()
grid = endorsements.select_dtypes([np.number])
ax.set_title("NBA Player Endorsement, Social Power, On-Court Performance, Team Valuation Heatmap:  2016-2017 Season")
sns.heatmap(grid,annot=True, yticklabels=endorsements["PLAYER"],fmt='g', cmap="Accent", cbar=False, norm=norm)
図 9. NBA プレイヤーのスポンサー収入、ソーシャル・メディアの影響、試合出場中の成績、チームの評価額のヒートマップ: 2016 年~ 2017 年のシーズン
ヒートマップを示す画像
ヒートマップを示す画像

最後の描画では、R 言語を使用して、ggplot で多次元グラフを作成します。リスト 14 にコードを記載し、図 10 にその出力を示します。R のネイティブ ggplot ライブラリーは、色、サイズ、ファセット、形状を使用した多次元グラフを作成できる、強力でユニークなグラフ作成ライブラリーです。時間をかけて調査する価値は十分にあります。

リスト 14. R ベースの高度な ggplot

ggplot(nba_players_stats, aes(x=WINS_RPM, y=PAGEVIEWS,
                color=SALARY_MILLIONS, size=TWITTER_FAVORITE_COUNT)) + geom_point() +
                geom_smooth() + scale_color_gradient2(low = "blue", mid = "grey", high =
                "red", midpoint = 15) + labs(y="Wikipedia Median Daily Pageviews", x="WINS
                Attributed to Player( WINS_RPM)", title = "Social Power NBA 2016-2017
                Season: Wikipedia Daily Median Pageviews and Wins Attributed to Player
                (Adusted Plus Minus)") +
                geom_text(vjust="inward",hjust="inward",color="black",size=4,check_overlap
                = TRUE, data=subset(nba_players_stats, SALARY_MILLIONS > 25 | PAGEVIEWS
                > 4500 | WINS_RPM > 15), aes(WINS_RPM,label=PLAYER )) +
                annotate("text", x=8, y=13000, label= "NBA Fans Value Player Skill More
                Than Salary, Points, Team Wins or Another Other Factor?", size=5) +
                annotate("text", x=8, y=11000, label=paste("PAGEVIEWS/WINS Correlation:
                28%"),size=4) + annotate("text", x=8, y=10000,
                label=paste("PAGEVIEWS/Points Correlation 44%"),size=4) + annotate("text",
                x=8, y=9000, label=paste("PAGEVIEWS/WINS_RPM Correlation: 49%"),size=4,
                color="red") + annotate("text", x=8, y=8000,
                label=paste("SALARY_MILLIONS/TWITTER_FAVORITE_COUNT: 24%"),size=4)
図 10. NBA プレイヤーとソーシャル・メディアの力: 2016 年~ 2017 年のシーズン
NBA ファンは、年俸、ポイント、または他の要因よりもプレイヤーのスキルに価値を置いているという最終結果を表すグラフの画像
NBA ファンは、年俸、ポイント、または他の要因よりもプレイヤーのスキルに価値を置いているという最終結果を表すグラフの画像

まとめ

このシリーズの第 1 回では、機械学習の基礎を学び、教師なしクラスタリング手法を使用してチームの評価額について調査しました。このデータ・サイエンスではツールとして Python と、Jupyter Notebook と組み合わせた高度なグラフを使用しました。

この第 2 回では、NBA プレイヤーとソーシャル・メディアの影響、年俸、そして試合出場中の成績の間にある関係を探りました。Jupyter Notebook 内で数多くの高度なグラフを作成した一方、R も少々使用しました。

この一連の過程で取り上げた質問、またはさらに調査が必要であった質問の答えを以下にまとめます (誤った仮説である可能性もあります)。

  • プレイヤーに支払われる年俸は、勝利を予測するのに最適な判断材料ではない。
  • ファンは、(例えば収入の高さではなく) スキルに優れた選手にエンゲージする傾向がある。
  • スポンサー収入は、所属するチームの勝利数に相関するため、プレイヤーはどのチームに移転するか、慎重になることが考えられる。
  • 試合を生で見る観客と、ソーシャル・メディアにエンゲージする観客との間には違いがある。試合を生で見る観客のほうが、チームのスキル不足を気にする。

他にも調べられることはあるので、GitHub に用意されているデータ・セットに教師付き機械学習と教師なし機械学習の両方を適用してみてください。皆さんがこのプロジェクトを試すことができように、このリンク先の Kaggle 上にデータ・セットをアップロードしておきました。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=ビジネス・アナリティクス, Open source
ArticleID=1057175
ArticleTitle=NBAにおけるソーシャル・メディアの力、影響、実績, 第 2 回: 個々の NBA プレイヤーを調査する
publish-date=01252018