Quantcast
Channel: pandas - よちよちpython
Viewing all articles
Browse latest Browse all 30

【Numpy・Pandas・Scikit-learn】成績表のDataFrameを行でシャッフルし、クラス分けする

$
0
0

pandas.DataFrameの行をシャッフルし、クラス分けする

今回は、NumpyとPandasとScikit-learnを使って、2次元配列やDataFrameを行でシャッフルする方法と、Numpyで配列要素の繰り返し配列を生成する方法です。

  • DataFrameを行でシャッフル
    • numpy.random.permutation(): 2次元のnumpy配列を行でシャッフル
    • pandas.DataFrame.sample(frac=1) : DataFrameのまま行でシャッフル
    • sklearn.utils.shuffle() : DataFrameのまま行でシャッフル

  • 配列を繰り返した配列を生成
    • np.repeat()

の使い方を学びます。




5教科の成績表をもとに100人を4つにクラス分けする方法を考えています。シリーズ3回目。
クラス分けをする際、人数は同じに、成績表の合計点と教科ごとでも適度に分散し平均的になるように分けたい。

標準偏差」を用いればバラツキが抑えられるのではないか。
バラツキが大きいと標準偏差も大きくなりますので、標準偏差が小さくなるようにクラス分け出来れば目標に近づけるはず・・・



【クラス分けの方法について】

方法の手順として、

  1. データフレームをランダムにシャッフルして、クラス番号を順に割り振る
  2. クラスごとで教科や合計等の平均値を出す
  3. クラス間で平均値の標準偏差を出しバラツキを見る
  4. クラス間の教科や合計の標準偏差の更に標準偏差を出し、その最小値を探す



でやってみようと思います。上手く行くかは・・・
とりあえず、今回は上の①のみ。シャッフルしてクラス番号をつける。



【前回までの投稿内容】

前々回は、学校のテストの成績表のようなものを適当に作り、その合計点を算出してPandasで順位付けを行いました。
また、Pandasのグラフ作成機能を使って、積み上げ棒グラフを作成しました。

前回は趣旨を離れて、機械学習ライブラリScikit-learnk-means法(k-平均法)を使った教師なし学習によるクラスタリングで4クラスにおまかせクラス編成する方法をやりました。
k-平均法のお任せクラスタ分類では似たメンバーが偏り、人数がバラバラなクラス編成になりました。



【実行環境】

  • Windows10
  • WSL:Ubuntu:Anaconda4.10.1
  • Python3.8.10
  • Jupyter Notebook6.4.0
  • 使用ライブラリ
    • numpy1.20.2、pandas1.2.4、matplotlib3.3.4、scikit-learn0.24.2、japanize-matplotlib(グラフの日本語表示用)



Anacondaをインストールすると今回使うライブラリは最初から全部入っているようなので(japanize-matplotlib以外は)インストールは不要です。
入ってないようならcondapip

$ pip install notebook numpy pandas matplotlib sklearn japanize-matplotlib

など環境に合わせてインストールしておきます。



成績表をランダム生成する

前回、前々回と同様に、国・数・社・理・英、各100点満点のテストの成績表を100人分、一様分布のランダムな値で生成します。
5教科の合計点を個人ごとに算出し、その値の大きい順から順位付けを行います。

python日本語の文字を変数として使えるので、横着して・・・

# 成績表の生成import numpy as np
import pandas as pd
np.random.seed(0) # ランダムの固定# 教科ごとのランダムな配列を生成国 = np.random.randint(0,101,100)
数 = np.random.randint(0,101,100)
社 = np.random.randint(0,101,100)
理 = np.random.randint(0,101,100)
英 = np.random.randint(0,101,100)

# 成績表データフレームの作成
df = pd.DataFrame({
    "国語" : 国,
    "数学" : 数,
    "社会" : 社,
    "理科" : 理,
    "英語" : 英
})

# 5教科合計列の追加
df['合計'] = df.sum(axis=1)

# 合計点順位列の追加 (降順で順位付け、同一順位は最小値を取る、順位を整数値に変換)
df['順位'] = df['合計'].rank(method='min', ascending=False).astype('int')

