pandas で行と列に合計値を追加する


*pandas* を使うときの備忘録として書いておきます。ここの回答に書いてあることです。

* [python - Appending column totals to a Pandas DataFrame - Stack Overflow](http://stackoverflow.com/questions/20804673/appending-column-totals-to-a-pandas-dataframe)

```python
df = pd.concat([df,pd.DataFrame(df.sum(axis=0),columns=['Grand Total']).T])
df = pd.concat([df,pd.DataFrame(df.sum(axis=1),columns=['Total'])],axis=1)
```

*pandas* の基本は公式マニュアルに10分くらいで読めるようにまとめられています。

* [10 Minutes to pandas — pandas 0.14.1 documentation](http://pandas.pydata.org/pandas-docs/stable/10min.html)

テストデータとして、気象庁からダウンロードできる過去の気象情報を使います。東京の天気概況１年分（2013年1月〜12月）を月ごとに集計し、行と列の合計を計算します。

[気象庁|過去の気象データ・ダウンロード](http://www.data.jma.go.jp/gmd/risk/obsdl/index.php#)

![気象庁 過去の気象データ・ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/37996/25d84ca9-dee4-8f61-1692-b22d545d57cb.png "気象庁 過去の気象データ・ダウンロード.png")

## 環境設定

`pyvenv` で独立した Python のライブラリを使えるようにしておきます。

```bash
$ PYVENV_DIR=$HOME/.pyvenv/data-analytics
$ pyvenv-3.4 $PYVENV_DIR
$ source $PYVENV_DIR/bin/activate
$ pip install pandas
```

気象庁からダウンロードしたファイルを `weather-tokyo-2013.csv` とします。ダウンロードしたままでは *pandas* で扱いにくいので、前処理を実施しておきます。

* 文字コードを UTF-8 に変換します。
* 複数行あるヘッダーを１行にしておきます。
* ヘッダー行を英語名に書き換えます。
    * 日付の列を "day" にします。
    * 天気概況（昼）の列を "weather_day" にします。

## pandas でデータ読み込みの確認

```python
>>> import pandas as pd

# Read CSV file, whose "day" column is parsed with `dateutil.parser.parse`.
>>> fname = 'weather-tokyo-2013.csv'
>>> df = pd.read_csv(fname, parse_dates=['day'])

# Check DataFrame overview.
>>> df.dtypes
>>> df.head(3)
>>> df.describe()
```

CSV ファイルを *DataFrame* に読み込めていることを確認できました。

次に、"day" 列から「月」の部分を抽出して "month" 列に追加します。
"day" 列は CSV を読み込むときに日付時刻として認識されていますので、datetime の API が使えます。

```python
>>> df['month'] = df['day'].apply(lambda x: x.month)
```


## groupby で集計して行と列の合計値を追加

*DataFrame* には `groupby` メソッドがあり、SQL の "GROUP BY" のような動作を提供してくれます。今回は「天気概況(昼)」と「月」で日数を集計してみます。例えば、快晴の月ごとの日数が分かります。

```python
>>> grouped = df.groupby(['weather_day', 'month'])['day'].count()
>>> type(grouped)
>>> grouped.index
>>> grouped.head(3)
```

`groupby` の戻り値は *Series* なので、 `unstack` メソッドで *DataFrame* に変換します。こうすると、冒頭の処理で行と列の合計、この場合は天気概況の月ごとの合計を計算できます。

```python
>>> monthly = grouped.unstack()

# Add "Total" column on each row.
>>> monthly = pd.concat([monthly,
                         pd.DataFrame(monthly.sum(axis=1), columns=['Total'])],
                        axis=1)
# Add "Grand Total" row on each column.
>>> monthly = pd.concat([monthly,
                         pd.DataFrame(monthly.sum(axis=0), columns=['Grand Total']).T])
```

単純な合計以外に、行や列の名称でフィルタして合算することもできます。天気概況に「雨」が含まれる日を雨っぽい日、「晴」が含まれる日を晴れっぽい日と考えると、index 名を使って以下のように計算できます。（実際のデータには「晴一時雨」のような天気もありますので、重複して集計されている日もあります。）

```python
>>> monthly = pd.concat([monthly,
                         pd.DataFrame(monthly[monthly.index.map(lambda x: '雨' in x)].sum(axis=0), columns=['Rainy']).T])
>>> monthly = pd.concat([monthly,
                         pd.DataFrame(monthly[monthly.index.map(lambda x: '晴' in x)].sum(axis=0), columns=['Fine']).T])

```

最後に、集計結果をテキストファイルに書き出します。ヘッダーや index 名などはオプション引数で制御できます。たとえば、区切り文字は *sep* で指定します。

```python
>>> fname = 'weather-tokyo-2013-monthly-weather_name.csv'
>>> monthly.to_csv(fname, sep=',')
```

あとは結果をエクセルなどの表計算ソフトで確認します。

## ipython を使ってグラフを描く

エクセルでデータファイルを開いてグラフを描画することもできますが、Python でも *matplotlib* を使って描画できます。ちょっと使ってみる場合は *ipython* を使いましょう。グラフ描画処理の雑務を裏で実行してくれます。

* [Plotting — pandas 0.14.1 documentation](http://pandas.pydata.org/pandas-docs/stable/visualization.html)
* [IPython Interactive Computing](http://ipython.org/)

```bash
$ pip install matplotlib ipython
$ ipython
```

これで IPython のインタラクティブシェルに入ります。
先ほど書き出したデータを読み込み、晴れっぽい日と雨っぽい日の月ごとの推移をグラフに描画します。

```ipython

In [1]: import pandas as pd

In [2]: pd.options.display.mpl_style = 'default'

In [3]: fname = 'weather-tokyo-2013-monthly-weather_name.csv'

In [4]: df = pd.read_csv(fname, index_col=0)

In [5]: df.T[['Fine', 'Rainy']][:12].plot(color=['r', 'b']).set_xlabel('Month')
Out[5]: <matplotlib.text.Text at 0x106cb89e8>

```

* ２行目のオプションは *pandas* が *matplotlib* に渡します。
* *DataFrame* の "T" は `transpose()` のことで、行と列を転置したものです。
	* [pandas.DataFrame.transpose — pandas 0.14.1 documentation](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.transpose.html)
* 前の処理で行と列には合計値が付与されていますので、12月までのデータをスライスしています。
* 系列ごとに色を指定するために `plot()` に `color` 引数を渡しています。列指定の順番に一致しますので、Fine が "r" = Red, Rainy が "b" = Blue になります。


別ウィンドウでこんな感じのグラフが描画されているはずです。

![月別の天候変化_東京.png](https://qiita-image-store.s3.amazonaws.com/0/37996/5e5f79ca-c4bc-eb9b-1b19-a7f638d097dc.png "月別の天候変化_東京.png")

6月は梅雨なので雨が多く（と言っても半分以下の日数）、1月は雨の日がほとんどないことが分かります。10月は天気が変わりやすいですが、それでも雨がぱらつく日よりは晴れている日の方が多いようです。
なお、実装が簡単で変動が分かりやすい荒い集計条件にしていますので、細かい分析をする場合は天気概況の値をカテゴリ分けしてから集計しましょう。