PythonでExcelの日時から時間帯ごとの件数を集計する手順|何時台に多いかを1時間ごとに数える
問い合わせの受付日時が入ったExcelから、pandasのdt.hourで何時台に件数が多いかを数える手順を解説します。件数の無い時間帯を0で残す方法と、四捨五入で時間帯がずれるつまずきを、架空の明細で試しました。
目次
この記事でわかること
- 受付日時の列から、何時台かを表す0〜23の数を取り出す方法(dt.hour)
- 時間帯ごとの件数を数え、0時台から23時台まで24行の表にする方法
- 件数の無い時間帯を、0の行として表に残す方法
- 四捨五入でまとめると時間帯がずれる理由と、時刻だけの列の扱い方
問い合わせや注文の明細から「何時ごろに多いか」を知りたいとき、受付日時を目で追って時間帯ごとに数えるのは手間がかかります。この記事では、pandasで受付日時から時を取り出し、時間帯ごとの件数を24行の表にして書き出すまでを、手元で試した結果を添えて順にまとめます。
よりどころにしたのは、pandas 3.1.0の公式の資料(Series.dtのhour・round・floor、Series.value_counts、Series.reindex、Series.idxmax、pandas.to_datetime)です。原文は2026年11月3日に読みました。受付日時と件数は、試すために作った架空の明細のものです。
先に結論: dt.hourで時を取り出し、value_countsで数えて24行にそろえる
受付日時をpd.to_datetimeで日時の型に直し、dt.hourで時を取り出してvalue_countsで数えます。 そのままでは件数の多い順に並び、件数の無い時間帯は出てこないので、reindexで0から23の24行にそろえ、無い時間帯を0で埋めます。
| やること | 使うもの | 試した明細での結果 |
|---|---|---|
| 日時の型にそろえる | pd.to_datetime(errors="coerce") | 17行のうち1行が空(NaT) |
| 何時台かを取り出す | dt.hour | 0から23の数 |
| 時間帯ごとに数える | value_counts | 件数のある11の時間帯 |
| 24行にそろえる | reindex(range(24), fill_value=0) | 24行で合計16件 |
dt.hourとvalue_countsのしくみ
pandasの資料では、dt.hourは日時の値ごとに「時」の部分を返し、値は0から23の24時間の表し方になる、と説明しています。9時45分の受付なら9、23時50分なら23です。分の部分は使われないので、9時0分から9時59分までが同じ「9時台」になります。
value_countsは、値ごとの件数を数えます。資料のとおり、結果は件数の多い順に並び、最初の行がいちばん多い値です。空の値は初めの設定では数えません。どの時間帯が多いかを見るには便利ですが、0時から順に並べて見たいときや、件数の無い時間帯も表に残したいときは、次のreindexが要ります。
件数の無い時間帯を0で残す(reindex)
reindexに行の並びを渡すと、表がその並びに組み直されます。元の表に無かった行の値はNaNです。fill_valueを指定しておけば、NaNではなくその値で埋まります。range(24)の0から23を渡せば、受付が1件も無かった時間帯も0件の行として残り、0時台から23時台まで順に並びます。
手順: 受付の明細から時間帯ごとの件数を書き出す
次のコードは、架空の受付の明細 uketsuke.xlsx(受付日時・内容の2列)を読み込み、時間帯ごとの件数を hourly_count.xlsx に書き出します。uketsuke.xlsx には何も書き込みません。ファイルの名前と列の名前は、手元の明細に合わせて書きかえます。
import pandas as pd
# 問い合わせの受付の明細を読む(元のファイルには書き込まない)
calls = pd.read_excel("uketsuke.xlsx")
# 受付日時を日時の型に直す。日時として読めない値はNaTになるので、その行数を出す
calls["受付日時"] = pd.to_datetime(calls["受付日時"], errors="coerce")
print("日時として読めなかった行:", calls["受付日時"].isna().sum())
# 時(0〜23)を取り出して件数を数え、0時台から23時台の24行に並べ直す
hour = calls["受付日時"].dt.hour
hourly = hour.value_counts().reindex(range(24), fill_value=0)
hourly.index = [f"{h}時台" for h in hourly.index]
hourly.index.name = "時間帯"
hourly.name = "件数"
print(hourly.idxmax(), hourly.max()) # いちばん多い時間帯とその件数
print("数えた件数:", hourly.sum()) # 明細の行数から読めなかった行を引いた数と比べる
hourly.to_excel("hourly_count.xlsx")
pandasでxlsxのファイルを読み書きするには、openpyxlも入れておく必要があります。最後の「数えた件数」を、明細の行数から読めなかった行を引いた数と見比べると、数え漏れがないかを確かめられます。
試した結果
2026年10月5日と6日の2日分の、17行の架空の受付の明細で動かしました。受付日時は9時台と10時台に多く、0時10分・8時45分・17時59分・23時50分の受付も入れました。1行は「2026/10/06 16:20」という文字で入れ、もう1行は受付日時の欄に「不明」と書きました。
- 「日時として読めなかった行: 1」と表示されました。「不明」と書いた行です
- いちばん多い時間帯は「10時台 4」で、9時台が3件で続きました
- 文字で入れた「2026/10/06 16:20」の行も、16時台の1件に数えられました
- 8時45分は8時台、17時59分は17時台、23時50分は23時台に入りました
- 「数えた件数: 16」と出ました。17行から読めなかった1行を引いた数と同じです
- hourly_count.xlsx は、時間帯と件数の2列・24行で、0時台から23時台まで並んでいました。件数の無い13の時間帯は0です
- 元の uketsuke.xlsx は、実行の前と後でハッシュ値が同じでした
試したのはMacの作業PCで、Python本体が3.12.15、pandasが3.1.0、openpyxlが3.1.5でした。Excelのアプリでは開いておらず、書き出したファイルの中身はopenpyxlで読み直して確かめました。
自分で数えてよいか、相談するかの判断
目安は、「明細の受付日時が、問い合わせの来た時刻そのものか」です。電話やフォームの受付の時刻がそのまま記録されているなら、上のコードの列の名前を変えるだけで使えます。
担当者が後でまとめて入力した時刻や、メールを読んだ時刻が入っている明細では、数えた時間帯が実際の混み方とずれます。どの時刻を記録しているかを、明細を作っている人に確かめてから数えます。夜の0時をまたいで営業する窓口で、深夜の受付を前の日の分に数えたいといった決まりがあるときも、先に数え方を決めておきます。
つまずきやすいところ
- 件数の多い順に並び、無い時間帯が出ない: value_countsだけで止めると、試した明細では10時台・9時台が先頭に来て、件数のある11の時間帯しか並びませんでした。0時台から順に24行で見たいときは、reindexまで続けます
- 「10.0時台」のように小数が付く: 日時として読めない行があると、dt.hourの結果は小数の列になりました。reindexを通さずに名前を付けると、「10.0時台」のような名前になります。reindex(range(24))の後なら、0から23の整数の名前になりました
- dt.roundで時間帯にまとめる: dt.round("h")は近いほうの時刻に丸めるため、8時45分が9時台、17時59分が18時台、23時50分は翌日の0時台に入りました。何時台かを数えるときは、分を切り捨てるdt.hour(またはdt.floor("h"))を使います
- 日付と時刻が別の列になっている: 時刻だけの列は、試したときも日時の型ではなく時刻のまま読み込まれ、.dtを使うと「Can only use .dt accessor」で始まる例外が出ました。pd.to_datetimeでerrors="coerce"を付けると、例外は出ないかわりに5行すべてがNaTになりました。日付の列と時刻の列を文字にしてつないでから、pd.to_datetimeで直します
- 曜日ごとの傾向も見たい: 日付から曜日を取り出して月曜から並べる方法は、PythonでExcelの日付から曜日ごとの合計を出す手順で紹介しています
- 月ごとの件数の移り変わりを見たい: 日付を年月にまとめて集計する方法は、PythonでExcelの日付から月ごとの合計を出す手順にまとめています
- 曜日と時間帯の組み合わせで数えたい: 曜日を行、時間帯を列に並べた件数の表は、PythonでExcelの2つの項目の組み合わせごとに件数を集計する手順のcrosstabで作れます
まとめ
- 受付日時をpd.to_datetimeで日時の型に変え、NaTになった行がいくつあるかを先に見ます
- dt.hourで時を取り出してvalue_countsで数え、reindexで0時台から23時台の24行にそろえます
- 何時台かを数えるときは、四捨五入のdt.roundではなく、分を切り捨てるdt.hourを使います
- 数えた件数の合計を、明細の行数から読めなかった行を引いた数と見比べます
よくある質問
数えた結果は、元のExcelのファイルに書き込まれますか?
書き込まれません。記事のコードは受付の明細を開いて読むだけで、時間帯ごとの件数は別のファイルの hourly_count.xlsx に保存します。試したときも、元の uketsuke.xlsx のハッシュ値は実行の前と後で同じでした。
1時間ごとではなく、30分ごとに数えられますか?
数えられます。dt.hourの代わりにdt.floor("30min")で受付日時を30分の区切りに切り捨て、時刻の文字にしてから件数を数えます。試した明細では、9時30分から9時59分までの受付が2件と数えられました。
何日分かの明細を数えたとき、1日あたりの件数はどう出しますか?
時間帯ごとの件数を、明細に出てくる日付の数で割ります。日付の数は、dt.normalizeで時刻を0時にそろえてから、nuniqueで数えられます。試した明細は2日分で、10時台の4件は1日あたり2件でした。
営業時間の9時から18時の時間帯だけを表にしたいときは、どうしますか?
reindexにrange(9, 19)を渡すと、9時台から18時台の10行の表になります。ただし、範囲の外の受付は表から消えます。試した明細では0時台・8時台・23時台の3件が消え、表の合計は13件でした。消えた件数は、全体の件数と表の合計の差で確かめておきます。
いちばん多い時間帯が2つあるときは、どちらが表示されますか?
pandasの資料では、idxmaxは最大の値が複数あるとき、最初の行のラベルを返すとしています。24行の表は0時台から順に並んでいるので、早いほうの時間帯が表示されます。同じ件数の時間帯がほかにもないかは、表を見て確かめます。