カテゴリー

pandas

【pd&MultiColumns】pandasで複数ヘッダーの設定と書き読み

2021年6月20日

こんな人にオススメ

以前pandasのMultiIndexについての解説があったけど、こっちはインデックスが複数個あるデータフレーム。

じゃあ、ヘッダが複数個あるデータフレームは一体どうなるの?

ということで、今回は以前の続きでMultiColumnsについて解説する。表のイメージは以下の感じ。

col_A col_B
hdr0 hdr1 hdr2 hdr3
index0 0 10 20 30
index1 10 20 30 40
index2 20 30 40 50
index3 30 40 50 60
index4 40 50 60 70
index5 50 60 70 80

MultiIndexと似たところが多くあるので、まずはMultiIndexの記事を見ていただいてから今回のMultiColulmnsの内容を見ていただくとより一層わかるのではないだろうか。

【pd&MultiIndex】pandasで複数インデックスの設定と書き読み

こんな人にオススメ pandasで行| ...

続きを見る

python環境は以下。

  • Python 3.9.4
  • numpy 1.20.3
  • pandas 1.2.4

運営者のメガネとです。YouTubeTwitterInstagramも運営中。

自己紹介はこちらから、お問い合わせはこちらからお願いいたします。

運営者メガネ

下準備

まずは本記事で共通の処理。

モジュールのimport

import numpy as np
import pandas as pd

本記事で使用するモジュール、numpypandasimport

作成するデータの行数と列数

# 6行4列の配列を作成
rows, cols = 6, 4

本記事で作成するデータ部分(ヘッダやインデックスは含まない)は、6行4列にするため予め行数と列数を定義。

辞書からMultiColumnsデータフレームを作成してみる

まずは以前のMultiIndexと同じように、辞書からMultiColumnsのデータフレームを作成してみる。

浅いkeysを浅いヘッダーに設定

dct = {
    'col_A':
        {
            'hdr0': np.array([0, 10, 20, 30, 40, 50]),
            'hdr1': np.array([10, 20, 30, 40, 50, 60]),
        },
    'col_B':
        {
            'hdr2': np.array([20, 30, 40, 50, 60, 70]),
            'hdr3': np.array([30, 40, 50, 60, 70, 80]),
        },
}
index = [f"index{n}" for n in range(rows)]
print(dct)
# {'col_A': {'hdr0': array([ 0, 10, 20, 30, 40, 50]), 'hdr1': array([10, 20, 30, 40, 50, 60])}, 'col_B': {'hdr2': array([20, 30, 40, 50, 60, 70]), 'hdr3': array([30, 40, 50, 60, 70, 80])}}

シンプルにレベルの浅い(より上にある)ヘッダを浅い辞書のkeysに持ってきて、深いレベルのヘッダをより深いkeysに設定する方法。MultiIndexではこの方法が使えたが、MultiColumnsではどうなるかというとうまくいかない。

print(pd.DataFrame(dct, index=index))
#         col_A  col_B
# index0    NaN    NaN
# index1    NaN    NaN
# index2    NaN    NaN
# index3    NaN    NaN
# index4    NaN    NaN
# index5    NaN    NaN

col_Acol_Bの下に辞書を置いたので認識されずに全てのデータがNaNになってしまった。

浅いヘッダーを複数keysに設定

dct = {
    'col_A': {'hdr0': np.array([0, 10, 20, 30, 40, 50]), },
    'col_A': {'hdr1': np.array([10, 20, 30, 40, 50, 60]), },
    'col_B': {'hdr2': np.array([20, 30, 40, 50, 60, 70]), },
    'col_B': {'hdr3': np.array([30, 40, 50, 60, 70, 80]), },
}
print(dct)
# {'col_A': {'hdr1': array([10, 20, 30, 40, 50, 60])}, 'col_B': {'hdr3': array([30, 40, 50, 60, 70, 80])}}

では、ヘッダごとにレベルが浅いヘッダと深いヘッダを設定すればいいじゃないかとなるが、然うは問屋が卸さない。辞書は同じkeysは許さないから。同じkeysが出るとエラーは出ないがkeysが上書きされて消える。

