目录5

文章绘图第 17 章

HEATMAP

绘图 · 第 17 / 36 章

基础热力图

chart_061

基础热力图
Python34 行
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

BG = "#F7F3EC"
CMAP = LinearSegmentedColormap.from_list(
    "theme", ["#F7F3EC", "#D0A64F", "#A8552E"], N=256
)

np.random.seed(42)
data = np.random.uniform(0, 100, (8, 12))
rows = [f"Row {i+1}" for i in range(8)]
cols = [f"C{j+1}" for j in range(12)]

fig, ax = plt.subplots(figsize=(10, 6))

im = ax.imshow(data, cmap=CMAP, aspect="auto")

ax.set_xticks(range(len(cols)))
ax.set_yticks(range(len(rows)))
ax.set_xticklabels(cols)
ax.set_yticklabels(rows)
ax.tick_params(axis="both", colors="#3F2E18")

ax.set_title("Heatmap — Sequential", fontsize=15,
             color="#3F2E18", pad=12)

cbar = fig.colorbar(im, ax=ax, shrink=0.85, pad=0.02)
cbar.set_label("Value", fontsize=11, color="#3F2E18")
cbar.ax.tick_params(colors="#3F2E18")
cbar.outline.set_edgecolor("#3F2E18")

plt.tight_layout()
plt.show()

带数字标注

chart_062

带数字标注
Python44 行
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

BG = "#F7F3EC"
CMAP = LinearSegmentedColormap.from_list(
    "theme", ["#F7F3EC", "#D0A64F", "#A8552E"], N=256
)

np.random.seed(0)
data = np.random.uniform(0, 100, (7, 10))
rows = [f"Row {i+1}" for i in range(7)]
cols = [f"Col {j+1}" for j in range(10)]

fig, ax = plt.subplots(figsize=(10, 6))

im = ax.imshow(data, cmap=CMAP, aspect="auto")

# 数字标注:深色背景用白字
vmin, vmax = data.min(), data.max()
thresh = vmin + (vmax - vmin) * 0.6
for i in range(data.shape[0]):
    for j in range(data.shape[1]):
        color = "white" if data[i, j] > thresh else "#3F2E18"
        ax.text(j, i, f"{data[i, j]:.0f}",
                ha="center", va="center",
                fontsize=9, color=color)

ax.set_xticks(range(len(cols)))
ax.set_yticks(range(len(rows)))
ax.set_xticklabels(cols, rotation=45, ha="right")
ax.set_yticklabels(rows)
ax.tick_params(axis="both", colors="#3F2E18")

ax.set_title("Heatmap with Annotations", fontsize=15,
             color="#3F2E18", pad=12)

cbar = fig.colorbar(im, ax=ax, shrink=0.85, pad=0.02)
cbar.set_label("Value", fontsize=11, color="#3F2E18")
cbar.ax.tick_params(colors="#3F2E18")
cbar.outline.set_edgecolor("#3F2E18")

plt.tight_layout()
plt.show()

发散热力图

chart_063

发散热力图
Python45 行
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

BG = "#F7F3EC"
CMAP = LinearSegmentedColormap.from_list(
    "theme", ["#35666E", "#F7F3EC", "#A8552E"], N=256
)

np.random.seed(1)
data = np.random.normal(0, 1, (8, 10))
rows = [f"Row {i+1}" for i in range(8)]
cols = [f"C{j+1}" for j in range(10)]

fig, ax = plt.subplots(figsize=(10, 6))

# 关键:vmin/vmax 对称,0 映射到中点
vmax = np.abs(data).max()
im = ax.imshow(data, cmap=CMAP, aspect="auto",
               vmin=-vmax, vmax=vmax)

# 数字标注
for i in range(data.shape[0]):
    for j in range(data.shape[1]):
        color = "white" if abs(data[i, j]) > vmax * 0.6 else "#3F2E18"
        ax.text(j, i, f"{data[i, j]:.1f}",
                ha="center", va="center",
                fontsize=8, color=color)

