機械学習のためのPandas利用
機械学習のための言語はPython,Pandas,Numpy,Scikit-learn等を使うことが多いです。
データの前処理のためには、特にPandasを使うことが多いので、よく使うものやこれはいいなと思うものを自分用のメモとしてここに残すことにします。
使い方集
具体的なデータがあったほうがいいので、3つのDataFrameを使って覚えた使い方を羅列していきます。
DataFrameは学校の生徒6人分のいろんなデータを扱うことにして、
- df:ID、氏名、クラス、身長
- df1:ID、住んでる駅、生年月日
- df2:ID、好きな色
といったデータをそれぞれのDataFrameに格納します。
コマンドは全てIPython上で実行しています。
データ作成~中身調べ
データは実際はcsvとかsqlの結果とかをDataFrameに変換するのが通常ですが簡易的なデータが欲しいので作成します。
import pandas as pd df = pd.DataFrame({'id' : [100, 101, 102, 103, 104, 105], 'name' : ['tom', 'kei', 'jasmin', 'chris', 'ford', 'marie'], 'class' : ['A', 'A', 'A', 'B', 'B', 'C'], 'height': [170, 162, 157, 177, 180, 156]}, index=[0, 1, 2, 3, 4, 5]) df1 = pd.DataFrame({'id' : [100, 101, 102, 103, 104, 105], 'station' : ['harborfront', 'orchard', 'marina bay', 'orchard', 'bugis', 'bugis'], 'birthday': ['1992-05-01 12:10:05', '1992-04-04 20:50:39', '1991-01-07 07:28:00', '1992-10-21 08:17:59', '1992-09-27 03:50:25', '1992-06-04 02:23:41']}, index=[0, 1, 2, 3, 4, 5]) df2 = pd.DataFrame({'id' : [100, 100, 101, 101, 102, 102, 103, 104, 105, 105, 105], 'color' : ['red', 'black', 'pink', 'orange', 'green', 'red', 'blue', 'white', 'gray', 'yellow', 'red']}, index=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
データをパッと見たい時は.head()
最初の5レコード分を表示します。
In [2]: df.head() Out[2]: class height id name 0 A 170 100 tom 1 A 162 101 kei 2 A 157 102 jasmin 3 B 177 103 chris 4 B 180 104 ford
指定した列にどんな内容のデータがそれぞれいくつあるのかを調べるとき。
In [3]: df['class'].value_counts() Out[3]: A 3 B 2 C 1 Name: class, dtype: int64
DataFrameの各データの型なんかを知りたいときは.info()
In [4]: df.info()Int64Index: 6 entries, 0 to 5 Data columns (total 4 columns): class 6 non-null object height 6 non-null int64 id 6 non-null int64 name 6 non-null object dtypes: int64(2), object(2) memory usage: 240.0+ bytes
複数DataFrameのマージ
複数DataFrameを特定の列の値でもってマージする場合は.merge()でできます。
複数のDataFrameでキーを共有している場合に使えます。
第一、第二引数に結合するDataFrameを指定、howはインナージョインか、アウタージョインか、レフトアウタージョインか、ライトアウタージョインか。(この例だとどれを指定しても変わらないです。キーは両方のDataFrameで同じものを1つずつもっているから)
onにマージするためのキーを指定。
In [5]: df = pd.merge(df, df1, on='id') In [6]: df Out[6]: class height id name birthday station 0 A 170 100 tom 1992-05-01 12:10:05 harborfront 1 A 162 101 kei 1992-04-04 20:50:39 orchard 2 A 157 102 jasmin 1991-01-07 07:28:00 marina bay 3 B 177 103 chris 1992-10-21 08:17:59 orchard 4 B 180 104 ford 1992-09-27 03:50:25 bugis 5 C 156 105 marie 1992-06-04 02:23:41 bugis
行列の削除
行単位、列単位での削除は.drop()です。
axis=1を指定すると列単位での削除になります。
In [10]: df.drop(['station'], axis=1) Out[10]: class height id name birthday 0 A 170 100 tom 1992-05-01 12:10:05 1 A 162 101 kei 1992-04-04 20:50:39 2 A 157 102 jasmin 1991-01-07 07:28:00 3 B 177 103 chris 1992-10-21 08:17:59 4 B 180 104 ford 1992-09-27 03:50:25 5 C 156 105 marie 1992-06-04 02:23:41
重複の操作
重複する値が複数レコードにまたがってあり、その中の一行だけ残す時は.drop_duplicates
keep=hogehogeでどの行を残すかを指定できます。
クラスの代表をIDの数字の若い順で決めるとして、各クラスの代表一覧を欲しかったら下記のように行います。
In [11]: df.drop_duplicates(['class'],keep='first') Out[11]: class height id name station 0 A 170 100 tom harborfront 3 B 177 103 chris orchard 5 C 156 105 marie bugis
重複する複数レコードをぎゅっとまとめて、その上で何かの値を得たいときは.froupby()
所属しているクラスの人数をカウントしてみます。
A、B、Cクラスそれぞれ3レコード、2レコード、1レコードなのでその値を得ます。
In [19]: df.groupby(['class'])['id'].transform('count') Out[19]: 0 3 1 3 2 3 3 2 4 2 5 1 dtype: int64
.transform()で指定した列の値に対して何かを行いますが(上の例だとID)
クラス毎の身長の最大値を求めたりもできます(最小値とか平均値とかもmin、mean等で取得できます。)
In [20]: df.groupby(['class'])['height'].transform('max') Out[20]: 0 170 1 170 2 170 3 180 4 180 5 156 dtype: int64
この後.drop_duplicatesを行えば、1クラス1レコードのDataFrameを得ることができるので、クラスごとの平均身長のみを取り出す、といったことができます。
時間の操作
時間の操作はまず.to_datetime()で型を変換します。
In [13]: time = pd.to_datetime(df1['birthday']) In [14]: time.map(lambda x: x.strftime('%d-%m-%Y')) Out[14]: 0 01-05-1992 1 04-04-1992 2 07-01-1991 3 21-10-1992 4 27-09-1992 5 04-06-1992 Name: birthday, dtype: object
時間の一部を切り取るときは式を定義して下記のように行います。
In [15]: time.map(lambda x: x.strftime('%H')) Out[15]: 0 12 1 20 2 07 3 08 4 03 5 02 Name: birthday, dtype: object
同じキーが重複する複数レコードを他のDataFrameに渡す
df2で定義した生徒毎の好きな色(複数個あるのでレコードも複数行)
をdfに渡してあげるときには、一旦df2で.groupby()しつつ情報をぎゅっとまとめて、dfにマージした後にまとまった情報を再度広げます。
idをキーにまとめつつ、生徒毎の複数ある好きな色をまとめて一つのカラムに格納します。
In [11]: df2 = df2.groupby('id')['color'].apply(lambda x: " ".join(set("color:" + str(s) for s in x ))) In [12]: df2 Out[12]: id 100 color:black color:red 101 color:orange color:pink 102 color:green color:red 103 color:blue 104 color:white 105 color:red color:yellow color:gray Name: color, dtype: object
dfに新しい列を追加。.groupby()した後のデータ型はDataFrameではなくSeriesになっているので、ここでは.merge()ではなく.map()を使います。
In [18]: df['color'] = df['id'].map(df2) In [19]: df Out[19]: class height id name birthday station \ 0 A 170 100 tom 1992-05-01 12:10:05 harborfront 1 A 162 101 kei 1992-04-04 20:50:39 orchard 2 A 157 102 jasmin 1991-01-07 07:28:00 marina bay 3 B 177 103 chris 1992-10-21 08:17:59 orchard 4 B 180 104 ford 1992-09-27 03:50:25 bugis 5 C 156 105 marie 1992-06-04 02:23:41 bugis color 0 color:black color:red 1 color:orange color:pink 2 color:green color:red 3 color:blue 4 color:white 5 color:red color:yellow color:gray
そして追加したcolorという列を、半角スペースで区切って複数のレコードに分けます。
例えばクラスで生徒に”好きな色のアンケート(複数個回答可能)”をとって、それをクラスごとに集計した、と考えると下記のような方法になります。
In [22]: df_color = pd.concat([pd.Series(row['class'], row['color'].split(' '))for _, row in df.iterrows()]).reset_index() In [23]: df_color.columns = ['color', 'class'] In [24]: df_color Out[24]: color class 0 color:black A 1 color:red A 2 color:orange A 3 color:pink A 4 color:green A 5 color:red A 6 color:blue B 7 color:white B 8 color:red C 9 color:yellow C 10 color:gray C