2022年6月17日金曜日

NBA選手データを整える

 NBAはデータを豊富に提供しているので分析に適しています。

ただし、身長や体重が日本と異なる表記なので、日本で分かりやすいようにデータを変換してみます。

ついでに欠損値除去や微調整してデータを取り扱い安くします。


まず、元データ player_data.csv の中身を確認します。

filename = "player_data.csv"

df = pd.read_csv(filename)

print(df.head())
print()
print(df.shape)
print()
print(df.info())


                  name  year_start  year_end position height  weight        birth_date                                college

0       Alaa Abdelnaby        1991      1995      F-C   6-10   240.0     June 24, 1968                        Duke University

1      Zaid Abdul-Aziz        1969      1978      C-F    6-9   235.0     April 7, 1946                  Iowa State University

2  Kareem Abdul-Jabbar        1970      1989        C    7-2   225.0    April 16, 1947  University of California, Los Angeles

3   Mahmoud Abdul-Rauf        1991      2001        G    6-1   162.0     March 9, 1969             Louisiana State University

4    Tariq Abdul-Wahad        1998      2003        F    6-6   223.0  November 3, 1974              San Jose State University


(4550, 8)


<class 'pandas.core.frame.DataFrame'>

RangeIndex: 4550 entries, 0 to 4549

Data columns (total 8 columns):

 #   Column      Non-Null Count  Dtype  

---  ------      --------------  -----  

 0   name        4550 non-null   object 

 1   year_start  4550 non-null   int64  

 2   year_end    4550 non-null   int64  

 3   position    4549 non-null   object 

 4   height      4549 non-null   object 

 5   weight      4544 non-null   float64

 6   birth_date  4519 non-null   object 

 7   college     4248 non-null   object 

dtypes: float64(1), int64(2), object(5)


データは4550件、birth_dateやweightに欠損値が散見されます。


print(df[df['weight'].isnull()])
print()
print(df[df['birth_date'].isnull()].sort_values('year_end', ascending=False))

df2 = df.dropna(how="any")
欠損値データを確認すると昔の人ばかりなので、昔の年代を分析しない場合は削除して構わないでしょう。dropna(how="any")で欠損値を取り除いたデータフレームにします。


# 1foot = 30.48, 1inch = 2.54cm
FOOT= 30.48
INCH = 2.54

# 1pond = 0.4536kg
LB = 0.4536

# フィートをセンチに
def toCenti(height):

    ft, inc = height.split("-")
    cent = int(ft) * FOOT + int(inc) * INCH

    return round(cent, 2)


# ポンドをキログラムに
def toKilogram(weight):

    kg = weight * LB
    
    return round(kg, 2)
    
 
# 以上の関数を身長体重データに適用させる
df2['height'] = df2['height'].apply(toCenti)
df2['weight'] = df2['weight'].apply(toKilogram)


print(df2.head())
print()
print(df2.shape)



                  name  year_start  year_end position  height  weight        birth_date                                college
0       Alaa Abdelnaby        1991      1995      F-C  208.28  108.86     June 24, 1968                        Duke University
1      Zaid Abdul-Aziz        1969      1978      C-F  205.74  106.60     April 7, 1946                  Iowa State University
2  Kareem Abdul-Jabbar        1970      1989        C  218.44  102.06    April 16, 1947  University of California, Los Angeles
3   Mahmoud Abdul-Rauf        1991      2001        G  185.42   73.48     March 9, 1969             Louisiana State University
4    Tariq Abdul-Wahad        1998      2003        F  198.12  101.15  November 3, 1974              San Jose State University

(4213, 8)

変換できました。

player_data_a.csv
完成データは 

2022年6月11日土曜日

matplotlibで一次関数グラフを作る

前回、matplotlibで方眼紙の様な正方形グリッドが作れたら、数学の関数グラフも容易に作れます。

matplotlibはデータをプロットするライブラリなので式をそのままグラフにするのはひと工夫必要です。

