pandas.DataFrameの行・列を任意の順に並べ替えるreindex
pandasでDataFrameの行・列を任意の順番に並べ替えるにはreindex()を使う。Seriesにも同様のメソッドがある。ラベル(行名・列名)のリストを指定する。
- pandas.DataFrame.reindex — pandas 2.1.4 documentation
- pandas.Series.reindex — pandas 2.1.4 documentation
別のDataFrameの行・列と同じように並べるreindex_like()というメソッドもある。
- pandas.DataFrame.reindex_like — pandas 2.1.4 documentation
- pandas.Series.reindex_like — pandas 2.1.4 documentation
いずれのメソッドでも、既存の行・列を並べ替えるだけでなく、新たな行・列を追加することが可能。
以下のサンプルコードでは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[列名のリスト]とすることでも実現可能。
- 関連記事: pandasのインデックス指定で行・列を抽出
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
元のDataFrameのindexが単調増加または単調減少である場合は、引数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
引数indexやcolumnで指定した順番とは関係なく、元のDataFrameのindexをもとに値が選択されるので注意。
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となるため、上の例のように最終的に全ての値が整数値となってもdtypeはfloatのまま。要注意。
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に対象のDataFrameのcolumns, 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が単調増加または単調減少であれば引数methodやlimitを指定できる。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()が返すDataFrameのindexはノーマルのIndexとなり時系列データではなくなるので注意。interpolate(method='time')はDatetimeIndexのDataFrameでのみ実行可能。
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
時系列データのリサンプリングについては以下の記事も参照。