This request has already been treated.

  1. mizoe@github
Changes in body
Source | HTML | Preview

伝えたい事

R言語におけるmagrittrそしてdplyrによるデータハンドリングはとても心地が良いです.
データの処理のパイプラインが実に美しいです.

一方,いろんな方が書いたpandasにおけるデータを処理のコードを見ていると

df = df[df.a >0]
df = ...

と再帰代入を何度も繰り返していて美しくない.ややこしいことをいうと参照透過性を損う.

でも!!そんな再帰代入を繰り返さなくてもいい,pandasでもメソッドチェーンでデータ処理ができるってのが今日伝えたいことです.

とくにぜひ皆さんに使って欲しいmethodたち

  • pipe
  • assign
  • reset_index
  • set_index
  • group_by
  • pivot
  • stack

これらを頭に叩き込んで使ってもらうべく紹介するってのがこの記事の趣旨です.notebookからのmarkdown吐き出しなので多少の崩れはご容赦ください.

import numpy as np
import pandas as pd

今回使用するデータの生成

乱数で生成したこのデータが今日の相棒です.

df = pd.DataFrame(np.random.randn(20,5), columns=list('ABCDE'))
df
A B C D E
0 -0.489997 0.169059 1.743541 -0.432755 -0.595007
1 -1.530363 0.711810 -0.382714 -0.139916 -1.734980
2 -0.334975 1.045337 0.757287 1.351123 0.557953
3 0.744929 0.484988 0.429293 -3.272832 0.261902
4 0.407953 0.403938 0.265210 0.818324 -0.657293
5 -0.208507 -0.144381 -0.553875 -3.878549 -0.480763
6 0.019297 1.503824 0.680546 1.415549 -1.554857
7 1.544483 -0.209555 -0.446111 -0.785419 0.220515
8 -1.312732 1.321994 0.320443 -2.245312 -0.409711
9 -0.699864 0.050989 -0.189841 0.269374 -1.098471
10 1.120992 0.856170 0.223470 -0.822884 -0.140143
11 -1.173929 0.155096 -0.589718 0.049350 -0.001723
12 -0.295916 1.322868 -0.731492 -1.995471 -0.991209
13 1.804382 0.626075 -0.383017 1.078767 1.487689
14 -0.345145 -0.318551 -1.244325 0.541235 -0.261979
15 -1.335849 0.355642 2.910935 0.734953 -0.342311
16 1.148712 -0.285870 0.162114 -0.547444 2.438300
17 0.073855 -0.402590 -1.529420 -1.099915 -0.197386
18 -0.445434 0.468738 0.097532 1.569179 -0.393965
19 -1.885451 1.426260 -1.775148 0.993585 0.834851

lambda式

pythonには簡易な関数リテラルがあります.使い方は

lambda 仮引数: 返り値

です.

例:

func = lambda x: x**2
func(2)
4
func(3)
9

apply

各列(axis=0)または各行(axis=1)に対して関数を適用するメソッドです.返り値はpandas.Series

例:

df.apply(lambda x: sum(x), axis=1)
0     0.394842
1    -3.076163
2     3.376725
3    -1.351720
4     1.238132
5    -5.266074
6     2.064360
7     0.323913
8    -2.325318
9    -1.667813
10    1.237605
11   -1.560923
12   -2.691220
13    4.613897
14   -1.628766
15    2.323369
16    2.915812
17   -3.155455
18    1.296050
19   -0.405903
dtype: float64

assign

新たな列を作成するメソッドです.

返り値は同じくpandas.DataFrame
与える関数はpandas.DataFrameをうけとってpandas.Seriesを返すものであれば自由です.

