pandas shift()の使い方|前の行・次の行の値を取得して比較する方法

「今日の売上を、前日の売上と比べたい」「前の行の値を別の列に入れたい」と思っても、行をずらす方向や、先頭にNaNが出る理由が分かりにくいことがあります。

前の行の値を取得する基本形はdf['売上'].shift(1)です。 shift(1)で1行前、shift(2)で2行前、shift(-1)で次の行の値を取得できます。

ただし、shift()が参照するのは「前日」ではなく、現在の並び順における前の行です。日付データでは、先に日付型へ変換して昇順に並べることが重要です。

この記事でわかること

  • shift()で1行前・2行前・次の行の値を取得する方法
  • periodsの正負と、値が移動する方向
  • 元の売上と前日売上を比較し、差額や増減率を計算する方法
  • 先頭や末尾にNaNが出る理由
  • shift()diff()pct_change()rolling()の使い分け
  • コードは動いても結果を誤解しやすい「並び順」の注意点

shift()は、DataFrameの確認・日付変換・並び替えを終えた後に、時系列データの比較元を作る処理です。差額や増減率を求める前の基礎として位置づけられます。

先に結論:目的に応じて4つの機能を使い分ける

比較元となる値を列に残して確認したい場合はshift()を選びます。求めたい結果が最初から決まっている場合は、専用のメソッドを使うと簡潔です。

機能名 何を求めるか 選ぶ場面 注意点
shift() 前後の行の値 比較元の値を列に残したい 行の並び順が基準になる
diff() 前の行との差額 増減した量だけを求めたい 最初の比較不能行はNaNになる
pct_change() 前の行からの増減率 前日比・前月比を求めたい 0.1は10%を表す
rolling() 一定範囲の集計値 移動平均などで変動をならしたい window分の行をまとめて計算する

この記事では、比較元を作るshift()に焦点を絞ります。差額・増減率・移動平均の詳しい使い方は、それぞれの関連記事で確認できます。

日別売上のサンプルデータを準備する

日付と売上を持つDataFrameを作ります。自分のデータへ置き換えるときも、最初に日付の型と並び順を確認してください。

import pandas as pd

df = pd.DataFrame({
    "日付": ["2026-08-01", "2026-08-02", "2026-08-03", "2026-08-04", "2026-08-05"],
    "売上": [100, 120, 90, 135, 150]
})

df["日付"] = pd.to_datetime(df["日付"])
df = df.sort_values("日付").reset_index(drop=True)
df

実行結果

日付 売上
0 2026-08-01 100
1 2026-08-02 120
2 2026-08-03 90
3 2026-08-04 135
4 2026-08-05 150

日付は2026年8月1日から5日までの昇順です。8月2日の1行前は8月1日なので、このサンプルでは「前の行の売上」を「前日売上」として扱えます。

ただし、日付が連続していないデータでは、取得できるのは前日ではなく「前の観測日の値」です。 自分のデータでは、並び順だけでなく日付の欠けも確認してください。

日付変換は「pandas to_datetime()の使い方」、並び替えは「pandas 並び替え(sort)入門」で詳しく解説しています。

shift(1)で1行前の値を取得する

Series.shift(1)を使い、売上列の1行前の値を「前日売上」列へ追加します。shift()periodsは省略すると1なので、shift()shift(1)は同じ結果です。

df["前日売上"] = df["売上"].shift(1)
df

処理前後を比べると、売上の値が1行下へずれています。

