日別売上やアクセス数を折れ線グラフにしても、値が大きく上下していると「増えているのか、単にばらついているだけなのか」が分かりにくいことがあります。
このようなときに使えるのが、pandasのrolling()です。直近の複数行を少しずつずらしながら平均を計算し、短期的な変動をならして傾向を見やすくできます。
先に結論を示すと、rolling(window=3).mean()は、原則として現在行を含む直近3行の平均を求めます。データが1日1行で日付も連続していれば、「3日移動平均」として読めます。ただし、window=3自体が自動的に3日間を意味するわけではありません。
また、結果の先頭に出るNaNはエラーではありません。3行分のデータがまだそろっていないためです。
最初に覚える手順は、「日付順に並べる → rolling()で計算する → 元の値と比べる」の3つだけです。
この記事では、Google Colabで実行できる日別売上データを使い、3日・7日移動平均、min_periods、resample()との違い、折れ線グラフでの確認までを順番に解説します。
- この記事でわかること
- rolling()は直近の複数行をずらしながら計算する
- rolling()・resample()・通常のmean()の違い
- 日別売上のサンプルデータを用意する
- rolling()の前に日付型と並び順を確認する
- rolling(window=3).mean()で3日移動平均を計算する
- 先頭2行がNaNになる理由
- center=Trueは結果を中央の行に置く
- 3日移動平均と7日移動平均を使い分ける
- rolling().mean()とrolling().sum()の違い
- 元の売上と移動平均を折れ線グラフで比較する
- rolling(3)とrolling(“3D”)は対象範囲が違う
- rolling()でよくあるミスと注意点
- データ分析の流れでrolling()を使う位置
- まとめ
- 次に読みたい関連記事
この記事でわかること
rolling()を使う場面と基本的な書き方rolling(window=3).mean()のwindowが表す範囲- 3日移動平均と7日移動平均の計算方法
- 先頭に
NaNが出る理由とmin_periodsの役割 rolling()・resample()・通常のmean()の違いcenter=Trueで結果を置く位置を変える方法- 行数指定と
rolling("3D")による期間指定の違い - 元の値と移動平均を折れ線グラフで比較する方法
この記事のゴールは、目的に合ったwindowを選び、移動平均列を作って、元の値と比較しながら傾向を説明できるようになることです。
rolling()は直近の複数行をずらしながら計算する
rolling()の「rolling window」は、日本語では「移動窓」と考えると分かりやすいでしょう。
たとえばwindow=3なら、対象範囲を次のように1行ずつ移動させます。
1. 1〜3行目を使って計算する 2. 2〜4行目を使って計算する 3. 3〜5行目を使って計算する
平均を求める場合の基本形は次のとおりです。
df["売上"].rolling(window=3).mean()
列を1つ選ぶとSeriesになるため、これはSeriesにrolling()を適用して平均を求める書き方です。複数の数値列を選んだDataFrameにもrolling()を使えますが、この記事では1列に絞って仕組みを確認します。
rolling()・resample()・通常のmean()の違い
似て見える処理でも目的が異なります。最初に次の基準で選びましょう。
| 方法 | 目的 | 計算対象 | 行数の変化 | 使用場面 | 代表的な書き方 |
|---|---|---|---|---|---|
rolling() |
直近の値をならす・合計する | 現在行を含む直近n行 | 原則として元と同じ | 3日移動平均、直近7件の平均 | s.rolling(3).mean() |
resample() |
時間単位を変えて再集計する | 日・週・月などの期間 | 通常は変わる | 日別データを月別合計にする | df.resample("MS").sum() |
通常のmean() |
列全体の平均を1つ求める | 列全体 | 1つの値になる | 全期間の平均売上を知る | s.mean() |
迷ったときは、出力後も1日ごとの行を残したいかで判断します。 1日ごとの行を残して傾向をならすならrolling()、月別・週別などに行をまとめるならresample()です。列全体を代表する平均値が1つだけ必要なら、通常のmean()を使います。
月別・週別集計は、pandas resample()の使い方で詳しく解説しています。
日別売上のサンプルデータを用意する
Google Colabで次のコードを実行してください。2026年8月1日から14日まで、1日1行で日付が連続するデータです。
売上は日ごとに上下しますが、全体として少しずつ増える値にしています。このデータなら、移動平均によって短期的な上下がならされる様子を確認できます。
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({
"日付": [
"2026-08-01", "2026-08-02", "2026-08-03", "2026-08-04",
"2026-08-05", "2026-08-06", "2026-08-07", "2026-08-08",
"2026-08-09", "2026-08-10", "2026-08-11", "2026-08-12",
"2026-08-13", "2026-08-14"
],
"売上": [100, 160, 120, 190, 150, 220, 180, 250, 210, 280, 230, 310, 270, 340]
})
df
| 日付 | 売上 | |
|---|---|---|
| 0 | 2026-08-01 | 100 |
| 1 | 2026-08-02 | 160 |
| 2 | 2026-08-03 | 120 |
| 3 | 2026-08-04 | 190 |
| 4 | 2026-08-05 | 150 |
| 5 | 2026-08-06 | 220 |
| 6 | 2026-08-07 | 180 |
| 7 | 2026-08-08 | 250 |
| 8 | 2026-08-09 | 210 |
| 9 | 2026-08-10 | 280 |
| 10 | 2026-08-11 | 230 |
| 11 | 2026-08-12 | 310 |
| 12 | 2026-08-13 | 270 |
| 13 | 2026-08-14 | 340 |
自分のCSVをGoogle Colabへ読み込む手順は、Google ColabでCSVを読み込む方法を参照してください。DataFrame自体の基本操作を確認したい場合は、Pandas DataFrame入門が役立ちます。
rolling()の前に日付型と並び順を確認する
移動平均は「直前の行」を使うため、行の順番が重要です。日付が文字列のままならdatetime型に変換し、古い日付から新しい日付の順へ並べます。
今回は日付が連続する1日1行のデータなので、window=3を3日移動平均として説明できます。欠損日があるデータでは、同じコードは「直近3日」ではなく直近3行の平均です。
df["日付"] = pd.to_datetime(df["日付"])
df = df.sort_values("日付").reset_index(drop=True)
df.dtypes
| 日付 | datetime64[ns] |
| 売上 | int64 |
日付がdatetime64[ns]になっていれば変換できています。
- 日付変換の詳しい方法:pandas to_datetime()の使い方
- 並び替えの詳しい方法:pandas 並び替え(sort)入門
- 日付をインデックスにする方法:pandas set_index()の使い方
rolling(window=3).mean()で3日移動平均を計算する
売上列にrolling(window=3).mean()を適用し、結果を「3日移動平均」という新しい列へ追加します。
df["3日移動平均"] = df["売上"].rolling(window=3).mean()
df[["日付", "売上", "3日移動平均"]]
| 日付 | 売上 | 3日移動平均 | |
|---|---|---|---|
| 0 | 2026-08-01 | 100 | NaN |
| 1 | 2026-08-02 | 160 | NaN |
| 2 | 2026-08-03 | 120 | 126.67 |
| 3 | 2026-08-04 | 190 | 156.67 |
| 4 | 2026-08-05 | 150 | 153.33 |
| 5 | 2026-08-06 | 220 | 186.67 |
| 6 | 2026-08-07 | 180 | 183.33 |
| 7 | 2026-08-08 | 250 | 216.67 |
| 8 | 2026-08-09 | 210 | 213.33 |
| 9 | 2026-08-10 | 280 | 246.67 |
| 10 | 2026-08-11 | 230 | 240.0 |
| 11 | 2026-08-12 | 310 | 273.33 |
| 12 | 2026-08-13 | 270 | 270.0 |
| 13 | 2026-08-14 | 340 | 306.67 |
window=3が対象にする値を処理前後表で確認する
最初の5行は次のように計算されます。
| 日付 | 元の売上 | 平均対象となる値 | 3日移動平均 |
|---|---|---|---|
| 2026-08-01 | 100 | 3行未満 | NaN |
| 2026-08-02 | 160 | 3行未満 | NaN |
| 2026-08-03 | 120 | 100・160・120 | 126.67 |
| 2026-08-04 | 190 | 160・120・190 | 156.67 |
| 2026-08-05 | 150 | 120・190・150 | 153.33 |
8月3日の値は、(100 + 160 + 120) ÷ 3 = 126.67です。次の行では対象が1行ずれ、8月2日〜4日の売上を平均します。
この表で重要なのは、各行の結果が、その行と直前2行から作られていることです。移動平均は元の行数を保ちながら各行に対応する平均を返し、列全体の平均を1つだけ返すdf["売上"].mean()とは異なります。
先頭2行がNaNになる理由
初期設定では、windowに指定した数の有効な値がそろうまで結果を計算しません。window=3の場合は次の状態になります。
- 1行目:利用できる値が1個だけなのでNaN
- 2行目:利用できる値が2個だけなのでNaN
- 3行目:3個そろうので移動平均を計算
したがって、先頭のNaNはエラーではなく、同じ条件の3行平均をまだ計算できないことを示す正常な結果です。単純にfillna(0)で0へ置き換えると、「平均売上が0だった」と誤解されるので避けましょう。
min_periods=1で途中から計算する場合
min_periodsは、結果を計算するために最低限必要な有効値の数です。min_periods=1を指定すると、最初の行から結果が出ます。
df["3日移動平均_min1"] = (
df["売上"]
.rolling(window=3, min_periods=1)
.mean()
)
df[["日付", "売上", "3日移動平均", "3日移動平均_min1"]].head(5)
| 日付 | 売上 | 3日移動平均 | 3日移動平均_min1 | |
|---|---|---|---|---|
| 0 | 2026-08-01 | 100 | NaN | 100.0 |
| 1 | 2026-08-02 | 160 | NaN | 130.0 |
| 2 | 2026-08-03 | 120 | 126.67 | 126.67 |
| 3 | 2026-08-04 | 190 | 156.67 | 156.67 |
| 4 | 2026-08-05 | 150 | 153.33 | 153.33 |
初期設定とmin_periods=1の違いを整理します。
| 日付 | 利用できる値の数 | 初期設定の結果 | min_periods=1 | 比較時の注意点 |
|---|---|---|---|---|
| 2026-08-01 | 1 | NaN | 100.00 | 1個だけの平均 |
| 2026-08-02 | 2 | NaN | 130.00 | 2個の平均 |
| 2026-08-03 | 3 | 126.67 | 126.67 | 3個の平均 |
min_periods=1はNaNを減らせますが、先頭の値は3個ではなく1個または2個の平均です。すべての行を同じ条件で比較したい場合は、初期設定のNaNを残すほうが分かりやすいことがあります。
center=Trueは結果を中央の行に置く
初期設定のcenter=Falseでは、計算結果をwindowの末尾、つまり現在行に置きます。center=Trueを指定すると、結果をwindowの中央に置きます。
| 指定 | 3行の計算結果を置く位置 | 向いている場面 |
|---|---|---|
center=False(初期設定) |
3行目(末尾) | 現在までの値から傾向を確認する |
center=True |
2行目(中央) | 前後の値を使って中央時点をならす |
同じ3つの値を平均しても、結果が表示される行が変わります。
center比較 = pd.DataFrame({
"日付": df["日付"],
"売上": df["売上"],
"center=False": df["売上"].rolling(window=3).mean(),
"center=True": df["売上"].rolling(window=3, center=True).mean()
})
center比較.head(6)
| 日付 | 売上 | center=False | center=True | |
|---|---|---|---|---|
| 0 | 2026-08-01 | 100 | NaN | NaN |
| 1 | 2026-08-02 | 160 | NaN | 126.67 |
| 2 | 2026-08-03 | 120 | 126.67 | 156.67 |
| 3 | 2026-08-04 | 190 | 156.67 | 153.33 |
| 4 | 2026-08-05 | 150 | 153.33 | 186.67 |
| 5 | 2026-08-06 | 220 | 186.67 | 183.33 |
たとえば100・160・120の平均126.67は、初期設定では3行目の8月3日、center=Trueでは中央に当たる2行目の8月2日に表示されます。
この記事の移動平均は「現在までの直近値から傾向を見る」ことが目的なので、以降は初期設定のcenter=Falseを使います。
3日移動平均と7日移動平均を使い分ける
次に、同じ売上データへ7行の移動平均も追加します。サンプルは1日1行で日付が連続しているため、ここでは7日移動平均として扱えます。
df["7日移動平均"] = df["売上"].rolling(window=7).mean()
df[["日付", "売上", "3日移動平均", "7日移動平均"]]
| 日付 | 売上 | 3日移動平均 | 7日移動平均 | |
|---|---|---|---|---|
| 0 | 2026-08-01 | 100 | NaN | NaN |
| 1 | 2026-08-02 | 160 | NaN | NaN |
| 2 | 2026-08-03 | 120 | 126.67 | NaN |
| 3 | 2026-08-04 | 190 | 156.67 | NaN |
| 4 | 2026-08-05 | 150 | 153.33 | NaN |
| 5 | 2026-08-06 | 220 | 186.67 | NaN |
| 6 | 2026-08-07 | 180 | 183.33 | 160.0 |
| 7 | 2026-08-08 | 250 | 216.67 | 181.43 |
| 8 | 2026-08-09 | 210 | 213.33 | 188.57 |
| 9 | 2026-08-10 | 280 | 246.67 | 211.43 |
| 10 | 2026-08-11 | 230 | 240.0 | 217.14 |
| 11 | 2026-08-12 | 310 | 273.33 | 240.0 |
| 12 | 2026-08-13 | 270 | 270.0 | 247.14 |
| 13 | 2026-08-14 | 340 | 306.67 | 270.0 |
| 比較項目 | window=3 | window=7 |
|---|---|---|
| 使うデータ数 | 直近3行 | 直近7行 |
| 滑らかさ | 小さい | 大きい |
| 急な変化への反応 | 比較的速い | 比較的遅い |
| 向いている目的 | 短期的な動きの確認 | やや長い傾向の確認 |
| 先頭のNaN | 2行 | 6行 |
windowを大きくすると線は滑らかになりますが、急な上昇や下降が遅れて表れます。3日と7日のどちらが常に優れているわけではなく、どの程度の短期変動をならしたいかで選びます。
なお、移動平均は過去の値をならして見せる方法であり、将来を予測する機能ではありません。
rolling().mean()とrolling().sum()の違い
rolling()の後ろには、目的に合った集計メソッドを続けます。
mean():直近の平均を求め、変動をならすsum():直近の合計を求める
たとえば「直近3日間の売上合計」を求める場合は、sum()を使います。
df["直近3日売上合計"] = df["売上"].rolling(window=3).sum()
df[["日付", "売上", "3日移動平均", "直近3日売上合計"]].head(7)
| 日付 | 売上 | 3日移動平均 | 直近3日売上合計 | |
|---|---|---|---|---|
| 0 | 2026-08-01 | 100 | NaN | NaN |
| 1 | 2026-08-02 | 160 | NaN | NaN |
| 2 | 2026-08-03 | 120 | 126.67 | 380.0 |
| 3 | 2026-08-04 | 190 | 156.67 | 470.0 |
| 4 | 2026-08-05 | 150 | 153.33 | 460.0 |
| 5 | 2026-08-06 | 220 | 186.67 | 560.0 |
| 6 | 2026-08-07 | 180 | 183.33 | 550.0 |
同じwindowでも、mean()は傾向をならす用途、sum()は直近期間の累計を確認する用途に向いています。
rolling()の後ろで使える主な集計メソッド
rolling()は対象範囲を決める処理です。その後ろに、目的に合った集計メソッドを続けます。
| メソッド | 求める値 | 使用例 |
|---|---|---|
mean() |
平均 | 売上の短期変動をならす |
sum() |
合計 | 直近3日分の売上合計を求める |
min() |
最小値 | 直近期間の最低売上を確認する |
max() |
最大値 | 直近期間の最高売上を確認する |
median() |
中央値 | 極端な値の影響を抑えて中心を見る |
std() |
標準偏差 | 直近期間のばらつきを確認する |
count() |
有効値の個数 | window内のデータ数を確認する |
初心者は、まずmean()とsum()を使い分けられれば十分です。ほかのメソッドも書き方は同じで、必要になったときに選べます。
元の売上と移動平均を折れ線グラフで比較する
数値表だけでなく、元の売上、3日移動平均、7日移動平均を同じグラフへ描くと、windowによる滑らかさの違いが分かります。
ここではグラフの装飾ではなく、元の値の上下が移動平均でどのようにならされるかに注目してください。
plt.figure(figsize=(10, 5))
plt.plot(df["日付"], df["売上"], marker="o", alpha=0.55, label="Daily sales")
plt.plot(df["日付"], df["3日移動平均"], linewidth=2.5, label="3-row moving average")
plt.plot(df["日付"], df["7日移動平均"], linewidth=2.5, label="7-row moving average")
plt.xlabel("Date")
plt.ylabel("Sales")
plt.title("Daily sales and moving averages")
plt.grid(alpha=0.3)
plt.legend()
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
3日移動平均は元の売上の変化に比較的早く反応し、7日移動平均はさらに滑らかになります。一方で、windowが大きいほど直近の変化が遅れて見える点には注意が必要です。
グラフの色・線種・凡例などを詳しく設定したい場合は、Matplotlib折れ線グラフの描き方を参照してください。
rolling(3)とrolling(“3D”)は対象範囲が違う
ここまで使ったrolling(3)は直近3行を対象にします。一方、日付をインデックスにした時系列データでは、rolling("3D")のように実際の期間を指定できます。
違いが分かるように、8月3日と4日が欠けたデータで比較します。
df_gap = pd.DataFrame({
"日付": pd.to_datetime(["2026-08-01", "2026-08-02", "2026-08-05", "2026-08-06"]),
"売上": [100, 160, 120, 190]
}).set_index("日付")
df_gap["直近3行平均"] = df_gap["売上"].rolling(3).mean()
df_gap["直近3日平均"] = df_gap["売上"].rolling("3D").mean()
df_gap
| 売上 | 直近3行平均 | 直近3日平均 | |
|---|---|---|---|
| 2026-08-01 | 100.0 | NaN | 100.0 |
| 2026-08-02 | 160.0 | NaN | 130.0 |
| 2026-08-05 | 120.0 | 126.67 | 120.0 |
| 2026-08-06 | 190.0 | 156.67 | 155.0 |
8月5日の「直近3行平均」は8月1日・2日・5日の3行を使います。一方、「直近3日平均」は実際の日付間隔を見るため、8月5日時点の直近3日内にある値だけが対象です。
| 書き方 | windowの基準 | 欠損日がある場合 |
|---|---|---|
rolling(3) |
行数 | 日付が離れていても直近3行を使う |
rolling("3D") |
実際の3日間 | その期間内にある行だけを使う |
1日1行で日付が連続していれば両者は似た結果になりますが、欠損日や不規則な時刻があるデータでは一致しません。期間指定にはDatetimeIndexが必要です。この記事の中心は行数ベースの移動平均なので、期間指定はこの違いだけ押さえれば十分です。
rolling()でよくあるミスと注意点
window=3を必ず3日間だと思ってしまう
数値のwindow=3は基本的に3行です。1日に複数行がある場合や、日付が抜けている場合は3日間と一致しません。期間ベースではrolling("7D")のような指定もできますが、日付インデックスなどの前提が増えるため、この記事では詳しく扱いません。
日付順に並べず計算する
rolling()は現在の行順を使います。日付が前後したままでは、時間の流れに沿った移動平均になりません。先にdatetime型へ変換し、sort_values()で並べ替えましょう。
先頭のNaNをエラーだと思う
必要な値がwindowの数に達していないためです。0埋めで隠すのではなく、NaNが生じる理由を確認します。
rolling()だけで元のDataFrameが変わると思う
rolling()の計算結果は自動的に元のDataFrameへ追加されません。残したい場合は、この記事の例のように新しい列へ代入します。列追加の基本は、pandasで新しい列を追加する方法で確認できます。
欠損値があっても同じように計算できると思う
元データに欠損値があると、window内の有効値の数とmin_periodsによって結果が変わります。まず欠損の場所と意味を確認し、必要な件数を満たしているか判断してください。
データ分析の流れでrolling()を使う位置
rolling()は、データを読み込んだ直後ではなく、日付や並び順を整えた後に使います。
1. CSVやExcelを読み込む 2. head()やdtypesで内容と型を確認する 3. 日付列をto_datetime()で変換する 4. 日付順に並べ替える 5. 必要に応じてresample()で時間単位を整える 6. rolling()で短期変動をならす 7. 折れ線グラフで元の値と比較する
年・月・曜日を取り出して別の切り口で集計したい場合は、pandas dtの使い方へ進んでください。
発展的な機能には、開始行から対象範囲を広げるexpanding()、新しい値へ大きな重みを付けるewm()、グループ別に計算するgroupby().rolling()などがあります。center=Trueによる結果位置の変更や欠損日の補間も含め、これらは基本を理解してから学べば十分です。
まとめ
rolling()は、直近の複数行を1行ずつずらしながら計算するrolling(window=3).mean()は、原則として現在行を含む直近3行の平均を求める- 数値のwindowは基本的に行数であり、自動的に日数を意味するわけではない
- 3行そろう前のNaNはエラーではなく、同じ条件で計算できないことを示す
min_periods=1なら先頭から計算できるが、利用する値の数が行ごとに異なるcenter=Trueを指定すると、計算結果をwindowの中央に置けるrolling(3)は直近3行、rolling("3D")は実際の直近3日間を対象にする- window=3は変化へ比較的早く反応し、window=7は滑らかだが反応が遅くなる
- 傾向を見るなら
mean()、直近の合計を見るならsum()を使う - 集計単位を変える
resample()と、値をならすrolling()は目的が異なる - 計算前に日付型と並び順を確認し、計算後は元の値とグラフで比較する
まずはこの記事のサンプルで3日移動平均を実行し、windowを7へ変えたときに結果がどのように変化するか確認してみてください。
次に読みたい関連記事
- Google ColabでCSVを読み込む方法|アップロード・Drive・read_csv
- Pandas DataFrame入門|作り方・基本操作をわかりやすく解説
- pandas to_datetime()の使い方|文字列の日付変換とformat・NaT対処
- pandas 並び替え(sort)入門|sort_values・sort_indexの違い
- pandas set_index()の使い方|列をインデックスにする方法
- pandas dtの使い方|日付から年・月・曜日を取り出す方法
- pandas resample()の使い方|日付データを月別・週別に集計
- pandasで新しい列を追加する方法
- Matplotlib折れ線グラフの描き方
コメント