2024年6月30日日曜日

NBA選手の年齢分布

 年齢別のあるデータセットからNBA選手の年齢データを割り出してみます。

データセットの範囲 1950年〜2017年

Year           Player   Age   Tm     G  GS  MP  PER     FG  3P     FT    FTA  ORB  DRB  TRB    AST  STL  BLK  TOV     PF    PTS   PPG

0  1950.0  Curly Armstrong  31.0  FTW  63.0 NaN NaN  NaN  144.0 NaN  170.0  241.0  NaN  NaN  NaN  176.0  NaN  NaN  NaN  217.0  458.0  7.27

1  1950.0     Cliff Barker  29.0  INO  49.0 NaN NaN  NaN  102.0 NaN   75.0  106.0  NaN  NaN  NaN  109.0  NaN  NaN  NaN   99.0  279.0  5.69

2  1950.0    Leo Barnhorst  25.0  CHS  67.0 NaN NaN  NaN  174.0 NaN   90.0  129.0  NaN  NaN  NaN  140.0  NaN  NaN  NaN  192.0  438.0  6.54

3  1950.0       Ed Bartels  24.0  TOT  15.0 NaN NaN  NaN   22.0 NaN   19.0   34.0  NaN  NaN  NaN   20.0  NaN  NaN  NaN   29.0   63.0  4.20

4  1950.0       Ed Bartels  24.0  DNN  13.0 NaN NaN  NaN   21.0 NaN   17.0   31.0  NaN  NaN  NaN   20.0  NaN  NaN  NaN   27.0   59.0  4.54



import matplotlib.pyplot as plt
import seaborn as sns

fn = "data/Seasons_Stats.csv"
df = pd.read_csv(fn, index_col=0)

print("最低年齢: ", df['Age'].min())
print("最高年齢: ", df['Age'].max())

print(df['Age'].value_counts())

最低年齢: 18.0 
最高年齢: 44.0

Age
24.0    2794
23.0    2748
25.0    2518
26.0    2380
27.0    2149
22.0    1926
28.0    1823
29.0    1576
30.0    1433
31.0    1179
32.0     952
33.0     713
21.0     609
34.0     538
35.0     347
20.0     288
36.0     210
37.0     143
19.0     111
38.0      92
39.0      48
40.0      16
18.0      13
41.0       5
42.0       3
43.0       1
44.0       1
Name: count, dtype: int64

value_countsで見ると、23〜24歳が最も多く、最高年齢は一人しかいません。

グラフで可視化します。

bins = len(df['Age'].value_counts())
print(bins)

# 年齢分だけbinsを分けます
plt.hist(df["Age"], bins=bins)
plt.title("NBAの年齢分布(1950〜2017)")
plt.xlabel("年齢")
plt.ylabel("人数")
plt.show()

長く続けるのは、体調管理や怪我などある中、相当厳しい世界だと見て取れます。


2024年6月26日水曜日

NBA選手の平均体重

ついでに平均体重の推移もグラフ化しました。
前回のコードのheightをweightに変えるだけです。
plt.plot(weight.index, weight)
plt.xticks(weight.index, rotation=90)
plt.ylabel("weight(kg)")
plt.xlabel("year")
plt.show()


 

身長の変化よりは大きいようです。
1980年の92kg辺りから、2000年初頭には100kg超えてきています。

大きな体がぶつかり合うNBAでは、トレーニング方法も進化しており、体重があったほうが有利だったのでしょうか?
それでも近年のスピード重視の戦略でまた下がる傾向にあるようです。

2024年6月25日火曜日

NBA選手の平均身長

NBAデータを触ってるのでいくつかグラフを出してみます。

NBA平均身長の推移

バスケットボールといえば身長が高い選手が多いと思い浮かべると思いますが、平均身長の推移をグラフ化してみましょう。

手元にあるのは1980年〜2021年のデータです。

 Player    Ht     Wt      Colleges Pos  Age   Tm   G  GS    MP   FG   FGA  ...  Pts Max  Share                    Team   W   L   W/L%    GB   PS/G   PA/G   SRS                                                                                  ...                                                                                                 
