Pandasの基本的な使い方

# 概要
https://openbook4.me/projects/183
にまとめていた内容のうち基礎的な部分を移動

# pandas
pythonでデータ解析とかするのに便利なlibrary.
numpyも使うので, ここでは両方共importした状態にしておく.

```python
import numpy as np
import pandas as pd
```

# Series
軸にラベルを付けた1次元の配列.

```python
>> pd.Series([1,2,3])
0    1
1    2
2    3
dtype: int64

>> s = pd.Series([1,2,3], index=['a','b','c'])
>> s
a    1
b    2
c    3
dtype: int64

>> s.index
Index([u'a', u'b', u'c'], dtype='object')

>> s.max()   # 最大値
3

>> s.min()   # 最小値
1

>> s.mean()   # 平均値
2.0

>> s.median()   # 中央値
2.0

>> s.var()   # 分散
1.0

>> s.sum()   # 合計値
6

>> s.mod(2)   # modの計算, この場合2で割った余り
a    1
b    0
c    1

>> s.cumsum()   # 累積にする. 累積のグラフを作るとき便利
a    1
b    3
c    6
dtype: int64

>> s.apply(lambda x: x*3)   # 特定の関数を各値に対して適応
a    3
b    6
c    9
dtype: int64

>> s.map({1: 10, 2: 200})   # 引数で与えた値に対応する値を変換
a     10
b    200
c    NaN
dtype: float64

>> s.argmax()   # 最大値のindexを取得
'c'

>> s.argmin()   # 最小値のindexを取得
'a'

>> s.tolist()   # listに変更
[1, 2, 3]

>> s.to_dict()   # dictonaryに変更
{'a': 1, 'b': 2, 'c': 3}

>> s.to_json   # jsonに変更
'{"a":1,"b":2,"c":3}'

```

# DataFrame
## DataFrameの作成

```python
df = pd.DataFrame([[1,4,7],[2,5,8],[3,6,9]],
                  index=['i1','i2','i3'],
                  columns=list("abc"))
>> df
    a  b  c
i1  1  4  7
i2  2  5  8
i3  3  6  9
```

## 値の操作
### 範囲を指定して取り出す

行に関連する操作は、.ixを使う。

```python
>> df
    a  b  c
i1  1  4  7
i2  2  5  8
i3  3  6  9

# 指定した行の取り出し。index名かindex番号で与えられる。
>> df.ix['i1']
a    1
b    4
c    7
Name: i1, dtype: int64

>> df.ix[1]
a    2
b    5
c    8
Name: i2, dtype: int64

# 2つ目のparameterに列を渡すことで、行を取得できる。
>> df.ix['i1','a']
 1

# : は全指定する。(R, scilabと対応)
>> df.ix[:, 'a']
i1    1
i2    2
i3    3
Name: a, dtype: int64

# 複数の指定も配列で可能。
>> df.ix[[1,2], ['b','c']]
    b  c
i2  5  8
i3  6  9

>> df.ix[[1,2], [1,2]]
    b  c
i2  5  8
i3  6  9
```

列に関する操作は[column名]で渡す。

```python
>> df['a']
i1    1
i2    2
i3    3
Name: a, dtype: int64

# arrayとして取得する。
>> df['a'].values
array([1, 2, 3])

# さらにindex名を指定することで値として取得できる。
>> df['a']['i3']
 3
```

DataFrameをtableとみなして、位置指定から値を明示的に取る方法。

```python
>> df.iloc[0,0]
1
(df.ix[0,0]と同じ。)

# 列の取得
>> df.iloc[2]
a    3
b    6
c    9
Name: i3, dtype: int64

```


## 条件を満たすに要素に値を代入
例えば行列において、1より大きい値を全部1にしたい場合

```python
>> a = pd.DataFrame(np.random.randint(3, size=(5,3)))
   0  1  2
0  0  2  1
1  2  0  1
2  0  0  2
3  2  1  0
>> b[b>1]   # 条件を満たすものだけを抽出したDataFrame
    0   1   2
0 NaN   2 NaN
1   2 NaN NaN
2 NaN NaN   2
3   2 NaN NaN
>> b[b>1] = -1  # 条件をみたすものに1を代入
>> b
   0  1  2
0  0  1  1
1  1  0  1
2  0  0  1
3  1  1  0
```

