pandasで行・列の差分・変化率を取得するdiff, pct_change

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

pandas.DataFrame, pandas.Seriesの行または列の差分・変化率を取得するにはdiff(), pct_change()メソッドを使う。例えば一行前のデータとの差分・変化率を取得したりできる。

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

  • diff()の使い方
    • 比較対象との差: 引数periods
    • 行 or 列を指定: 引数axis
    • pandas.Seriesに対するdiff()
  • pct_change()の使い方
    • 比較対象との差: 引数periods
    • 行 or 列を指定: 引数axis
    • pandas.Seriesに対するpct_change()
  • 欠損値NaNの処理
  • 時系列データに対する差分・変化率
    • 時系列データにdiff(), pct_change()をそのまま使う
    • pct_change()の引数freqを指定して変化率取得
    • shift()でずらして差分取得

diff()の使い方

以下の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.diff())
#      a    b     c
# 0  NaN  NaN   NaN
# 1  1.0  3.0   7.0
# 2  1.0  5.0  19.0
# 3  1.0  7.0  37.0
# 4  1.0  9.0  61.0

比較対象との差: 引数periods

比較対象との差(何行前または後の行との差分を取るかなど)は第一引数periodsで指定する。デフォルトは1で1行前。

print(df.diff(2))
#      a     b     c
# 0  NaN   NaN   NaN
# 1  NaN   NaN   NaN
# 2  2.0   8.0  26.0
# 3  2.0  12.0  56.0
# 4  2.0  16.0  98.0

負の値も指定可能。

print(df.diff(-1))
#      a    b     c
# 0 -1.0 -3.0  -7.0
# 1 -1.0 -5.0 -19.0
# 2 -1.0 -7.0 -37.0
# 3 -1.0 -9.0 -61.0
# 4  NaN  NaN   NaN

行 or 列を指定: 引数axis

引数axis=1とすると列ごとの差分が算出される。

print(df.diff(axis=1))
#     a     b      c
# 0 NaN   0.0    0.0
# 1 NaN   2.0    4.0
# 2 NaN   6.0   18.0
# 3 NaN  12.0   48.0
# 4 NaN  20.0  100.0

print(df.diff(-1, axis=1))
#       a      b   c
# 0   0.0    0.0 NaN
# 1  -2.0   -4.0 NaN
# 2  -6.0  -18.0 NaN
# 3 -12.0  -48.0 NaN
# 4 -20.0 -100.0 NaN

pandas.Seriesに対するdiff()

pandas.Seriesに対するdiff()メソッドも同様。引数も同じ。

df['b_diff'] = df['b'].diff(-1)
print(df)
#    a   b    c  b_diff
# 0  1   1    1    -3.0
# 1  2   4    8    -5.0
# 2  3   9   27    -7.0
# 3  4  16   64    -9.0
# 4  5  25  125     NaN

pct_change()の使い方

pct_change()の使い方もdiff()とほぼ同じ。

diff()A - Bで差分を算出するのに対し、pct_change()(A - B) / Bで変化率を算出する。

以下の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.pct_change())
#           a         b         c
# 0       NaN       NaN       NaN
# 1  1.000000  3.000000  7.000000
# 2  0.500000  1.250000  2.375000
# 3  0.333333  0.777778  1.370370
# 4  0.250000  0.562500  0.953125

比較対象との差: 引数periods

比較対象との差(何行前または後の行との変化率を取るかなど)は第一引数periodsで指定する。デフォルトは1で1行前。

print(df.pct_change(2))
#           a         b         c
# 0       NaN       NaN       NaN
# 1       NaN       NaN       NaN
# 2  2.000000  8.000000  26.00000
# 3  1.000000  3.000000   7.00000
# 4  0.666667  1.777778   3.62963

負の値も指定可能。

print(df.pct_change(-1))
#           a         b         c
# 0 -0.500000 -0.750000 -0.875000
# 1 -0.333333 -0.555556 -0.703704
# 2 -0.250000 -0.437500 -0.578125
# 3 -0.200000 -0.360000 -0.488000
# 4       NaN       NaN       NaN

行 or 列を指定: 引数axis

引数axis=1とすると列ごとの変化率が算出される。

print(df.pct_change(axis=1))
#     a    b    c
# 0 NaN  0.0  0.0
# 1 NaN  1.0  1.0
# 2 NaN  2.0  2.0
# 3 NaN  3.0  3.0
# 4 NaN  4.0  4.0

print(df.pct_change(-1, axis=1))
#           a         b   c
# 0  0.000000  0.000000 NaN
# 1 -0.500000 -0.500000 NaN
# 2 -0.666667 -0.666667 NaN
# 3 -0.750000 -0.750000 NaN
# 4 -0.800000 -0.800000 NaN

pandas.Seriesに対するpct_change()

pandas.Seriesに対するpct_change()メソッドも同様。引数も同じ。

df['b_pct_change'] = df['b'].pct_change(-1)
print(df)
#    a   b    c  b_pct_change
# 0  1   1    1     -0.750000
# 1  2   4    8     -0.555556
# 2  3   9   27     -0.437500
# 3  4  16   64     -0.360000
# 4  5  25  125           NaN

欠損値NaNの処理

diff()pct_change()も結果に欠損値NaNが生じる。

欠損値NaNを削除したり穴埋めしたりするにはdropna(), fillna()を使う。

print(df.diff(2).dropna())
#      a     b     c
# 2  2.0   8.0  26.0
# 3  2.0  12.0  56.0
# 4  2.0  16.0  98.0

