pandas でデータを操作する時の Tips (前編) です。最近, pandas を使う機会が増えてきたので備忘録を残しておきます。前編は基本的な前処理に関する内容です。
環境は Python 2.7.11, pandas 0.20.3 です。
今回扱う内容は以下です。
- 要約統計量
- 相関・共分散
- 文字列の分割
- ダミー変数化
- long と wide の変換
- 欠測値の削除
- 欠測値の補完
- 値でソート
- インデックスの振り直し
- 重複値の削除
- 順序性を持つ質的変数の数値化
- 値の部分置換
- 分位数
- datetime への変換
pandas のデータ構造
pandas の基本的なデータ構造を簡単に振り返る。
- pandas.Series: インデックス付きの1次元配列。
- pandas.DataFrame: R の data.frame のような行と列から構成されたデータ構造。 行列と異なるのは各列が同じ型を持つ必要がない点。
- pandas.Index: 軸ラベルやメタデータを保持。
1. 要約統計量
pandas.DataFrame.describe は列ごとの平均, 標準偏差, 最大最小, 四分位数を返す。
In [1]: import numpy as np
import pandas as pd
In [2]: iris = pd.read_csv('iris.csv')
In [3]: iris.describe()
Out[3]:
sepal_length sepal_width petal_length petal_width
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.054000 3.758667 1.198667
std 0.828066 0.433594 1.764420 0.763161
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000
2. 相関・共分散
pandas.DataFrame.corr は相関行列, pandas.DataFrame.cov は共分散を返す。
In [4]: iris.corr()
Out[4]:
sepal_length sepal_width petal_length petal_width
sepal_length 1.000000 -0.109369 0.871754 0.817954
sepal_width -0.109369 1.000000 -0.420516 -0.356544
petal_length 0.871754 -0.420516 1.000000 0.962757
petal_width 0.817954 -0.356544 0.962757 1.000000
In [5]: iris.cov()
Out[5]:
sepal_length sepal_width petal_length petal_width
sepal_length 0.685694 -0.039268 1.273682 0.516904
sepal_width -0.039268 0.188004 -0.321713 -0.117981
petal_length 1.273682 -0.321713 3.113179 1.296387
petal_width 0.516904 -0.117981 1.296387 0.582414
3. 文字列の分割
pandas.Series.str.split は文字列パターンで Series を分割する。
In [6]: df_ceo = pd.DataFrame({'name': ['Marissa Mayer', 'Elon Musk', 'Steve Jobs'],'gender': ['f', 'm', 'm']})
In [7]: df_ceo.name.str.split(" ", expand=True)
Out[7]:
0 1
0 Marissa Mayer
1 Elon Musk
2 Steve Jobs
4. ダミー変数化
pandas.get_dummies は質的変数をダミー変数化 (One-Hot Encoding) する。
drop_first=True の場合, 基準レベルは除く。
In [8]: pd.get_dummies(df_ceo['gender'], drop_first=True)
Out[8]:
m
0 0
1 1
2 1
In [9]: pd.get_dummies(df_ceo['gender'])
Out[9]:
f m
0 1 0
1 0 1
2 0 1
5. long と wide の変換
pandas.DataFrame.pivot は DataFrame を long形式 から wide形式 に変換する。 pandas.DataFrame.stack は列から行へ回転した DataFrame を返し, 逆に pandas.DataFrame.unstack は行から列へ回転した DataFrame を返す。 unstack を使って wide形式 から long形式 に戻してみる。
In [10]: df_long = pd.DataFrame({'id': [1, 1, 1, 2, 2, 2], 'times': [1, 2, 3, 1, 2, 3], 'score': [6,
5, 5, 8, 6, 9]})
In [11]: df_wide = df_long.pivot(index='id', columns='times', values='score')
In [12]: df_long
Out[12]:
id score times
0 1 6 1
1 1 5 2
2 1 5 3
3 2 8 1
4 2 6 2
5 2 9 3
In [13]: df_wide
Out[13]:
times 1 2 3
id
1 6 5 5
2 8 6 9
In [14]: df_wide.unstack().reset_index().sort_values("id")
Out[14]:
times id 0
0 1 1 6
2 2 1 5
4 3 1 5
1 1 2 8
3 2 2 6
5 3 2 9
6. 欠測値の削除
pandas.DataFrame.isnull は各要素に対して欠測値か否かを boolean で表した DataFrame を返す。pandas.DataFrame.any と組み合わせることで列や行単位で欠測値を含むかわかる。
In [15]: df_nan = pd.DataFrame({'A': [np.nan]*3+[1,2,3], 'B': [np.nan]*3+[4,5,6], 'C': range(1,7)})
In [16]: df_nan
Out[16]:
A B C
0 NaN NaN 1
1 NaN NaN 2
2 NaN NaN 3
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
In [17]: df_nan.isnull()
Out[17]:
A B C
0 True True False
1 True True False
2 True True False
3 False False False
4 False False False
5 False False False
In [18]: df_nan.isnull().any()
Out[18]:
A True
B True
C False
dtype: bool
pandas.DataFrame.dropna は axis=0 で NA/NaN を含む行を削除, axis=1 で NA/NaN を含む列を削除する。
In [19]: dropped = df_nan.dropna()
In [20]: dropped
Out[20]:
A B C
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
In [21]: df_nan.dropna(axis=1)
Out[21]:
C
0 1
1 2
2 3
3 4
4 5
5 6
7. 欠測値の補完
pandas.Series.fillna は axis=0 で行方向の NA/Nan, axis=1 で列方向の NA/NaN に指定した定数や関数適用により補完する。
dict を与えることで行または列ごとに異なる定数で補完できる。また, 時系列データの欠測値を前後の値で補完したい場合があるが method=’ffill’ で前の観測値, method=’bfill’ で後ろの観測値で補完できる。
In [22]: imputed = df_nan.apply(lambda x: x.fillna(x.median()), axis=0)
In [23]: imputed
Out[23]:
A B C
0 2.0 5.0 1
1 2.0 5.0 2
2 2.0 5.0 3
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
In [24]: df_nan.apply(lambda x: x.fillna(x.median()), axis=1)
Out[24]:
A B C
0 1.0 1.0 1.0
1 2.0 2.0 2.0
2 3.0 3.0 3.0
3 1.0 4.0 4.0
4 2.0 5.0 5.0
5 3.0 6.0 6.0
In [25]: df_nan.fillna({'A': 1, 'B': 2})
Out[25]:
A B C
0 1.0 2.0 1
1 1.0 2.0 2
2 1.0 2.0 3
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
In [26]: df_nan.fillna(method='bfill')
Out[26]:
A B C
0 1.0 4.0 1
1 1.0 4.0 2
2 1.0 4.0 3
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
8. 値でソート
pandas.DataFrame.sort_values は axis=0 で行方向の値、axis=1 で列方向の値でソートする。第一引数 by には複数の列名を指定できる。
ascending=True で昇順, ascending=False で降順でソートする。(default: True)
In [27]: dropped.sort_values(['A', 'B'], ascending=True)
Out[27]:
A B C
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
In [28]: dropped.sort_values(['A'], ascending=False)
Out[28]:
A B C
5 3.0 6.0 6
4 2.0 5.0 5
3 1.0 4.0 4
MultiIndex を持つ DataFrame に対してソートする場合, 以下のように全ての level を tuple で指定する。
In [29]: iris[['petal_width', 'species']].groupby(u'species').agg(['mean']).sort_values([('petal_width', 'mean')], ascending=False)
Out[29]:
petal_width
mean
species
virginica 2.026
versicolor 1.326
setosa 0.244
9. インデックスの振り直し
まずは DataFrame.index に index オブジェクトを代入する方法。 pd.Index() の name オプションに値を指定すると named index となる。index オブジェクト自体は immutable である。
In [30]: dropped.index = pd.Index(range(1,4))
In [31]: dropped
Out[31]:
A B C
1 1.0 4.0 4
2 2.0 5.0 5
3 3.0 6.0 6
pandas.DataFrame.reset_index は 0 から始まる新しい index を振った DataFrame を返す。 drop=False を指定した場合, 古い index が列として追加される。
In [32]: dropped.reset_index(drop=True)
Out[32]:
A B C
0 1.0 4.0 4
1 2.0 5.0 5
2 3.0 6.0 6
また, inplace=True を指定した場合, 新しい DataFrame を返さずに元の DataFrame 自身を書き換える。集約結果に対しては reset_index(inplace=True) しておく方が安全な場合が多い。
In [33]: dropped2 = dropped.copy()
In [34]: dropped2
Out[34]:
A B C
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
In [35]: dropped2.reset_index(inplace=True, drop=True)
In [36]: dropped2
Out[36]:
A B C
0 1.0 4.0 4
1 2.0 5.0 5
2 3.0 6.0 6
pandas.DataFrame.reindex は DataFrame に新しい index を振った DataFrame を返す。元の index に値がない場合 NA/NaN となるが fill_value で定数補完したり method=’nearest’ で最も近い値で補完したりできる。
In [37]: dropped.reindex(range(1,10))
Out[37]:
A B C
1 1.0 4.0 4.0
2 2.0 5.0 5.0
3 3.0 6.0 6.0
4 NaN NaN NaN
5 NaN NaN NaN
6 NaN NaN NaN
7 NaN NaN NaN
8 NaN NaN NaN
9 NaN NaN NaN
In [38]: dropped.reindex(range(1,10), fill_value=0)
Out[38]:
A B C
1 1.0 4.0 4
2 2.0 5.0 5
3 3.0 6.0 6
4 0.0 0.0 0
5 0.0 0.0 0
6 0.0 0.0 0
7 0.0 0.0 0
8 0.0 0.0 0
9 0.0 0.0 0
In [39]: dropped.reindex(range(1,10), method='nearest')
Out[39]:
A B C
1 1.0 4.0 4
2 2.0 5.0 5
3 3.0 6.0 6
4 3.0 6.0 6
5 3.0 6.0 6
6 3.0 6.0 6
7 3.0 6.0 6
8 3.0 6.0 6
9 3.0 6.0 6
10. 重複値の削除
pandas.DataFrame.duplicated は各行が重複しているか否かの論理値を Series で返す。pandas.DataFrame.drop_duplicates は重複行を削除した DataFrame を返す。
In [40]: imputed
Out[40]:
A B C
0 2.0 5.0 1
1 2.0 5.0 2
2 2.0 5.0 3
3 1.0 4.0 4
4 2.0 5.0 5
5 3.0 6.0 6
In [41]: imputed.duplicated(subset='A')
Out[41]:
0 False
1 True
2 True
3 False
4 True
5 False
dtype: bool
In [42]: imputed.drop_duplicates(subset='A')
Out[42]:
A B C
0 2.0 5.0 1
3 1.0 4.0 4
5 3.0 6.0 6
pandas.Index.duplicated は index が重複している否かの論理値を numpy.ndarray で返す。
In [43]: dropped.index = [0, 1, 1]
In [44]: dropped
Out[44]:
A B C
0 1.0 4.0 4
1 2.0 5.0 5
1 3.0 6.0 6
In [45]: dropped[~dropped.index.duplicated(keep='first')]
Out[45]:
A B C
0 1.0 4.0 4
1 2.0 5.0 5
11. 順序性を持つ質的変数の数値化
pandas.DataFrame.replace は第一引数に置換後の値, 第二引数に置換対象の値を指定する。第一引数には dict を指定することができ key を置換対象の値, value を置換後の値とする。
In [46]: df_ceo = pd.DataFrame({'name': ['Marissa Mayer', 'Elon Musk', 'Steve Jobs'], 'size': ['S', 'M', 'L']})
In [47]: df_ceo
Out[47]:
name size
0 Marissa Mayer S
1 Elon Musk M
2 Steve Jobs L
In [48]: mapper = {'S': 1, 'M': 2, 'L': 3}
In [49]: df_ceo.replace(mapper)
Out[49]:
name size
0 Marissa Mayer 1
1 Elon Musk 2
2 Steve Jobs 3
順序尺度の質的変数を数値化する際は, 順序間の距離にどのような仮定を置くかは考慮すべき点である。
12. 値の部分置換
要素が持つ値を部分的に置換したい時は pandas.DataFrame.replace の引数 regex を True に設定し, 置換対象を正規表現で指定する。
In [50]: mapper = {r'(.*)s(.*)': r'\1S\2', r'^M
13. 分位数
分位数 (quantile) は昇順で並べられたデータを n 個に均等に分割するときの境界値で, pandas.DataFrame.quantile で求めることができる。
In [52]: iris.quantile(np.arange(.1, 1., 0.1))
Out[52]:
sepal_length sepal_width petal_length petal_width
0.1 4.80 2.50 1.40 0.20
0.2 5.00 2.70 1.50 0.20
0.3 5.27 2.80 1.70 0.40
0.4 5.60 3.00 3.90 1.16
0.5 5.80 3.00 4.35 1.30
0.6 6.10 3.10 4.64 1.50
0.7 6.30 3.20 5.00 1.80
0.8 6.52 3.40 5.32 1.90
0.9 6.90 3.61 5.80 2.20
14. datetime への変換
pandas.to_datetime は様々な入力を datetime に変換するユーティリティ関数。入力が Series の場合は datetime64 を返す。
In [53]: df_ts = pd.DataFrame({'start': ['2018-01-03 01:00:00', '2018-01-04 01:00:00', '2018-01-05 01:
...: 00:00'],'end': ['2018-01-03 06:00:00', '2018-01-05 11:00:00', '2018-01-06 21:00:00']})
In [54]: df_ts["start"] = pd.to_datetime(df_ts["start"])
In [55]: df_ts["end"] = pd.to_datetime(df_ts["end"])
In [56]: df_ts.dtypes
Out[56]:
start datetime64[ns]
end datetime64[ns]
dtype: object
In [57]: df_ts['end'] - df_ts['start']
Out[57]:
0 0 days 05:00:00
1 1 days 10:00:00
2 1 days 20:00:00
dtype: timedelta64[ns]
おわりに
前編では Pandas を使ったデータの要約の出力や欠測値の削除・補完など前処理に関する内容を紹介しました。中編では連結・結合・グループ化について紹介します。
[1] Python pandas データ選択処理をちょっと詳しく <中編>
[2] Elegantly Reading Multiple CSVs Into Pandas