pandasのDataFrameにおいて、行と列は交互に入れ替えることが可能です。ここでは入れ替える方法についていくつか紹介します。
DataFrameの行と列を単純に入れ替える場合、T
属性かtranspose()
メソッドを使います。
>>> import pandas
>>> import io
>>> s = '''a,b,c
... hoge,1,2
... fuga,3,4
... hoge,5,6
... hoge,7,8
... piyo,9,10
... fuga,11,12
... piyo,3,6
... hoge,7,12
... fuga,1,8
... piyo,1,2
... '''
>>> df = pandas.read_csv(io.StringIO(s))
>>> df
a b c
0 hoge 1 2
1 fuga 3 4
2 hoge 5 6
3 hoge 7 8
4 piyo 9 10
5 fuga 11 12
6 piyo 3 6
7 hoge 7 12
8 fuga 1 8
9 piyo 1 2
>>> df.T
0 1 2 3 4 5 6 7 8 9
a hoge fuga hoge hoge piyo fuga piyo hoge fuga piyo
b 1 3 5 7 9 11 3 7 1 1
c 2 4 6 8 10 12 6 12 8 2
>>> df.transpose()
0 1 2 3 4 5 6 7 8 9
a hoge fuga hoge hoge piyo fuga piyo hoge fuga piyo
b 1 3 5 7 9 11 3 7 1 1
c 2 4 6 8 10 12 6 12 8 2
T
属性による処理もtranspose()
メソッドによる処理も結果は一緒です。また、どちらも新しいオブジェクトを返し、元のDataFrameは変更しません。行と列を単純に入れ替えるだけの処理なので、集計など入れ替え以外の処理はできません。必要であれば、入れ替え後のオブジェクトを用いて集計などを行うことになります。
「横に長いテーブル(ワイドフォーマット)」についてある列に注目し「縦に長いテーブル(ロングフォーマット)」に変換する場合は、単純に行と列を入れ替えるだけのT
属性などでは対応できません。こういう場合は、melt()
メソッドかstack()
メソッドを利用します。
>>> import pandas as pd
>>> import io
>>> s = '''id,name,category,2022-01,2022-02,2022-03,2022-04,2022-05,2022-06,2022-07,2022-08,2022-09,2022-10,2022-11,2022-12,2023-01
... 1,hoge,foo,10,11,12,13,14,15,16,17,18,19,120,121,122
... 2,fuga,foo,20,21,22,23,24,25,26,27,28,29,220,221,222
... 3,hoge,baz,30,31,32,33,34,35,36,37,38,39,320,321,322
... 4,piyo,bar,40,41,42,43,44,45,46,47,48,49,420,421,422
... 5,hoge,bar,50,51,52,53,55,55,56,57,58,59,520,521,522
... 6,piyo,baz,60,61,62,63,66,66,66,67,68,69,620,621,622
... '''
>>> df = pd.read_csv(io.StringIO(s))
>>> df
id name category 2022-01 2022-02 2022-03 2022-04 2022-05 2022-06 2022-07 2022-08 2022-09 2022-10 2022-11 2022-12 2023-01
0 1 hoge foo 10 11 12 13 14 15 16 17 18 19 120 121 122
1 2 fuga foo 20 21 22 23 24 25 26 27 28 29 220 221 222
2 3 hoge baz 30 31 32 33 34 35 36 37 38 39 320 321 322
3 4 piyo bar 40 41 42 43 44 45 46 47 48 49 420 421 422
4 5 hoge bar 50 51 52 53 55 55 56 57 58 59 520 521 522
5 6 piyo baz 60 61 62 63 66 66 66 67 68 69 620 621 622
>>> df.melt()
variable value
0 id 1
1 id 2
2 id 3
3 id 4
4 id 5
.. ... ...
91 2023-01 222
92 2023-01 322
93 2023-01 422
94 2023-01 522
95 2023-01 622
[96 rows x 2 columns]
>>> df.drop('id', axis=1).melt(id_vars='name')
name variable value
0 hoge category foo
1 fuga category foo
2 hoge category baz
3 piyo category bar
4 hoge category bar
.. ... ... ...
79 fuga 2023-01 222
80 hoge 2023-01 322
81 piyo 2023-01 422
82 hoge 2023-01 522
83 piyo 2023-01 622
[84 rows x 3 columns]
>>> df.drop('id', axis=1).melt(id_vars=['name', 'category'])
name category variable value
0 hoge foo 2022-01 10
1 fuga foo 2022-01 20
2 hoge baz 2022-01 30
3 piyo bar 2022-01 40
4 hoge bar 2022-01 50
.. ... ... ... ...
73 fuga foo 2023-01 222
74 hoge baz 2023-01 322
75 piyo bar 2023-01 422
76 hoge bar 2023-01 522
77 piyo baz 2023-01 622
[78 rows x 4 columns]
もともと横に長かった変換元のテーブルについて、melt()
を実行すると縦に長いテーブルへ変換できます。
T
属性などと異なる点は、列名がそのまま行名にならずある項目の1データに変換されることです。ここではvariable
という項目列がそれにあたり、この行にもともとの列名が格納されます。そして、value
という項目列がもともとの実データを持つ構造になっています。つまり、変換後のテーブルが持つ列名および行名は変換元のDataFrameには存在しないものです。
stack()
も基本的にな動作は同じで、ワイドなテーブルをロングなテーブルに変換します。
>>> df.stack()
0 id 1
name hoge
category foo
2022-01 10
2022-02 11
...
5 2022-09 68
2022-10 69
2022-11 620
2022-12 621
2023-01 622
Length: 96, dtype: object
ただ、stack()
の変換結果は縦に長いテーブルへ変換されているものの、melt()
とは実行結果が異なっています。
>>> type(df)
<class 'pandas.core.frame.DataFrame'>
>>> type(df.melt())
<class 'pandas.core.frame.DataFrame'>
>>> type(df.stack())
<class 'pandas.core.series.Series'>
stack()
がmelt()
と異なる最大の点は、返ってくる値の型が異なることです。
melt()
はDataFrameが返ってきますが、stack()
はSeries
が返ってきます。stack()
の結果は、見た目にはなんとなくDataFrameとして複数の列が存在しているように見えますが、実際は1列のSeriesでマルチインデックスになっています。注意!
先ほどとは逆で、「縦に長いテーブル(ロングフォーマット)」についてある行に注目し「横に長いテーブル(ワイドフォーマット)」に変換する場合は、pivot()
メソッドかunstack()
メソッドを利用します。
なお、melt()
とpivot()
は相互関係にあるため、melt()
で変換したオブジェクトに対しpivot()
を実行することで、変換前の状態に戻すことが可能です。ただ、厳密にはpivot()
の処理直後はオプションの指定によりマルチインデックスになるなど、脳死でmelt()
実行前の状態に戻せるわけではないので注意。マルチインデックスを解消するなどの対処を実行してやれば、変換前のDataFrameの状態に戻せはしますが。
>>> df
id name category 2022-01 2022-02 2022-03 2022-04 2022-05 2022-06 2022-07 2022-08 2022-09 2022-10 2022-11 2022-12 2023-01
0 1 hoge foo 10 11 12 13 14 15 16 17 18 19 120 121 122
1 2 fuga foo 20 21 22 23 24 25 26 27 28 29 220 221 222
2 3 hoge baz 30 31 32 33 34 35 36 37 38 39 320 321 322
3 4 piyo bar 40 41 42 43 44 45 46 47 48 49 420 421 422
4 5 hoge bar 50 51 52 53 55 55 56 57 58 59 520 521 522
5 6 piyo baz 60 61 62 63 66 66 66 67 68 69 620 621 622
>>> df_melted = df.melt(id_vars=['id', 'name', 'category'])
>>> df_melted
id name category variable value
0 1 hoge foo 2022-01 10
1 2 fuga foo 2022-01 20
2 3 hoge baz 2022-01 30
3 4 piyo bar 2022-01 40
4 5 hoge bar 2022-01 50
.. .. ... ... ... ...
73 2 fuga foo 2023-01 222
74 3 hoge baz 2023-01 322
75 4 piyo bar 2023-01 422
76 5 hoge bar 2023-01 522
77 6 piyo baz 2023-01 622
[78 rows x 5 columns]
>>> df_melted.pivot(index=['id', 'name', 'category'], columns='variable', values='value')
variable 2022-01 2022-02 2022-03 2022-04 2022-05 2022-06 2022-07 2022-08 2022-09 2022-10 2022-11 2022-12 2023-01
id name category
1 hoge foo 10 11 12 13 14 15 16 17 18 19 120 121 122
2 fuga foo 20 21 22 23 24 25 26 27 28 29 220 221 222
3 hoge baz 30 31 32 33 34 35 36 37 38 39 320 321 322
4 piyo bar 40 41 42 43 44 45 46 47 48 49 420 421 422
5 hoge bar 50 51 52 53 55 55 56 57 58 59 520 521 522
6 piyo baz 60 61 62 63 66 66 66 67 68 69 620 621 622
もともとのDataFrameから1回melt()
した結果に対し、pivot()
を実行してみます。その結果は上記のとおりで、index
オプションで指定した列についてはマルチインデックスとなるため、厳密にmelt()
による変換前の状態に戻ったわけではない点に注意です。
>>> df
id name category 2022-01 2022-02 2022-03 2022-04 2022-05 2022-06 2022-07 2022-08 2022-09 2022-10 2022-11 2022-12 2023-01
0 1 hoge foo 10 11 12 13 14 15 16 17 18 19 120 121 122
1 2 fuga foo 20 21 22 23 24 25 26 27 28 29 220 221 222
2 3 hoge baz 30 31 32 33 34 35 36 37 38 39 320 321 322
3 4 piyo bar 40 41 42 43 44 45 46 47 48 49 420 421 422
4 5 hoge bar 50 51 52 53 55 55 56 57 58 59 520 521 522
5 6 piyo baz 60 61 62 63 66 66 66 67 68 69 620 621 622
>>> df.melt()
variable value
0 id 1
1 id 2
2 id 3
3 id 4
4 id 5
.. ... ...
91 2023-01 222
92 2023-01 322
93 2023-01 422
94 2023-01 522
95 2023-01 622
[96 rows x 2 columns]
>>> df.melt().pivot(columns='variable', values='value')
variable 2022-01 2022-02 2022-03 2022-04 2022-05 2022-06 2022-07 2022-08 2022-09 2022-10 2022-11 2022-12 2023-01 category id name
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 2 NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 3 NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 4 NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 5 NaN
.. ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
91 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 222 NaN NaN NaN
92 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 322 NaN NaN NaN
93 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 422 NaN NaN NaN
94 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 522 NaN NaN NaN
95 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 622 NaN NaN NaN
[96 rows x 16 columns]
ただし、melt()
を実行する際にまったくオプションを指定していなかった場合、pivot()
での再現ができませんでした。実際にpivot()
を実行すると、上記のように欠損値の多いオブジェクトに変換されてしまいます。melt()
を実行した時点で列がvariable
とvalue
の2つだけになっており、変換元の構造をほぼ失っているせいだと思われるため、こうなるとindex
オプションによる指定も不可でmelt()
による変換前の状態再現が難しそうです。自分は今の所再現方法がわかりません・・・。
>>> df
id name category 2022-01 2022-02 2022-03 2022-04 2022-05 2022-06 2022-07 2022-08 2022-09 2022-10 2022-11 2022-12 2023-01
0 1 hoge foo 10 11 12 13 14 15 16 17 18 19 120 121 122
1 2 fuga foo 20 21 22 23 24 25 26 27 28 29 220 221 222
2 3 hoge baz 30 31 32 33 34 35 36 37 38 39 320 321 322
3 4 piyo bar 40 41 42 43 44 45 46 47 48 49 420 421 422
4 5 hoge bar 50 51 52 53 55 55 56 57 58 59 520 521 522
5 6 piyo baz 60 61 62 63 66 66 66 67 68 69 620 621 622
>>> df.stack().unstack()
id name category 2022-01 2022-02 2022-03 2022-04 2022-05 2022-06 2022-07 2022-08 2022-09 2022-10 2022-11 2022-12 2023-01
0 1 hoge foo 10 11 12 13 14 15 16 17 18 19 120 121 122
1 2 fuga foo 20 21 22 23 24 25 26 27 28 29 220 221 222
2 3 hoge baz 30 31 32 33 34 35 36 37 38 39 320 321 322
3 4 piyo bar 40 41 42 43 44 45 46 47 48 49 420 421 422
4 5 hoge bar 50 51 52 53 55 55 56 57 58 59 520 521 522
5 6 piyo baz 60 61 62 63 66 66 66 67 68 69 620 621 622
unstack()
は、stack()
処理後のようなSeriesについて実行可能で、横に長いテーブルへ変換します。pivot()
による変換とは異なり、unstack()
でstack()
を処理する前の状態に戻すには、上記のように脳死で実行可能です。
>>> type(df.melt().pivot(columns='variable', values='value'))
<class 'pandas.core.frame.DataFrame'>
>>> type(df.stack().unstack())
<class 'pandas.core.frame.DataFrame'>
なおmelt()
とstack()
の場合とは異なり、pivot()
もunstack()
も返すオブジェクトはどちらもDataFrameです。
「行と列を入れ替える」という作業はあまり頻繁に行うものでもないと思います。いざ必要になったときには思い出せるよう、頭の片隅にでも入れておきたいと思います。
それはそうと、melt()
のオプションなしはどうやってpivot()
で戻すのよ・・・?_(┐「ε:)_