0              Alaa Abdelnaby  6-10  240.0          Duke  PF   22  POR  43   0   6.7  1.3   2.7  ...      0.0    0.0  Portland Trail Blazers  63  19  0.768   0.0  114.7  106.0  8.47  
1                 Danny Ainge   6-4  175.0           BYU  SG   31  POR  80   0  21.4  4.2   8.9  ...      0.0    0.0  Portland Trail Blazers  63  19  0.768   0.0  114.7  106.0  8.47  
2                 Mark Bryant   6-9  245.0    Seton Hall  PF   25  POR  53   0  14.7  1.9   3.8  ...      0.0    0.0  Portland Trail Blazers  63  19  0.768   0.0  114.7  106.0  8.47  
3                Wayne Cooper  6-10  220.0   New Orleans   C   34  POR  67   1  11.1  0.9   2.2  ...      0.0    0.0  Portland Trail Blazers  63  19  0.768   0.0  114.7  106.0  8.47 
4                Walter Davis   6-6  193.0           UNC  SG   36  POR  71  14  20.9  5.7  12.1  ...      0.0    0.0  Portland Trail Blazers  63  19  0.768   0.0  114.7  106.0  8.47  

身長がfeet-inch表記で文字列、体重がポンド表記なので以前の関数を使い、cmとkg表記に変換しheightとweightにし、数値データとしてデータフレームに入れ替えておきます。

#! /usr/bin/env python
# -*- coding:utf-8 -*-

fn = "data/cleaned_player.csv"
df = pd.read_csv(fn, index_col=0)

# データを年別に分ける
year = df.groupby('Year')

# 年ごとの平均身長と平均体重のデータフレームを生成
height = year['height'].mean(numeric_only=True)
weight = year['weight'].mean(numeric_only=True)

# indexはYear
plt.plot(height.index, height)
plt.xticks(height.index, rotation=90)
plt.ylabel("height(cm)")
plt.xlabel("year")
plt.show()



おおよそ200cm前後を推移してるようです。
一応アメリカでは1987年の身長がピークとは言われてますが、全選手の平均身長を取ったこのデータでは2003年あたりがピークとなっています。
シャキール・オニール選手やティム・ダンカン選手など活躍したビックマン全盛と言われてた時代です。
とはいえ2mの1,2cmは誤差の範囲でしょうし、インチだと1cm単位の差は無視されます。
今回使用したデータセットはinchの小数点以下がありませんでした。
毎年測定されてもおらず、サバ読み選手も多々いたと聞くので真相は分かりません。
2019年からシューズを脱いだ身長に変更されているので平均も下がっています。


2024年6月24日月曜日

NBA選手の身長ランキング

以前NBAのデータなどを整形したので、データを比較してみたいと思います。 データは1980〜2017年までのデータになります。
#! /usr/bin/env python
# -*- coding:utf-8 -*-

fn = "data/player_stats.csv"
df = pd.read_csv(fn, index_col=0)

# playerでグループ分け
players = df.groupby('Player')

# 各スタッツの平均を出してデータフレームにする
player_means = players.mean(numeric_only=True).reset_index()

# 必要なカラムだけ抽出
player_stats = player_means[['Player', 'height', 'weight', 'aPTS']]

sort_heights = player_stats.sort_values('height', ascending=False)

# 高身長ランキング
print(sort_heights.head(10))

sort_heights = player_stats.sort_values('height', ascending=True)

# 低身長ランキング
print(sort_heights.head(10))

NBA高身長ランキング 

            Player  height  weight       aPTS
0  Gheorghe Muresan   231.0   137.0   7.366667
1        Manute Bol   231.0    90.0   1.984615
2     Shawn Bradley   229.0   106.0   9.068750
3          Yao Ming   229.0   140.0  18.562500
4        P.J. Brown   229.0   106.0   8.746667
5        Peter John   226.0   117.0   1.800000
6     Slavko Vranes   226.0   124.0   0.000000
7      Chuck Nevitt   226.0    98.0   1.809091
8       Sim Bhullar   226.0   163.0   0.700000
9   Pavel Podkolzin   226.0   117.0   1.600000



NBA低身長ランキング 

               Player  height  weight       aPTS
0   Muggsy Bogues   160.0    61.0   6.412500
1    Earl Boykins   165.0    61.0   7.821053
2       Spud Webb   168.0    60.0   9.092857
3      Greg Grant   170.0    63.0   2.481818
4  Keith Jennings   170.0    72.0   7.233333
5   Charlie Criss   173.0    74.0   7.200000
6     Yuta Tabuse   175.0    74.0   1.800000
7   Nate Robinson   175.0    81.0   8.676471
8      Kay Felder   175.0    79.0   4.000000
9   Calvin Murphy   175.0    74.0  14.925000


