pandasでデータを行・列(縦・横)方向にずらすshift

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

pandas.DataFrame, pandas.Seriesのデータを行または列方向にずらすにはshift()を使う。

行または列の差分や変化率を取得するにはdiff()pct_change()が使えるのでshift()の出番はあまりないが、時系列データの差分を取る際にshift()だと期間を指定できるので便利。

diff(), pct_change()については以下の記事を参照。

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

  • shift()の使い方
    • ずらし幅を指定: 引数periods
    • ずらす方向を指定: 引数axis
  • 時系列データに対するshift()
    • ずらす期間を指定: 引数freq

shift()の使い方

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

import pandas as pd

df = pd.DataFrame({'a': range(1, 6),
                   'b': [x**2 for x in range(1, 6)],
                   'c': [x**3 for x in range(1, 6)]})

print(df)
#    a   b    c
# 0  1   1    1
# 1  2   4    8
# 2  3   9   27
# 3  4  16   64
# 4  5  25  125

デフォルトでは下方向に1行ずれる。行数はそのままなので、最後の行のデータは削除される。

print(df.shift())
#      a     b     c
# 0  NaN   NaN   NaN
# 1  1.0   1.0   1.0
# 2  2.0   4.0   8.0
# 3  3.0   9.0  27.0
# 4  4.0  16.0  64.0

欠損値NaNの処理については以下の記事を参照。

なお、NaNは浮動小数点数float型なので、NaNが存在する列のデータ型dtypefloatになる。

ずらし幅を指定: 引数periods

第一引数periodsにずらし幅を指定する。デフォルトはperiods=1

print(df.shift(2))
#      a    b     c
# 0  NaN  NaN   NaN
# 1  NaN  NaN   NaN
# 2  1.0  1.0   1.0
# 3  2.0  4.0   8.0
# 4  3.0  9.0  27.0

負の値もOK。上方向にずらされる。

print(df.shift(-1))
#      a     b      c
# 0  2.0   4.0    8.0
# 1  3.0   9.0   27.0
# 2  4.0  16.0   64.0
# 3  5.0  25.0  125.0
# 4  NaN   NaN    NaN

ずらす方向を指定: 引数axis

デフォルトでは縦方向にずらされる。引数axis=1とすると横方向にずらされる。

print(df.shift(axis=1))
#     a    b     c
# 0 NaN  1.0   1.0
# 1 NaN  2.0   4.0
# 2 NaN  3.0   9.0
# 3 NaN  4.0  16.0
# 4 NaN  5.0  25.0

print(df.shift(-1, axis=1))
#       a      b   c
# 0   1.0    1.0 NaN
# 1   4.0    8.0 NaN
# 2   9.0   27.0 NaN
# 3  16.0   64.0 NaN
# 4  25.0  125.0 NaN

時系列データに対するshift()

以下の時系列データを例とする。

import pandas as pd

df = pd.DataFrame({'value': range(1, 16, 2)},
                  index=pd.date_range('2018-01-01', '2018-01-15', freq='2D'))

print(df)
#             value
# 2018-01-01      1
# 2018-01-03      3
# 2018-01-05      5
# 2018-01-07      7
# 2018-01-09      9
# 2018-01-11     11
# 2018-01-13     13
# 2018-01-15     15

これまでの例と同様に第一引数periodsに数値を指定して行単位でずらすことが可能。

print(df.shift())
#             value
# 2018-01-01    NaN
# 2018-01-03    1.0
# 2018-01-05    3.0
# 2018-01-07    5.0
# 2018-01-09    7.0
# 2018-01-11    9.0
# 2018-01-13   11.0
# 2018-01-15   13.0

ずらす期間を指定: 引数freq

時系列データに対しては、引数freqでずらす期間を指定できる。

引数freqにはD(日)、H(時)などの頻度コードを指定する。頻度コードについては以下の記事を参照。

引数freqを指定した場合、データはそのままでインデックス列の値が変わる。

print(df.shift(freq='D'))
#             value
# 2018-01-02      1
# 2018-01-04      3
# 2018-01-06      5
# 2018-01-08      7
# 2018-01-10      9
# 2018-01-12     11
# 2018-01-14     13
# 2018-01-16     15

例えば3日分ずらしたい場合は、freq='3D'としてもいいし、periods=3, freq='D'としてもOK。

print(df.shift(freq='3D'))
#             value
# 2018-01-04      1
# 2018-01-06      3
# 2018-01-08      5
# 2018-01-10      7
# 2018-01-12      9
# 2018-01-14     11
# 2018-01-16     13
# 2018-01-18     15

print(df.shift(3, freq='D'))
#             value
# 2018-01-04      1
# 2018-01-06      3
# 2018-01-08      5
# 2018-01-10      7
# 2018-01-12      9
# 2018-01-14     11
# 2018-01-16     13
# 2018-01-18     15

注意点

WMは注意が必要。Wの場合は次の日曜日、Mの場合は次の月末にインデックスの値が変わる。1週間分あるいは1ヶ月分ずれるわけではない。

print(df.shift(freq='W'))
#             value
# 2018-01-07      1
# 2018-01-07      3
# 2018-01-07      5
# 2018-01-14      7
# 2018-01-14      9
# 2018-01-14     11
# 2018-01-14     13
# 2018-01-21     15

print(df.shift(freq='M'))
#             value
# 2018-01-31      1
# 2018-01-31      3
# 2018-01-31      5
# 2018-01-31      7
# 2018-01-31      9
# 2018-01-31     11
# 2018-01-31     13
# 2018-01-31     15

1週間分あるいは1ヶ月分ずらしたい場合は'7D''31D'などを指定する。

print(df.shift(freq='7D'))
#             value
# 2018-01-08      1
# 2018-01-10      3
# 2018-01-12      5
# 2018-01-14      7
# 2018-01-16      9
# 2018-01-18     11
# 2018-01-20     13
# 2018-01-22     15

print(df.shift(freq='31D'))
#             value
# 2018-02-01      1
# 2018-02-03      3
# 2018-02-05      5
# 2018-02-07      7
# 2018-02-09      9
# 2018-02-11     11
# 2018-02-13     13
# 2018-02-15     15

なお、元のデータが月末の日付である場合はfreq='M'としても問題ない。

df_m = pd.DataFrame({'value': range(1, 13)},
                    index=pd.date_range('2018-01-01', '2018-12-31', freq='M'))

print(df_m)
#             value
# 2018-01-31      1
# 2018-02-28      2
# 2018-03-31      3
# 2018-04-30      4
# 2018-05-31      5
# 2018-06-30      6
# 2018-07-31      7
# 2018-08-31      8
# 2018-09-30      9
# 2018-10-31     10
# 2018-11-30     11
# 2018-12-31     12

print(df_m.shift(freq='M'))
#             value
# 2018-02-28      1
# 2018-03-31      2
# 2018-04-30      3
# 2018-05-31      4
# 2018-06-30      5
# 2018-07-31      6
# 2018-08-31      7
# 2018-09-30      8
# 2018-10-31      9
# 2018-11-30     10
# 2018-12-31     11
# 2019-01-31     12

関連カテゴリー

関連記事