日付 売上(処理前) 前日売上(shift(1)
2026-08-01 100 NaN
2026-08-02 120 100
2026-08-03 90 120
2026-08-04 135 90
2026-08-05 150 135

注目するのは8月2日以降の「前日売上」列です。8月2日には8月1日の100、8月3日には8月2日の120が入ります。

8月1日の前にはデータがないため、比較元が存在せずNaNになります。元のDataFrameから行が削除されたわけではなく、shift()がずらした結果を新しい列として返しています。

periodsで2行前・次の行の値を取得する

periodsには、ずらす行数を指定します。正の値では過去側の行、負の値では未来側の行を各行から参照できます。

指定 各行に入る値 値の見かけ上の移動 比較元がない場所
shift(1) 1行前の値 1行下へ移動 先頭1行
shift(2) 2行前の値 2行下へ移動 先頭2行
shift(-1) 次の行の値 1行上へ移動 末尾1行

「正なら上、負なら下」と暗記するより、現在の行から見て、どの行の値が入るかで考えると混乱しにくくなります。

df["2日前売上"] = df["売上"].shift(2)
df["翌日売上"] = df["売上"].shift(-1)

df[["日付", "売上", "前日売上", "2日前売上", "翌日売上"]]
日付 売上 前日売上 2日前売上 翌日売上
2026-08-01 100 NaN NaN 120
2026-08-02 120 100 NaN 90
2026-08-03 90 120 100 135
2026-08-04 135 90 120 150
2026-08-05 150 135 90 NaN

8月3日の行を見ると、2日前の売上は8月1日の100、翌日の売上は8月4日の135です。shift(-1)では末尾より後の行が存在しないため、最後の「翌日売上」がNaNになります。

shift(1)は1行前、shift(-1)は次の行です。時系列分析では、過去の値を表す列をラグ(lag)、次の値を表す考え方を**リード(lead)**と呼ぶことがあります。

前日売上と比較して差額・増減率を計算する

比較元の「前日売上」列を残しておくと、計算過程を確認しながら差額や増減率を求められます。

  • 差額:当日売上 - 前日売上
  • 増減率:(当日売上 - 前日売上) / 前日売上
df["前日差"] = df["売上"] - df["前日売上"]
df["前日比"] = (df["売上"] - df["前日売上"]) / df["前日売上"]

df[["日付", "売上", "前日売上", "前日差", "前日比"]]
日付 売上 前日売上 前日差 前日比
2026-08-01 100 NaN NaN NaN
2026-08-02 120 100 20 0.20
2026-08-03 90 120 -30 -0.25
2026-08-04 135 90 45 0.50
2026-08-05 150 135 15 約0.1111

8月3日は売上90、前日売上120なので、前日差は-30、前日比は-0.25です。つまり、前日より30減り、25%減少したと読めます。

前日比の0.20は0.20%ではなく**20%**です。表示をパーセントに変える場合も、元の値の意味は変わりません。

result = df[["日付", "売上", "前日売上", "前日差", "前日比"]].copy()
result["前日比(表示用)"] = result["前日比"].map(
    lambda x: f"{x:.1%}" if pd.notna(x) else "—"
)
result

実行結果

日付 売上 前日売上 前日差 前日比 前日比(表示用)
0 2026-08-01 100 NaN NaN NaN
1 2026-08-02 120 100.0 20.0 0.200000 20.0%
2 2026-08-03 90 120.0 -30.0 -0.250000 -25.0%
3 2026-08-04 135 90.0 45.0 0.500000 50.0%
4 2026-08-05 150 135.0 15.0 0.111111 11.1%

表示用の列では、0.2020.0%-0.25-25.0%になります。計算に使う列は数値のまま残し、文字列へ変えた表示用の列とは分けておくと安全です。

差額だけが必要なら「pandas diff()の使い方」、増減率だけが必要なら「pandas pct_change()の使い方」を使うと、同じ計算をより簡潔に書けます。shift()を使う利点は、比較元の値を見ながら結果を検算できることです。

shift()・diff()・pct_change()の結果を同じデータで比較する

条件をそろえて、3つの方法の役割を確認します。shift()は前の値、diff()は差額、pct_change()は増減率を返します。

comparison = df[["日付", "売上"]].copy()
comparison["shift_前の値"] = comparison["売上"].shift(1)
comparison["diff_差額"] = comparison["売上"].diff()
comparison["pct_change_増減率"] = comparison["売上"].pct_change(fill_method=None)
comparison

実行結果

日付 売上 shift_前の値 diff_差額 pct_change_増減率
0 2026-08-01 100 NaN NaN NaN
1 2026-08-02 120 100.0 20.0 0.200000
2 2026-08-03 90 120.0 -30.0 -0.250000
3 2026-08-04 135 90.0 45.0 0.500000
4 2026-08-05 150 135.0 15.0 0.111111

8月4日の行では、shift()が比較元の90diff()が差額の45pct_change()が増減率の0.50を返します。どれが優れているという関係ではなく、何を結果として残したいかで選びます。

複数日の値をまとめて移動平均を求めたい場合は、前後の1行を参照するshift()ではなく「pandas rolling()の使い方」が適しています。

コードは動くが結果を誤解しやすい例:日付が並んでいない

shift()は、日付の大小ではなく現在の行順を基準にします。次のDataFrameは日付が不規則ですが、コード自体はエラーになりません。

df_unsorted = pd.DataFrame({
    "日付": pd.to_datetime(["2026-08-03", "2026-08-01", "2026-08-02"]),
    "売上": [90, 100, 120]
})

df_unsorted["前の行の売上"] = df_unsorted["売上"].shift(1)
df_unsorted
日付 売上 前の行の売上
2026-08-03 90 NaN
2026-08-01 100 90
2026-08-02 120 100

症状・原因・確認方法・修正方法

確認項目 内容
症状 8月1日の「前の行」に8月3日の売上90が入る
原因 shift()の実行前に日付順へ並べていない
確認方法 日付列と行順を表示し、昇順になっているか確認する
修正方法 sort_values('日付')を実行してからshift(1)を使う

この結果の90を「前日売上」と呼ぶのは誤りです。8月1日の1つ前に並んでいる行が8月3日だから入った値であり、暦上の前日を表していません。

df_sorted = df_unsorted[["日付", "売上"]].sort_values("日付").reset_index(drop=True)
df_sorted["前日売上"] = df_sorted["売上"].shift(1)
df_sorted

実行結果

日付 売上 前日売上
0 2026-08-01 100 NaN
1 2026-08-02 120 100.0
2 2026-08-03 90 120.0

修正後は、8月2日の前日売上に8月1日の100が入ります。日付が欠けている場合は前日ではなく前の観測行になるため、基本例で確認したとおり日付の連続性にも注意してください。

NaNは無条件に0で埋めない

shift(1)の先頭やshift(-1)の末尾に出るNaNは、比較元の行が存在しないことを表します。これは処理の失敗ではありません。

先頭の前日売上を0で埋めると、「前日の売上は0だった」という別の意味に変わります。その事実を確認できない場合は、NaNのまま残すか、比較計算から除外するのが安全です。

fill_valueを使う場合も値の意味を確認する

shift(1, fill_value=0)のように指定すると、移動によって空いた場所を0で埋められます。ただし、これは実際の前日売上を取得したわけではありません。0に分析上の根拠がある場合だけ使い、単にNaNを消す目的では使わないようにしましょう。

欠損値を何で埋めるかは分析目的によって異なります。「pandas fillna()の使い方」で選択基準を確認してください。

「値をずらす」と「日付をずらす」は同じではない

この記事のdf['売上'].shift(1)は、売上の値を1行ずらし、比較元を作る処理です。日付列の値自体は変わりません。

一方、日付インデックスを持つデータでは、freqを指定してインデックスを時間方向へ移動する使い方もあります。これは行の値を前後比較する今回の目的とは挙動が異なります。初心者のうちは、まずfreqを指定しない基本形で「行をずらす」考え方を身につければ十分です。

データ分析の流れでshift()を使う位置

日別データで前日値を比較するときは、次の順番で進めます。

  1. DataFrameを読み込み、列名・値・データ型を確認する
  2. 日付列をto_datetime()で日付型へ変換する
  3. sort_values()で日付の昇順へ並べる
  4. shift()で比較元となる前の行の値を作る
  5. 目的に応じて差額・増減率を計算する
  6. 表やグラフで変化を確認する

DataFrameの基本は「Pandas DataFrame入門」、列を追加する方法全般は「pandasで新しい列を追加する方法」で確認できます。

自分のデータへ置き換えるときは、日付型・並び順・日付の欠け・比較元が存在しない行の4点を確認してください。

まとめ

  • 前の行の値はshift(1)、2行前はshift(2)、次の行はshift(-1)で取得する
  • shift(1)では値が1行下へずれ、各行に1行前の値が入る
  • 先頭や末尾のNaNは比較元が存在しないことを示すため、根拠なく0で埋めない
  • shift()は前後の値、diff()は差額、pct_change()は増減率、rolling()は一定範囲の集計値を求める
  • shift()が参照するのは前日ではなく、現在の並び順における前の行である
  • 日付データでは、日付型への変換と昇順への並び替えを済ませてから実行する

比較元の値を残して計算過程を確かめたいときにshift()を使えば、前の行との比較を安全に組み立てられます。

関連記事

▲ ページトップへ戻る

shift(1)とshift(-1)の違いは何ですか?

shift(1)は各行に1行前の値を入れ、先頭がNaNになります。shift(-1)は各行に次の行の値を入れ、末尾がNaNになります。

先頭にNaNが出るのはなぜですか?

shift(1)を実行した先頭行には、比較元となる1行前のデータがないためです。エラーではなく、比較不能であることを示す自然な結果です。

shift()とdiff()の違いは何ですか?

shift()は前の行の値そのものを返し、diff()は現在値と前の行との差額を返します。比較元を確認したいならshift()、差額だけが必要ならdiff()を選びます。

shift()は必ず前日の値を取得しますか?

いいえ。取得するのは現在の並び順における前の行です。前日値として扱うには、日付順に並んでいることに加え、必要に応じて日付の欠けも確認します。

shift()で生じたNaNを0で埋めてもよいですか?

前の行の値が実際に0だったと判断できる場合に限ります。fill_value=0でも同じで、比較元が存在しないという意味を消すため、根拠なく指定しないでください。

Series.shift()とDataFrame.shift()の違いは何ですか?

Seriesに使うと1列の値、DataFrameに使うと対象DataFrame全体がずれます。この記事では比較対象を明確にするため、df['売上'].shift()というSeriesの形を使っています。

日付インデックスを1日ずらす処理と同じですか?

同じではありません。この記事ではfreqを指定せず、値を行単位でずらしています。freqを指定する使い方は日付インデックスを時間方向へ移動するため、別の目的として区別してください。

コメント

タイトルとURLをコピーしました