うまくいく気がしない

ということで、MultiIndexの時と同じようにしてもうまくいく気がしないので中止。データ、インデックス、ヘッダをバラバラに作成したほうが恐らく楽なのでそうする。

配列でバラバラに設定してMultiColumnsデータフレームを作成

データ、インデックス、ヘッダをバラバラにしてデータフレームを作成する。手順は増えるかもしれないが単純でわかりやすい。

columnsindexの設定

# level0がより左側のインデックスで、階層は上のインデックス
level0 = ['col_A', 'col_A', 'col_B', 'col_B']
level1 = ['hdr0', 'hdr1', 'hdr2', 'hdr3']
columns = [level0, level1]
print(columns)
# [['col_A', 'col_A', 'col_B', 'col_B'], ['hdr0, hdr1, hdr2, hdr3']]

index = [f"index{n}" for n in range(rows)]
print(index)
# ['index0', 'index1', 'index2', 'index3', 'index4', 'index5']

level0がより浅い、上に位置するヘッダでlevel1level0の下にくるヘッダ。インデックスは今回は1列にしてMultiにしない。

データの作成

# データ自体を作成
data = []
for col in range(cols):
    # データは10ずつズラす
    arr = 10 * np.arange(col, col + rows)
    data.append(arr)
data = np.array(data)
# このままだと行と列の数が合わない
print(data)
# [[ 0 10 20 30 40 50]
#  [10 20 30 40 50 60]
#  [20 30 40 50 60 70]
#  [30 40 50 60 70 80]]

データは10ずつ値をズラし他ものを使用。単純な配列で良かったがなんかこうした。完全に無駄に複雑な配列。しかし、このままでは配列の行と列の個数が合わないので転置。

# 行と列を入れ替え
data = data.T
print(data)
# [[ 0 10 20 30]
#  [10 20 30 40]
#  [20 30 40 50]
#  [30 40 50 60]
#  [40 50 60 70]
#  [50 60 70 80]]

MultiColumnsのデータフレーム作成

df = pd.DataFrame(data, index=index, columns=columns)
print(df)
#        col_A      col_B
#         hdr0 hdr1  hdr2 hdr3
# index0     0   10    20   30
# index1    10   20    30   40
# index2    20   30    40   50
# index3    30   40    50   60
# index4    40   50    60   70
# index5    50   60    70   80

pd.DataFrameにデータ、インデックス、ヘッダの情報を入れてデータフレームを作成。ヘッダ部分が2段になっているのでこのデータフレームがMultiColumnsになっていることがわかる。

MultiColumnsのデータ抽出

MultiColumnsはヘッダが通常データフレームよりも多い。じゃあどうやって取り出すのかってこと。

MultiColumns全体を抽出

# MultiColumnsだろうが取り出せばMultiIndexと同じ
columns_val = df.keys()
print(columns_val)
# MultiIndex([('col_A', 'hdr0'),
#             ('col_A', 'hdr1'),
#             ('col_B', 'hdr2'),
#             ('col_B', 'hdr3')],
#            )

print(type(columns_val))
# <class 'pandas.core.indexes.multi.MultiIndex'>

データフレームのヘッダが欲しいならシンプルに.keys()で取り出し可能。.keys()以外にも.columnsでも同様に取り出し可能。

# .columnsでも取り出し可能
columns_val = df.columns
print(columns_val)
# MultiIndex([('col_A', 'hdr0'),
#             ('col_A', 'hdr1'),
#             ('col_B', 'hdr2'),
#             ('col_B', 'hdr3')],
#            )

print(type(columns_val))
# pandas.core.indexes.multi.MultiIndex

一応、データフレームではlist(df)にするとヘッダだけが取り出されるが、この場合はもちろんlistで返される。

# listにするとヘッダは取得可能だがもちろんlistで返される
columns_val = list(df)
print(columns_val)
# [('col_A', 'hdr0'), ('col_A', 'hdr1'), ('col_B', 'hdr2'), ('col_B', 'hdr3')]

各レベルのMultiColumnsを抽出

