pandasによるデータ加工メモ

# pandasによるデータ加工メモ
Python pandasを利用したデータ加工方法をメモしておきます。

## pandasのロード
pandasをpdという名前でimportする。

```python
import pandas as pd
```

## CSVデータの読み込み
read_csv()でCSVファイルを読み込み、DataFrame型に格納する。

```
>>> pd.read_csv('test.csv')
       作成日時  地域ID       売上日        残高
0  20190517     6  20190502  23933698
1  20190517     6  20190504  23868717
2  20190517     6  20190505  23880357
3  20190517     6  20190511  23952177
4  20190517     6  20190512  25113212
```

### 文字コードを指定して読み込む
UTF-8以外のファイルを読み込む場合、encodingパラメタに文字コードを指定する。

```
>>> pd.read_csv("test_sjis.csv", encoding="SHIFT-JIS")
```

### データタイプを指定して読み込む
CSVファイルを読み込む際、カラムのデータ型を明に指定できる。

* dtypeパラメタでデータタイプを指定
* parse_datesパラメタで日付データのカラムを指定

```
>>> pd.read_csv('test.csv', parse_dates=[0,2], dtype={'残高':float})
        作成日時  地域ID        売上日          残高
0 2019-05-17     6 2019-05-02  23933698.0
1 2019-05-17     6 2019-05-04  23868717.0
2 2019-05-17     6 2019-05-05  23880357.0
3 2019-05-17     6 2019-05-11  23952177.0
4 2019-05-17     6 2019-05-12  25113212.0
```

### データタイプを後付で変更する
CSVの読み込み後にデータタイプを変更することも可能。

```
>>> df = pd.read_csv('test.csv')
>>> df['作成日時'] = pd.to_datetime(df['作成日時'], format='%Y%m%d')
>>> df['売上日'] = pd.to_datetime(df['売上日'], format='%Y%m%d')
>>> df['残高'] = df['残高'].astype(float)
```

## DatetimeIndexの操作(日時配列)
時系列データに使用する日時配列を作成する。

```
>>> pd.date_range('2019-05-01', '2019-05-08')
DatetimeIndex(['2019-05-01', '2019-05-02', '2019-05-03', '2019-05-04',
               '2019-05-05', '2019-05-06', '2019-05-07', '2019-05-08'],
              dtype='datetime64[ns]', freq='D')
```

### 日時の間隔について
date_range()のfreqパラメタで間隔を変更できる。

* freq='D': 1日単位(デフォルト)
* freq='2D': 2日単位
* freq='H': 1時間単位
* freq='2D5H': 2日と5H単位


## DataFrameの操作
### 読み込んだデータの参照
#### データ全体を参照

```
>>> df = pd.read_csv('test.csv')
>>> df.values
>>> df.values
array([[20190517,        6, 20190502, 23933698],
       [20190517,        6, 20190504, 23868717],
       [20190517,        6, 20190505, 23880357],
       [20190517,        6, 20190511, 23952177],
       [20190517,        6, 20190512, 25113212]], dtype=int64)
```

#### 行を指定して参照
```
>>> df.values[0]
array([20190517,        6, 20190502, 23933698], dtype=int64)
```

#### 行列を指定して参照
```
>>> df.values[0][0]
20190517
```

#### カラム名を指定して参照

```
>>> df['作成日時']
0    20190517
1    20190517
2    20190517
3    20190517
4    20190517
Name: 作成日時, dtype: int64
```

### カラム名の変更
カラム名「作成日時」を「Date」に変更する例。

```
>>> df.rename(columns={'作成日時': 'Date'})
       Date  地域ID       売上日        残高
0  20190517     6  20190502  23933698
1  20190517     6  20190504  23868717
2  20190517     6  20190505  23880357
3  20190517     6  20190511  23952177
4  20190517     6  20190512  25113212
```

### DatetimeIndexからDataFrameへの変換
DatetimeIndexからDataFrameに変換する際にカラム名を付ける場合、columnsパラメタを指定する。

```
>>> didx = pd.date_range('2019-05-01', '2019-05-05')
>>> pd.DataFrame(didx, columns=['売上日'])
         売上日
0 2019-05-01
1 2019-05-02
2 2019-05-03
3 2019-05-04
4 2019-05-05
```

### ソート
sort_values()でbyパラメタにカラム名を指定することでソートできる。

```
>>> df = pd.read_csv('test.csv', parse_dates=[0,2], dtype={'残高':float})
>>> df.sort_values(by='残高')
        作成日時  地域ID        売上日          残高
1 2019-05-17     6 2019-05-04  23868717.0
2 2019-05-17     6 2019-05-05  23880357.0
0 2019-05-17     6 2019-05-02  23933698.0
3 2019-05-17     6 2019-05-11  23952177.0
4 2019-05-17     6 2019-05-12  25113212.0
```

### マージ
2つのDataFrameをマージして時系列データを作成する。
マージ後に値が埋まらないフィールドには欠損値(NaN)が格納される。

```
>>> df1 = pd.DataFrame(pd.date_range('2019-05-01', '2019-05-13'), columns=['売上日'])
>>> df2 = pd.read_csv('test.csv', parse_dates=[0,2], dtype={'残高':float})
>>> pd.merge(left=df1, right=df2, how='outer', left_on='売上日', right_on='売上日')
          売上日       作成日時  地域ID          残高
0  2019-05-01        NaT   NaN         NaN
1  2019-05-02 2019-05-17   6.0  23933698.0
2  2019-05-03        NaT   NaN         NaN
3  2019-05-04 2019-05-17   6.0  23868717.0
4  2019-05-05 2019-05-17   6.0  23880357.0
5  2019-05-06        NaT   NaN         NaN
6  2019-05-07        NaT   NaN         NaN
7  2019-05-08        NaT   NaN         NaN
8  2019-05-09        NaT   NaN         NaN
9  2019-05-10        NaT   NaN         NaN
10 2019-05-11 2019-05-17   6.0  23952177.0
11 2019-05-12 2019-05-17   6.0  25113212.0
12 2019-05-13        NaT   NaN         NaN
```

