PythonでExcelの売上から移動平均の列を作る手順|rollingで直近の数日の平均を出す
Pythonのpandasのrollingで、Excelの日ごとの売上に「直近3日の平均」のような移動平均の列を足す手順を解説します。行の数で数える平均と日付の幅で数える平均の違いや、抜けた日のつまずきも、架空の売上の表で試して紹介します。
目次
この記事でわかること
- 日ごとの売上に、直近の数日の平均(移動平均)の列を足す方法(rolling)
- 行の数で数える平均と、日付の幅で数える平均の違い
- 最初の行が空になる理由と、埋める方法
- 抜けた日・並べ替え・店舗ごとの計算で気をつけること
日ごとの売上はばらつきが大きく、流れを見るために「直近3日の平均」を出したいとき、Excelで1行ずつAVERAGEの範囲をずらして入れていませんか。この記事では、移動平均の列をpandasで一度に足す手順を紹介します。
見たのは、pandas 3.1.0のDataFrame.rollingの説明と、ユーザーガイドの窓の計算の章で、2026年10月31日に原文に当たりました。売上の表は、この記事のための架空のデータです。
先に結論: 行の数で幅を決めるか、日付の幅で決めるかを選ぶ
rollingに3を渡すと直近3行、"3D"と日付の列を渡すと直近3日の平均になります。 抜けた日がない表ならどちらも同じですが、抜けた日があると結果が変わります。
試した表で、10月5日が抜けているときの値は次のとおりです(売上の単位は万円)。
| 日付 | 売上 | 直近3行の平均 | 直近3日の平均 |
|---|---|---|---|
| 10月4日 | 40 | 30 | 30 |
| 10月6日 | 50 | 40 | 45 |
| 10月7日 | 60 | 50 | 55 |
| 10月8日 | 70 | 60 | 60 |
rollingのしくみ
pandasの資料では、rollingを、決まった大きさの窓をずらしながら集計などをする機能だと説明しています。窓は、その行から窓の長さの分だけさかのぼって作られます。
幅に数字を渡すと、行の数で窓を作ります。"3D"のような日付の幅を渡すと、その期間に入る行で窓を作るので、窓の中の行の数は変わります。日付の幅を使うときは、日付が順に並んでいる必要があります。日付の幅のときは、初期設定で1行あれば計算します。
結果は、初期設定では窓の右の端、つまり計算したその日の行に置かれます。
手順: 移動平均の列を足す
次のコードは、架空の表 daily_sales.xlsx の「日付」と「売上」から、「3行の平均」と「3日の平均」の2つの列を作り、daily_sales_ma.xlsx に保存します。読み込んだ元の表は書き換えません。
import pandas as pd
# 日ごとの売上の表を読む(平均を足した表は別の名前で保存する)
table = pd.read_excel("daily_sales.xlsx")
table = table.sort_values("日付") # 日付の順に並べてから計算する
# 直近3行の平均(3行そろうまでは空)
table["3行の平均"] = table["売上"].rolling(3).mean()
# 直近3日の平均(抜けた日を数えず、日付の幅で見る)
table["3日の平均"] = table.rolling("3D", on="日付")["売上"].mean()
print(table)
table.to_excel("daily_sales_ma.xlsx", index=False)
onに日付の列の名前を渡すと、行の番号ではなくその列で日付の幅を測ります。日付の列は、Excelで日付として入っていることが前提です。文字で入っているときは、先に日付に直します。
試した結果
10月1日から8日までのうち、5日だけが抜けた7行の作り物の表で動かしました。売上は10万円から70万円まで10万円ずつ増える値です。
- 3行の平均は、最初の2行が空で、3行目から20・30・40・50・60になりました
- 3日の平均は、最初の行から10・15・20・30・45・55・60になり、空の行はありませんでした
- 抜けた日の次の10月6日は、3行の平均が40、3日の平均が45で、値が分かれました
- 元の daily_sales.xlsx のハッシュ値は、実行の前後で同じでした
計算はMac(作業PC)上で、pandas 3.1.0とopenpyxl 3.1.5を入れたPython 3.12.15で行いました。書き出したファイルをExcelのアプリで開いてはいません。
自分で直すか、相談するかの判断
目安は、「平均の幅を、日数で決めるか件数で決めるかを言えるか」です。「直近3日」なら日付の幅、「直近3回の注文」なら行の数と、決まりが言えればそのままコードになります。
休業日をどう扱うか、祝日を数えるかといった決まりが部署で決まっていない場合は、先にその決まりを決めます。どの幅の平均を報告に使うかは、表を見る人と相談して決めます。
つまずきやすいところ
- 並べ替えずに計算する: 行の数の平均は、表に並んだ順で窓を作ります。試したときも、日付をばらばらにした表では、同じ日の平均が違う値になりました。日付の幅の平均は、並んでいないとValueErrorで止まりました
- 抜けた日に気づかない: 行の数で幅を決めると、抜けた日の前後もつながって数えられます。日付の幅で決めるか、先に抜けた日を確かめます
- 店舗ごとに分けずに計算する: 2つの店舗が交互に並んだ表では、店舗をまたいで平均されます。店舗ごとの前月比を出す考え方は、PythonでExcelの売上から前月比と累計を計算する手順で紹介しています
- 日ごとではなく月ごとに見たい: 先に月ごとの合計を出す方法は、PythonでExcelの日付から月ごとの合計を出す手順にまとめています
- 平均ではなく割合を見たい: 店舗の合計に対する割合の列を足す方法は、PythonでExcelのグループごとの割合の列を作る手順で紹介しています
まとめ
- rollingに数字を渡すと行の数、"3D"のような幅を渡すと日付の幅で平均します
- 行の数で幅を決めると、そろうまでの最初の行は空になります
- 抜けた日があると、行の数と日付の幅で結果が変わります
- 日付の順に並べてから計算し、店舗ごとならgroupbyで分けます
よくある質問
最初の数行が空になるのはなぜですか?
行の数で幅を決めた移動平均は、初期設定では、幅の分の値がそろうまで計算しないためです。公式の資料では、値の数が足りないと空になり、行の数で幅を決めたときの必要な数は幅と同じだとしています。
そろっていない最初の行も、あるだけの値で平均したいときは?
rollingにmin_periods=1を渡します。試した表でも、最初の2行は10と15になり、空ではなくなりました。
その日を真ん中にした平均にできますか?
center=Trueを付けます。公式の資料では、初期設定は幅の右の端に結果を置き、center=Trueで真ん中に置くとしています。その日より後の値も使うので、その日の時点では計算できない点に気をつけます。
店舗ごとに移動平均を出せますか?
出せます。店舗でgroupbyし、transformの中でrollingを使うと、元の行の並びのまま店舗ごとの平均が入ります。試した表でも、2つの店舗の値が混ざらずに計算されました。
週や月の単位の平均にしたいときは?
日付の幅を"7D"のように変えます。月のように日数が決まっていない幅は、日付の幅としては指定できないため、先に月ごとに集計してから行の数で幅を決めます。