# レベルごとに値を取り出し可能
level0_ind = df.columns.get_level_values(0)
print(level0_ind)
# Index(['col_A', 'col_A', 'col_B', 'col_B'], dtype='object')

level1_ind = df.columns.get_level_values(1)
print(level1_ind)
# Index(['hdr0', 'hdr1', 'hdr2', 'hdr3'], dtype='object')

各レベルのヘッダを取り出すには.get_level_values。一番レベルの浅い(上にあるヘッダ)が0でそこから1レベル上がるごとに+1される。

列指定でデータを抽出

# 階層の浅いものから順番に指定する必要がある
col_A = df['col_A']
print(col_A)
#         hdr0  hdr1
# index0     0    10
# index1    10    20
# index2    20    30
# index3    30    40
# index4    40    50
# index5    50    60

print(type(col_A))
# <class 'pandas.core.frame.DataFrame'>

列ごと取得したい場合はシンプルに[]で欲しいヘッダ名を指定。この状態からさらにhdrを指定することも可能。

hdr1 = col_A['hdr1']
print(hdr1)
# index0    10
# index1    20
# index2    30
# index3    40
# index4    50
# index5    60
# Name: hdr1, dtype: int64

print(type(hdr1))
# <class 'pandas.core.series.Series'>

一気に指定すると以下のようになる。

# 一気に処理するとこうなる
hdr1 = df['col_A']['hdr1']
print(hdr1)
# index0    10
# index1    20
# index2    30
# index3    40
# index4    50
# index5    60
# Name: hdr1, dtype: int64

boolはインデックスの抽出方法になるので注意

print(df)
#        col_A      col_B
#         hdr0 hdr1  hdr2 hdr3
# index0     0   10    20   30
# index1    10   20    30   40
# index2    20   30    40   50
# index3    30   40    50   60
# index4    40   50    60   70
# index5    50   60    70   80

level0_ind = columns_val.get_level_values(0)
print(level0_ind)
# Index(['col_A', 'col_A', 'col_B', 'col_B'], dtype='object')

condition = level0_ind == 'col_A'
print(condition)
# [ True  True False False]

取り出した特定のレベルのヘッダに対して条件を加えることも可能。しかし、この条件のboolで指定できるのはインデックス対してのみ。今回のヘッダに対して行うとエラー。

# boolの配列を入れて抽出するのはインデックスのみ
col_A = df[condition]
# ValueError: Item wrong length 4 instead of 6.

試しにインデックスと同じ要素数のbool配列を適用するとインデックスが制限される。

# インデックスの数と同じ数のboolを使うとインデックスで条件を決められる
condition = [True, True, False, False, True, False]
print(df[condition])
#        col_A      col_B
#         hdr0 hdr1  hdr2 hdr3
# index0     0   10    20   30
# index1    10   20    30   40
# index4    40   50    60   70

大人しくdf['col_A']とするのが賢い。

MultiColumnsのレベル1で条件抽出

もし、col_Aではなくhdr1のデータだけ全て欲しい、となった場合はどのように指定すればいいのか。これまでのデータフレームではhdrの名称がダブっていないので、ダブったデータフレームを作成。

# level0がより左側のインデックスで、階層は上のインデックス
level0 = ['col_A'] * 4 + ['col_B'] * 4
print(level0)
# ['col_A', 'col_A', 'col_A', 'col_A', 'col_B', 'col_B', 'col_B', 'col_B']
level1 = ['hdr0', 'hdr1', 'hdr2', 'hdr3'] * 2
print(level1)
# ['hdr0', 'hdr1', 'hdr2', 'hdr3', 'hdr0', 'hdr1', 'hdr2', 'hdr3']
columns = [level0, level1]
print(columns)
# [['col_A', 'col_A', 'col_A', 'col_A', 'col_B', 'col_B', 'col_B', 'col_B'], ['hdr0', 'hdr1', 'hdr2', 'hdr3', 'hdr0', 'hdr1', 'hdr2', 'hdr3']]

index = [f"index{n}" for n in range(rows)]
print(index)
# ['index0', 'index1', 'index2', 'index3', 'index4', 'index5']