データについては申告時のもので実際の身長と異なる選手もいるようです。
上はゲオルゲ・ムレシャン選手の231cmと高く、下はマグジー・ボーグス選手の160cmと身長差が凄いですね。
231cmのマヌート・ボル選手と160cmのマグジー・ボーグス選手の比較写真は多く出回っています。

高身長が有利と言われるバスケットボールで160cmでも活躍したマグジー・ボーグス選手の運動能力に驚かされます。

2024年6月22日土曜日

scikit-learnのデータセット、Bunchクラスはとても有用

scikit-learnのデータセット

scikit-learnは機械学習を扱う便利なライブラリですが、簡単に試せるようにサンプル・データセットも用意されています。 
多くの方はscikit-learnの入門にこのデータセットを利用されると思います。

sklearn.__version__ : 1.4.2

 サンプルデータセットには小さいサイズのToy Datasetsと画像などの大きなサイズのReal World Datasetsの2種類が用意されています。


#! /usr/bin/env python
# -*- coding:utf-8 -*-

iris_data = sklearn.datasets.load_iris()

print(type(iris_data))


<class 'sklearn.utils._bunch.Bunch'>

データセットはBunchクラスのオブジェクトとして読み込まれます。

Bunchクラスはdictのサブクラスでdictと同じように扱えるので、keys()でメンバ一覧出来ます。

print(iris_data.keys())

dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename', 'data_module'])


Bunchクラスのデータをpandasのデータフレームに変換する

Bunchクラスのデータをpandasのデータフレームとして扱うには、データの中身をdata変数、列名をfeature_namesから取ります。

 
 df = pd.DataFrame(iris_data.data, columns=iris_data.feature_names)
 
 pritn(df.head())
 
 

 sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0                5.1               3.5                1.4               0.2
1                4.9               3.0                1.4               0.2
2                4.7               3.2                1.3               0.2
3                4.6               3.1                1.5               0.2
4                5.0               3.6                1.4               0.2

このようにデータフレームとしても扱えますが、Bunchオブジェクトは説明変数、目的変数、注釈などに分けてあるので通常のデータフレームよりデータの内容が理解しやすいです。

通常のデータフレームは行と列ですぐに扱えますが、扱いやすい分、列名が短縮系で意味が分からなかったり、データ自身を解釈するのに他に説明が必要です。

Bunchクラスではデータの意味や説明まで含まれているので、データをどのように扱うか他を参照しなくても理解出来ます。

scikit-learnは急速に進化中ですが、Bunchクラスはサンプルデータだけでなく、もっと広まっても良い気がします。

foliumのスタイルを変える

foliumのタイル

foliumで見た目を変えるにはタイルを指定します。

 folium.__version__ 

 0.16.0

タイルは built-inタイルとして、最初からいくつか使えます。

 #!/usr/bin/env python
# -*- coding: utf-8 -*-

import folium
import os

latlng = [35.682012250347064, 139.76718125374543]
mp = folium.Map(location=latlng, zoom_start=10, tiles='OpenStreetMap')

html = "tile1.html"
mp.save(html)
os.system("chrome " + html)



tiles='OpenStreetMap'の部分がタイル指定で、OpenStreetMap はデフォルトのタイルなので指定しなくても同じ結果です。

 mp = folium.Map(location=latlng, tiles="Cartodb Positron", zoom_start=10)

 mp = folium.Map(location=latlng, tiles="Cartodb dark_matter", zoom_start=10)

カスタムタイル

標準以外にカスタムタイルもあります。無料有料などあるので随時調べてください。

custom tile

カスタムファイルはtilesにタイルのリンク、attrにクレジットを指定します。


mp = folium.Map(location=latlng, zoom_start=10,
                tiles='https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
                attr='Map data: © OpenStreetMap contributors, SRTM | Map style: © OpenTopoMap (CC-BY-SA)'
                )

mp = folium.Map(location=latlng, zoom_start=10,
                tiles='https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
                attr='CyclOSM | Map data: © OpenStreetMap contributors'
                )

2024年6月21日金曜日

Pythonで地図を扱う(folium)

foliumを使う

Pythonで地図を扱う方法は色々ありますが、今回はwebで扱いやすくデータを容易にプロット出来るfoliumを使ってみたいと思います。
 pip install folium 

 folium.__version__ 

 0.16.0
 #!/usr/bin/env python
