pandas.DataFrameの行・列を任意の順に並べ替えるreindex

Modified: | Tags: Python, pandas, 時系列データ

pandasでDataFrameの行・列を任意の順番に並べ替えるにはreindex()を使う。Seriesにも同様のメソッドがある。ラベル(行名・列名)のリストを指定する。

別のDataFrameの行・列と同じように並べるreindex_like()というメソッドもある。

いずれのメソッドでも、既存の行・列を並べ替えるだけでなく、新たな行・列を追加することが可能。

以下のサンプルコードではDataFrameを例とするが、Seriesでも使い方は同じ。Seriesにはcolumnsが無いのでindexのみを指定する。

任意の順番ではなく昇順・降順に並べ替えたい場合はsort_index()を使う。

都道府県名を都道府県コード順にソートするといった、任意のルールに従って並べ替えたい場合は以下の記事を参照。

本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。

import pandas as pd

print(pd.__version__)
# 2.1.4

reindex()の基本的な使い方

以下のDataFrameを例とする。

df = pd.DataFrame({'A': [1, 2, 3], 'B': [10, 20, 30], 'C': [100, 200, 300]},
                  index=['One', 'Two', 'Three'])
print(df)
#        A   B    C
# One    1  10  100
# Two    2  20  200
# Three  3  30  300

行・列の順番の指定: 引数index, columns

引数index, columnsにそれぞれ行名・列名を任意の順番に並べたリストを指定すると、その通りに並べ替えられた新たなDataFrameが返される。同時に指定することも可能。

print(df.reindex(index=['Two', 'Three', 'One']))
#        A   B    C
# Two    2  20  200
# Three  3  30  300
# One    1  10  100

print(df.reindex(columns=['B', 'C', 'A']))
#         B    C  A
# One    10  100  1
# Two    20  200  2
# Three  30  300  3

print(df.reindex(index=['Two', 'Three', 'One'], columns=['B', 'C', 'A']))
#         B    C  A
# Two    20  200  2
# Three  30  300  3
# One    10  100  1

既存の行名・列名を全て使わなくてもよい。

print(df.reindex(columns=['B', 'A'], index=['Three', 'One']))
#         B  A
# Three  30  3
# One    10  1

第一引数labelsにリスト、引数axisで行か列かを指定する方法もある。行の場合は0または'index'、列の場合は1または'columns'とする。

print(df.reindex(['Two', 'Three', 'One'], axis=0))
#        A   B    C
# Two    2  20  200
# Three  3  30  300
# One    1  10  100

print(df.reindex(['B', 'C', 'A'], axis='columns'))
#         B    C  A
# One    10  100  1
# Two    20  200  2
# Three  30  300  3

なお、列の並び替えはdf[列名のリスト]とすることでも実現可能。

print(df[['B', 'C', 'A']])
#         B    C  A
# One    10  100  1
# Two    20  200  2
# Three  30  300  3

reindex()で新たな行名・列名を指定する場合

reindex()で新たな行名・列名を指定すると、デフォルトではすべての要素が欠損値NaNとして追加される。

df = pd.DataFrame({'A': [1, 2, 3], 'B': [10, 20, 30], 'C': [100, 200, 300]},
                  index=['One', 'Two', 'Three'])
print(df)
#        A   B    C
# One    1  10  100
# Two    2  20  200
# Three  3  30  300

print(df.reindex(columns=['B', 'X', 'C'], index=['Two', 'One', 'Four']))
#          B   X      C
# Two   20.0 NaN  200.0
# One   10.0 NaN  100.0
# Four   NaN NaN    NaN

行・列の追加については以下の記事も参照。

任意の値で穴埋め: 引数fill_value

引数fill_valueに任意の値を指定するとその値で穴埋めされる。

print(df.reindex(columns=['B', 'X', 'C'], index=['Two', 'One', 'Four'],
                 fill_value=0))
#        B  X    C
# Two   20  0  200
# One   10  0  100
# Four   0  0    0

前後の要素の値で穴埋め: 引数method

元のDataFrameindexが単調増加または単調減少である場合は、引数methodを使って前後の要素の値で穴埋めすることも可能。

以下のDataFrameを例とする。

df = pd.DataFrame({'A': [1, 2], 'B': [10, 20], 'C': [100, 200]},
                  index=[10, 20])
print(df)
#     A   B    C
# 10  1  10  100
# 20  2  20  200