##NaNの処理
### NaNの削除
dropnaで削除できる

```python
>> df
    a   b  c
0   1   3  5
1   2 NaN  6
2 NaN   4  7

>> df.dropna()   # NaNを含む行を削除
   a  b  c
0  1  3  5

>> df.dropna(axis=1)   # NaNを含む列を削除
   c
0  5
1  6
2  7

>> df.dropna(subset=['b'])   # 特定カラムを指定することも可能
    a  b  c
0   1  3  5
2 NaN  4  7
```

### NaNを埋める
fillnaを使う。fillnaで指定した値で埋めることができる以外に、
直前の値で補完(pad/ffill)、直後の値で補完(bfill)などがある。

```python
>> df
    a   b  c
0   1   3  5
1   2 NaN  6
2 NaN   4  7

>> df.fillna(-1)
   a  b  c
0  1  3  5
1  2 -1  6
2 -1  4  7


>> df.fillna(method='pad')
   a  b  c
0  1  3  5
1  2  3  6
2  2  4  7

>> df.fillna(method='bfill')
    a  b  c
0   1  3  5
1   2  4  6
2 NaN  4  7
```

misssing valueの前後の線形の値で埋めたい場合は pd.Series.interpolate をapplyしてやる。


```python
>> df
    a   b
0   1   2
1   2 NaN
2 NaN   3

>> df.apply(pd.Series.interpolate)
   a    b
0  1  2.0
1  2  2.5
2  2  3.0
```

### 重複した値の処理
duplicatedで重複を調べれる。重複を調査するcolumnを指定することも可能。

```python
>> df
   0  1
0  A  1
1  A  1
2  A  2
3  B  3
4  B  4

>> df.duplicated()
0    False
1     True
2    False
3    False
4    False
dtype: bool

>> df.duplicated(0)
0    False
1     True
2     True
3    False
4     True
dtype: bool

>> df.duplicated(1)
0    False
1     True
2    False
3    False
4    False
dtype: bool
```

drop_duplicatesで重複した値を削除できる。duplicatedと同様にcolumn指定も可能。
take_last=Trueオプションで重複した場合に最後の値を取ることができる。

```python
>> df.drop_duplicates()
   0  1
0  A  1
2  A  2
3  B  3
4  B  4

>> df.drop_duplicates(0)
   0  1
0  A  1
3  B  3

>> df.drop_duplicates(0, take_last=True)
   0  1
2  A  2
4  B  4
```

# Pandas: DataFrameの変形

ここではDataFrameの形を変える方法を順に見ていく。

次のようなdfについて処理する。

```python
df = pd.DataFrame(np.reshape(np.arange(9), (3,3)),
                  columns=['a', 'b', 'c'])
>> df
   a  b  c
0  0  1  2
1  3  4  5
2  6  7  8
```


## DataFrameの一部を取る

n行を取る。

```python
# 最初のn行を取る。
>>　df.head(2)
   a  b  c
0  0  1  2
1  3  4  5

# 終わりn行取る。
>> df.tail(2)
   a  b  c
1  3  4  5
2  6  7  8
```

index, columnを取る。

```python
>> df.index
Int64Index([0, 1, 2], dtype='int64')
>> df.columns
Index([u'a', u'b', u'c'], dtype='object')
```

## DataFrameの形式を変える。

### indexとcolumnを逆にする。

```python
>> df.T
   0  1  2
a  0  3  6
b  1  4  7
c  2  5  8
```

この操作は非破壊的なので、df自体が反転するわけではない。

(使用するときは、rdf = df.Tのようにすればよい。)

### index, columnの順番を反転させる。

```python
# columnについての順番を逆転させる。
>> df.sort_index(axis=1, ascending=False)
   c  b  a
0  2  1  0
1  5  4  3
2  8  7  6
```

### index, columnに対して、値の順番を並び替える。