例:

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)
A B C D E round_A round_B total
0 -0.489997 0.169059 1.743541 -0.432755 -0.595007 -0 0 0.394842
1 -1.530363 0.711810 -0.382714 -0.139916 -1.734980 -2 1 -3.076163
2 -0.334975 1.045337 0.757287 1.351123 0.557953 -0 1 3.376725
3 0.744929 0.484988 0.429293 -3.272832 0.261902 1 0 -1.351720
4 0.407953 0.403938 0.265210 0.818324 -0.657293 0 0 1.238132
5 -0.208507 -0.144381 -0.553875 -3.878549 -0.480763 -0 -0 -5.266074
6 0.019297 1.503824 0.680546 1.415549 -1.554857 0 2 2.064360
7 1.544483 -0.209555 -0.446111 -0.785419 0.220515 2 -0 0.323913
8 -1.312732 1.321994 0.320443 -2.245312 -0.409711 -1 1 -2.325318
9 -0.699864 0.050989 -0.189841 0.269374 -1.098471 -1 0 -1.667813
10 1.120992 0.856170 0.223470 -0.822884 -0.140143 1 1 1.237605
11 -1.173929 0.155096 -0.589718 0.049350 -0.001723 -1 0 -1.560923
12 -0.295916 1.322868 -0.731492 -1.995471 -0.991209 -0 1 -2.691220
13 1.804382 0.626075 -0.383017 1.078767 1.487689 2 1 4.613897
14 -0.345145 -0.318551 -1.244325 0.541235 -0.261979 -0 -0 -1.628766
15 -1.335849 0.355642 2.910935 0.734953 -0.342311 -1 0 2.323369
16 1.148712 -0.285870 0.162114 -0.547444 2.438300 1 -0 2.915812
17 0.073855 -0.402590 -1.529420 -1.099915 -0.197386 0 -0 -3.155455
18 -0.445434 0.468738 0.097532 1.569179 -0.393965 -0 0 1.296050
19 -1.885451 1.426260 -1.775148 0.993585 0.834851 -2 1 -0.405903

列の選択

sqlで言うところのSelectは

df[['A', 'B']]

で実現できます

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']]
round_A round_B total
0 -0 0 0.394842
1 -2 1 -3.076163
2 -0 1 3.376725
3 1 0 -1.351720
4 0 0 1.238132
5 -0 -0 -5.266074
6 0 2 2.064360
7 2 -0 0.323913
8 -1 1 -2.325318
9 -1 0 -1.667813
10 1 1 1.237605
11 -1 0 -1.560923
12 -0 1 -2.691220
13 2 1 4.613897
14 -0 -0 -1.628766
15 -1 0 2.323369
16 1 -0 2.915812
17 0 -0 -3.155455
18 -0 0 1.296050
19 -2 1 -0.405903

rename

列の名前を書き換えたいときはrenameメソッドを使います

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
})
id key value
0 -0 0 0.394842
1 -2 1 -3.076163
2 -0 1 3.376725
3 1 0 -1.351720
4 0 0 1.238132
5 -0 -0 -5.266074
6 0 2 2.064360
7 2 -0 0.323913
8 -1 1 -2.325318
9 -1 0 -1.667813
10 1 1 1.237605
11 -1 0 -1.560923
12 -0 1 -2.691220
13 2 1 4.613897
14 -0 -0 -1.628766
15 -1 0 2.323369
16 1 -0 2.915812
17 0 -0 -3.155455
18 -0 0 1.296050
19 -2 1 -0.405903

groupby().集計関数()

max以外にもavgsumなど基本的な集計関数が標準で備え付けられています.

返り値には階層的インデックスを持つDataFrameが返ってくるのですがこれが実はpandasのハマりどこだったりします.

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max()
value
id key
-2 1 -0.405903
-1 0 2.323369
1 -2.325318
-0 0 1.296050
1 3.376725
2 2.064360
1 0 2.915812
1 1.237605
2 0 0.323913
1 4.613897

reset_indexとset_index

indexをリセットしてデータとして扱い直します.新しいindexには再度通番が振られます
set_indexは指定した列でindexを上書きします.

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index()
id key value
0 -2 1 -0.405903
1 -1 0 2.323369
2 -1 1 -2.325318
3 -0 0 1.296050
4 -0 1 3.376725
5 -0 2 2.064360
6 1 0 2.915812
7 1 1 1.237605
8 2 0 0.323913
9 2 1 4.613897
df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id')
key value
id
-2 1 -0.405903
-1 0 2.323369
-1 1 -2.325318
-0 0 1.296050
-0 1 3.376725
-0 2 2.064360
1 0 2.915812
1 1 1.237605
2 0 0.323913
2 1 4.613897