# データ自体を作成
data = np.arange(len(index) * len(level0)).reshape(len(index), -1)
print(data)
# [[ 0  1  2  3  4  5  6  7]
#  [ 8  9 10 11 12 13 14 15]
#  [16 17 18 19 20 21 22 23]
#  [24 25 26 27 28 29 30 31]
#  [32 33 34 35 36 37 38 39]
#  [40 41 42 43 44 45 46 47]]

上記の計算で作成されるデータフレームが以下。

df = pd.DataFrame(data, index=index, columns=columns)
print(df)
#        col_A                col_B
#         hdr0 hdr1 hdr2 hdr3  hdr0 hdr1 hdr2 hdr3
# index0     0    1    2    3     4    5    6    7
# index1     8    9   10   11    12   13   14   15
# index2    16   17   18   19    20   21   22   23
# index3    24   25   26   27    28   29   30   31
# index4    32   33   34   35    36   37   38   39
# index5    40   41   42   43    44   45   46   47

例えばhdr1の列だけ欲しい場合はどうすればいいのか、という問いに関しての答えは以下の2種類の配列を取得するということ。本当はデータフレームとして出力されるのでインデックスの情報とかもつくが、データ部分だけ見ると以下。

  • 1, 9, 17, 25, 33, 41
  • 5, 13, 21, 29, 37, 45

転置してヘッダをインデックスにして抽出して元に戻す

trnsps = df.T
print(trnsps)
#             index0  index1  index2  index3  index4  index5
# col_A hdr0       0       8      16      24      32      40
#       hdr1       1       9      17      25      33      41
#       hdr2       2      10      18      26      34      42
#       hdr3       3      11      19      27      35      43
# col_B hdr0       4      12      20      28      36      44
#       hdr1       5      13      21      29      37      45
#       hdr2       6      14      22      30      38      46
#       hdr3       7      15      23      31      39      47

先ほど、boolの配列をデータフレームに入れるとインデックスに適用されるということを書いた。なら、ヘッダをインデックスにしてしまえばいいんじゃないという力技。df.Tで転置ができるので転置するとヘッダがインデックスに、インデックスがヘッダに入れ替わる。もちろんデータも転置される。

実際にインデックスをとってみると元ヘッダを取り出すことができる。したがって、この状態でhdr1に当てはまる条件を適用して最後に転置で元に戻せば一件落着。

condition = index1 == 'hdr1'
hdr1_onlyT = trnsps[condition]
print(hdr1_onlyT)
#             index0  index1  index2  index3  index4  index5
# col_A hdr1       1       9      17      25      33      41
# col_B hdr1       5      13      21      29      37      45

hdr1_only = hdr1_onlyT.T
print(hdr1_only)
#        col_A col_B
#         hdr1  hdr1
# index0     1     5
# index1     9    13
# index2    17    21
# index3    25    29
# index4    33    37
# index5    41    45

条件のbool配列をlocに適用

print(df)
#        col_A                col_B
#         hdr0 hdr1 hdr2 hdr3  hdr0 hdr1 hdr2 hdr3
# index0     0    1    2    3     4    5    6    7
# index1     8    9   10   11    12   13   14   15
# index2    16   17   18   19    20   21   22   23
# index3    24   25   26   27    28   29   30   31
# index4    32   33   34   35    36   37   38   39
# index5    40   41   42   43    44   45   46   47

level1_ind = df.columns.get_level_values(1)
print(level1_ind)
# Index(['hdr0', 'hdr1', 'hdr2', 'hdr3', 'hdr0', 'hdr1', 'hdr2', 'hdr3'], dtype='object')

# このconditionを直接データフレームに入れると、インデックスが認識されてうまくいかなかった
condition = level1_ind == 'hdr1'
print(condition)
# [False  True False False False  True False False]

条件を入れたboolの配列を.locでデータフレームに適用することででも条件の適用を行うことが可能。似たようなものとして.ilocがあるが違いは以下。

  • loc: 行、列の名称で指定
  • iloc: 行、列のインデックス番号で指定

ただし、どちらもbool配列での適用には対応しているので、ここではbool配列でデータフレームの条件適用を行うことにする。