```python
# 大きい順に並び替える。(今回だと全部逆転したように見えるけれど...)
>> df.sort(columns='b', ascending=False)
   a  b  c
2  6  7  8
1  3  4  5
0  0  1  2

# 小さい順に並び替える。(今回だと変わらないけれど...)
>> df.sort(columns='b')
   a  b  c
0  0  1  2
1  3  4  5
2  6  7  8
```

### 各値を取る
``` python
df[],df.loc[],df.iloc[],...
```

などなど前章でやった！

## マスクする(ほしい条件のもとで、dfから選びとる。)


```python
# df["条件文をかく"]という形。
>> df.a
0    0
1    3
2    6
# に対して、2以上の1,2とついたindexのみ取ってくる。

>> df[df.a>2]
   a  b  c
1  3  4  5
2  6  7  8

```

**全体に対するoperationだと条件に合うもの、合わないもの(NaN)を表示する。**

```python
# 3より大きい所をとってくる
>> df[df > 3]
    a   b   c
0 NaN NaN NaN
1 NaN   4   5
2   6   7   8
```

```python
# 3より大きいところだけ、2倍する。
>> df[df > 3] = df*2
>> df
    a   b   c
0   0   1   2
1   3   8  10
2  12  14  16
```


## columnの追加

**よく使うので重要です！**

```python
>> df['e'] = ['one','two','three']
>> df
   a  b  c      e
0  0  1  2    one
1  3  4  5    two
2  6  7  8  three
```

## NaNのdataを含むものを消去する。

```python
# NaNのものを消去する。
>> df[df > 3].dropna(how='any')
   a  b  c
2  6  7  8
```

```python
# NaNを別のもので埋める。

>> df[df > 3].fillna('Ice')
     a    b    c
0  Ice  Ice  Ice
1  Ice    4    5
2    6    7    8
```

## DataFrameをくっつける
df2を作成する。

```python
>> df2 = pd.DataFrame(np.reshape(np.arange(6), (3,2)),
                     columns=['e', 'f'])
>> df2
   e  f
0  0  1
1  2  3
2  4  5
```

#### concat

```python
列でくっつける。
>> newdf = pd.concat([df, df2],axis=1)
>> newdf
   a  b  c  e  f
0  0  1  2  0  1
1  3  4  5  2  3
2  6  7  8  4  5
```

```python
行についてくっつける。
>> newdf = pd.concat([df, df2],axis=0)
>> newdf
    a   b   c   e   f
0   0   1   2 NaN NaN
1   3   4   5 NaN NaN
2   6   7   8 NaN NaN
0 NaN NaN NaN   0   1
1 NaN NaN NaN   2   3
2 NaN NaN NaN   4   5
```

#### append

行に対して追加する。(dataを増やす)

```python
>> result = df.append(df2)
>> result
    a   b   c   e   f
0   0   1   2 NaN NaN
1   3   4   5 NaN NaN
2   6   7   8 NaN NaN
0 NaN NaN NaN   0   1
1 NaN NaN NaN   2   3
2 NaN NaN NaN   4   5
```

## DataFrameのindex, columnを大きく変える。
### pivot table
pivot_tableでは、既存のカラムから新しくindex、columnsを定義したDataFrameを作成できる。

```python
>> df = pd.DataFrame({'col1' : ['one', 'two', 'three', 'four'] * 3,
                    'col2' : ['A', 'B', 'C'] * 4,
                    'col3' : ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2,
                    'col4' : np.arange(1, 13),
                    'col5' : np.arange(13, 25)})
>> df
     col1 col2 col3  col4  col5
0     one    A  foo     1    13
1     two    B  foo     2    14
2   three    C  foo     3    15
3    four    A  bar     4    16
4     one    B  bar     5    17
5     two    C  bar     6    18
6   three    A  foo     7    19
7    four    B  foo     8    20
8     one    C  foo     9    21
9     two    A  bar    10    22
10  three    B  bar    11    23
11   four    C  bar    12    24
```

col1をindex、col2をcolumnsでcol4を値としたdfは以下のように作る。

```python
>> df.pivot_table(values='col4', index='col1', columns='col2')

col2    A   B   C
col1
four    4   8  12
one     1   5   9
three   7  11   3
two    10   2   6
```

