pandas実践入門〜Kaggleなどのテーブル形式のデータを分析するために〜

# はじめに
テーブルデータの分析やKaggleに参加するなら必須であるpandasでよく使用するデータ処理をまとめました。

強い分析者になるならこんな所でつまづきたくない！また、ちょっとしたことで地味に使うけど、再度検索するのはめんどくさいといったようなtipsも含んでいます。
#### 注意
`import pandas as pd`やデータ生成については省略しています。
ご自分のプロジェクトやデータセットがあって、これから分析を始めたいといった方向けに書いております。

## Options
JupyterやPyCharmなどの分析環境でDataFrameの表示が省略されないようにします。 特にカラムは全て表示しておきたいと思うことがよくあります。

```Python
pd.set_option('display.max_columns', 50)

pd.set_option('display.max_rows', 50)
```

全部見える必要がなくなった場合には、表示設定を元に戻します。

```Python
pd.reset_option("^display")
```

## DaraFrame 書き出し
### indexがいらない場合 (Kaggle は不要)

```Python
df.to_csv('output.csv', index=False)
```

### Excelでcsvデータを扱う際に文字化けを防ぐオプション
地味tipsですが、人に見せる時にはExcelを使うことが多いので非常に有用だと思います。

```Python
df.to_csv('output.csv', encoding='utf_8_sig')
```

## データクリーニング

### 欠損データ処理

```Python
# 欠損値の数を把握する
df.isnull().sum()

# Nanがある行を全て取り除く（リストワイズ削除）
df.dropna()

# Nanを0で埋める
df.fillna(0)

# Nanを直前の行の値で埋める
df.fillna(method = 'ffill')

# Nanを各列の平均値で埋める(平均値代入法)
df.fillna(df.mean())
```

### 重複排除

重複している場合、先に出てくる行が残り、後ろの行が削除されることに注意してください。

```Python
# 行全体が重複しているものを削除(すべての列の要素が一致しているときに重複行であると判定)
df.drop_duplicates()

# 重複しているカラムの指定
df.drop_duplicates(['col_1'])

# 重複している複数カラムの指定
df.drop_duplicates(subset=['col1', 'col2'])
```

## データフレーム操作

### Slice(iloc, loc)

```Python
# 3行目から4行目まで, 0列目から1列目まで取得する
df.iloc[3:5, 0:2]

# 全行、col_1、col_2を選択して取得する
df.loc[:, ['col_1', 'col_2']]
```

`.loc`の方が列名を直接入れることができ、直感的に使えるので僕は好んで使用しています。


### indexリセット

```Python
# 結合したりしてインデックスを振り直したい時に使用する
df = df.reset_index()
```

### 列削除
axis指定でいつも迷ってしまいますが、axis=1で列を削除することができます。

```Python
# "col_1"のみを削除する
df = df.drop(['col_1'], axis=1)
```

## 連結・結合

SQLを普段からよく書いている方にとっては全く問題ないと思いますが、僕はよくどっち方向に結合されるのか、どうやって連結されるのか迷ってしまうのでまとめておきます。

### 連結

```Python
## 縦に積む(行方向に増やす, 縦に結合)
df = pd.concat([df_1, df_2, df_3])

## 横につなげる
df = pd.concat([df_1, df_2], axis=1)
```

`pd.concat()`を使う際に、`pd.merge()`と同じ感覚でやってしまい、( )の中の[ ]をよく忘れてしまいます。

### merge: キーを指定しての結合

```Python
# "key"列をもとに結合をする
df = pd.merge(df, df_sub, on='key')

# 複数のカラムをキーとする
df = pd.merge(df, df_sub, on=['key_1', 'key_2'])

# 左結合
df = pd.merge(df, df_sub, on='key', how='left')
```

### ソート

```Python
# "col_1"でソートする（デフォルトは昇順、つまり小さい値から大きい値）
df.sort_values(by='col_1')

# 降順でソートする（大きい方から順に落ちていく）
df.sort_values('col_1', ascending=False)

# 列名columnsでソートする
df.sort_index(axis=1, ascending=False)
```