# locで全行を選択することで抽出可能
# 「loc」は行・列をラベルで指定、ilocがインデックスで指定
# boolならどちらも同様に可能
hdr1_only = df.loc[:, condition]
print(hdr1_only)
#        col_A col_B
#         hdr1  hdr1
# index0     1     5
# index1     9    13
# index2    17    21
# index3    25    29
# index4    33    37
# index5    41    45

locloc[(行), (列)]の順番で指定する。今回は列方向に条件を適用させ、行方向に対しては全データを使用することにする。したがって行については全行を表す:を使用する。なお、行方向にも条件を追加したい場合はスライスの要領でインデックスの名称を入れたらいい。

# 行の指定も可能
hdr1_only14 = df.loc['index1':'index4', condition]
print(hdr1_only14)
#        col_A col_B
#         hdr1  hdr1
# index1     9    13
# index2    17    21
# index3    25    29
# index4    33    37

ヘッダ名の配列をlocに適用

# pd.IndexSliceを使用することで一気にlevel1の指定が可能
slice = pd.IndexSlice[['col_A', 'col_B'], 'hdr1']
print(slice)
# (['col_A', 'col_B'], 'hdr1')

print(type(slice))
# <class 'tuple'>

hdr1_only = df.loc[:, slice]
print(hdr1_only)
#        col_A col_B
#         hdr1  hdr1
# index0     1     5
# index1     9    13
# index2    17    21
# index3    25    29
# index4    33    37
# index5    41    45

locに直接ヘッダの名前を入れることでも条件を適用することが可能。ヘッダ名の配列にはpd.IndexSliceを使用。この関数ではtupleが作られるので、自分でtupleを作成して適用してもいい。

# pd.IndexSliceで作成した配列はrupleなので、もちろん簡単に自作も可能
slice = (['col_A', 'col_B'], 'hdr1')
print(df.loc[:, slice])
#        col_A col_B
#         hdr1  hdr1
# index0     1     5
# index1     9    13
# index2    17    21
# index3    25    29
# index4    33    37
# index5    41    45

ただし、tupleではなくlistにするとエラー。

# listにするとエラー
slice = [['col_A', 'col_B'], 'hdr1']
print(df.loc[:, slice])
# NotImplementedError: Index._join_level on non-unique index is not implemented

また、level0のデータをヘッダから自動で取得して指定することも可能。コードが長くて面倒だけどlevel0の情報が多い時には使えそう。

# sliceのlevel0の配列をいちいち打ち込むのが面倒なら、データフレームから取得すればいい
level0_ind = df.columns.get_level_values(0)
print(level0_ind)
# Index(['col_A', 'col_A', 'col_A', 'col_A', 'col_B', 'col_B', 'col_B', 'col_B'], dtype='object')

# ダブってるヘッダー名を消してユニークにする
level0_ind = df.columns.get_level_values(0).unique()
print(level0_ind)
# Index(['col_A', 'col_B'], dtype='object')

slice = pd.IndexSlice[level0_ind, 'hdr1']
print(slice)
# (Index(['col_A', 'col_B'], dtype='object'), 'hdr1')

print(df.loc[:, slice])
#        col_A col_B
#         hdr1  hdr1
# index0     1     5
# index1     9    13
# index2    17    21
# index3    25    29
# index4    33    37
# index5    41    45

MultiColumnsデータフレームをtxtファイルに書き出し、読み込み

MultiColumnsのデータフレームを作成したので、今度はこのデータフレームを書き出す。そして、書き出したファイルを再度読み込んでみる。

データの書き出し

print(df)
#            col_A                col_B
#             hdr0 hdr1 hdr2 hdr3  hdr0 hdr1 hdr2 hdr3
# index0     0    1    2    3     4    5    6    7
# index1     8    9   10   11    12   13   14   15
# index2    16   17   18   19    20   21   22   23
# index3    24   25   26   27    28   29   30   31
# index4    32   33   34   35    36   37   38   39
# index5    40   41   42   43    44   45   46   47