print(df.diff(2).fillna(0))
#      a     b     c
# 0  0.0   0.0   0.0
# 1  0.0   0.0   0.0
# 2  2.0   8.0  26.0
# 3  2.0  12.0  56.0
# 4  2.0  16.0  98.0

print(df.diff(2).fillna(method='bfill'))
#      a     b     c
# 0  2.0   8.0  26.0
# 1  2.0   8.0  26.0
# 2  2.0   8.0  26.0
# 3  2.0  12.0  56.0
# 4  2.0  16.0  98.0

pct_change()でも同じ。

print(df.pct_change(2).dropna())
#           a         b         c
# 2  2.000000  8.000000  26.00000
# 3  1.000000  3.000000   7.00000
# 4  0.666667  1.777778   3.62963

print(df.pct_change(2).fillna(0))
#           a         b         c
# 0  0.000000  0.000000   0.00000
# 1  0.000000  0.000000   0.00000
# 2  2.000000  8.000000  26.00000
# 3  1.000000  3.000000   7.00000
# 4  0.666667  1.777778   3.62963

print(df.pct_change(2).fillna(method='bfill'))
#           a         b         c
# 0  2.000000  8.000000  26.00000
# 1  2.000000  8.000000  26.00000
# 2  2.000000  8.000000  26.00000
# 3  1.000000  3.000000   7.00000
# 4  0.666667  1.777778   3.62963

dropna(), fillna()についての詳細は以下の記事を参照。

時系列データに対する差分・変化率

時系列データに対してもdiff(), pct_change()をそのまま使えるが、指定した日時ぶん離れたデータとの差分・変化率を取得することも可能。

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

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

diff(), pct_change()をそのまま使う

まずはdiff(), pct_change()を通常のデータと同じように使う例。行単位での差分・変化率を取得できる。

diff()

デフォルトでは一つ上の行との差分が取得できる。第一引数periodsを指定すると比較する行との差を指定可能。

print(df.diff())
#             value
# 2018-01-01    NaN
# 2018-01-03    2.0
# 2018-01-05    2.0
# 2018-01-07    2.0
# 2018-01-09    2.0
# 2018-01-11    2.0
# 2018-01-13    2.0
# 2018-01-15    2.0

print(df.diff(2))
#             value
# 2018-01-01    NaN
# 2018-01-03    NaN
# 2018-01-05    4.0
# 2018-01-07    4.0
# 2018-01-09    4.0
# 2018-01-11    4.0
# 2018-01-13    4.0
# 2018-01-15    4.0

pct_change()

pct_change()diff()と同様。

print(df.pct_change())
#                value
# 2018-01-01       NaN
# 2018-01-03  2.000000
# 2018-01-05  0.666667
# 2018-01-07  0.400000
# 2018-01-09  0.285714
# 2018-01-11  0.222222
# 2018-01-13  0.181818
# 2018-01-15  0.153846

print(df.pct_change(2))
#                value
# 2018-01-01       NaN
# 2018-01-03       NaN
# 2018-01-05  4.000000
# 2018-01-07  1.333333
# 2018-01-09  0.800000
# 2018-01-11  0.571429
# 2018-01-13  0.444444
# 2018-01-15  0.363636

pct_change()の引数freqを指定して変化率取得

pct_change()には引数freqを指定できる。D(日)、H(時)などの頻度コードを指定すると、インデックスを任意の日時ぶんずらすことができる。

以下の例では2行前のデータではなく2日前のデータとの変化率となる。

print(df.pct_change(freq='2D'))
#                value
# 2018-01-01       NaN
# 2018-01-03  2.000000
# 2018-01-05  0.666667
# 2018-01-07  0.400000
# 2018-01-09  0.285714
# 2018-01-11  0.222222
# 2018-01-13  0.181818
# 2018-01-15  0.153846

元データに無い日時のデータを指定すると欠損値NaNになる。

print(df.pct_change(freq='D'))
#             value
# 2018-01-01    NaN
# 2018-01-03    NaN
# 2018-01-05    NaN
# 2018-01-07    NaN
# 2018-01-09    NaN
# 2018-01-11    NaN
# 2018-01-13    NaN
# 2018-01-15    NaN

shift()でずらして差分取得

diff()には引数freqがないので、代わりにshift()を使う。

shift()の引数freqD(日)、H(時)などの頻度コードを指定すると、インデックスを任意の日時ぶんずらすことができる。

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

これを元データから引くと、指定した日時ぶん離れたデータとの差分を取得可能。以下の例では2行前のデータではなく2日前のデータとの差分となる。

print(df - df.shift(freq='2D'))
#             value
# 2018-01-01    NaN
# 2018-01-03    2.0
# 2018-01-05    2.0
# 2018-01-07    2.0
# 2018-01-09    2.0
# 2018-01-11    2.0
# 2018-01-13    2.0
# 2018-01-15    2.0
# 2018-01-17    NaN

元データに無い日時のデータを引くと欠損値NaNになる。以下の例のように隔日のデータしかないのに1日前のデータとの差分を取ろうとするとすべてNaNになる。

print(df - df.shift(freq='D'))
#             value
# 2018-01-01    NaN
# 2018-01-02    NaN
# 2018-01-03    NaN
# 2018-01-04    NaN
# 2018-01-05    NaN
# 2018-01-06    NaN
# 2018-01-07    NaN
# 2018-01-08    NaN
# 2018-01-09    NaN
# 2018-01-10    NaN
# 2018-01-11    NaN
# 2018-01-12    NaN
# 2018-01-13    NaN
# 2018-01-14    NaN
# 2018-01-15    NaN
# 2018-01-16    NaN

関連カテゴリー

関連記事