df.pivot_tableでなく、pd.pivot_tableでも同様の操作ができる。

```python
>> pd.pivot_table(df, values='col4', index='col1', columns='col2')

col2    A   B   C
col1
four    4   8  12
one     1   5   9
three   7  11   3
two    10   2   6
```

pivot_tableでは、indexやcolumnを階層化することができる。
ここでは、col1とcol2をmulti-index、col3をcolumn, col4を表の値としたtableに作り変える。

```python
>> df.pivot_table(values='col4', index=['col1', 'col2'], columns=['col3'])

col3         bar  foo
col1  col2
four  A      4.0  NaN
      B      NaN  8.0
      C     12.0  NaN
one   A      NaN  1.0
      B      5.0  NaN
      C      NaN  9.0
three A      NaN  7.0
      B     11.0  NaN
      C      NaN  3.0
two   A     10.0  NaN
      B      NaN  2.0
      C      6.0  NaN
```

pivot_tableにて、一つのセルに値が複数入る場合がある。
この場合、デフォルトではmean、つまり平均値が集計される。
もし集計方法を変えたい場合はaggfuncオプションに関数を与えれば良い。

```python
>> df.pivot_table(values='col4', index='col2', columns='col3')

col3  bar  foo
col2
A       7    4
B       8    5
C       9    6

>> df.pivot_table(values='col4', index='col2', columns='col3', aggfunc=np.sum)

col3  bar  foo
col2
A      14    8
B      16   10
C      18   12

>> df.pivot_table(values='col4', index='col2', columns='col3', aggfunc=np.max)

col3  bar  foo
col2
A      10    7
B      11    8
C      12    9
```

### pivot
pivot_tableでは値の候補が複数ある場合の処理を気にしなければならないが、
値がかぶらないことが保証されているときはpivotで処理するのが簡単である。
pivotではpivot_tableと同様に新しくindexとcolumnとvalueになるcolumnを指定する。

```python
>> df.pivot(values='col4', index='col1', columns='col2')

col2    A   B   C
col1
four    4   8  12
one     1   5   9
three   7  11   3
two    10   2   6
```

pivotの場合、値の候補が複数ある場合は、エラーがでる。

```python
>> df.pivot(values='col4', index='col2', columns='col3')

ValueError: Index contains duplicate entries, cannot reshape
```

### stack

Multi-indexのDataFrameを作成する。

```python
>> tuples = list(zip(*[['bar', 'bar', 'baz', 'baz',
                      'foo', 'foo', 'qux', 'qux'],
                     ['one', 'two', 'one', 'two',
                      'one', 'two', 'one', 'two']]))
>> tuples
[('bar', 'one'),
 ('bar', 'two'),
 ('baz', 'one'),
 ('baz', 'two'),
 ('foo', 'one'),
 ('foo', 'two'),
 ('qux', 'one'),
 ('qux', 'two')]
>> index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
>> index
MultiIndex(levels=[[u'bar', u'baz', u'foo', u'qux'], [u'one', u'two']],
           labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
           names=[u'first', u'second'])

>> df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])
>> df
                     A         B
first second
bar   one     0.201808  0.617744
      two    -0.272022  1.012488
baz   one     0.944162 -0.243223
      two     0.259391  0.064546
foo   one     0.222640  0.283878
      two    -0.975535  0.461281
qux   one    -0.437626  0.366836
      two    -0.029409  0.141013
```

これをcolumnについて並列化する。

```python
>> stacked = df.stack()
>> stacked
first  second
bar    one     A    0.201808
               B    0.617744
       two     A   -0.272022
               B    1.012488
baz    one     A    0.944162
               B   -0.243223
       two     A    0.259391
               B    0.064546
foo    one     A    0.222640
               B    0.283878
       two     A   -0.975535
               B    0.461281
qux    one     A   -0.437626
               B    0.366836
       two     A   -0.029409
               B    0.141013

>> stacked[0:3]
first  second
bar    one     A    0.201808
               B    0.617744
       two     A   -0.272022
A, Bというlabelを合わせてぐっちゃぐちゃで縦に処理できる。

```

### unstack

stackの逆。戻すレベルを指定する。

