Pandas 集中講座 その5 グループ、再構成
Pandasライブラリを覚えたい!公式「10 Minutes to pandas」をモチーフに使って実際に動作させ学習する。第5回 グループ、再構成
Pandas 集中講座(5)
Pandas 集中講座 その1 - chiyoh’s blog
Pandas 集中講座 その2 - chiyoh’s blog
Pandas 集中講座 その3 - chiyoh’s blog
Pandas 集中講座 その4 - chiyoh’s blog
Pandas 集中講座 その5 - chiyoh’s blog
Pandas 集中講座 その6 - chiyoh’s blog
Pandasとは、なにか?
初め考えていたのは、EXCELシートも扱えるPython版スプレッドシートライブラリ。 VBAの代わりにマクロをPython上で実行できる便利なものと考えていた。
Pandasを実際に使ってみると
始めて使ったのは、テキストデータをEXCELのxlsxに変換しておきたかったから。実際に変換してみると思ったより難しい(実際には、我々がEXCELを表計算としてとらえていなくて計算機能と印刷機能を持つワープロとして使っているのが原因)。また、Pandasも高機能なので出来ることが多く大いに迷ったが、それでもEXCELファイルの書き出しに成功したのでこれは便利だ!ということでこれを機に少し使ってみようと考えた。
公式「10 Minutes to pandas」を実際にやってみる (5/6)
Pandasを覚える!さて、グーグル先生に教えてもらえば大抵のことは分かるのだが、それが今の最適解なのか?と考えるとそうではない。グーグル先生のサーチ機能は、カンニングペーパーであり、単語帳である。欲しい答えを最小ステップでたどり着くための方法。例えるなら、望遠鏡、又は顕微鏡で拡大されたエリアで見つけた希望(答え)。なぜそうなるのか?なぜそう書けば動くのか?など、答えに対する成り立ちがすっぽり抜けてしまうのである。最近のPython環境は、Jupyter Notebookで敷地も下がって来ている。入門書や学習本を眺めて図を見てなんとなくへーとか言って分かった気になって何も残らないパターンになりかねない。今回選んだ方法は、グーグル先生の支援のもと公式のチュートリアル以前のPandas概要説明『10 Minutes to pandas』を上から順にJupyter Notebookを使い実行していってみようと思う。これにより、学習講座のWorkshop形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。
【解説】公式「10 Minutes to pandas」をJupyter Notebookを使って動作させながら確認し、突っ込みを入れる!(この文章を見る価値としては、原書が何をやっているのかが分かる、、、分かるはずである)
公式「10 Minutes to pandas」を実際にやってみる
7.グループ化
「グループ化」とは、以下のステップのうちの1つ以上を含むプロセスを指します。
- いくつかの基準に基づいてデータをグループに分割する
- 各グループに独立して機能を適用する
- 組み合わせデータ構造に結果を
グループ化の節を参照してください。
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar','foo', 'bar', 'foo', 'foo'], 'B': ['one', 'one', 'two', 'three','two', 'two', 'one', 'three'], 'C': np.random.randn(8), 'D': np.random.randn(8)}) df
A | B | C | D | |
---|---|---|---|---|
0 | foo | one | 0.447141 | 0.013312 |
1 | bar | one | 0.546690 | 0.039544 |
2 | foo | two | -0.520826 | 1.888179 |
3 | bar | three | 0.788315 | -0.085696 |
4 | foo | two | 0.542692 | -1.728545 |
5 | bar | two | -1.449532 | -0.242919 |
6 | foo | one | 1.238537 | -0.221444 |
7 | foo | three | 0.633757 | 0.419697 |
グループ化してsum()から、結果のグループに関数を適用します。
df.groupby('A').sum()
C | D | |
---|---|---|
A | ||
bar | -0.114527 | -0.289071 |
foo | 2.341301 | 0.371199 |
【コメント】B列が消えてしまうんですね。
複数の列でグループ化することで階層的なインデックスが形成され、やはりsum関数を適用できます。
df.groupby(['A', 'B']).sum()
C | D | ||
---|---|---|---|
A | B | ||
bar | one | 0.546690 | 0.039544 |
three | 0.788315 | -0.085696 | |
two | -1.449532 | -0.242919 | |
foo | one | 1.685677 | -0.208132 |
three | 0.633757 | 0.419697 | |
two | 0.021867 | 0.159634 |
8.再整形
階層化された索引付けと 再整形に関するセクションを見てください。
スタック
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=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']],
codes=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=['first', 'second'])
【解説】マルチインデックスのindexを作っております。タプルから組み合わせを作りindex名を付けて用意する
df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B']) df
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 | |
foo | one | -0.054618 | 0.587165 |
two | 0.411690 | 0.241520 | |
qux | one | 0.683476 | 1.856185 |
two | -0.435505 | 0.604112 |
【解説】index8つに対してデータ割り当てDataFrameを作ります。マルチインデックスの役割は他で調べてください。ほかのページでも説明してます。
df2 = df[:4]
df2
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 |
【解説】マルチインデックスでも、上から4つをスライスしたデータを取り出せます。
このstack()メソッドは、DataFrameの列のレベルを「圧縮」します。
stacked = df2.stack() stacked
first second
bar one A 0.681572
B 0.922410
two A -0.273510
B -1.867662
baz one A -0.072902
B 0.386001
two A -1.570309
B -0.933379
dtype: float64
【解説】stack()
メソッドでfirst,secondが同じindex同士のA,Bを3th indexに変換している。
「スタック」DATAFRAMEまたはシリーズ(持つMultiIndexように index)、逆の操作のstack()IS unstack()、どのunstacksデフォルトで最後のレベル:
stacked.unstack()
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 |
stacked.unstack(1)
second | one | two | |
---|---|---|---|
first | |||
bar | A | 0.681572 | -0.273510 |
B | 0.922410 | -1.867662 | |
baz | A | -0.072902 | -1.570309 |
B | 0.386001 | -0.933379 |
stacked.unstack(0)
first | bar | baz | |
---|---|---|---|
second | |||
one | A | 0.681572 | -0.072902 |
B | 0.922410 | 0.386001 | |
two | A | -0.273510 | -1.570309 |
B | -1.867662 | -0.933379 |
stacked.unstack(2)
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 |
【解説】unstack()を使ってマルチインデックスからcolumnに展開できる
ピボットテーブル
ピボットテーブルのセクションを参照してください。
df = pd.DataFrame({'A': ['one', 'one', 'two', 'three'] * 3, 'B': ['A', 'B', 'C'] * 4, 'C': ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2, 'D': np.random.randn(12), 'E': np.random.randn(12)}) df
A | B | C | D | E | |
---|---|---|---|---|---|
0 | one | A | foo | -0.264204 | 0.644464 |
1 | one | B | foo | 0.725091 | 1.411540 |
2 | two | C | foo | 0.685939 | 0.292828 |
3 | three | A | bar | 0.902787 | 1.326445 |
4 | one | B | bar | 0.213242 | 0.233129 |
5 | one | C | bar | -1.297688 | 0.455064 |
6 | two | A | foo | 1.245784 | 0.281448 |
7 | three | B | foo | 0.453100 | 0.087195 |
8 | one | C | foo | 0.275489 | -0.662055 |
9 | one | A | bar | -1.138988 | -0.573590 |
10 | two | B | bar | -0.674417 | -0.639242 |
11 | three | C | bar | 0.099451 | 0.757837 |
このデータからピボットテーブルを非常に簡単に作成できます。
pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'])
C | bar | foo | |
---|---|---|---|
A | B | ||
one | A | -1.138988 | -0.264204 |
B | 0.213242 | 0.725091 | |
C | -1.297688 | 0.275489 | |
three | A | 0.902787 | NaN |
B | NaN | 0.453100 | |
C | 0.099451 | NaN | |
two | A | NaN | 1.245784 |
B | -0.674417 | NaN | |
C | NaN | 0.685939 |
【解説】ピボットテーブルが簡単はいいけど説明がないとわからん! * 集計する列 values='D' * インデックス index=['A', 'B'] * 列 columns=['C']
D列が使われている。A,B列がマルチインデックスになる。C列が列として扱うそれで? * A,B列でマルチインデックスになっておりソートされている。 * C列のデータをもとにして種類があるだけ列データとして展開する。 * D列の内容を当てはめるという感じか
df = pd.DataFrame({'氏名': ['Aさん', 'Aさん', 'Bさん', 'Cさん'] * 3, '年度': ['2017', '2018', '2019'] * 4, '半期': ['上期', '上期', '上期', '下期', '下期', '下期'] * 2, '出張数': np.random.randint(0, 20, size=12), 'レシオ': np.random.randn(12)}) df
氏名 | 年度 | 半期 | 出張数 | レシオ | |
---|---|---|---|---|---|
0 | Aさん | 2017 | 上期 | 17 | -1.333364 |
1 | Aさん | 2018 | 上期 | 19 | 0.556026 |
2 | Bさん | 2019 | 上期 | 19 | -0.196643 |
3 | Cさん | 2017 | 下期 | 9 | 1.896971 |
4 | Aさん | 2018 | 下期 | 14 | -1.753202 |
5 | Aさん | 2019 | 下期 | 10 | -0.348465 |
6 | Bさん | 2017 | 上期 | 4 | -0.884795 |
7 | Cさん | 2018 | 上期 | 18 | 0.002419 |
8 | Aさん | 2019 | 上期 | 1 | 0.292434 |
9 | Aさん | 2017 | 下期 | 13 | -0.913100 |
10 | Bさん | 2018 | 下期 | 2 | -0.892237 |
11 | Cさん | 2019 | 下期 | 2 | -0.023665 |
pd.pivot_table(df, values='出張数', index=['氏名', '年度'], columns=['半期'],fill_value=0)
半期 | 上期 | 下期 | |
---|---|---|---|
氏名 | 年度 | ||
Aさん | 2017 | 17 | 13 |
2018 | 19 | 14 | |
2019 | 1 | 10 | |
Bさん | 2017 | 4 | 0 |
2018 | 0 | 2 | |
2019 | 19 | 0 | |
Cさん | 2017 | 0 | 9 |
2018 | 18 | 0 | |
2019 | 0 | 2 |
【解説】やっぱり、one、two、さん、しーとかじゃ!わからんて。適当に当てはめてみました。どうでしょうか。出張がなかったのでindexには上がってこないためNaNになっているのは、理由がはっきりしているのでfill_value=0で置き換えております。