note.nkmk.me

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

Posted: 2020-06-06 / Tags: Python, pandas, 時系列データ

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

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

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

ここでは以下の内容について説明する。

  • reindex()の基本的な使い方
    • 行・列の順番の指定: 引数index, columns
  • 新たな行名・列名を指定する場合
    • 任意の値で穴埋め: 引数fill_value
    • 前後の要素の値で穴埋め: 引数method
    • その他の穴埋め・補間方法
  • reindex_like()の使い方
  • 時系列データの例

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

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

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

スポンサーリンク

reindex()の基本的な使い方

以下のpandas.DataFrameを例とする。

import pandas as pd

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にそれぞれ行名・列名を任意の順番に並べたリストを指定すると、その通りに並べ替えられた新たなpandas.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

新たな行名・列名を指定する場合

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

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

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

以下のpandas.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

'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で指定した順番とは関係なく、元のpandas.DataFrameindexをもとに値が選択されるので注意。

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

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

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

欠損値NaNの処理にはfillna()interpolate()を使う方法もある。

print(df.reindex(index=[5, 10, 15, 20, 25]).fillna(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]).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()methodでは列を追加した場合には左右の要素からの穴埋めはできないが、fillna(), 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']).fillna(method='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='ffill'))
# ValueError: index must be monotonic increasing or decreasing

fillna(), 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]).fillna(method='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

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

reindex_like()の使い方

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

以下の2つのpandas.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に対象のpandas.DataFramecolumns, index属性を指定すると、その通りの順番に並べ替えられたpandas.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バージョン1.0.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: 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()は時系列データのindexを揃えたい場合に便利。

行を並べ替えるというよりも、データ点を揃えるイメージ。

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

new_index = pd.date_range('2020-06-01', '2020-06-05', freq='1D')
print(new_index)
# DatetimeIndex(['2020-06-01', '2020-06-02', '2020-06-03', '2020-06-04',
#                '2020-06-05'],
#               dtype='datetime64[ns]', freq='D')

print(df.reindex(index=new_index))
#               A     B      C
# 2020-06-01  1.0  10.0  100.0
# 2020-06-02  NaN   NaN    NaN
# 2020-06-03  3.0  30.0  300.0
# 2020-06-04  NaN   NaN    NaN
# 2020-06-05  5.0  50.0  500.0

引数methodで前後の値で穴埋めしたり、interpolate()で引数method='time'として日時をもとに線形補間したりできる。

print(df.reindex(index=new_index, method='bfill'))
#             A   B    C
# 2020-06-01  1  10  100
# 2020-06-02  3  30  300
# 2020-06-03  3  30  300
# 2020-06-04  5  50  500
# 2020-06-05  5  50  500

print(df.reindex(index=new_index).interpolate(method='time'))
#               A     B      C
# 2020-06-01  1.0  10.0  100.0
# 2020-06-02  2.0  20.0  200.0
# 2020-06-03  3.0  30.0  300.0
# 2020-06-04  4.0  40.0  400.0
# 2020-06-05  5.0  50.0  500.0

なお、元のpandas.DataFrameindexpd.date_range()などで生成したDatetimeIndexである場合、reindex()の引数indexに指定するのもDatetimeIndexである必要がある。

文字列のリストではダメ。print()での表示は同じだが、元のpandas.DataFrameindexと一致しないので新たな行とみなされてしまう。要注意。

print(df.reindex(index=['2020-06-01', '2020-06-02', '2020-06-03']))
#              A   B   C
# 2020-06-01 NaN NaN NaN
# 2020-06-02 NaN NaN NaN
# 2020-06-03 NaN NaN NaN

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

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

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

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

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

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事