ワードクラウドを生成・表示する
wordcloud の WordCloud クラスに対して .generate() にスペース区切りの単語列を渡すだけで画像オブジェクトが生成されます。生成後は matplotlib の imshow() で表示します。
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# PART 02 で作成した load_and_tokenize() を呼んで単語列を取得
# (ここでは簡略化のためハードコードした単語列を使用)
tokenized_text = (
"データベース 接続 エラー 発生 サービス 停止 コネクション プール 設定 ミス "
"ネットワーク 障害 タイムアウト 多発 ネットワーク 機器 ファームウェア 不具合 "
"メモリ 不足 アプリケーション サーバー クラッシュ メモリ リーク バグ "
"ディスク 容量 不足 ログ 書き込み 失敗 ログ ローテーション 設定 漏れ "
"データベース スロークエリ 応答 時間 増大 インデックス 未設定 "
"認証 サーバー 接続 タイムアウト ログイン 認証 サーバー 過負荷 "
"レートリミット 超過 リクエスト 拒否 バッチ 処理 並列 設定 ミス "
"証明書 期限切れ 通信 切断 証明書 更新 作業 漏れ "
"キャッシュ サーバー 障害 データベース 負荷 集中 キャッシュ 冗長 不足 "
"デプロイ 設定 ファイル 読み込み エラー 環境変数 設定 漏れ"
)
# ── WordCloud 生成 ─────────────────────────────────────────────────────
FONT_PATH = "C:/Windows/Fonts/meiryo.ttc" # ※ OS に合わせて変更
wc = WordCloud(
font_path = FONT_PATH,
width = 800,
height = 400,
background_color = "white",
max_words = 50,
colormap = "tab10",
collocations = False, # 2語連語の自動結合を無効化
prefer_horizontal= 0.9, # 横書き優先率(0.0〜1.0)
)
wc.generate(tokenized_text)
# ── matplotlib で表示 ──────────────────────────────────────────────────
plt.figure(figsize=(12, 6))
plt.imshow(wc, interpolation="bilinear")
plt.axis("off")
plt.title("障害管理表 ワードクラウド", fontsize=16, pad=14)
plt.tight_layout()
plt.show()
╔══════════════════════════════════════════════════════════╗ ║ 障害管理表 ワードクラウド ║ ║ ║ ║ データベース 設定 ネットワーク ║ ║ エラー 障害 タイムアウト 接続 ║ ║ メモリ サーバー ログ キャッシュ ║ ║ ミス 不具合 クラッシュ 証明書 認証 ║ ║ 漏れ インデックス デプロイ 環境変数 容量 ║ ║ ║ ╚══════════════════════════════════════════════════════════╝
「データベース」「設定」「ネットワーク」「エラー」が大きく表示され、これらが障害の主要キーワードであることが一目でわかります。「設定」「ミス」「漏れ」が多く出ていることから、設定作業の見直しが有効な対策であると読み取れます。
日本語フォントの指定
wordcloud は日本語フォントを自動検出しないため、font_path に TrueType フォント (.ttc / .ttf) のフルパスを指定する必要があります。フォントを指定しないと日本語が豆腐(□)になります。
| OS | フォント例 | パス例 |
|---|---|---|
| Windows | メイリオ | C:/Windows/Fonts/meiryo.ttc |
| Windows | 游ゴシック | C:/Windows/Fonts/YuGothM.ttc |
| macOS | ヒラギノ角ゴシック | /System/Library/Fonts/ヒラギノ角ゴシック W3.ttc |
| Linux (Ubuntu) | Noto Sans CJK JP | /usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc |
import sys
import pathlib
def get_default_font() -> str:
"""OS に応じてデフォルト日本語フォントパスを返す"""
if sys.platform == "win32":
candidates = [
pathlib.Path("C:/Windows/Fonts/meiryo.ttc"),
pathlib.Path("C:/Windows/Fonts/YuGothM.ttc"),
]
elif sys.platform == "darwin":
candidates = [
pathlib.Path("/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc"),
pathlib.Path("/System/Library/Fonts/Hiragino Sans GB W3.ttc"),
]
else: # Linux
candidates = [
pathlib.Path("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"),
pathlib.Path("/usr/share/fonts/truetype/fonts-japanese-gothic.ttf"),
]
for path in candidates:
if path.exists():
return str(path)
raise FileNotFoundError(
"日本語フォントが見つかりません。font_path を手動で指定してください。"
)
FONT_PATH = get_default_font()
print(f"使用フォント: {FONT_PATH}")
主要パラメータ一覧
WordCloud の主なパラメータを把握しておくと、出力の見栄えを細かく調整できます。
| パラメータ | デフォルト | 説明 |
|---|---|---|
width | 400 | 画像の幅(ピクセル) |
height | 200 | 画像の高さ(ピクセル) |
background_color | "black" | 背景色。"white" が読みやすい |
max_words | 200 | 表示する最大単語数 |
min_font_size | 4 | 最小フォントサイズ(pt) |
max_font_size | 自動 | 最大フォントサイズ(pt) |
colormap | "viridis" | matplotlib のカラーマップ名 |
collocations | True | False で 2 語連語の自動結合を無効化(日本語では False 推奨) |
prefer_horizontal | 0.9 | 横書き単語の比率。0.5 にすると縦横半々 |
stopwords | STOPWORDS | 除外する単語のセット |
mask | None | 表示形状を定義する NumPy 配列(白 = 空白) |
PNG 画像として保存する
ワードクラウドの保存方法は 2 とおりあります。方法 A(wc.to_file())は WordCloud オブジェクトから直接保存する方法で、コードが最もシンプルです。方法 B(plt.savefig())はタイトルや軸ラベルを含む matplotlib の図全体を保存します。
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# (生成済みの wc オブジェクトがある前提)
# ── 方法 A: WordCloud オブジェクトから直接保存 ─────────────────────────
wc.to_file("wordcloud_incident.png")
print("✅ wordcloud_incident.png を保存しました")
# ── 方法 B: matplotlib の figure ごと保存(タイトル込み) ─────────────────
fig, ax = plt.subplots(figsize=(12, 6))
ax.imshow(wc, interpolation="bilinear")
ax.axis("off")
ax.set_title("障害管理表 ワードクラウド", fontsize=16, pad=14)
fig.tight_layout()
fig.savefig("wordcloud_incident_with_title.png", dpi=150, bbox_inches="tight")
plt.close(fig)
print("✅ wordcloud_incident_with_title.png を保存しました")
💡 dpi を上げると高解像度になる
dpi=150 で通常スクリーン向けの十分なサイズになります。プレゼンテーション用など印刷品質が必要な場合は dpi=300 を指定してください。ファイルサイズは約 4 倍になります。
ストップワードを追加する
形態素解析でノイズ語を除いても、業務固有の不要語(「発生」「対応」「確認」など、どの障害にも必ず出てくる語)が大きく表示されてしまうことがあります。stopwords パラメータにセットを渡して除外できます。
from wordcloud import WordCloud, STOPWORDS
# デフォルトの英語ストップワードセットに追加
custom_stopwords = set(STOPWORDS) | {
# ── 動詞・形容詞の一般語 ──────────────────────────────────────────
"する", "なる", "ある", "れる", "られる",
# ── 汎用名詞(障害票どこにでも出る語) ────────────────────────────
"発生", "対応", "確認", "処理", "作業", "ため", "より", "こと",
}
wc = WordCloud(
font_path = FONT_PATH,
width = 800,
height = 400,
background_color = "white",
max_words = 50,
colormap = "Set2",
collocations = False,
stopwords = custom_stopwords, # ← ここで渡す
)
wc.generate(tokenized_text)
wc.to_file("wordcloud_filtered.png")
print("✅ wordcloud_filtered.png を保存しました")
✅ 効果的なストップワードの見つけ方
まずストップワードなしで生成し、大きく表示されている語のうち「どの障害にも使う一般語」を特定します。その語をストップワードに追加して再生成すると、より業務特有のキーワードが際立ちます。この調整は 2〜3 回繰り返すと最適な結果が得られます。
応用 — 月次自動生成・カテゴリ別分析
ワードクラウドのパイプラインが完成したら、DataFrame のフィルタリングと組み合わせることで多様な分析が可能になります。
月次ワードクラウードの自動生成
import pandas as pd
from wordcloud import WordCloud
import pathlib
def generate_monthly_wordclouds(
filepath: str,
text_cols: list[str],
date_col: str,
output_dir: str = "output",
font_path: str = "C:/Windows/Fonts/meiryo.ttc",
) -> None:
"""月ごとのワードクラウド PNG を output_dir に出力する"""
df = pd.read_excel(filepath)
df[date_col] = pd.to_datetime(df[date_col])
df["年月"] = df[date_col].dt.strftime("%Y-%m")
pathlib.Path(output_dir).mkdir(exist_ok=True)
wc = WordCloud(
font_path = font_path,
width = 800,
height = 400,
background_color = "white",
max_words = 40,
colormap = "tab20",
collocations = False,
)
for month, group in df.groupby("年月"):
texts = group[text_cols].fillna("").apply(
lambda r: "。".join(r.values.astype(str)), axis=1
)
combined = " ".join(texts.tolist())
tokenized = extract_words(combined) # PART 02 の関数
if not tokenized.strip():
print(f"⚠️ {month}: 単語が抽出できませんでした(データなし)")
continue
wc.generate(tokenized)
out = pathlib.Path(output_dir) / f"wordcloud_{month}.png"
wc.to_file(str(out))
print(f"✅ {out} を保存しました")
# 実行
generate_monthly_wordclouds(
filepath = "incident_log.xlsx",
text_cols = ["障害内容", "原因"],
date_col = "発生日",
)
✅ output/wordcloud_2024-01.png を保存しました ✅ output/wordcloud_2024-02.png を保存しました ✅ output/wordcloud_2024-03.png を保存しました ✅ output/wordcloud_2024-04.png を保存しました ✅ output/wordcloud_2024-05.png を保存しました
カテゴリ・重要度別の分析
import pandas as pd
from wordcloud import WordCloud
df = pd.read_excel("incident_log.xlsx")
# ── 例1: 特定の重要度(列があると仮定)に絞る ──────────────────────────
# df_high = df[df["重要度"] == "高"]
# ── 例2: 特定のシステム領域でフィルタ ─────────────────────────────────
# df_db = df[df["カテゴリ"] == "データベース"]
# ── 例3: 期間で絞る ──────────────────────────────────────────────────
df["発生日"] = pd.to_datetime(df["発生日"])
df_q1 = df[df["発生日"].between("2024-01-01", "2024-03-31")]
# フィルタ結果からワードクラウドを生成
texts = df_q1[["障害内容", "原因"]].fillna("").apply(
lambda r: "。".join(r.values.astype(str)), axis=1
)
combined = " ".join(texts.tolist())
tokenized = extract_words(combined) # PART 02 の関数
wc = WordCloud(
font_path = "C:/Windows/Fonts/meiryo.ttc",
width = 800,
height = 400,
background_color = "white",
max_words = 40,
colormap = "Reds",
collocations = False,
)
wc.generate(tokenized)
wc.to_file("wordcloud_q1.png")
print(f"✅ Q1 ({len(df_q1)} 件) のワードクラウドを保存しました")
シリーズ完結版コード
3 パートの処理を 1 ファイルにまとめた完結版です。このファイルだけで「サンプル Excel 生成 → 形態素解析 → ワードクラウド PNG 保存」が完結します。
"""
wordcloud_incident_full.py
==========================
障害管理表 Excel → ワードクラウド PNG 生成の完結版スクリプト。
Usage:
python wordcloud_incident_full.py # サンプルデータを生成して実行
python wordcloud_incident_full.py myfile.xlsx # 既存 Excel を使う
"""
import sys
import pathlib
import pandas as pd
from janome.tokenizer import Tokenizer
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# ── 設定 ───────────────────────────────────────────────────────────────
FONT_PATH = "C:/Windows/Fonts/meiryo.ttc"
OUTPUT_FILE = "wordcloud_incident.png"
TEXT_COLS = ["障害内容", "原因"]
# ── サンプルデータ生成 ─────────────────────────────────────────────────
def create_sample_excel(filepath: str) -> None:
records = [
{"障害ID": "INC-001", "発生日": "2024-01-10",
"障害内容": "データベース接続エラーが発生し、サービスが停止した", "原因": "コネクションプールの設定ミス"},
{"障害ID": "INC-002", "発生日": "2024-01-15",
"障害内容": "ネットワーク障害によりAPIタイムアウトが多発した", "原因": "ネットワーク機器のファームウェア不具合"},
{"障害ID": "INC-003", "発生日": "2024-02-03",
"障害内容": "メモリ不足でアプリケーションサーバーがクラッシュした", "原因": "メモリリークのバグ"},
{"障害ID": "INC-004", "発生日": "2024-02-18",
"障害内容": "ディスク容量不足によりログ書き込みに失敗した", "原因": "ログローテーション設定の漏れ"},
{"障害ID": "INC-005", "発生日": "2024-03-05",
"障害内容": "データベースのスロークエリにより応答時間が増大した", "原因": "インデックス未設定"},
{"障害ID": "INC-006", "発生日": "2024-03-12",
"障害内容": "認証サーバーへの接続タイムアウトでログインできなかった", "原因": "認証サーバーの過負荷"},
{"障害ID": "INC-007", "発生日": "2024-04-01",
"障害内容": "APIのレートリミット超過によりリクエストが拒否された", "原因": "バッチ処理の並列数設定ミス"},
{"障害ID": "INC-008", "発生日": "2024-04-20",
"障害内容": "証明書の期限切れによりHTTPS通信が切断された", "原因": "証明書更新の作業漏れ"},
{"障害ID": "INC-009", "発生日": "2024-05-08",
"障害内容": "キャッシュサーバーの障害でデータベースに負荷が集中した", "原因": "キャッシュの冗長化不足"},
{"障害ID": "INC-010", "発生日": "2024-05-22",
"障害内容": "デプロイ後に設定ファイルの読み込みエラーが発生した", "原因": "環境変数の設定漏れ"},
]
pd.DataFrame(records).to_excel(filepath, index=False)
print(f"✅ {filepath} を作成しました")
# ── 形態素解析 ─────────────────────────────────────────────────────────
def extract_words(text: str) -> str:
t = Tokenizer()
keep_pos = {"名詞", "動詞", "形容詞"}
exclude_sub = {"数", "接尾", "非自立", "代名詞"}
words = []
for token in t.tokenize(text):
parts = token.part_of_speech.split(",")
pos, sub = parts[0], parts[1]
surface = token.surface
if pos not in keep_pos: continue
if pos == "名詞" and sub in exclude_sub: continue
if pos == "動詞" and sub == "非自立": continue
if len(surface) <= 1 or surface.isdigit(): continue
words.append(surface)
return " ".join(words)
# ── メイン ─────────────────────────────────────────────────────────────
def main() -> None:
# 引数でファイルパスを受け取る(省略時はサンプル生成)
excel_path = sys.argv[1] if len(sys.argv) > 1 else "incident_log.xlsx"
if not pathlib.Path(excel_path).exists():
create_sample_excel(excel_path)
# Excel 読み込み → テキスト結合
df = pd.read_excel(excel_path)
combined = " ".join(
df[TEXT_COLS].fillna("").apply(
lambda r: "。".join(r.values.astype(str)), axis=1
).tolist()
)
# 形態素解析
tokenized = extract_words(combined)
print(f"総単語数: {len(tokenized.split())}")
# ワードクラウド生成
wc = WordCloud(
font_path = FONT_PATH,
width = 1200,
height = 600,
background_color = "white",
max_words = 60,
colormap = "tab10",
collocations = False,
)
wc.generate(tokenized)
# 表示
plt.figure(figsize=(14, 7))
plt.imshow(wc, interpolation="bilinear")
plt.axis("off")
plt.title("障害管理表 ワードクラウド", fontsize=18, pad=16)
plt.tight_layout()
# 保存 & 表示
wc.to_file(OUTPUT_FILE)
print(f"✅ {OUTPUT_FILE} を保存しました")
plt.show()
if __name__ == "__main__":
main()
まとめ
3 回シリーズで実装したデータパイプラインを振り返ります。
今回構築したパイプラインは障害管理表に限らず、アンケート自由記述・コールセンターログ・社内レビューコメントなど、Excel にテキスト列を持つあらゆるデータに転用できます。ストップワードの調整と対象列の変更だけで、すぐに別用途に応用できます。
💡 さらに発展させるなら
WordCloud のマスク機能を使えば、会社ロゴや雲の形に単語を配置できます。PIL.Image で読み込んだ画像を NumPy 配列に変換して mask パラメータに渡すだけです。また、Word2Vec や TF-IDF で単語の重み付けを変えることで、単純な出現頻度以上の示唆を得られます。