```python
>> stacked_unstacked = stacked.unstack(0)
>> stacked_unstacked
first          bar       baz       foo       qux
second
one    A  0.201808  0.944162  0.222640 -0.437626
       B  0.617744 -0.243223  0.283878  0.366836
two    A -0.272022  0.259391 -0.975535 -0.029409
       B  1.012488  0.064546  0.461281  0.141013


>> stacked_unstacked = stacked.unstack(1)
>> stacked_unstacked
second        one       two
first
bar   A  0.201808 -0.272022
      B  0.617744  1.012488
baz   A  0.944162  0.259391
      B -0.243223  0.064546
foo   A  0.222640 -0.975535
      B  0.283878  0.461281
qux   A -0.437626 -0.029409
      B  0.366836  0.141013
```

### get_dummies
ダミー変数を作成する。カテゴリカルデータで機械学習などをしたいときなどに便利。

```python
>> df = pd.DataFrame({'A' : ['one', 'one', 'two', 'three'] * 3,
                    'B' : ['A', 'B', 'C'] * 4,
                    'C' : ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2})
>> df
        A  B    C
0     one  A  foo
1     one  B  foo
2     two  C  foo
3   three  A  bar
4     one  B  bar
5     one  C  bar
6     two  A  foo
7   three  B  foo
8     one  C  foo
9     one  A  bar
10    two  B  bar
11  three  C  bar

>> df.C.str.get_dummies()

    bar  foo
0     0    1
1     0    1
2     0    1
3     1    0
4     1    0
5     1    0
6     0    1
7     0    1
8     0    1
9     1    0
10    1    0
11    1    0
```

もしくは、

```python
>> pd.get_dummies(df.C)

    bar  foo
0     0    1
1     0    1
2     0    1
3     1    0
4     1    0
5     1    0
6     0    1
7     0    1
8     0    1
9     1    0
10    1    0
11    1    0
```

df.Cにはbar, fooという文字列が入っており、それぞれbar, fooというカラムを
もつ、DataFrameを作成している。
値は、0, 1が入っているが、各インデックスに対して、barならbarカラムを1にしfooカラムを0にし、
fooならばfooカラムを1としbarカラムを0としている。
つまり、各indexがbarに属しているかfooに属しているかを0, 1に変更している。

そのままの値をカラム名としたくない場合はprefixをつけることも可能。

```python
>> pd.get_dummies(df.C, prefix='category')

    category_bar  category_foo
0              0             1
1              0             1
2              0             1
3              1             0
4              1             0
5              1             0
6              0             1
7              0             1
8              0             1
9              1             0
10             1             0
11             1             0
```

# Pandas: DataFrameに数式をあてはめる

前回までと同じく

```python
>> df = pd.DataFrame(np.reshape(np.arange(9), (3,3)),
                     columns=['a', 'b', 'c'])
>> df
   a  b  c
0  0  1  2
1  3  4  5
2  6  7  8
```

を使って数式による計算を適用してみる。

## apply
とてもよくお世話になるメソッド。

#### 累積(np.cumsum)
columnについて累積をとる。

```python
>> df.apply(np.cumsum)
   a   b   c
0  0   1   2
1  3   5   7
2  9  12  15
```

#### 根号(np.sqrt)

```python
>> df.apply(np.sqrt)
          a         b         c
0  0.000000  1.000000  1.414214
1  1.732051  2.000000  2.236068
2  2.449490  2.645751  2.828427
```

### lambda関数

```python
# 各列ごとの最大と最小の差
>> df.apply(lambda x: x.max() - x.min())
a    6
b    6
c    6
```

## 基礎統計

#### 平均を取る

```python
列について
>> df.mean(0)
a    3
b    4
c    5
行について
>> df.mean(1)
0    1
1    4
2    7
```
# ファイル操作
ファイルからデータを読み込んだり、ファイルにデータを吐き出すことがよくあるので、
ファイル入出力の説明

# ファイル出力
## Series
to_csv関数を使う.

```python
>> s
a    1
b    2
c    3
dtype: int64
>> s.to_csv('output.csv')
```

とすると, index付きのファイルが出力される.

