pandasノススメ -SQLと同じようにできること-

# 背景
最近csvを貰い、「この列の○○が欲しい」とか「このcsvとあのcsvをこの列でjoinした感じのものが欲しい」とかとにかくcsvファイルやエクセルファイルをもらうことが多くなった。
そういうものに限って複雑な文字列の操作を行う必要があったりしてSQL単体だと出来そうにもない。
そこで目を付けたのが**pandas**。
pythonでSQLライクな操作ができてpythonだから複雑な文字列処理もなんのその。ということで以下にpandasの使い方を記述する。
書いてて辛くなったのでSQLと同じように出来ることとpandasだからできることの2段階に分ける

# pandasを使う上で必要な話
pandasにはいくつかクラスが用意されている
・DataFrameクラス：2次元のデータ、基本はこれで操作する。
・Seriesクラス：1次元のデータ。DataFrameの一列だけ抽出するとこれになる。
・GroupByクラス：Grouop Byをするとこれになる。

pandasで複数の列を操作したいときは**リストで渡すとだいたいなんとかなる**
(でも公式ドキュメント見た方がはやかったりする)

## 読み込み
pandasでのcsv, tsv, xlsxファイルの読み込み方は以下の通り。

```py
import pandas as pd

#csvファイル読み込み
csv_data = pd.read_csv("hoge.csv")


#tsvファイル読み込み
csv_data = pd.read_csv("hoge.tsv", delimiter = '\t')


#excelファイル読み込み
csv_data = pd.read_excel("hoge.xlsx")
```
### 注意点
列名が付加されていないデータ(↓のa,b,cが存在しないようなデータ)に対しては読み込む際に`header=None`を付けたり`names=('a','b','c')`で列名を付けられる

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|4  |5  |6  |

使い方は以下に示す。

```py
#csvファイル読み込み(読み込む際に列名を0,1,2と順番に付けてくれる)
csv_data = pd.read_csv("hoge.csv", header=None)
```
|0  |1  |2  |
|---|---|---|
|1  |2  |3  |
|4  |5  |6  |



```py
#tsvファイル読み込み（読み込む際に列名を付けてくれる）
csv_data = pd.read_csv("hoge.tsv", delimiter = '\t', name=("d","e","f"))
```
|d  |e  |f  |
|---|---|---|
|1  |2  |3  |
|4  |5  |6  |

#SQLと同じように出来ること

## JOIN
merge関数とjoin関数があるがここではmerge関数を紹介する(join関数はデフォルトがleft joinなのでデフォルトがinner joinのこっちを紹介したい)
第1引数と第2引数にjoinするデータフレーム、on句には列名を記述する。
**複数列でjoinしたいときはon句にリストで渡すとよい**

```py
import pandas as pd

#1列で結合する
pd.merge(csv_data, tsv_data, on='a')

#複数列で結合する
#pd.merge(csv_data, tsv_data, on=['a','b']) #csv_dataにｂ列は存在しないが便宜的にb列と書いている
```
↓csv_data　　　　↓tsv_data

|a  |e  |f  |  |a  |b  |c  |
|---|---|---|---|---|---|---|
|1  |2  |3  |  |1  |2  |3  |
|4  |5  |6  |  |4  |5  |6  |


↓　join後

|a  |e  |f  |b  |c  |
|---|---|---|---|---|
|1  |2  |3  |2  |3  |
|4  |5  |6  |5  |6  |

howを追加することで**left join**,**right join**,**full outer join**もできる

```py
import pandas as pd

pd.merge(csv_data, tsv_data, on='a' , how='left')#left join
pd.merge(csv_data, tsv_data, on='a' , how='right')#right join
pd.merge(csv_data, tsv_data, on='a' , how='outer')#Full Outer join
```

##ソート
ソートしたいデータフレームを数値でソートする際sort_values関数を用いる。
by句にはソートしたい列、ascending句にはTrue(昇順)かFalse(降順)かを指定する
こちらもjoinと同様に**複数指定したい場合はリストで渡す**
``csv_data.sort_values(by=['a','b'], ascending=[True,False])``
と書くとa列を昇順、b列を降順でソートすることになる

```py
import pandas as pd
csv_data = pd.read_csv("csv_data.csv")
csv_data.sort_values(by=['a'], ascending=True)#昇順ソート
csv_data.sort_values(by=['a'], ascending=False)#降順ソート

##複数の列でソート
#a列を昇順、b列を降順でソート
csv_data.sort_values(by=['a','b'], ascending=[True,False])
```


↓csv_data　

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|1  |5  |3  |
|4  |5  |6  |

↓a列を昇順、b列を降順でソート

|a  |e  |f  |
|---|---|---|
|1  |5  |3  |
|1  |2  |3  |
|4  |5  |6  |

##抽出
抽出だってなんのその

### 列抽出
```py
import pandas as pd
cd = pd.read_csv("csv_data.csv")

cd['a'] #一列抽出
cd[['a','b']] #複数列抽出
```