pipe

データフレーム全体に対する関数の適用です.とくに
python
df.pipe(lambda df: df[df.f1 > 0])

みたいないわゆるselect-where構文らしきあは頻出のイディオムです.

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
)
key value
id
-2 1 -0.405903
-1 0 2.323369
-0 0 1.296050
-0 1 3.376725
-0 2 2.064360
1 0 2.915812
1 1 1.237605
2 0 0.323913
2 1 4.613897
df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r')) # suffixを追加して自分と結合
)
key_l value_l key_r value_r
id
-2 1 -0.405903 1 -0.405903
-1 0 2.323369 0 2.323369
-0 0 1.296050 0 1.296050
-0 0 1.296050 1 3.376725
-0 0 1.296050 2 2.064360
-0 1 3.376725 0 1.296050
-0 1 3.376725 1 3.376725
-0 1 3.376725 2 2.064360
-0 2 2.064360 0 1.296050
-0 2 2.064360 1 3.376725
-0 2 2.064360 2 2.064360
1 0 2.915812 0 2.915812
1 0 2.915812 1 1.237605
1 1 1.237605 0 2.915812
1 1 1.237605 1 1.237605
2 0 0.323913 0 0.323913
2 0 0.323913 1 4.613897
2 1 4.613897 0 0.323913
2 1 4.613897 1 4.613897

一旦いろいろ整理

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']]
key value
0 label-4 -0.405903
1 label23 2.323369
2 label39 6.737135
3 label101 6.737135
4 label62 6.737135
5 label58 4.153417
6 label25 4.153417
7 label6 4.937809
8 label92 4.937809

pivot

いわゆる'縦持ち'から'横持ち'への変換です.

ちなみに無名のindexに対してreset_index()を使うと新たにindexという列が出来る

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']].reset_index()
index key value
0 0 label-4 -0.405903
1 1 label23 2.323369
2 2 label39 6.737135
3 3 label101 6.737135
4 4 label62 6.737135
5 5 label58 4.153417
6 6 label25 4.153417
7 7 label6 4.937809
8 8 label92 4.937809
df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']].reset_index().pivot(
    'index',
    'key',
    'value'
)
key label-4 label101 label23 label25 label39 label58 label6 label62 label92
index
0 -0.405903 NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN 2.323369 NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN 6.737135 NaN NaN NaN NaN
3 NaN 6.737135 NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN 6.737135 NaN
5 NaN NaN NaN NaN NaN 4.153417 NaN NaN NaN
6 NaN NaN NaN 4.153417 NaN NaN NaN NaN NaN
7 NaN NaN NaN NaN NaN NaN 4.937809 NaN NaN
8 NaN NaN NaN NaN NaN NaN NaN NaN 4.937809

fillna

いわゆるnull値を埋める.

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']].reset_index().pivot(
    'index',
    'key',
    'value'
).fillna(0)
key label-4 label101 label23 label25 label39 label58 label6 label62 label92
index
0 -0.405903 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
1 0.000000 0.000000 2.323369 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
2 0.000000 0.000000 0.000000 0.000000 6.737135 0.000000 0.000000 0.000000 0.000000
3 0.000000 6.737135 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
4 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 6.737135 0.000000
5 0.000000 0.000000 0.000000 0.000000 0.000000 4.153417 0.000000 0.000000 0.000000
6 0.000000 0.000000 0.000000 4.153417 0.000000 0.000000 0.000000 0.000000 0.000000
7 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 4.937809 0.000000 0.000000
8 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 4.937809

astype