# 表示
df
国語数学社会理科英語合計順位
0444870185123164
1474985173022866
2646931935331017
3674113845826343
467357124321872
........................
9558616791983752
96238335519728924
97793330994328429
981332291839598
998510033341226439

100 rows × 7 columns

100人分の成績表ができました。
インデックス番号が0~99まで順に付いています。



データフレームを行でランダムにシャッフルする

データフレームをインデックス(行)でシャッフルします。
上の表は0番~99番までインデックスが順番に並んでいますが、シャッフルする事でバラバラに組み替わります。

データフレームを行でシャッフルする方法として、

  • Numpy : numpy.random.permutation()
  • Pandas : pandas.DataFrame.sample(frac=1)
  • Scikit-learn : sklearn.utils.shuffle()


などの便利なメソッドがあるようです。

NumPyを使う場合は、一旦valuesを取り出して行いますので、折角のデータフレームのインデックスとカラム名を再設定しないといけなくなります。
PandasかScikit-learnの方を使えばデータフレームそのままでシャッフル出来るので便利です。



numpy.random.permutation()で2次元配列を行でシャッフル

2次元の配列を行でランダムにシャッフルします。

# numpy配列を行シャッフル
np.random.seed(0) # 乱数固定
na = np.arange(15).reshape(-1, 3) # 配列生成
shuffled_na = np.random.permutation(na) # 行でシャッフル# 表示print('元の配列')
print(na)
print('\n行でランダムにシャッフル')
print(shuffled_na)
元の配列
[[ 0  1  2]
 [ 3  4  5]
 [ 6  7  8]
 [ 9 10 11]
 [12 13 14]]

行でランダムにシャッフル
[[ 6  7  8]
 [ 0  1  2]
 [ 3  4  5]
 [ 9 10 11]
 [12 13 14]]



pandas.DataFrame.sample(frac=1)で行シャッフル

データフレームを行でシャッフルします。

# データフレーム作成
values = np.arange(1,16).reshape(-1,3)
name = ["項目1", "項目2", "項目3"]
_df = pd.DataFrame(values, columns=name)
# 表示
_df
項目1項目2項目3
0123
1456
2789
3101112
4131415
# pandasで行シャッフル
_df.sample(frac=1, random_state=0)  # random_stateで乱数固定
項目1項目2項目3
2789
0123
1456
3101112
4131415

行でシャッフルされました。
random_stateで乱数固定すると、何度実行しても同じ出力が返ります。外せば実行毎に違う出力になる。



次はScikit-learnでやってみます。

sklearn.utils.shuffle()で行シャッフル

これもデータフレームのまま行でシャッフルします。

# データフレーム生成
na = np.arange(25).reshape(5,5)
_df = pd.DataFrame(na)
_df
01234
001234
156789
21011121314
31516171819
42021222324
# データフレームの行をランダムにシャッフルするimport sklearn
sklearn.utils.shuffle(_df, random_state=0)  # (random_stateでランダムを固定)
01234
21011121314
001234
156789
31516171819
42021222324

行でシャッフルされました。

では次に、成績表でやってみます。Scikit-learnを使って行ってみます。

成績表のデータフレームをScikit-learnで行シャッフル

# 成績表dfの行をランダムに入れ替える
df_shuffled = sklearn.utils.shuffle(df, random_state=0)
df_shuffled
国語数学社会理科英語合計順位
2620598646821971
86477561117026439
2646931935331017
5503823875720579
7506633833621872
........................
96238335519728924
67659839148329921
645822725912195
47744353561003268
44573651672323461

100 rows × 7 columns

成績表が行でランダムにシャッフルされました。
インデックス番号がランダムに入れ替わっています。
以上で、行方向のシャッフルについては終わりです。


次に、↑でシャッフルしたデータフレームにクラス番号を割り振ります。
0組~3組の4つに分けます。上から25人ずつ割り振っていきます。



クラス番号列の生成