df.to_csv('./multicolumns.txt', sep='\\t')
# 	        col_A	col_A	col_A	col_A	col_B	col_B	col_B	col_B
# 	        hdr0	hdr1	hdr2	hdr3	hdr0	hdr1	hdr2	hdr3
# index0	0	1	2	3	4	5	6	7
# index1	8	9	10	11	12	13	14	15
# index2	16	17	18	19	20	21	22	23
# index3	24	25	26	27	28	29	30	31
# index4	32	33	34	35	36	37	38	39
# index5	40	41	42	43	44	45	46	47

データの書き出しは.to_csvで行う。txtファイルとして出力する場合は区切り文字sepsep='\t'としてタブ区切りにすると便利。csvファイルにする際にはsep=','にしてカンマ区切りにする。

データの読み込み

# 普通に読み込むだけだとMultiColumnsにならない
df_read = pd.read_csv('./multicolumns.txt', sep='\\t')
print(df_read)
#   Unnamed: 0 col_A col_A.1 col_A.2 col_A.3 col_B col_B.1 col_B.2 col_B.3
# 0        NaN  hdr0    hdr1    hdr2    hdr3  hdr0    hdr1    hdr2    hdr3
# 1     index0     0       1       2       3     4       5       6       7
# 2     index1     8       9      10      11    12      13      14      15
# 3     index2    16      17      18      19    20      21      22      23
# 4     index3    24      25      26      27    28      29      30      31
# 5     index4    32      33      34      35    36      37      38      39
# 6     index5    40      41      42      43    44      45      46      47

前節で作成したデータフレームを書き出した。ここではそのデータフレームを再度読み込んでみる。シンプルに.read_csvでデータを読み込んでもMultiColumnsにはならずにlevel0だけがヘッダとして認識される。また、インデックスの部分がNaNになったりUnnamed: 0になったりしてしまっている。

これを解消するためには、ヘッダが2行あることを指定しないといけない。読み込み時にheader引数で0行目と1行目がヘッダーであると指定する。

# ヘッダーが2行であることを設定する必要がある
df_read = pd.read_csv('./multicolumns.txt', sep='\\t', header=[0, 1])
print(df_read)
#        col_A                col_B
#         hdr0 hdr1 hdr2 hdr3  hdr0 hdr1 hdr2 hdr3
# index0     0    1    2    3     4    5    6    7
# index1     8    9   10   11    12   13   14   15
# index2    16   17   18   19    20   21   22   23
# index3    24   25   26   27    28   29   30   31
# index4    32   33   34   35    36   37   38   39
# index5    40   41   42   43    44   45   46   47

ヘッダ名で指定することはできない。あくまでもintでの指定となる。

# ヘッダ名の名称での指定はできない
header = [
    ['col_A', 'col_A', 'col_A', 'col_A', 'col_B', 'col_B', 'col_B', 'col_B'],
    ['hdr0', 'hdr1', 'hdr2', 'hdr3', 'hdr0', 'hdr1', 'hdr2', 'hdr3']
]
# df_read = pd.read_csv('./multicolumns.txt', sep='\\t', header=header)
# ValueError: header must be integer or list of integers

MultiIndexMultiColumnsを合わせる

ここでは、前回のMultiIndexと今回のMultiColumnsを組み合わせたデータフレームを作成。その後、作成したデータフレームをtxtファイルに書き出し、再度読み込む。

データの作成

c_level0 = ['col_A', 'col_A', 'col_B', 'col_B']
c_level1 = ['hdr0', 'hdr1', 'hdr2', 'hdr3']
columns = [c_level0, c_level1]

i_level0 = ['ind_A', 'ind_A', 'ind_B', 'ind_B', 'ind_C', 'ind_C']
i_level1 = [1, 2, 3, 4, 5, 6]
indices = [i_level0, i_level1]

# データ自体を作成
data = np.arange(len(i_level0) * len(c_level0)).reshape(len(index), -1)
print(data)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]
#  [12 13 14 15]
#  [16 17 18 19]
#  [20 21 22 23]]

ヘッダー・インデックスともに1つのlevel0に対して2つのlevel1を対応させ、ヘッダは2種類、インデックスは3種類のデータでデータフレームを作成。