# -*- coding: utf-8 -*-

import folium
import os

# ①
mp = folium.Map()

# ② htmlとして保存
html = "map.html"
# 使用するwebブラウザ
browser = "chrome "

# マップをhtmlに保存
mp.save(html)

# webブラウザで表示
os.system(browser + html)
使い方自体はとても簡単で、たった1行でマップが生成出来ます。# ①



生成は簡単ですが、htmlを保存してwebブラウザに表示するためのコードが# ②以下のコードです。
foliumオブジェクトのsave関数で保存し、os.systemコマンドでwebブラウザにすぐに表示出来るようにしています。
osライブラリを使うためにimport osをしています。


緯度経度で場所を指定

これだけだとどこを表示したいのか分からないので、多くは緯度経度をして使用します。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import folium
import os

# 緯度経度の指定 (この例では東京駅)
latlng = [35.682012250347064, 139.76718125374543]

# ①
mp = folium.Map(location=latlng, zoom_start=14)

html = "map2.html"
mp.save(html)
os.system("chrome " + html)
緯度経度は、Google Mapから場所を指定して右クリックしたりして取得してください。


locationは地図の中心にする場所の緯度経度をリストで指定し、zoom_startは倍率を指定します。
これはGoogle Map APIなど他のマップサービスでもよくある指定方法なので、分かりやすいですね。

2024年6月16日日曜日

Pythonでランダムな文字列生成

Pythonでランダムな文字列を生成する関数です。 

Pythonでデータフレームなどを扱う際、とりあえずダミーデータなどで試したい時に、適当な文字列があればと思い作りました。


 #!/usr/bin/env python
# -*- coding: utf-8 -*-

import random
import string

# numは生成する数、strsはその文字列からランダムに選択、lengthは一つの文字列の長さ
def word_generate(num, strs=string.ascii_lowercase, length=5):
    words = []
    for i in range(num):
        word = ""                        # ①
        for j in range(length):          # ①
            word += random.choice(strs)  # ①
        words.append(word)
    return words

print("[word generate]")
print("")

#アルファベット小文字
let = string.ascii_lowercase

print(let)
print("")

words1 = word_generate(5)
print(words1)
print("")

#よく使われる上位文字
let2 = 'etaonirsh'

words2 = word_generate(10, let2)
print(words2)
print("")

words3 = word_generate(5, let2, 8)
print(words3)
print("")
 

結果:

abcdefghijklmnopqrstuvwxyz

['lpiil', 'akoqp', 'bstzb', 'cteta', 'alyvd']

['ootot', 'sseni', 'nnntn', 'nhieh', 'rhnon', 'rsiat', 'aostn', 'rsert', 'reern', 'oittn']

['iitniess', 'oinshhri', 'raseoino', 'eniohoht', 'iasrsntr']


①の部分をchoiceからchoicesに変え        

word = "".join(random.choices(strs, k=length))

と書き換えても同じ結果になります。

2024年6月4日火曜日

NBAの平均得点の変遷

NBAのデータを調べてみたいと思い、年間平均得点の推移を出してみます

元データはNBA データAPIかKaggle辺りでゲームスコアの記録を見つけてきます。

1950年くらいから現在までの推移を見てみます。

#! /usr/bin/env python
# -*- coding:utf-8 -*-

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


data_file = "data/teams_data.csv"
df = pd.read_csv(data_file)

print("[NBA average score per year]")

# ゲームをシーズンごとのグループに分けます
season_score = df.groupby('season')

# シーズンを入れるリスト
years = []
# 平均得点を入れるリスト
average_score = []

for y, stat in season_score:
	years.append(y)
    average_score.append(stat['PTS'].mean())
    
plt.figure(figsize=(16, 6))
plt.grid()
plt.plot(year, average_score)
plt.show()

結果

これを見るとまだ3Pルールがない1960年~1970年辺りが高く、そこから下がっていき2000年代辺りが低くなってまた増加傾向にあるのが分かります。

レギュレーションや戦術の移り変わりが反映されてるのでしょう。

Pythonで地図空間データを扱う⑤

ベースの地図が出来た所で、他のデータを被せてみます。 国土地理院の  500mメッシュ別将来推計人口データ  を使用します。 同じく神奈川県のデータ  500m_mesh_suikei_2018_shape_14.zip をダウンロードします。 ベースの地図データと同じ場所に展開...