pandas
を使っているとき,MultiIndex
を使いたくなる時が(あまりやりたくはないですが)あります.そしてMultiIndex
と普通の階層のないインデックスを混在させたいときもあります.例えば以下のような感じ.
df = pd.DataFrame(
[
[ "ピカチュウ", "10まんボルト", "でんこうせっか", "アイアンテール", "エレキネット" ],
[ "ルカリオ", "はどうだん", "インファイト", "かげぶんしん", "きしかいせい" ],
[ "カイリュー", "りゅうのまい", "ドラゴンクロー", "ぼうふう", "りゅうせいぐん" ],
],
columns=pd.MultiIndex.from_tuples( [("なまえ", ""), ("わざ", "1"), ("わざ", "2"), ("わざ", "3"), ("わざ", "4")] )
)
print( df )
以下出力.
# 出力は以下のような感じ
なまえ わざ
1 2 3 4
0 ピカチュウ 10まんボルト でんこうせっか アイアンテール エレキネット
1 ルカリオ はどうだん インファイト かげぶんしん きしかいせい
2 カイリュー りゅうのまい ドラゴンクロー ぼうふう りゅうせいぐん
わざ
はMultiIndex
,なまえ
は普通のインデックスです.
MultiIndex
に対して,インデックスに階層がない(ように見える)ものを「普通のインデックス」と呼んでいますが,お察しの通りこれは適切でない表現です.あくまで第0レベルのみのMultiIndex
が妥当な表現と思われます.
このような時に列を取り出すときのTipsを備忘録としてまとめます.
またこのようなデータをテキストデータとして保存した後,再度読み込んだ時のTipsも.
列を取り出したいよー
print( df[ ("わざ", "1" ) ] ) # MultiIndexな方にアクセスするとき
print( df[ "なまえ" ] ) # MultiIndexじゃないほうにアクセスするとき
# # MultiIndexじゃないほうにアクセスするとき,以下の記法でもOK
# print( df[ ("なまえ", "") ] )
# print( df[ ("なまえ", ) ] )
print( type( df[ "なまえ" ] ) ) # 型も見ておく
print( df.columns ) # ついでに列の情報もみておく
以下出力.
0 10まんボルト
1 はどうだん
2 りゅうのまい
Name: (わざ, 1), dtype: object
0 ピカチュウ
1 ルカリオ
2 カイリュー
Name: なまえ, dtype: object
<class 'pandas.core.series.Series'>
MultiIndex( [
('なまえ', ''),
( 'わざ', '1'),
( 'わざ', '2'),
( 'わざ', '3'),
( 'わざ', '4')
], )
一度テキストに保存し,その後読み込んだ時
DataFrame
を poke.txt
として保存し,読み込んで同じことをします.
df = pd.read_table( r"./poke.txt", delimiter="\t", header=[0, 1] )
print( df )
print( df[ ("わざ", "1" ) ] ) # これはもともとの通り
print( df["なまえ"] ) # ここが微妙
print( type( df["なまえ"] ) ) # ここも微妙
print( df.columns ) # ここが問題な感じ
結果は以下.
なまえ わざ
Unnamed: 0_level_1 1 2 3 4
0 ピカチュウ 10まんボルト でんこうせっか アイアンテール エレキネット
1 ルカリオ はどうだん インファイト かげぶんしん きしかいせい
2 カイリュー りゅうのまい ドラゴンクロー ぼうふう りゅうせいぐん
0 10まんボルト
1 はどうだん
2 りゅうのまい
Name: (わざ, 1), dtype: object
Unnamed: 0_level_1
0 ピカチュウ
1 ルカリオ
2 カイリュー
<class 'pandas.core.frame.DataFrame'>
MultiIndex( [
('なまえ', 'Unnamed: 0_level_1'),
( 'わざ', '1'),
( 'わざ', '2'),
( 'わざ', '3'),
( 'わざ', '4')
], )
もともと列なまえ
は第0レベルだけで,その下の第1レベルは空値でした.しかし読み込む時に空値の部分には,Unnamed: 0_level_1
が自動で入力されてしまうようです.すると df["なまえ"]
が pd.Series
ではなく pd.DataFrame
として扱われてしまうなど,ちょっと微妙です.
そのため,列のIndexの中にある,Unnamed: 0_level_1
を空文字で置き換えてしまいましょう.
df = pd.read_table( r"./poke.txt", delimiter="\t", header=[0, 1] )
df.columns = df.columns.map( lambda x: tuple("" if y.startswith("Unnamed") else y for y in x) )
Unnamed
で始まる列の要素名を空文字に置き換えればOKです.
おわりに
なにか他にいい方法あれば教えてください.