df = pd.DataFrame(data, index=indices, columns=columns)
df.index.names = ['index0', 'index1']
print(df)
#               col_A      col_B
#                hdr0 hdr1  hdr2 hdr3
# index0 index1
# ind_A  1          0    1     2    3
#            2          4    5     6    7
# ind_B  3          8    9    10   11
#            4         12   13    14   15
# ind_C  5         16   17    18   19
#            6         20   21    22   23

データを抽出

print(df.columns)
# MultiIndex([('col_A', 'hdr0'),
#             ('col_A', 'hdr1'),
#             ('col_B', 'hdr2'),
#             ('col_B', 'hdr3')],
#            )

print(df.index)
# MultiIndex([('ind_A', 1),
#             ('ind_A', 2),
#             ('ind_B', 3),
#             ('ind_B', 4),
#             ('ind_C', 5),
#             ('ind_C', 6)],
#            names=['index0', 'index1'])

データフレームからヘッダ、インデックスの抽出はそれぞれ.columns.indexだった。今回はMultiということで出力される配列はMultiに対応したものとなっている。特定のlevelのデータだけ抽出するには.get_level_valuesを使用する。

print(df.columns.get_level_values(0))
# Index(['col_A', 'col_A', 'col_B', 'col_B'], dtype='object')

print(df.index.get_level_values(0))
# Index(['ind_A', 'ind_A', 'ind_B', 'ind_B', 'ind_C', 'ind_C'], dtype='object', name='index0')

ある特定の列に絞って抽出するにはdf['col_A']のようにデータ名を使用する。

print(df['col_A'])
#                hdr0  hdr1
# index0 index1
# ind_A  1          0     1
#            2          4     5
# ind_B  3          8     9
#            4         12    13
# ind_C  5         16    17
#            6         20    21

print(df['col_A']['hdr1'])
# index0  index1
# ind_A   1          1
#             2          5
# ind_B   3          9
#             4         13
# ind_C   5         17
#             6         21
# Name: hdr1, dtype: int64

さらにインデックスまで指定するなら.loc。もちろん指定の仕方を変えると.ilocも使用可能。

print(df['col_A']['hdr1'].loc['ind_A'])
# index1
# 1    1
# 2    5
# Name: hdr1, dtype: int64

print(df['col_A']['hdr1'].loc[['ind_A']])
# index0  index1
# ind_A   1         1
#             2         5
# Name: hdr1, dtype: int64

print(df['col_A']['hdr1'].loc[['ind_A', 'ind_C']])
# index0  index1
# ind_A   1          1
#             2          5
# ind_C   5         17
#             6         21
# Name: hdr1, dtype: int64

データの書き出しと読み込み

file = './multicolumns.txt'
df.to_csv(file, sep='\\t')
# 		col_A	col_A	col_B	col_B
# 		hdr0	hdr1	hdr2	hdr3
# index0	index1
# ind_A	1	0	1	2	3
# ind_A	2	4	5	6	7
# ind_B	3	8	9	10	11
# ind_B	4	12	13	14	15
# ind_C	5	16	17	18	19
# ind_C	6	20	21	22	23

データフレームの書き出しは先ほど同様.to_csvで可能。読み込みも指定しないとうまくいかない。

# 指定しないとインデックスもヘッダも設定されない
df_read = pd.read_csv(file, sep='\\t')
print(df_read)
#   Unnamed: 0 Unnamed: 1 col_A col_A.1 col_B col_B.1
# 0        NaN        NaN  hdr0    hdr1  hdr2    hdr3
# 1     index0     index1   NaN     NaN   NaN     NaN
# 2      ind_A          1     0       1     2       3
# 3      ind_A          2     4       5     6       7
# 4      ind_B          3     8       9    10      11
# 5      ind_B          4    12      13    14      15
# 6      ind_C          5    16      17    18      19
# 7      ind_C          6    20      21    22      23

ヘッダー、インデックスともに2段になっているのでheader=[0, 1], index_col=[0, 1]としてヘッダーとインデックスの行番号を指定。

