【Python】pandas でデータ操作 (前編)

| 0件のコメント

pandas でデータを操作する時の Tips (前編) です。最近, pandas を使う機会が増えてきたので備忘録を残しておきます。前編は基本的な前処理に関する内容です。

環境は Python 2.7.11, pandas 0.20.3 です。

今回扱う内容は以下です。

  1. 要約統計量
  2. 相関・共分散
  3. 文字列の分割
  4. ダミー変数化
  5. long と wide の変換
  6. 欠測値の削除
  7. 欠測値の補完
  8. 値でソート
  9. インデックスの振り直し
  10. 重複値の削除
  11. 順序性を持つ質的変数の数値化
  12. 値の部分置換
  13. 分位数
  14. 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

: 'Midium'} In [51]: df_ceo.replace(mapper, regex=True) Out[51]: name size 0 MarisSa Mayer S 1 Elon MuSk Midium 2 Steve JobS L 

13. 分位数

分位数 (quantile) は昇順で並べられたデータを n 個に均等に分割するときの境界値で, pandas.DataFrame.quantile で求めることができる。


14. datetime への変換

pandas.to_datetime は様々な入力を datetime に変換するユーティリティ関数。入力が Series の場合は datetime64 を返す。


おわりに

前編では pandas を使ったデータの要約の出力や欠測値の削除・補完など前処理に関する内容を紹介しました。中編では連結・結合・グループ化について紹介します。

[1] Python pandas データ選択処理をちょっと詳しく <中編>
[2] Elegantly Reading Multiple CSVs Into Pandas