引数method'bfill'または'backfill'とすると後方(下)の値、'ffill'または'pad'とすると前方(上)の値、'nearest'とすると近い値(等距離の場合は後方の値)で穴埋めされる。

print(df.reindex(index=[5, 10, 15, 20, 25]))
#       A     B      C
# 5   NaN   NaN    NaN
# 10  1.0  10.0  100.0
# 15  NaN   NaN    NaN
# 20  2.0  20.0  200.0
# 25  NaN   NaN    NaN

print(df.reindex(index=[5, 10, 15, 20, 25], method='bfill'))
#       A     B      C
# 5   1.0  10.0  100.0
# 10  1.0  10.0  100.0
# 15  2.0  20.0  200.0
# 20  2.0  20.0  200.0
# 25  NaN   NaN    NaN

print(df.reindex(index=[5, 10, 15, 20, 25], method='ffill'))
#       A     B      C
# 5   NaN   NaN    NaN
# 10  1.0  10.0  100.0
# 15  1.0  10.0  100.0
# 20  2.0  20.0  200.0
# 25  2.0  20.0  200.0

print(df.reindex(index=[5, 10, 15, 20, 25], method='nearest'))
#     A   B    C
# 5   1  10  100
# 10  1  10  100
# 15  2  20  200
# 20  2  20  200
# 25  2  20  200

引数limitに整数値を指定すると、欠損値NaNが連続する場合にlimit個のみ穴埋めされる。

print(df.reindex(index=[10, 12, 14, 16, 18, 20]))
#       A     B      C
# 10  1.0  10.0  100.0
# 12  NaN   NaN    NaN
# 14  NaN   NaN    NaN
# 16  NaN   NaN    NaN
# 18  NaN   NaN    NaN
# 20  2.0  20.0  200.0

print(df.reindex(index=[10, 12, 14, 16, 18, 20], method='bfill', limit=2))
#       A     B      C
# 10  1.0  10.0  100.0
# 12  NaN   NaN    NaN
# 14  NaN   NaN    NaN
# 16  2.0  20.0  200.0
# 18  2.0  20.0  200.0
# 20  2.0  20.0  200.0

引数indexcolumnで指定した順番とは関係なく、元のDataFrameindexをもとに値が選択されるので注意。

print(df.reindex(index=[5, 10, 15, 20, 25], method='bfill'))
#       A     B      C
# 5   1.0  10.0  100.0
# 10  1.0  10.0  100.0
# 15  2.0  20.0  200.0
# 20  2.0  20.0  200.0
# 25  NaN   NaN    NaN

print(df.reindex(index=[25, 20, 15, 10, 5], method='bfill'))
#       A     B      C
# 25  NaN   NaN    NaN
# 20  2.0  20.0  200.0
# 15  2.0  20.0  200.0
# 10  1.0  10.0  100.0
# 5   1.0  10.0  100.0

その他の穴埋め・補間方法

欠損値NaNの穴埋めにはfillna()ffill(), bfill()、補間にはinterpolate()を使う方法もある。

df = pd.DataFrame({'A': [1, 2], 'B': [10, 20], 'C': [100, 200]},
                  index=[10, 20])
print(df)
#     A   B    C
# 10  1  10  100
# 20  2  20  200

print(df.reindex(index=[5, 10, 15, 20, 25]).bfill())
#       A     B      C
# 5   1.0  10.0  100.0
# 10  1.0  10.0  100.0
# 15  2.0  20.0  200.0
# 20  2.0  20.0  200.0
# 25  NaN   NaN    NaN

print(df.reindex(index=[5, 10, 15, 20, 25]).interpolate())
#       A     B      C
# 5   NaN   NaN    NaN
# 10  1.0  10.0  100.0
# 15  1.5  15.0  150.0
# 20  2.0  20.0  200.0
# 25  2.0  20.0  200.0

reindex()では列を追加した場合に左右の要素からの穴埋めはできないが、fillna()ffill(), bfill(), interpolate()では可能。

print(df.reindex(columns=['A', 'X', 'C'], method='bfill'))
#     A   X    C
# 10  1 NaN  100
# 20  2 NaN  200

print(df.reindex(columns=['A', 'X', 'C']).bfill(axis=1))
#       A      X      C
# 10  1.0  100.0  100.0
# 20  2.0  200.0  200.0

print(df.reindex(columns=['A', 'X', 'C']).interpolate(axis=1))
#       A      X      C
# 10  1.0   50.5  100.0
# 20  2.0  101.0  200.0