### 出現頻度カウント (value_counts)

```Python
# NaN除いてそれぞれの要素がどれだけ含まれているか確認する
df['col_1'].value_counts()

# NaNを含めてカウントする
df['col_1'].value_counts(dropna=False)
```


### 値の書き換え (apply, map, (lambda))

**Series各要素の書き換え: map**

```Python
df = pd.DataFrame(data=[[11,12,13,14],[21,22,23,24],[31,32,33,34]],columns=["a","b","c","d"])
> df
    a   b   c   d
0  11  12  13  14
1  21  22  23  24
2  31  32  33  34

# 各要素に特定の処理
func = lambda x: '[{}]'.format(x)
df['a'].map(func)
> df['a'].map(func)
0    [11]
1    [21]
2    [31]
```

funcは`.map()`の中に入れてしまってOKです。できるだけ細かく分けて、初学者の方も理解できるような書き方をしてみました。


**DataFrameの各行・各列に適用: apply()**

```Python
# デフォルトでは各列に適用される
> df.apply(lambda x: max(x) - min(x))
a    31
b    32
c    33
d    34
dtype: int64

# axis=1で各行に適用される
> df.apply(lambda x: max(x) - min(x), axis=1)
0    3
1    3
2    3
dtype: int64
```

`apply()`, `map()`, `lambda`は動作に慣れると非常に便利に感じます。

## カテゴリ変数エンコーディング

One-Hot Encoding

```Python
# データ生成
df = pd.DataFrame(data=[["tarou","male"],["kenji","male"], ["yuka", "female"]],columns=["name","gender"])
> df
    name  gender
0  tarou    male
1  kenji    male
2   yuka  female

# prefixを付けることでone-hotの動作をわかりやすくする
tmp = pd.get_dummies(df['gender'], prefix='gender')
> tmp
   gender_female  gender_male
0              0            1
1              0            1
2              1            0

# 結合したあと元のカラムを削除する
df = df.join(tmp).drop('gender', axis=1)
> df
    name  gender_female  gender_male
0  tarou              0            1
1  kenji              0            1
2   yuka              1            0

# 上記で扱ったconcat()でも可
pd.concat([df, tmp], axis=1).drop('gender', axis=1)
> pd.concat([df, tmp], axis=1).drop('gender', axis=1)
    name  gender_female  gender_male
0  tarou              0            1
1  kenji              0            1
2   yuka              1            0

```


## 文字列操作

```Python
# 文字数をカウントする
series.str.len()

# aを空白文字に置換する（応用が効きやすい）
df["name"].str.replace("a", "")
> df["name"].str.replace("a", "")
0     trou
1    kenji
2      yuk
Name: name, dtype: object


# 表現を含んでいるかどうか(例は, aからbまでのアルファベットを含んでいるかどうかを確認している)
pattern = r'[a-b]'
df["name"].str.contains(pattern)
> df["name"].str.contains(pattern)
# kenjiはaもbも含んでいないのでFalse
0     True
1    False
2     True
Name: name, dtype: bool

# 全ての文字列を大文字または小文字にする
series.str.upper()  # .lower() 

# 頭文字を大文字にする (male → Male)
series.str.capitalize()

# 前後の空白を削除する
series.str.strip()
```

僕は、Seriesの後に`.str`をつけることを忘れて無駄なエラーを出してしまうことがあります。

こちらもご参照ください。

https://qiita.com/gonmurai/items/754040051572d02efb41


## 最後に
pandasを実際に書いていく際、変な所でつまってしまうのを防ぐために使い方をまとめてみました。

この記事は以下の情報を参考にして執筆しました。

https://note.nkmk.me/python-pandas-duplicated-drop-duplicates/

https://naotaka1128.hatenadiary.jp/entry/pandas-start-guide

https://note.nkmk.me/python-pandas-map-applymap-apply/