### 欠損値の処理
#### 欠損値の判定
isnull()でTrueを返すものが欠損値である。

```
>>> df = pd.merge(left=df1, right=df2, how='outer', left_on='売上日', right_on='売上日')
>>> df.isnull()
      売上日   作成日時   地域ID     残高
0   False   True   True   True
1   False  False  False  False
2   False   True   True   True
3   False  False  False  False
4   False  False  False  False
5   False   True   True   True
6   False   True   True   True
7   False   True   True   True
8   False   True   True   True
9   False   True   True   True
10  False  False  False  False
11  False  False  False  False
12  False   True   True   True
```

どのカラムに欠損値が含まれるかはany()で判定できる。

```
>>> df.isnull().any()
売上日     False
作成日時     True
地域ID     True
残高       True
dtype: bool
```

#### 前行の値を設定
前行がNaNの場合、欠損値のままとなる。

```
>>> df.fillna(method='ffill')
          売上日       作成日時  地域ID          残高
0  2019-05-01        NaT   NaN         NaN
1  2019-05-02 2019-05-17   6.0  23933698.0
2  2019-05-03 2019-05-17   6.0  23933698.0
3  2019-05-04 2019-05-17   6.0  23868717.0
4  2019-05-05 2019-05-17   6.0  23880357.0
5  2019-05-06 2019-05-17   6.0  23880357.0
6  2019-05-07 2019-05-17   6.0  23880357.0
7  2019-05-08 2019-05-17   6.0  23880357.0
8  2019-05-09 2019-05-17   6.0  23880357.0
9  2019-05-10 2019-05-17   6.0  23880357.0
10 2019-05-11 2019-05-17   6.0  23952177.0
11 2019-05-12 2019-05-17   6.0  25113212.0
12 2019-05-13 2019-05-17   6.0  25113212.0
```

#### 指定された値を設定
欠損値NaNのフィールドを指定された値に置き換える。

```
>>> df = df.fillna(method='ffill')
>>> df.fillna(0)
          売上日                 作成日時  地域ID          残高
0  2019-05-01                    0   0.0         0.0
1  2019-05-02  2019-05-17 00:00:00   6.0  23933698.0
2  2019-05-03  2019-05-17 00:00:00   6.0  23933698.0
3  2019-05-04  2019-05-17 00:00:00   6.0  23868717.0
4  2019-05-05  2019-05-17 00:00:00   6.0  23880357.0
5  2019-05-06  2019-05-17 00:00:00   6.0  23880357.0
6  2019-05-07  2019-05-17 00:00:00   6.0  23880357.0
7  2019-05-08  2019-05-17 00:00:00   6.0  23880357.0
8  2019-05-09  2019-05-17 00:00:00   6.0  23880357.0
9  2019-05-10  2019-05-17 00:00:00   6.0  23880357.0
10 2019-05-11  2019-05-17 00:00:00   6.0  23952177.0
11 2019-05-12  2019-05-17 00:00:00   6.0  25113212.0
12 2019-05-13  2019-05-17 00:00:00   6.0  25113212.0
```

#### 欠損値を削除
欠損値を含むデータを削除する関数。

```
>>> df = pd.merge(left=df1, right=df2, how='outer', left_on='売上日', right_on='売上日')
>>> df.dropna()
          売上日       作成日時  地域ID          残高
1  2019-05-02 2019-05-17   6.0  23933698.0
3  2019-05-04 2019-05-17   6.0  23868717.0
4  2019-05-05 2019-05-17   6.0  23880357.0
10 2019-05-11 2019-05-17   6.0  23952177.0
11 2019-05-12 2019-05-17   6.0  25113212.0
```

### データの削除
#### 行の削除
```
>>> df.drop(10, axis=0)
```
  * axis=0は省略できる
  * 削除対象は配列で指定できる

#### 列の削除
```
>>> df.drop('売上日', axis=1)
         作成日時  地域ID          残高
0         NaT   NaN         NaN
1  2019-05-17   6.0  23933698.0
2         NaT   NaN         NaN
3  2019-05-17   6.0  23868717.0
4  2019-05-17   6.0  23880357.0
5         NaT   NaN         NaN
6         NaT   NaN         NaN
7         NaT   NaN         NaN
8         NaT   NaN         NaN
9         NaT   NaN         NaN
10 2019-05-17   6.0  23952177.0
11 2019-05-17   6.0  25113212.0
12        NaT   NaN         NaN
```

### その他
#### オリジナルデータの変更
通常、DataFrameの操作はオリジナルデータを破壊しないが、「inplace=True」を指定することでオリジナルデータを直接変更できる。

```
>>> df.drop(index=0, axid=0, inplace=True)
```


# 参考
* [pandasの時系列データにおける頻度（引数freq）の指定方法](https://note.nkmk.me/python-pandas-time-series-freq/)

* [欠損値を処理する方法](https://qiita.com/ground0state/items/40c2cf0295af53d1193e)

* [Pandasで2つのデータを横方向に結合するmerge関数の使い方](https://deepage.net/features/pandas-merge.html)