また、上述のように、reindex()methodで穴埋めできるのは元のindexが単調増加または単調減少である場合に限られる。以下のような場合はエラーとなる。

df = pd.DataFrame({'A': [1, 2, 3], 'B': [10, 20, 30], 'C': [100, 200, 300]},
                  index=[20, 10, 30])
print(df)
#     A   B    C
# 20  1  10  100
# 10  2  20  200
# 30  3  30  300

# print(df.reindex(index=[10, 15, 20], method='bfill'))
# ValueError: index must be monotonic increasing or decreasing

fillna()ffill(), bfill(), interpolate()ならば問題ない。

print(df.reindex(index=[10, 15, 20]))
#       A     B      C
# 10  2.0  20.0  200.0
# 15  NaN   NaN    NaN
# 20  1.0  10.0  100.0

print(df.reindex(index=[10, 15, 20]).bfill())
#       A     B      C
# 10  2.0  20.0  200.0
# 15  1.0  10.0  100.0
# 20  1.0  10.0  100.0

print(df.reindex(index=[10, 15, 20]).interpolate())
#       A     B      C
# 10  2.0  20.0  200.0
# 15  1.5  15.0  150.0
# 20  1.0  10.0  100.0

なお、fillna()ffill(), bfill(), interpolate()を用いる場合、一度、欠損値NaNを含むDataFrameを経由する。NaNが含まれるとデータ型dtypeが浮動小数点数floatとなるため、上の例のように最終的に全ての値が整数値となってもdtypefloatのまま。要注意。

reindex_like()の使い方

reindex_like()を使うと、DataFrameの行・列を別のDataFrameと同じように並べ替えられる。

以下の2つのDataFrameを例とする。

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [10, 20, 30], 'C': [100, 200, 300]},
                   index=[10, 20, 30])
print(df1)
#     A   B    C
# 10  1  10  100
# 20  2  20  200
# 30  3  30  300

df2 = pd.DataFrame({'A': [1, 1, 1], 'C': [100, 100, 100]},
                   index=[10, 15, 20])
print(df2)
#     A    C
# 10  1  100
# 15  1  100
# 20  1  100

reindex()の引数columns, indexに対象のDataFramecolumns, index属性を指定すると、その通りの順番に並べ替えられたDataFrameが生成される。

print(df1.reindex(columns=df2.columns, index=df2.index))
#       A      C
# 10  1.0  100.0
# 15  NaN    NaN
# 20  2.0  200.0

同様のことがreindex_like()を使うとより簡単に実現できる。

print(df1.reindex_like(df2))
#       A      C
# 10  1.0  100.0
# 15  NaN    NaN
# 20  2.0  200.0

reindex_like()でもindexが単調増加または単調減少であれば引数methodlimitを指定できる。pandasバージョン2.1.4時点では引数fill_valueは実装されていないが、fillna()を使えばよい。

print(df1.reindex_like(df2, method='bfill'))
#     A    C
# 10  1  100
# 15  2  200
# 20  2  200

# print(df1.reindex_like(df2, fill_value=0))
# TypeError: NDFrame.reindex_like() got an unexpected keyword argument 'fill_value'

print(df1.reindex_like(df2).fillna(0))
#       A      C
# 10  1.0  100.0
# 15  0.0    0.0
# 20  2.0  200.0

時系列データに対するreindex(), reindex_like()

公式ドキュメントの例にもあるが、reindex()reindex_like()は時系列データのindexを揃えたい場合に便利。

以下のDataFrameを例とする。pd.date_range()DatetimeIndexを生成している。

df = pd.DataFrame({'A': [1, 3, 5], 'B': [10, 30, 50], 'C': [100, 300, 500]},
                  index=pd.date_range('2023-12-01', '2023-12-05', freq='2D'))
print(df)
#             A   B    C
# 2023-12-01  1  10  100
# 2023-12-03  3  30  300
# 2023-12-05  5  50  500

print(df.index)
# DatetimeIndex(['2023-12-01', '2023-12-03', '2023-12-05'], dtype='datetime64[ns]', freq='2D')

reindex()の例。interpolate()で引数method'time'とすると日時をもとに補間できる。