今度はオブジェクト指向で関数グラフを作り、一次関数をプロットします。


# オブジェクト指向でグラフ作成
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
 
# これで正方形になります。
ax.set_aspect('equal', adjustable='box')

# 表示する幅
ax.set_xlim(-10, 11)
ax.set_ylim(-10, 11)

# グラフの刻み
ax.set_xticks(np.arange(-10, 10, 5))
ax.set_yticks(np.arange(-10, 10, 5))
 
# 補助線をonにします
ax.minorticks_on() 

# グリッドの主線と補助線
ax.grid(which="major", color="black", alpha=0.5)
ax.grid(which="minor", color="gray", linestyle="--", alpha=0.5)

# 数値表示を0に合わせます。
ax.spines['bottom'].set_position("zero")
ax.spines['left'].set_position("zero")
 

今回はオブジェクト指向のプロットなので若干書き方が違います。特に正方形にするには set_aspect('equal', adjustable='box')という指定をします。 またグラフの数値を0に合わせるため、spines.set_position 関数で zero を指定します。

関数グラフの方眼紙が出来たら、後は一次関数のデータを生成する関数を作ればいいだけ。

# 一次関数のデータを作る関数 
def linear(a, b, ran=np.arange(-10,10)):

    arr = []
    for x in ran:
        y = x*a + b
        arr.append(y)

	# 式から配列データを返します。
    return arr
これを応用すれば二次関数や他の関数もプロットできますね。


[完成形]
#! /usr/bin/env python

# -*- coding:utf-8 -*-
 
 
import numpy as np
import matplotlib.pyplot as plt


# 一次関数のデータを作る関数 
def linear(a, b, ran=np.arange(-10,10)):

    arr = []
    for x in ran:
        y = x*a + b
        arr.append(y)

	# 式から配列データを返します。
    return arr

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
 
# これで正方形になります。
ax.set_aspect('equal', adjustable='box')

# 表示する幅
ax.set_xlim(-10, 11)
ax.set_ylim(-10, 11)

# グラフの刻み
ax.set_xticks(np.arange(-10, 10, 5))
ax.set_yticks(np.arange(-10, 10, 5))
 
# 補助線をonにします
ax.minorticks_on() 

# グリッドの主線と補助線
ax.grid(which="major", color="black", alpha=0.5)
ax.grid(which="minor", color="gray", linestyle="--", alpha=0.5)

# 数値表示を0に合わせます。
ax.spines['bottom'].set_position("zero")
ax.spines['left'].set_position("zero")

x = np.arange(-10, 10)

# y = 2x + 3の一次関数を生成
y = linear(2, 3, x)
ax.plot(x, y)


plt.show()

2022年6月9日木曜日

matplotlibで方眼紙のようなグリッドを作る

Pythonのmatplotlibはグラフ描画のためのライブラリですが、描画はデータによって自動的に調整されるので、時には方眼紙のように正確なグリッドが欲しい場合もあります。

正方形の方眼紙のようなグリッドは、plt.axis("square")で正方形に、ticksでマス目を刻みます。

#! /usr/bin/env python

# -*- coding:utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

# 正方形で表示
plt.axis("square")

# 表示範囲の指定
plt.xlim(-10, 10)
plt.ylim(-10, 10)

# 値の刻み
plt.xticks(np.arange(-10, 11, 5))
plt.yticks(np.arange(-10, 11, 5))

#grid マイナー線の指定をします
plt.minorticks_on()
plt.grid()
# マイナー線は破線で透明度50%
plt.grid(which="minor", color="gray", linestyle="--", alpha=0.5)

plt.show()
 
 
ポイントはaxis("square")です。これで数値通りの大きさで表示されます。
あとはxlimでX軸の、ylimでY軸のグラフの範囲、ticksで表示するメモリ数値の刻みを指定します。
グリッドの線はgrid関数で指定しますが、主線だけでなく副線も表示したい場合はminorticks_on関数を呼び出して表示します。
副線は同じgrid関数でwhich="minor"で呼び出します。
あとは"--"で破線、alphaで透明度など指定して好みの方眼紙に調整してください。



