「今日の売上を、前日の売上と比べたい」「前の行の値を別の列に入れたい」と思っても、行をずらす方向や、先頭に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.20が20.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()が比較元の90、diff()が差額の45、pct_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()を使う位置
日別データで前日値を比較するときは、次の順番で進めます。
- DataFrameを読み込み、列名・値・データ型を確認する
- 日付列を
to_datetime()で日付型へ変換する sort_values()で日付の昇順へ並べるshift()で比較元となる前の行の値を作る- 目的に応じて差額・増減率を計算する
- 表やグラフで変化を確認する
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を指定する使い方は日付インデックスを時間方向へ移動するため、別の目的として区別してください。
コメント