データ型のキャスト.csvに書きだしたとき 0.0000000....ってならないようになります.
本当はもうちょっと複雑ですがきほんはこれです.

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']].reset_index().pivot(
    'index',
    'key',
    'value'
).fillna(0).astype(np.int8)
key label-4 label101 label23 label25 label39 label58 label6 label62 label92
index
0 0 0 0 0 0 0 0 0 0
1 0 0 2 0 0 0 0 0 0
2 0 0 0 0 6 0 0 0 0
3 0 6 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 6 0
5 0 0 0 0 0 4 0 0 0
6 0 0 0 4 0 0 0 0 0
7 0 0 0 0 0 0 4 0 0
8 0 0 0 0 0 0 0 0 4

stack

いわゆる横持ちから縦持ちへの変換.

階層インデックス持ちのSeriesが帰ります.データフレームの世界で輝き続けるにはpipeと一緒に使います.

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']].reset_index().pivot(
    'index',
    'key',
    'value'
).fillna(0).astype(np.int8).stack()
index  key     
0      label-4     0
       label101    0
       label23     0
       label25     0
       label39     0
       label58     0
       label6      0
       label62     0
       label92     0
1      label-4     0
       label101    0
       label23     2
       label25     0
       label39     0
       label58     0
       label6      0
       label62     0
       label92     0
2      label-4     0
       label101    0
       label23     0
       label25     0
       label39     6
       label58     0
       label6      0
       label62     0
       label92     0
3      label-4     0
       label101    6
       label23     0
                  ..
5      label6      0
       label62     0
       label92     0
6      label-4     0
       label101    0
       label23     0
       label25     4
       label39     0
       label58     0
       label6      0
       label62     0
       label92     0
7      label-4     0
       label101    0
       label23     0
       label25     0
       label39     0
       label58     0
       label6      4
       label62     0
       label92     0
8      label-4     0
       label101    0
       label23     0
       label25     0
       label39     0
       label58     0
       label6      0
       label62     0
       label92     4
dtype: int8
df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']].reset_index().pivot(
    'index',
    'key',
    'value'
).fillna(0).astype(np.int8).pipe(
    lambda df: pd.DataFrame({'value':df.stack()})
)
value
index key
0 label-4 0
label101 0
label23 0
label25 0
label39 0
label58 0
label6 0
label62 0
label92 0
1 label-4 0
label101 0
label23 2
label25 0
label39 0
label58 0
label6 0
label62 0
label92 0
2 label-4 0
label101 0
label23 0
label25 0
label39 6
label58 0
label6 0
label62 0
label92 0
3 label-4 0
label101 6
label23 0
... ... ...
5 label6 0
label62 0
label92 0
6 label-4 0
label101 0
label23 0
label25 4
label39 0
label58 0
label6 0
label62 0
label92 0
7 label-4 0
label101 0
label23 0
label25 0
label39 0
label58 0
label6 4
label62 0
label92 0
8 label-4 0
label101 0
label23 0
label25 0
label39 0
label58 0
label6 0
label62 0
label92 4

81 rows × 1 columns

df.assign(
    round_A=lambda df: df.A.round(), # 四捨五入
    round_B=lambda df: df.B.round(), # 四捨五入
    total=lambda df: df.apply(lambda row: sum(row), axis=1) # A-Eの和
)[['round_A', 'round_B', 'total']].rename(columns={
        'round_A': 'id',
        'round_B': 'key',
        'total': 'value'
}).groupby(['id', 'key']).max().reset_index().set_index('id').pipe(
    lambda df: df[df.value > -2]
).pipe(
    lambda df: df.add_suffix('_l').join(df.add_suffix('_r'))
).reset_index().groupby(['id', 'key_l']).sum().reset_index().assign(
    key=lambda df: df.value_l.map(lambda x: x*10).round().map(lambda x: 'label' + str(int(x)))
).rename(columns={
        'value_r': 'value'
})[['key', 'value']].reset_index().pivot(
    'index',
    'key',
    'value'
).fillna(0).astype(np.int8).pipe(
    lambda df: pd.DataFrame({'value':df.stack()})
).pipe(
    lambda df: df[df.value != 0]
)
value
index key
1 label23 2
2 label39 6
3 label101 6
4 label62 6
5 label58 4
6 label25 4
7 label6 4
8 label92 4

さいごに

ほらね?再帰代入なんてもうやめよみんな!!