K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

カレンダーヒートマップで季節リズムを俯瞰

中級

06.7.6

カレンダーヒートマップで季節リズムを俯瞰

最終更新 2020-04-08 読了時間 3 分
まとめ
カレンダーヒートマップは、1 年 366 日の値を「月ごとの小さなカレンダー」の形に並べ、各日のマスを色で塗って季節と曜日の両方のパターンを一度に見せる図です。折れ線で 1 年分を描くと曜日の違いはギザギザのノイズに埋もれますが、行を曜日・列を週にした 7 × 6 の行列に置き直すと、平日の行だけ明るいといった構造が色の帯として浮かび上がります。実装の中心は、各日付を (weekday, week_of_month) の座標に変換して np.full((7, 6), np.nan) の行列に書き込み、ax.imshow で塗る処理です。読み終えると、12 枚のパネルから「夏に明るく、週末だけ暗い」というパターンを読み取り、自分の日次データを同じ座標変換で描けるようになります。

直感 #

サイトの平均滞在時間を 1 日ごとに 1 年分記録したとします。366 点の折れ線にすると、季節の大きなうねりと曜日ごとの小さな上下が混ざって、どちらも読みづらくなります。カレンダーヒートマップは、壁掛けカレンダーのように月ごとに 7 行(月〜日)× 6 列(第 1〜6 週)のマスを作り、各日のマスを値の大きさで色付けします。すると「土日の行だけ暗い」という曜日パターンは横縞に、「6 月と 7 月のパネル全体が明るい」という季節パターンは月ごとの色調の差として、別々の形で見えてきます。

Python で描く #

次のコードは、季節成分(振幅 25 の正弦波)と曜日成分(平日 +8、週末 −10)とノイズを混ぜた架空の滞在時間を作り、日付を「曜日 × 月内の週番号」の座標に変換して、12 か月分のヒートマップを 3 × 4 のグリッドに並べます。カラーバーの範囲を全パネルで共通にして、月をまたいだ色の比較ができるようにしています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

date_index = pd.date_range("2024-01-01", "2024-12-31", freq="D")

# 季節要因 + 曜日要因 + ノイズを混ぜた架空メトリクス
seasonal = 40 + 25 * np.sin(2 * np.pi * date_index.dayofyear / 365)
weekday_boost = np.where(date_index.weekday < 5, 8, -10)
rng = np.random.default_rng(42)
metric = seasonal + weekday_boost + rng.normal(0, 5, len(date_index))

calendar_df = pd.DataFrame({"date": date_index, "value": metric})
calendar_df["month"] = calendar_df["date"].dt.month
calendar_df["weekday"] = calendar_df["date"].dt.weekday

month_start_weekday = (
    calendar_df["date"].dt.to_period("M").dt.to_timestamp().dt.weekday
)
calendar_df["week_of_month"] = (
    (month_start_weekday + calendar_df["date"].dt.day - 1) // 7
).astype(int)

fig, axes = plt.subplots(3, 4, figsize=(12, 7), sharex=True, sharey=True)
vmin, vmax = calendar_df["value"].min(), calendar_df["value"].max()

for month in range(1, 13):
    ax = axes[(month - 1) // 4, (month - 1) % 4]
    month_df = calendar_df[calendar_df["month"] == month]
    matrix = np.full((7, 6), np.nan)

    for row in month_df.itertuples():
        matrix[row.weekday, row.week_of_month] = row.value

    im = ax.imshow(
        matrix,
        origin="upper",
        aspect="auto",
        cmap="viridis",
        vmin=vmin,
        vmax=vmax,
    )
    ax.set_title(f"{month}月", fontsize=11, pad=8)
    ax.set_xticks(range(6), labels=["1週", "2週", "3週", "4週", "5週", "6週"], fontsize=8)
    ax.set_yticks(range(7), labels=["月", "火", "水", "木", "金", "土", "日"], fontsize=8)
    ax.tick_params(length=0)

fig.suptitle("2024年 日別セッション滞在時間(分)", fontsize=14, y=0.98)
cbar = fig.colorbar(im, ax=axes, orientation="horizontal", fraction=0.035, pad=0.08)
cbar.set_label("平均滞在時間(分)")

fig.tight_layout()

plt.show()

年間の指標を曜日×週のマトリクスで並べるカレンダーヒートマップは、季節性や曜日依存のクセを一目で把握したいときに便利ですの図

実行結果の読み方 #

各パネルの上 5 行(月〜金)が明るく、下 2 行(土・日)が暗い横縞が全月に出ていれば、平日偏重の利用パターンが曜日効果として読めます。パネルどうしを見比べると、6 月や 7 月のパネルが全体に黄色寄り、12 月や 1 月が紫寄りになっており、これが季節成分です。特定の週だけ突出して明るいマスがあれば、その週にキャンペーンや障害があったかを calendar_df の日付と照らし合わせて確認できます。

何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:曜日ごとに色分け・平均すると、ノイズに隠れた週のパターンが取り出せる。

使いどころと落とし穴 #

カレンダーヒートマップは、1 年前後の日次データで曜日効果と季節効果を同時に確認したいときの図です。2 年以上を描くと 24 枚以上のパネルが並んで比較しにくくなるので、その場合は月や週で resample して折れ線に切り替えるか、年ごとに図を分けます。極端な外れ値があると vmax に引っ張られて他の日がすべて暗くなるため、np.percentile で上下 2% をクリップしてから色を割り当ててください。欠損日を 0 で埋めると「値が極端に低い日」と区別できないので、この例のように NaN のままにして透明(または cmap.set_bad("#e5e7eb") でグレー)にするのが正しい扱いです。