Python
pandas

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

More than 3 years have passed since last update.

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

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分くらいで読めるようにまとめられています。

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

気象庁|過去の気象データ・ダウンロード

気象庁 過去の気象データ・ダウンロード.png

環境設定

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

$ 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 に変換します。
  • 複数行あるヘッダーを1行にしておきます。
  • ヘッダー行を英語名に書き換えます。
    • 日付の列を "day" にします。
    • 天気概況(昼)の列を "weather_day" にします。

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

>>> 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 が使えます。

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

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

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

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

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

>>> 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 名を使って以下のように計算できます。(実際のデータには「晴一時雨」のような天気もありますので、重複して集計されている日もあります。)

>>> 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 で指定します。

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

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

ipython を使ってグラフを描く

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

$ pip install matplotlib 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>

  • 2行目のオプションは pandasmatplotlib に渡します。
  • DataFrame の "T" は transpose() のことで、行と列を転置したものです。
  • 前の処理で行と列には合計値が付与されていますので、12月までのデータをスライスしています。
  • 系列ごとに色を指定するために plot()color 引数を渡しています。列指定の順番に一致しますので、Fine が "r" = Red, Rainy が "b" = Blue になります。

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

月別の天候変化_東京.png

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