new_dt_index = pd.date_range('2023-12-01', '2023-12-05', freq='12H')
print(new_dt_index)
# DatetimeIndex(['2023-12-01 00:00:00', '2023-12-01 12:00:00',
#                '2023-12-02 00:00:00', '2023-12-02 12:00:00',
#                '2023-12-03 00:00:00', '2023-12-03 12:00:00',
#                '2023-12-04 00:00:00', '2023-12-04 12:00:00',
#                '2023-12-05 00:00:00'],
#               dtype='datetime64[ns]', freq='12H')

print(df.reindex(index=new_dt_index))
#                        A     B      C
# 2023-12-01 00:00:00  1.0  10.0  100.0
# 2023-12-01 12:00:00  NaN   NaN    NaN
# 2023-12-02 00:00:00  NaN   NaN    NaN
# 2023-12-02 12:00:00  NaN   NaN    NaN
# 2023-12-03 00:00:00  3.0  30.0  300.0
# 2023-12-03 12:00:00  NaN   NaN    NaN
# 2023-12-04 00:00:00  NaN   NaN    NaN
# 2023-12-04 12:00:00  NaN   NaN    NaN
# 2023-12-05 00:00:00  5.0  50.0  500.0

print(df.reindex(index=new_dt_index, method='ffill'))
#                      A   B    C
# 2023-12-01 00:00:00  1  10  100
# 2023-12-01 12:00:00  1  10  100
# 2023-12-02 00:00:00  1  10  100
# 2023-12-02 12:00:00  1  10  100
# 2023-12-03 00:00:00  3  30  300
# 2023-12-03 12:00:00  3  30  300
# 2023-12-04 00:00:00  3  30  300
# 2023-12-04 12:00:00  3  30  300
# 2023-12-05 00:00:00  5  50  500

print(df.reindex(index=new_dt_index).interpolate(method='time'))
#                        A     B      C
# 2023-12-01 00:00:00  1.0  10.0  100.0
# 2023-12-01 12:00:00  1.5  15.0  150.0
# 2023-12-02 00:00:00  2.0  20.0  200.0
# 2023-12-02 12:00:00  2.5  25.0  250.0
# 2023-12-03 00:00:00  3.0  30.0  300.0
# 2023-12-03 12:00:00  3.5  35.0  350.0
# 2023-12-04 00:00:00  4.0  40.0  400.0
# 2023-12-04 12:00:00  4.5  45.0  450.0
# 2023-12-05 00:00:00  5.0  50.0  500.0

新しいインデックスとして文字列のリストなどを指定すると、reindex()が返すDataFrameindexはノーマルのIndexとなり時系列データではなくなるので注意。interpolate(method='time')DatetimeIndexDataFrameでのみ実行可能。

new_index = ['2023-12-01', '2023-12-02', '2023-12-03']

print(df.reindex(index=new_index))
#               A     B      C
# 2023-12-01  1.0  10.0  100.0
# 2023-12-02  NaN   NaN    NaN
# 2023-12-03  3.0  30.0  300.0

print(df.reindex(index=new_index).index)
# Index(['2023-12-01', '2023-12-02', '2023-12-03'], dtype='object')

print(df.reindex(index=new_index, method='bfill'))
#             A   B    C
# 2023-12-01  1  10  100
# 2023-12-02  3  30  300
# 2023-12-03  3  30  300

# print(df.reindex(index=new_index).interpolate(method='time'))
# ValueError: time-weighted interpolation only works on Series or DataFrames with a DatetimeIndex

reindex_like()の例は以下の通り。

df2 = pd.DataFrame({'A': [1, 1, 1, 1, 1], 'C': [100, 100, 100, 100, 100]},
                   index=pd.date_range('2023-12-01', '2023-12-05', freq='D'))
print(df2)
#             A    C
# 2023-12-01  1  100
# 2023-12-02  1  100
# 2023-12-03  1  100
# 2023-12-04  1  100
# 2023-12-05  1  100

print(df.reindex_like(df2))
#               A      C
# 2023-12-01  1.0  100.0
# 2023-12-02  NaN    NaN
# 2023-12-03  3.0  300.0
# 2023-12-04  NaN    NaN
# 2023-12-05  5.0  500.0

print(df.reindex_like(df2).interpolate(method='time'))
#               A      C
# 2023-12-01  1.0  100.0
# 2023-12-02  2.0  200.0
# 2023-12-03  3.0  300.0
# 2023-12-04  4.0  400.0
# 2023-12-05  5.0  500.0

時系列データのリサンプリングについては以下の記事も参照。

関連カテゴリー

関連記事