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形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。

pandas.pydata.org

【解説】公式「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で置き換えております。

chiyoh.hatenablog.com
chiyoh.hatenablog.com