# インデックスとヘッダの設定
df_read = pd.read_csv(file, sep='\\t', header=[0, 1], index_col=[0, 1])
print(df_read)
#               col_A      col_B
#                hdr0 hdr1  hdr2 hdr3
# index0 index1
# ind_A  1          0    1     2    3
#            2          4    5     6    7
# ind_B  3          8    9    10   11
#            4         12   13    14   15
# ind_C  5         16   17    18   19
#            6         20   21    22   23

複雑なデータをパッと見やすく

今回と以前の2記事にわたってpandasのMultiIndexMultiColumnsについて解説した。今回この記事を書くきっかけが研究でパラメータが2種類あるデータを扱ったから。ちょうど今回のdfのようなcol_Ahdr0の関係。どうせならわかりやすくtxtファイルに書き出したいなということで色々と試した。

このように簡単なデータフレームでは使用する機会がないが知っておくとここぞという時に使うことができるようなコードなので是非とも活用してより見やすいようにしていただければと思う。

関連記事

【pd&MultiIndex】pandasで複数インデックスの設定と書き読み

こんな人にオススメ pandasで行| ...

続きを見る

【PEP8&flake8】pythonにおけるPEP8とflake8

こんな人にオススメ pythonにつ{ ...

続きを見る

多項式関数のグラフ
【辞書&pandas】dict{name: , val: {a: [~], b:[~]}}のpandas化

こんな人にオススメ ネスト ...

続きを見る

【辞書の結合】dictのマージ

こんな人にオススメlistは+記号 ...

続きを見る

【python&csv読み込み】pythonを使ってcsvを読み込み

こんな人にオススメ pythonを使{ ...

続きを見る

【python3.7以降&dictのkeys】ネスト(入れ子)されたdictのkeys一覧をカッコで出力

こんな人にオススメ dictとやら ...

続きを見る

【python&独学】独学である程度pythonを習得するマインド

こんな人にオススメ pythonを新{ ...

続きを見る

【python&初級】のlistとかforとかifとかまとめ

こんな人にオススメ pythonの初Ē ...

続きを見る

スイッチボット

2022/9/11

【SwitchBotロックレビュー】これからのスタンダードになりうるスマートロック

こんな人にオススメ SwitchBotからスマートロック「SwitchBotロック」が発売された ...

生活に役立つ

2022/10/25

【メガネ厳選】クソ便利に使っているサービスやアイテム達

このページでは執筆者「メガネ」が実際に使って便利だと感じているサ ...

マウス

2022/9/11

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【ながら聴きイヤホン比較】SONY LinkBuds、ambie、BoCoはどれがおすすめ?

こんな人におすすめ 耳を塞がない開放型のイヤホンに完全ワイヤレスӟ ...

macOSアプリケーション

2022/10/15

【M1 Mac】MacBook Proに入れている便利でニッチなアプリを21個紹介する

こんな人におすすめ MacBookを購入してLINEとか必要最低限のアプリは入れた。 ...

完全ワイヤレスイヤホン(TWS)

2022/10/23

【SENNHEISER MOMENTUM True Wireless 3レビュー】高レベルでバランス型の高音質イヤホン

こんな人におすすめ SENNHEISER MOMENTUM True Wireless 3って実際のところどうなの? 評判は良い ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【SONY WF-1000XM4レビュー】神とゴミのハーフ&ハーフ

こんな人におすすめ SONYのフラグシップモデル「SONY WF-1000XM4」ってどれくらい性 ...

完全ワイヤレスイヤホン(TWS)

2022/8/19

【Nothing ear (1)レビュー】ライトな完成度、アップデートに期待

こんな人にオススメ 完全ワイヤレスイヤホン(TWS)でスケルトンボディ ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

ベンチャー企業のWebエンジニア駆け出し。独学のPythonで天文学系の大学院を修了→新卒を1.5年で辞める→転職→今に至る。
常時金欠のガジェット好きでM1 MacBook Pro x Galaxy S22 Ultraの狂人。
人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂う→人生が楽しい

ガジェットのレビューとPythonコードを記事にしています。ぜひ楽しんでください🦊
自己紹介と半生→変わって楽しいの繰り返し

-pandas
-, ,