以上のようなグラフになります。


2022年5月21日土曜日

NBAデータから歴代得点王ランキングを出してみる

馴染みのあるデータや好きなスポーツのデータを扱ったりするとデータフレームを習得するのも楽しめていいですね。

今回はおなじくkaggleの NBA Players stats since 1950 から Seasons_Stats.csv を使用して歴代得点王を取り出したいと思います。


#! /usr/bin/env python

# -*- coding:utf-8 -*-

#
import pandas as pd
import numpy as np

filename = "../data/Seasons_Stats.csv"

print("[NBA]")
print()

df = pd.read_csv(filename, index_col=0)

print()

# 年間得点の多い順にソート
porder = df.sort_values('PTS', ascending=False)

# Year, Player, PTSのみのデータフレームにする
points = porder.loc[:, ['Year', 'Player', 'PTS']]

# Year, Player, PTSのみのDataFrame 数
print(points.shape)
print()

# 欠損値の削除
points = points.dropna()
print(points.shape)
print()

# 見やすいようにfloatからintへの変換
point = points.astype({'Year': 'int64', 'PTS': 'int64'})
print(points.dtypes)
print()
print(points.info())
print()

# 得点ランキングトップ50を表示する
points.reset_index(drop=True, inplace=True)
points.index = np.arange(1, len(points)+1)
print(points.head(50))


まず、年間得点(PTS)を sort_values() を使い得点の多い順(逆ソート)します。
得点だけのランキングなので記録年数(Year)、選手名(Player)、年間得点(PTS)だけのデータフレームを loc を使って切り出します。

このままだと年と年間得点もデータ型が float64 なのでintに変えたいと思いますが、欠損値があるのでデータ変換できません。
そこで先に欠損値行をdropna() で消します。
元の数(24691, 3) から (24624, 3) に減りました。
これで astype() を使い 年と年間得点を int に変換します。

もう一つソートされてもインデックスがそのままだと何の数字か分からないのでインデックスを reset_index() で振り直します。
インデックスをランキングにしたいなら0基準より1から数えたほうがいいでしょう。

すべて整ったら歴代得点ランキングトップ50を取り出してみます。

    Year                Player   PTS
1   1962     Wilt Chamberlain*  4029
2   1963     Wilt Chamberlain*  3586
3   1987       Michael Jordan*  3041
4   1961     Wilt Chamberlain*  3033
5   1964     Wilt Chamberlain*  2948
6   1988       Michael Jordan*  2868
7   2006           Kobe Bryant  2832
8   1975           Bob McAdoo*  2831
9   1972  Kareem Abdul-Jabbar*  2822
10  1967           Rick Barry*  2775
11  1990       Michael Jordan*  2753
12  1973       Tiny Archibald*  2719
13  1963         Elgin Baylor*  2719
14  1960     Wilt Chamberlain*  2707
15  1966     Wilt Chamberlain*  2649
16  1989       Michael Jordan*  2633
17  1971  Kareem Abdul-Jabbar*  2596
18  2014          Kevin Durant  2593
19  1980        George Gervin*  2585
20  1991       Michael Jordan*  2580
21  2017     Russell Westbrook  2558
22  1982        George Gervin*  2551
23  1993       Michael Jordan*  2541
24  1990          Karl Malone*  2540
25  1961         Elgin Baylor*  2538
26  1965     Wilt Chamberlain*  2534
27  1982         Moses Malone*  2520
28  1962         Walt Bellamy*  2495
29  1996       Michael Jordan*  2491
30  1964      Oscar Robertson*  2480
31  2006          LeBron James  2478
32  1966           Jerry West*  2476
33  2010          Kevin Durant  2472
34  2003           Kobe Bryant  2461
35  1982       Adrian Dantley*  2457
36  1981       Adrian Dantley*  2452
37  1975           Rick Barry*  2450
38  1962      Oscar Robertson*  2432
39  1997       Michael Jordan*  2431
40  2007           Kobe Bryant  2430
41  1962           Bob Pettit*  2429
42  1976           Bob McAdoo*  2427
43  1984       Adrian Dantley*  2418
44  1986         Alex English*  2414
45  1967      Oscar Robertson*  2412
46  2003         Tracy McGrady  2407
47  1992       Michael Jordan*  2404
48  1988    Dominique Wilkins*  2397
49  2009           Dwyane Wade  2386
50  1994       David Robinson*  2383


 Wilt Chamberlainの年間得点が異常と言われるほど凄い数字だというのが分かります。






 