ax.set_xticks(range(len(cols)))
ax.set_yticks(range(len(rows)))
ax.set_xticklabels(cols)
ax.set_yticklabels(rows)
ax.tick_params(axis="both", colors="#3F2E18")

ax.set_title("Heatmap — Diverging", fontsize=15,
             color="#3F2E18", pad=12)

cbar = fig.colorbar(im, ax=ax, shrink=0.85, pad=0.02)
cbar.set_label("Value", fontsize=11, color="#3F2E18")
cbar.ax.tick_params(colors="#3F2E18")
cbar.outline.set_edgecolor("#3F2E18")

plt.tight_layout()
plt.show()

时间 × 类别热力图

chart_064

时间 × 类别热力图
Python49 行
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

BG = "#F7F3EC"
CMAP = LinearSegmentedColormap.from_list(
    "theme", ["#F7F3EC", "#D0A64F", "#A8552E"], N=256
)

np.random.seed(2)
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
hours = [f"{h:02d}" for h in range(24)]

# 构造有周期性的数据:工作日高峰 + 周末平缓
data = np.zeros((7, 24))
for d in range(7):
    for h in range(24):
        base = 30
        # 工作时间高峰
        if 9 <= h <= 18 and d < 5:
            base += 50
        # 周末休闲高峰
        if d >= 5 and 14 <= h <= 22:
            base += 35
        data[d, h] = base + np.random.normal(0, 8)

fig, ax = plt.subplots(figsize=(12, 4.5))

im = ax.imshow(data, cmap=CMAP, aspect="auto")

ax.set_xticks(range(len(hours)))
ax.set_yticks(range(len(days)))
ax.set_xticklabels(hours)
ax.set_yticklabels(days)
ax.tick_params(axis="both", colors="#3F2E18")

ax.set_xlabel("Hour of Day", fontsize=12, color="#3F2E18")
ax.set_ylabel("Day of Week", fontsize=12, color="#3F2E18")

ax.set_title("Heatmap — Hour × Day Pattern", fontsize=15,
             color="#3F2E18", pad=12)

cbar = fig.colorbar(im, ax=ax, shrink=0.85, pad=0.02)
cbar.set_label("Activity", fontsize=11, color="#3F2E18")
cbar.ax.tick_params(colors="#3F2E18")
cbar.outline.set_edgecolor("#3F2E18")

plt.tight_layout()
plt.show()

聚类排序热力图

chart_065

聚类排序热力图
Python42 行
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
from scipy.cluster.hierarchy import linkage, leaves_list
from scipy.spatial.distance import pdist

BG = "#F7F3EC"
CMAP = LinearSegmentedColormap.from_list(
    "theme", ["#F7F3EC", "#D0A64F", "#A8552E"], N=256
)

np.random.seed(3)
# 构造两个明显不同的簇
cluster1 = np.random.normal(20, 5, (15, 10))
cluster2 = np.random.normal(70, 5, (15, 10))
data = np.vstack([cluster1, cluster2])

# 层次聚类重排
row_order = leaves_list(linkage(pdist(data), method="average"))
col_order = leaves_list(linkage(pdist(data.T), method="average"))
data_ord = data[np.ix_(row_order, col_order)]

fig, ax = plt.subplots(figsize=(10, 8))

im = ax.imshow(data_ord, cmap=CMAP, aspect="auto")

ax.set_xticks(range(data.shape[1]))
ax.set_yticks(range(data.shape[0]))
ax.set_xticklabels([f"C{i+1}" for i in col_order], fontsize=8)
ax.set_yticklabels([f"R{i+1}" for i in row_order], fontsize=8)
ax.tick_params(axis="both", colors="#3F2E18")

ax.set_title("Clustered Heatmap", fontsize=15,
             color="#3F2E18", pad=12)

cbar = fig.colorbar(im, ax=ax, shrink=0.7, pad=0.02)
cbar.set_label("Value", fontsize=11, color="#3F2E18")
cbar.ax.tick_params(colors="#3F2E18")
cbar.outline.set_edgecolor("#3F2E18")

plt.tight_layout()
plt.show()