Python pandas で動的に行を追加するTips（プログラマ向け）

# TL;DR

Python pandas の DataFrame を使っていると、いつもどうでもいい感じな初歩的なところではまります。
（わたしだけ...？）

このあたりの Tips がまとまっている素晴らしいサイトも多いのですが、なぜか、うまーい具合にわたしがやりたいことにピッタリはまらないことが多いので半分自分用にまとめておきました。

蛇足的な考察ですが、pandas DataFrame は、データ指向のデータ屋さんが使いやすいように作られているからかな、と思います。 わたしみたいなオブジェクト指向（≒機能指向）よりなプログラマだと、そもそもの発想がロジックよりになるためそれが原因で使いづらいことが多い気がします。

ということで、どちらかというと、わたしみたいなプログラマよりのTipsになります。

> **データ屋さんとプログラマの考え方の違い 例**
> 
> データ屋さんは、SQL の inner/outer join とかで頑張って表を結合します。
> プログラマは、表を2つ読み込んで for文とif文でぐるぐる回すロジックで解決したくなります...
> 静的に考えるか、動的に考えるかの差がけっこうあります。

# さっそくTips

## DataFrameを作ってみる

```python
import pandas as pd

a_df = pd.DataFrame()
print(a_df)
```

 ↓↓↓ 結果

```python
Empty DataFrame
Columns: []
Index: []
```

空のDataFrameが出来ました！
まずはこれが基本。

> Pythonでデータ分析していると、``DataFrame`` とか ``nparray`` とか ``Series`` とか色々と型を行き来することが多くなります。 なので、ハンガリアン記法的に ``{わかりやすい名前}_df`` とか書いておいて、この変数がDataFrameであることを明示しておくと何かと楽です。

## 初期値のはいっているDataFrameを作ってみる

```python
init_df = pd.DataFrame( [[10,20],[30,40]], columns=['A','B'] )
print(init_df)
```

 ↓↓↓ 結果

```python
    A   B
0  10  20
1  30  40
```

できました。 が、実際に何かしようと思ったときにデータをベタ書き（ハードコーディング）することはあまりないので... この書き方は実務ではあまり使わないかもしれないですね。 ``read_csv``というもので簡単に csv を読み込めるのですでに出来上がったデータを分析したいときはこちらを使いましょう。

## 型(カラム)だけ決まっているDataFarmeに値(行)を追加していく

これです！ とにかくわたしが頻繁にやりたいことがこれです。

例えば、外部サービスのAPIを呼び出して取得した json を元に DataFrame を作りたい、とかいう場合です。
json のデータに対してfor文でぐるぐるまわしながら1行ずつ追加したいですよね。

そういう場合はこんな風にします。

```python
list_df = pd.DataFrame( columns=['A','B'] )

for i in [0,1,2,3,4,5]:
    tmp_se = pd.Series( [ i, i*i ], index=list_df.columns )
    list_df = list_df.append( tmp_se, ignore_index=True )

print( list_df )
```

 ↓↓↓ 結果

```python
   A   B
0  0   0
1  1   1
2  2   4
3  3   9
4  4  16
5  5  25
```

ちょっと直観的じゃない部分もありますが、これでできます。
型（カラム）が決まっているタイプのデータでしたらこのパターンでだいたい実装可能です。

上では、行名(index)は、何も指定していませんが、もし何か指定しければ以下のようにしてください。

```python
list_df = pd.DataFrame( columns=['A','B'] )

for i in [0,1,2,3,4,5]:
    tmp_se = pd.Series( [ i, i*i ], index=list_df.columns, name='No.' + str(i) )
    list_df = list_df.append( tmp_se )

print( list_df )
```

 ↓↓↓ 結果


```python
      A   B
No.0  0   0
No.1  1   1
No.2  2   4
No.3  3   9
No.4  4  16
No.5  5  25
```

Seriesを作る時に ``name`` を指定しているのがポイントになります。

---

### **コードの補足（読み飛ばしOK）**

上のコードで動くと思うので大丈夫ですがすこしだけ補足しておきます。
まず、カラムが A B の空のDataFrameを作っています。

```python
list_df = pd.DataFrame( columns=['A','B'] )
```

次に、DataFrameに入れたいデータを使って、``Series`` という１次元配列みたいなものを一回作ります。

```python
    tmp_se = pd.Series( [ i, i*i ], index=list_df.columns )
```

ここが少しだけトリッキーで、本来は、行(横方向)を追加したいのに、列(縦方向)のデータを作ってるようにみえます。本当はこういうデータのイメージなんですが、

|   |A  |B  |
|:--|:--|:--|
|**-**  |1  |2  |


一回こういうデータを作る感じです。

|  | -  |
|:--|:--|
|**A**  |1  |
|**B**  |2  |

なのですが、実は``Series``は１次元の配列（ベクトル）なので縦とか横とか言う概念がないので、縦でも横でも同じことをさしているのだそうです。
追加するデータが出来たところでもとのDataFrameにappendします。

```python
list_df = list_df.append( tmp_se, ignore_index=True )
```

ここも要注意ポイントで、``.append`` は戻り値で新しいインスタンスを返します。 なので、代入しなおしてください。
このあたりがデータ屋さん的というか、Immutableな変数を使おうとする関数型的な思想が見えてドキドキします。

---

## カラム（型）が決まってない可変のデータの場合

たまにありますよね。こういうことも。そういう場合のやりざまです。
最初の json から json_data を作っている部分はサンプルで、本当はAPIとかからとってきてるイメージでいてください。

```python
import json

###### ここからダミーデータ作成
json_str = '''
{
    "values" : [
        { "col1" : 1, "col2" : 2, "col3" : 3 }, 
        { "col1" : 4, "col2" : 5, "col3" : 6 }, 
        { "col1" : 7, "col2" : 8, "col3" : 9 }
    ]
}
'''
json_data = json.loads(json_str)
###### ここまでダミーデータ作成

var_df = pd.DataFrame()
for value in json_data['values']:
    keys = []
    vals = []
    for k, v in value.items():
        keys.append( k )
        vals.append( v )
    tmp_se = pd.Series( vals, index=keys )
    var_df = var_df.append( tmp_se, ignore_index=True )

print( var_df )
```

 ↓↓↓ 結果

```python
   col1  col2  col3
0   1.0   2.0   3.0
1   4.0   5.0   6.0
2   7.0   8.0   9.0
```

コードは単純ですね。
結局のところ Series に一回いれてから、DataFrameにappendする、をやるだけです。


# さいごに

もしこのコードサンプルで少しでもみなさまのお役に立つことがあれば。
いいね でも押していってくださいませ m__m

> 他にも欲しいパターンがあったらコメントなりTwitterからのDMなりください！