NBAデータをDataFrameを使ってグループ分けをする

Pandasのデータフレームはデータ分析に適しています。

NBAデータから自分の取り出したいデータに分けてみます。

データ元は便利な kaggle から「NBA Players stats since 1950」というデータをお借りします。


Seasons_Stats.csvには1950-2017年まで(24691, 53)件という選手の膨大なデータがあります。


 #! /usr/bin/env python
 # -*- coding:utf-8 -*-
 
 #import pandas as pd
 import numpy as np
 
 filename = "Seasons_Stats.csv"
 
 df = pd.read_csv(filename)
 
 print(df.head())
 print()
 print(df.shape)
 print()


 Unnamed: 0    Year         Player Pos   Age  ...    STL   BLK    TOV     PF     PTS
9547        9547  1990.0     Mark Acres   C  27.0  ...   36.0  25.0   70.0  248.0   362.0
9548        9548  1990.0  Michael Adams  PG  27.0  ...  121.0   3.0  141.0  133.0 1221.0
9549        9549  1990.0   Mark Aguirre  SF  30.0  ...   34.0  19.0  121.0  201.0  1099.0
9550        9550  1990.0    Danny Ainge  PG  30.0  ...  113.0  18.0  185.0  238.0  1342.0
9551        9551  1990.0    Mark Alarie  PF  26.0  ...   60.0  39.0  101.0  219.0   860.0

(24691, 53)



これでは流石に大きすぎるので1990年代のデータだけ取り出してみましょう。

y1990 = df[df['Year'] == 1990]
y1990 = y1990.drop(y1990.columns[0], axis=1)
y1990 = y1990.reset_index(drop=True)

print(y1990.head())
print()
print(y1990.shape)
先頭のインデックスはいらないのでdrop関数で弾いて、reset_index関数で振り直しています。
引数のdrop=Trueは元のインデックスを残さないためのものです。


     Year         Player Pos   Age   Tm  ...    STL   BLK    TOV     PF     PTS
0  1990.0     Mark Acres   C  27.0  ORL  ...   36.0  25.0   70.0  248.0   362.0
1  1990.0  Michael Adams  PG  27.0  DEN  ...  121.0   3.0  141.0  133.0  1221.0
2  1990.0   Mark Aguirre  SF  30.0  DET  ...   34.0  19.0  121.0  201.0  1099.0
3  1990.0    Danny Ainge  PG  30.0  SAC  ...  113.0  18.0  185.0  238.0  1342.0
4  1990.0    Mark Alarie  PF  26.0  WSB  ...   60.0  39.0  101.0  219.0   860.0

(459, 52)

これで1990年のデータが取り出せました。

2022年3月5日土曜日

LaravelをApache2でルーティングさせる

Laravelは専用サーバでもApacheでも表示させることはできるが、Apacheでルーティングさせるにはrewrite機能が必要

そのためそのままでルーティングさせたページを表示してもNot Foundになる


$ apache2 -v
Server version: Apache/2.4.38 (Debian)


Debian系の場合Apache2の設定ファイルは /etc/apache2 にある apache2.conf
になる。(昔はhttpd.conf)

一枚に書かれていたhttpd.confと違いモジュール等がツリー形式にリンクされている。