↓cd

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|1  |5  |3  |
|4  |5  |6  |

↓a列を抽出

|a  |
|---|
|1  |
|1  |
|4  |


↓a列とb列を抽出

|a  |b  |
|---|---|
|1  |2  |
|1  |5  |
|4  |5  |

### where
a列が2以上のものを抽出とかそういうのもできる

```py
import pandas as pd
cd = pd.read_csv("csv_data.csv")

cd[cd['a'] >= 2] #aが2以上の行を抽出
```
↓cd

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|1  |5  |3  |
|4  |5  |6  |

↓抽出後

|a  |b  |c  |
|---|---|---|
|4  |5  |6  |

### where（複数条件）
or, and, notもできる

```py
import pandas as pd
cd = pd.read_csv("csv_data.csv")

#or
cd[ (cd['a'] >= 2 | cd['b'] == 2) ] #aが2以上もしくはbが2の行を抽出

#and
cd[ (cd['a'] == 1 & cd['b'] == 2) ] #aが1かつbが2の行を抽出

#not
cd[ ~(cd['a'] == 1) ] #aが1じゃない行を抽出
```
## Group by(SQLの1列でのgroupby準拠)
pandasのgroupbyはSQLのものとは毛色が異なる
a列の最大値を取るとき

```py
import pandas as pd
cd = pd.read_csv("csv_data.csv")
temp = cd.max() #各列で最大値をとる
temp = pd.DataFrame(temp) #シリーズ型をデータフレーム型に変換
temp = temp.T['a']#行列を転置してa列を抽出
temp = pd.DataFrame(temp) #シリーズ型をデータフレーム型に変換
```
↓cd

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|1  |5  |3  |
|4  |5  |6  |

↓上記処理でmergeで結合できる形まで持っていける

|a  |
|---|
|4  |

min,max,sumなど他にもいろいろ用意されている

## Group by(2列以上のgroupby準拠)
ある列のユニークな値毎に他の列の最大値を取りたいと言ったときが使い時
重複を削除するときはGroup byではなくdistinctに当たるものがあるのでそれを使う必要がある(pandasの場合groupbyを適用するとgroupbyクラスというものになってしまってmerge関数などが使えない形になってしまう)
ひとまず使用例を見て欲しい

```py
import pandas as pd
cd = pd.read_csv("csv_data.csv")
cd.groupby('a').mean() #平均
cd.groupby('a').max()  #最大値
cd.groupby('a').min()  #最小値
cd.groupby('a').count()#出現回数
cd.groupby('a').sum()  #合計値
cd.groupby('a').std()  #標準偏差
cd.groupby('a').sum()  #合計値
#まだまだある・・・
```
これらを実行するとgroupbyで集約したキーが列から消滅する
具体的に言うとこうなる
↓cd

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|1  |5  |3  |
|4  |5  |6  |

↓cd.groupby('a').max()

|   |b  |c  |
|---|---|---|
|1  |5  |3  |
|4  |5  |6  |

はい,aは消えました
**は？**
indexという形で集約した値は残っているが
cd.groupby('a').max()['a']とかではアクセスできない
集約したら集約前のデータに集約キーでJOINして情報付加したいだろ・・・
解決策だが
``as_index=False``を付けると集約した列も得られる

```py
cd.groupby('a' ,as_index=False).max()  #最大値
```

|a  |b  |c  |
|---|---|---|
|1  |5  |3  |
|4  |5  |6  |



##重複削除

```py
import pandas as pd

cd = pd.read_csv("csv_data.csv")
cd.drop_duplicates() #distinctに相当する
```
↓cd

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|1  |2  |3  |
|4  |5  |6  |

↓cd.drop_duplicates()

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|4  |5  |6  |

全ての列が一致した行が消える

## UNION
unionもできる

```py
import pandas as pd

cd = pd.read_csv("csv_data.csv")
td = pd.read_csv("tsv_data.tsv", delimiter='\t')

pd.concat([cd, td])
```

↓csv_data　　　　↓tsv_data

|a  |b  |c  |  |a  |b  |c  |
|---|---|---|---|---|---|---|
|1  |2  |3  |  |1  |2  |3  |
|4  |5  |6  |  |4  |5  |6  |

↓concat後

|a  |b  |c  |
|---|---|---|
|1  |2  |3  |
|4  |5  |6  |
|1  |2  |3  |
|4  |5  |6  |

列名が違う場合nullを表すNaNが入る
↓csv_data　　　　↓tsv_data

|a  |b  |c  |  |a  |d  |e  |
|---|---|---|---|---|---|---|
|1  |2  |3  |  |1  |2  |3  |
|4  |5  |6  |  |4  |5  |6  |

↓concat後

|a  |b  |c  |d  |e  |
|---|---|---|---|---|
|1  |2  |3  |NaN |NaN |
|4  |5  |6  |NaN |NaN |
|1  |NaN |NaN |2  |3  |
|4  |NaN |NaN |5  |6  |