```
a,1
b,2
c,3
```

indexを出力したくない場合は, index=False を指定する.

```python
>> s.to_csv('output.csv', index=False)
```

tsvなど他の形式にしたい場合はsep(separatorの略)で区切り文字を変更する.

```python
>> s.to_csv('output.tsv', sep='\t')
```

## DataFrame
Seriesと同様にto_csvを使う.

```python
>> df
   a  b  c
0  1  3  5
1  2  4  6

>> df.to_csv('output.csv')
```

headerも含めて出力される.

```
,a,b,c
0,1,3,5
1,2,4,6
```

headerを省略したい場合は, header=False を指定する.

```python
>> df.to_csv('output.csv', header=False)
```

```
0,1,3,5
1,2,4,6
```

Seriesの場合と同様に, index=False も指定できる.

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

```
a,b,c
1,3,5
2,4,6
```

区切り文字も同様に sep にて変更。

```python
>> df.to_csv('output.dat', sep='|')
```

```
|a|b|c
0|1|3|5
1|2|4|6
```

# ファイル入力

デフォルトではseparatorがcommaになるので、tsvファイルの場合などはsep="\t"と設定する必要がある。
headerがないファイルの場合はheader=Noneと指定しておかないと1行目が勝手にheaderとして認識されてしまうので注意。

```python
pd.read_csv("data.tsv", sep="\t", header=None)
```

明示的にheaderを指定してもよい

```python
pd.read_csv("data.tsv", sep="\t", names=["column1", "column2"])
```

indexが入っているファイル場合, indexが入っている列の番号を指定する.

```python
pd.read_csv("data.csv", index_col=0)
```


# 入出力例
## indexつきheaderつきのファイルの入出力

```python
>> df = pd.DataFrame({'a': [1,2], 'b': [3,4], 'c': [5,6]})
>> df
   a  b  c
0  1  3  5
1  2  4  6

>> df.to_csv('sample.csv')
```

これでsample.csvができる.

```:sample.csv
,a,b,c
0,1,3,5
1,2,4,6
```

sample.csvを読み込んでもとのDataFrameに戻すには, index_colを指定して読み込む.

```python
>> pd.read_csv('sample.csv', index_col=0)
   a  b  c
0  1  3  5
1  2  4  6
```

このときindex_colを指定しないと別途indexが指定されてしまう。

```python
>> pd.read_csv('sample.csv')
   Unnamed: 0  a  b  c
0           0  1  3  5
1           1  2  4  6
```

# データベース連携
実務上では, データベースからデータを取得して処理したいことがある。
ここではpandasでどのようにデータを取得して、DataFrameに格納するかを説明する。

# postgresqlからデータを取得する
pandasにはsqlに接続するための pandas.io.sql がある。
これにpostgresql接続用のライブラリpsycopg2を利用して接続する。

```python
import pandas as pd
import psycopg2

connection = psycopg2.connect(host='host', dbname='database', user='username', password='password')
result = pd.read_sql("SELECT * FROM users", connection)
```

### MySQLを使用する場合
MySQLを使用する場合は MySQLdb を利用する。connectionの部分をMySQLdb用に変更する.

```python
import pandas as pd
import MySQLdb

connection = MySQLdb.connect(host='host', dbname='database', user='username', password='password')
result = pd.read_sql("SELECT * FROM users", connection)
```

# read_sql
データベースに接続しその結果をtsvで吐き出す

```python
import pyodbc
import pandas.io.sql as psql
connection = pyodbc.connect(uid=user, dsn='instance', database='database', pwd='password')
column_data = psql.read_sql("SELECT * FROM hoge", connection)
print "\t".join(map(str,list(column_data.ix[0])))
```

ここでsqlの部分を

```sql
SELECT COUNT(fuga), MAX(fuga), MIN(fuga) FROM hoge;
```

のようにすると

```
AssertionError: Wrong number of items passed (3 vs 1)
```

というerrorが出ることがある.

これは

```sql
SELECT COUNT(fuga), MAX(fuga) AS max_value, MIN(fuga) AS min_value FROM hoge;
```

のように抽出後のcolumn名を指定すれば回避できる.