/etc/apache2/
# |-- apache2.conf
# | `--  ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf

モジュール有効化も直接書き換える形から a2enmod コマンドで有効化する形になっているので mods-available ディレクトリに rewrite.load があるか確認


$ ls mods-available/
..... request.load allowmethods.load     dav_lock.load
mime_magic.conf      rewrite.load

確認できたらモジュール有効化

$ sudo a2enmod rewrite

そして apache2.conf にある AllowOverride を All に書き換える

$ sudo vi /etc/apache2/apache2.conf

<Directory /var/www/>
        Options Indexes FollowSymLinks
        # AllowOverride None 
        AllowOverride All  # NoneからAllへと変更
        Require all granted
</Directory>


Apache2を再起動

$ sudo service apache2 restart

これでルーティングされたページを開いてもちゃんと表示される。









2018年12月16日日曜日

JavaScriptでPythonのrange関数っぽいものを作る

JavaScriptで連続した適当な配列を作りたい場合

const arr = [1, 2, 3, 4, 5];
でもいいですがPythonの様なrange関数でささっと作りたい場合があります。
Rangeオブジェクトは存在しますが、DOM用のオブジェクトでありPythonのものとは用途が違います。
underscore.jsで同じようなものが提供されているのでライブラリを使用するならそれがいいでしょう。

Pythonのrange関数

Pythonのrange関数はリスト(JavaScriptで言う配列)を容易に作れる関数です。
Pythonを使う方ならお馴染みでしょう。

主な仕様は、

range(0からの終了数)
range(開始数, 終了数)
range(開始数, 終了数, STEP数)

となります。引数の数によって動作が違います。


[ソース]

#! /usr/bin/env python

# -*- coding: utf-8 -*-

print("[range]")

for i in range(10):
    print(i, end=", ")
print()

for i in range(5, 10):
    print(i, end=", ")
print()

for i in range(0, 10, 2):
    print(i, end=", ")



[結果]

[range]
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
5, 6, 7, 8, 9,
0, 2, 4, 6, 8,

このような動作となります。
JavaScriptで同じような動作の関数を作ってみましょう。

ここあたり
https://dev.to/ycmjason/how-to-create-range-in-javascript-539i
を見ると、同じような考えでrange関数を作っている人がいます。

主な仕様は2番目のrange(開始数, 終了数)のようです。
Array.from() や Array.prototype.fill()を使った方法などがあるみたいです。
例にあるようなものに加え、STEP数まで加味出来る仕様にしましょう。


  function range(/* 開始数, 終了数, STEP数 */) {
    const len = arguments.length;
    const arr = [];
    let min = 0;
    let max = 0;
    let step = 1;

//引数の数により開始数と終了数を変える
    switch(len) {
      case 1:
        max = arguments[0];
      break;
      case 2:
        min = arguments[0];
        max = arguments[1];
      break;
      case 3:
        min = arguments[0];
        max = arguments[1];
        step = arguments[2];
      break;
    };

    for(let i = min; i < max; i++) {
// 第三引数があればSTEP数として処理をする
      if(step != 1) {
        if(i % step == 0) {
          arr.push(i);
        }
      }else {
        arr.push(i);
      }
    }

    return arr;
   }


//①
   const a1 = range(10);
   console.log(a1);


//②
   const a2 = range(5, 10);
   console.log(a2);


//③
   const a3 = range(1, 10, 2);
   console.log(a3);

//④
   for(let i of range2(5, 10)) {
      console.log(i);
   }


[結果]

[0,1,2,3,4,5,6,7,8,9]
[5,6,7,8,9]
[2,4,6,8]
5
6
7
8
9

レガシーな書き方ですが、こんな感じでしょうか?
④はPythonのrangeの様な使い方です。

NBAの1試合平均チームスタッツ (2025-26レギュラーシーズン)

前回 の元となったデータ、チーム毎の各スタッツ平均です。   team games w l win% PTS AST BLK STL ...