↑でシャッフルした成績表のデータフレームに、クラス番号の配列[0,0,・・・,1,1,・・・2,2,・・・,3,3・・・]のように数字が25個ずつ繰り返す列を追加したい。

[1,2,3]という配列を[1,1,2,2,3,3]にしたいような場合、numpy.repeat(配列, 回数, axis=整数)を使うと簡単にできます。

※ 参考:
[1,2,3][1,2,3,1,2,3,1,2,3]というタイル状に繰り返したい場合はnp.tile()が使えます。今回は使いません。



numpy.repeat(配列, 回数)で配列要素の繰り返し

まず、1次元配列で確認します。

# 配列の要素を増殖!
a1 = np.array([1,2,3]) # 1次元配列 リスト型やタプル型などでもOK
a1_3 = np.repeat(a1, 3) # 3回繰り返すprint("元の配列")
print(a1)
print("="*20)
print("3回繰り返し")
print(a1_3)
元の配列
[1 2 3]
====================
3回繰り返し
[1 1 1 2 2 2 3 3 3]

要素が指定回数に増えています。
np.repeat()の引数に渡す配列はリスト型やタプル型でもOKです。どれも同じ出力になります。

pd.Seriesデータを引数に渡すと、↓の例のようにSeriesデータで返ってきます。

# 配列の要素を増殖! (Seriesデータの場合)
a1_s = pd.Series(np.array([1,2,3])) # 1次元配列 Seriesに変換
a1_3_s = np.repeat(a1_s, 3) # 3回繰り返すprint("元の配列")
print(a1_s)
print("="*20)
print("3回繰り返し")
print(a1_3_s)
元の配列
0    1
1    2
2    3
dtype: int64
====================
3回繰り返し
0    1
0    1
0    1
1    2
1    2
1    2
2    3
2    3
2    3
dtype: int64



次は2次元配列で、axis=0とaxis=1の2パターン

# 配列の要素を増殖!
a2 = np.array([[1,2,3],[4,5,6],[7,8,9]]) # 2次元配列
a2_2_0 = np.repeat(a2, 2, axis=0) # 2回繰り返す axis=0方向
a2_3_1 = np.repeat(a2, 3, axis=1) # 3回繰り返す axis=1方向print("元の配列")
print(a2)
print("="*20)
print("2回繰り返す axis=0方向")
print(a2_2_0)
print("="*20)
print("3回繰り返す axis=1方向")
print(a2_3_1)
元の配列
[[1 2 3]
 [4 5 6]
 [7 8 9]]
====================
2回繰り返す axis=0方向
[[1 2 3]
 [1 2 3]
 [4 5 6]
 [4 5 6]
 [7 8 9]
 [7 8 9]]
====================
3回繰り返す axis=1方向
[[1 1 1 2 2 2 3 3 3]
 [4 4 4 5 5 5 6 6 6]
 [7 7 7 8 8 8 9 9 9]]

axis=0がデフォルトです。

np.repeat()の使い方を押さえたところで、

次は、クラス番号の配列[0,0,・・・,1,1,・・・2,2,・・・,3,3・・・]のように数字が25個ずつ繰り返すものを生成して、ランダムにシャッフルした成績表のデータフレームに列を追加します。

# クラス番号列を追加
df_shuffled_class = df_shuffled.copy() # コピー
df_shuffled_class['クラス番号'] = np.repeat(np.array([0,1,2,3]), 25)
df_shuffled_class
国語数学社会理科英語合計順位クラス番号
26205986468219710
864775611170264390
26469319353310170
55038238757205790
75066338336218720
...........................
962383355197289243
676598391483299213
6458227259121953
477443535610032683
445736516723234613

100 rows × 8 columns

ランダムにシャッフルした成績表にクラス番号を割り振りました。



今回はここまで。

次回はクラスごとに平均値を出し、標準偏差でクラス間のバラツキを見るところからです。
ありがとうございました。



【Numpy・Pandas・Scikit-learn】成績表からランダムにクラス分けしたバラツキ具合を標準偏差で確認する - よちよちpythonにつづく。


Viewing all articles
Browse latest Browse all 30

Trending Articles