膜厚の偏差(Deviation)をヒートマップで可視化する
はじめに
半導体製造工程において、ウェハ上の膜厚ムラや欠陥の分布を可視化するための「ウェハマップ」は非常に重要なツールです。
今回は、Pythonを用いて、散布図的な点データから「連続した色(ヒートマップ)」へと変換し、平均値からの偏差(Deviation)を強調して描画するスクリプトを作成しました。
解決した課題
-
離散データの補間: 単純なドットのプロットではなく、
scipy.griddataを用いて点と点の間の色を埋め、滑らかなヒートマップにする。 - 偏差の可視化: 生データ(mm)ではなく、平均値からの差分(nm)を抽出することで「異常」を見つけやすくする。
-
高度なカスタマイズ:
- カラーマップによる色のコントロール(
coolwarm等)。 - 範囲の固定(
vmin,vmax)により、複数ウェハの比較を可能にする。 - 背景透過や、特定の半径でのカットなど、レポートへの貼り付けやすさを考慮した出力。
- カラーマップによる色のコントロール(
使用技術
- Python 3.x
- Pandas: データ解析・CSV読み込み
- NumPy: 数値計算
- SciPy: 線形補間(griddata)
- Matplotlib: 高度な可視化
実装のポイント
1. 平均からの偏差への変換
生の膜厚データは非常に近い数値で推移するため、そのままでは色の変化がわかりにくいことがあります。平均値を算出し、各地点を「差分」に置き換えることで、「どこが異常か(高いか低いか)」を直感的に判断できるマップに変換します。
2. グリッドによる補間 (Interpolation)
散布図ではデータがない場所が空いてしまいます。scipy.griddata を用いて高解像度のグリッドを作成し、その上に元のデータをマッピングすることで、隙間のない滑らかなグラデーションを生成します。
3. カラーマップと範囲の固定
「異常がある=赤」といった意図を反映させるため、カラーマップに coolwarm を採用。また、vmin, vmax をあらかじめ設定することで、複数のウェハ間でも同一のスケールで比較できるようにしています。
サンプルコード
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from scipy.interpolate import griddata
# ==========================================
# 設定項目 (ここを書き換えることで動作を調整)
# ==========================================
CONFIG = {
"input_file": "wafermap.csv", # 入力ファイル名
"output_file": "wafer_deviation_map.png", # 出力ファイル名
# 【ウェハの形状設定】
"wafer_radius": 150, # ウェハの半径
"grid_res": 300, # ヒートマップの解像度(大きいほど滑らか)
# 【ヒートマップの色と範囲の設定】
"cmap": "coolwarm", # カラーマップ ('coolwarm', 'jet', 'viridis' など)
"vmin": -3.0, # 最小値(カラーバーの底)
"vmax": 3.0, # 最大値(カラーバーの頂点)
# 【背景と透明度の設定】
"bg_color": "#f0f0f0", # 背景色 (例: '#ffffff', 'gray' など)
"is_transparent": False, # 背景を透明にするか (True=透ける, False=指定の色で塗りつぶす)
# 【カラーバーのデザイン】
"cbar_shrink": 0.8, # カラーバーの高さ(0.0〜1.0)
"cbar_pad": 0.02, # カラーバーとグラフの距離
"cbar_label": "Deviation from Mean (nm)", # カラーバーのラベル名
# 【描画オプション】
"show_outline": True, # ウェハの外周線を描画するか
"show_axes": True, # 軸(目盛り、枠線)を表示するか
}
def create_wafer_map():
# --- 1. データ読み込み ---
try:
# 日本語が含まれる場合を考慮して cp932 を指定
df = pd.read_csv(CONFIG["input_file"], encoding='cp932')
print(f"Successfully loaded {CONFIG['input_file']}")
except Exception as e:
print(f"Error loading file: {e}")
return
# --- 2. データ処理(平均からの差分計算) ---
original_data = df['膜厚(nm)'].values
avg_value = original_data.mean()
diff_data = original_data - avg_value
print(f"Average Thickness: {avg_value:.2f} nm")
# --- 3. ヒートマップ用グリッド生成と補間 ---
radius = CONFIG["wafer_radius"]
# ここを正しく修正しました:CONFIG["grid_res"] を使用します
xi = np.linspace(-radius, radius, CONFIG["grid_res"])
yi = np.linspace(-radius, radius, CONFIG["grid_res"])
X, Y = np.meshgrid(xi, yi)
# 補間処理(点データから面データを作成)
Z = griddata((df['X'].values, df['Y'].values), diff_data, (X, Y), method='linear')
# ウェハ形状のマスク(半径外をカット)
dist_from_center = np.sqrt(X**2 + Y**2)
Z[dist_from_center > radius] = np.nan
# --- 4. 描画準備 ---
fig, ax = plt.subplots(figsize=(10, 10))
# 背景設定の反映
if CONFIG["is_transparent"]:
# 透明にする場合:透過度を調整
fig.patch.set_alpha(0.0)
ax.patch.set_alpha(0.0)
else:
# 指定された色で塗りつぶす場合
fig.patch.set_facecolor(CONFIG["bg_color"])
ax.set_facecolor(CONFIG["bg_color"])
# 軸の非表示設定(目盛り、枠線を消す)
if not CONFIG["show_axes"]:
ax.axis('off')
# --- 5. ヒートマップの描画 ---
im = ax.imshow(Z, extent=[-radius, radius, -radius, radius],
origin='lower', cmap=CONFIG["cmap"],
interpolation='bilinear',
vmin=CONFIG["vmin"], vmax=CONFIG["vmax"])
# ウェハの外周線を描画
if CONFIG["show_outline"]:
circ = Circle((0, 0), radius, color='black', fill=False, linewidth=2)
ax.add_patch(circ)
# --- 6. カラーバーの設定 ---
cbar = fig.colorbar(im, ax=ax, pad=CONFIG["cbar_pad"], shrink=CONFIG["cbar_shrink"])
cbar.set_label(CONFIG["cbar_label"])
# --- [将来用:軸やタイトルなど] ---
# 必要に応じて以下を有効にしてください
# ax.set_title("Wafer Thickness Deviation Map", fontsize=15)
# ax.set_xlabel("X coordinate")
# ax.set_ylabel("Y coordinate")
# --- 7. 保存処理 ---
# 背景が透明な場合は facecolor='none' を指定して透過を維持
save_face = None if CONFIG["is_transparent"] else CONFIG["bg_color"]
plt.savefig(CONFIG["output_file"], dpi=300, bbox_inches='tight', facecolor=save_face)
print(f"Success! Map saved as {CONFIG['output_file']}")
if __name__ == "__main__":
create_wafer_map()
サンプルデータ
X,Y,膜厚(nm)
0,0,102.1
0,35,101.8
24.7,24.7,101.9
35,0,101.7
24.7,-24.7,101.9
0,-35,101.6
-24.7,-24.7,101.8
-35,0,101.7
-24.7,24.7,102
0,70,100.7
26.8,64.7,100.9
49.5,49.5,101
64.7,26.8,100.6
70,0,100.5
64.7,-26.8,100.8
49.5,-49.5,100.6
26.8,-64.7,100.7
0,-70,100.4
-26.8,-64.7,100.5
-49.5,-49.5,100.8
-64.7,-26.8,100.6
-70,0,100.4
-64.7,26.8,100.7
-49.5,49.5,100.5
-26.8,64.7,100.9
0,105,99.2
52.5,90.9,99.1
90.9,52.5,98.9
105,0,98.8
90.9,-52.5,99.3
52.5,-90.9,99
0,-105,98.7
-52.5,-90.9,98.9
-90.9,-52.5,99.1
-105,0,98.6
-90.9,52.1,99
-52.5,90.9,99.3
0,140,97.2
70,121.2,97
121.2,70,96.8
140,0,96.5
121.2,-70,96.9
70,-121.2,97.1
0,-140,96.7
-70,-121.2,97.2
-121.2,-70,96.9
-140,0,96.6
-121.2,70,97
-70,121.2,97.3
出力結果(例1)
# 【背景と透明度の設定】
"bg_color": "#f0f0f0", # 背景色 (例: '#ffffff', 'gray' など)
"is_transparent": False, # 背景を透明にするか (True=透ける, False=指定の色で塗りつぶす)
# 【描画オプション】
"show_outline": True, # ウェハの外周線を描画するか
"show_axes": True,
出力結果(例2)
# 【背景と透明度の設定】
"bg_color": "#f0f0f0", # 背景色 (例: '#ffffff', 'gray' など)
"is_transparent": True, # 背景を透明にするか (True=透ける, False=指定の色で塗りつぶす)
# 【描画オプション】
"show_outline": False, # ウェハの外周線を描画するか
"show_axes": False,
まとめ
本スクリプトでは、単なる「点の可視化」から一歩踏み込み、分析に耐えうるヒートマップの生成を自動化しました。
特に scipy.griddata を用いた補間と、vmin/vmax によるカラースケールの固定は、製造現場におけるデータの比較において非常に強力な武器になります。
今後、このスクリプトをベースに、複数ウェハの一括処理などの拡張も検討しています。

