{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ๐ŸŽฏ [QLT05] ์™ธ์ฃผ์ •๋น„ ํ’ˆ์งˆ๊ฒ€์‚ฌ ์‹ค์ ยท๋ณ‘๋ชฉ ๋ถ„์„\n", "### Google Colab ์‹ค์Šต ํ™˜๊ฒฝ (ํ’ˆ์งˆยท๊ทœ๊ฒฉยท๊ฒ€์‚ฌ / Level 2)\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/edunlab/milicontent2/blob/main/uploads/QLT05/QLT05_solution.ipynb)\n", "\n", "---\n", "\n", "## ๐Ÿ“Œ 1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š” ๋ฐ ๋ฐฐ๊ฒฝ\n", "\n", "| ํ•ญ๋ชฉ | ๋‚ด์šฉ |\n", "| :--- | :--- |\n", "| **๊ณผ์ • ๋ถ„๋ฅ˜** | ํ’ˆ์งˆยท๊ทœ๊ฒฉยท๊ฒ€์‚ฌ (Level 2) |\n", "| **ํ•™์Šต ์†Œ์š” ์‹œ๊ฐ„** | 10๊ฐœ ๋ฏธ์…˜ (5์ผ / ์ด 28.5์‹œ๊ฐ„) |\n", "| **ํ™˜๊ฒฝ ์‚ฌ์–‘** | Python 3.10+, numpy, pandas, matplotlib, scipy, scikit-learn |\n", "\n", "### ๐Ÿ“– ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ\n", "ํ•ด์™ธ ์‚ฌ๋ก€์—์„œ ๊ตญ๋ฐฉ ๊ฐ์‚ฌ๊ธฐ๊ด€์ด ์™ธ์ฃผ์ •๋น„ ๊ฒ€์‚ฌ์‹ค์ ์„ ๋ถ„์„ํ•œ ๊ฒฐ๊ณผ, ๊ฒ€์‚ฌ ๋Œ€๊ธฐ๊ฐ€ ๊ธธ์–ด์ง€๋Š” ๋ณ‘๋ชฉ๊ณผ ์ •๋น„ ์งํ›„ ์žฌ๋ฐœํ•˜๋Š” ๊ฒฐํ•จ์ด ํ’ˆ์งˆ ๋ฌธ์ œ์˜ ์ฃผ์š” ๋‹จ์„œ๋กœ ์ง€์ ๋œ ๋ฐ” ์žˆ๋‹ค. ์•ผ์ „์—์„œ๋„ ์žฅ๋น„ ๊ฒฐํ•จ์ด ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR)์˜ ๋น„์ •ํ˜• ์„œ์ˆ ๋กœ ์Œ“์ด์ง€๋งŒ ๋ถ€์œ„, ์น˜๋ช…๋„, ์›์ธ์ด ์‚ฌ๋žŒ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ์ ํ˜€ ๋‹ค๋ฐœ ๊ฒฐํ•จ๊ณผ ์ฑ…์ž„ ์†Œ์žฌ๋ฅผ ๊ฐ€๋ ค๋‚ด๊ธฐ ์–ด๋ ต๋‹ค. ๋ณธ ํ”„๋กœ์ ํŠธ๋Š” ์™ธ์ฃผ์ •๋น„ ํ’ˆ์งˆ๊ฒ€์‚ฌ ์‹ค์ ๊ณผ QDR ํ…์ŠคํŠธ๋ฅผ ์—ฐ๊ฒฐํ•ด ๋ณ‘๋ชฉ๊ณผ ํ’ˆ์งˆ ์ทจ์•ฝ์ ์„ ๋ฐ์ดํ„ฐ๋กœ ์ฐพ๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ, ๊ต์œก์šฉ ๊ฐ€์ƒ QDR์™€ ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด ์ž๋ฃŒ๋ฅผ ์ฝ”๋“œ๋กœ ๋งŒ๋“ค์–ด ๋ถ„์„ํ•œ๋‹ค. QDR ๋ฐ์ดํ„ฐ ์Šคํ‚ค๋งˆ ์ •์˜, ์„œ์ˆ  ํ…์ŠคํŠธ ์ •์ œ์™€ ๋ถ€์œ„ยท๊ฒฐํ•จ ๋ผ๋ฒจ๋ง, ๋นˆ๋„ Baseline๊ณผ ํŒŒ๋ ˆํ† ยท์ƒ๊ด€ ๋ถ„์„, ํ† ํ”ฝ ๋ชจ๋ธ๋ง๊ณผ ์›”๊ฐ„ ๊ฑด์ˆ˜ ์˜ˆ์ธก, ์น˜๋ช…๋„ ๊ธฐ๋ฐ˜ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜, ์„ค๋ช…๊ฐ€๋Šฅํ•œ ๋ฐœ์ƒ ํ˜„ํ™ฉ ํ™”๋ฉด, ํ’ˆ์งˆ๊ด€ ์Šน์ธ(HITL) ๊ธฐ๋ฐ˜ ํ•˜์ž ๊ตฌ์ƒยท์„ค๊ณ„ ๋ณ€๊ฒฝ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, 10๋…„์น˜ ๋Œ€์šฉ๋Ÿ‰ ์ผ๊ด„ ๋ถ„์„, AAR ์›์ธ ๋ถ„๋ฅ˜ ๋ฃฐ ์žฌํŠœ๋‹, ์žฌํ•™์Šต์„ ํฌํ•จํ•œ ํ†ตํ•ฉ ๋„๊ตฌ๊นŒ์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ๊ตฌํ˜„ํ•œ๋‹ค. ์ตœ์ข… ๋ชฉํ‘œ๋Š” ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€์œ„์™€ ์™ธ์ฃผ์ •๋น„ ๋ณ‘๋ชฉ์„ ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ์‹๋ณ„ํ•˜๋˜, ์ œ์กฐ์‚ฌ ํ•˜์ž ๊ตฌ์ƒ๊ณผ ์„ค๊ณ„ ๋ณ€๊ฒฝ ์†Œ์š” ์ œ๊ธฐ๋Š” ํ’ˆ์งˆ๊ด€์ด ๊ทผ๊ฑฐ๋ฅผ ๋ณด๊ณ  ๊ฒฐ์ •ํ•˜๋„๋ก ์ง€์›ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.\n", "\n", "> **โš ๏ธ [๊ต์œก์šฉ ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ์…‹ ์›์น™]**\n", "> - ๋ณธ ๋…ธํŠธ๋ถ์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ(์ œ์›, ๋กœ๊ทธ, ์‹œ๋‚˜๋ฆฌ์˜ค ๋“ฑ)๋Š” ๋ณด์•ˆ ๊ทœ์ •์„ ์ค€์ˆ˜ํ•˜์—ฌ **์ฝ”๋“œ ๋‚ด ๋‚œ์ˆ˜ ๊ธฐ๋ฐ˜ ๊ฐ€์ƒ ๋ฐ์ดํ„ฐ๋กœ ์ƒ์„ฑ**๋ฉ๋‹ˆ๋‹ค.\n", "> - ์‹ค์ œ ๋ถ€๋Œ€ ์œ„์น˜, ์ž‘์ „ ์ธ๊ฐ€, ์‹ค์ œ ์ „์žฅ ์ •๋ณด, ๊ตฐ์‚ฌ ๊ธฐ๋ฐ€ ๋ฐ์ดํ„ฐ๋Š” ์ผ์ฒด ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.\n", "\n", "---\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ============================================================================\n", "# [Google Colab ํ™˜๊ฒฝ ์„ค์ •] ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ ๋ฐ ์ž„ํฌํŠธ\n", "# ============================================================================\n", "!pip install -q numpy pandas matplotlib scipy scikit-learn\n", "\n", "import os\n", "import sys\n", "import time\n", "import math\n", "import json\n", "import re\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# ๊ทธ๋ž˜ํ”„ ์˜๋ฌธ ํฐํŠธ ๊ธฐ๋ณธ ์„ค์ • (ํ™˜๊ฒฝ ๋…๋ฆฝ์„ฑ ๋ณด์žฅ)\n", "plt.rcParams['font.sans-serif'] = 'DejaVu Sans'\n", "plt.rcParams['axes.unicode_minus'] = False\n", "\n", "print(f\"์‹คํ–‰ ํ™˜๊ฒฝ ์ค€๋น„ ์™„๋ฃŒ (Python {sys.version.split()[0]})\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 01. ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR, Quality Deficiency Report) ๋ฐ์ดํ„ฐ ์Šคํ‚ค๋งˆ ๋ถ„์„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 1.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ๋ฐ์ดํ„ฐ ์Šคํ‚ค๋งˆ๋ฅผ ๋ถ„์„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR)์˜ ๋ณด๊ณ  ์ •๋ณด, ๊ฒฐํ•จ ์„œ์ˆ  ํ…์ŠคํŠธ, ์™ธ์ฃผ์ •๋น„ยท๊ฒ€์‚ฌ ์‹ค์ , ์กฐ์น˜ ๊ฒฐ๊ณผ ๋ฐ์ดํ„ฐ ์Šคํ‚ค๋งˆ์™€ ๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ, ๊ฒฐํ•จ ๋ถ„์„ ์—…๋ฌด ํ”„๋กœ์„ธ์Šค ๋งต์„ ์ •์˜ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **๊ฒฐํ•จ ๋ฐœ๊ฒฌ๋ถ€ํ„ฐ QDR ์ž‘์„ฑ, ์ ‘์ˆ˜ยท๊ฒ€ํ† , ๊ฒ€์‚ฌยทํŒ์ •, ์›์ธ ๋ถ„์„, ์กฐ์น˜, ์‹ค์  ์ง‘๊ณ„, ๋‹ค๋ฐœ ๊ฒฐํ•จ ์‹๋ณ„, ์‚ฌํ›„๊ฒ€ํ† ๊นŒ์ง€ ์—…๋ฌด ํ๋ฆ„์„ ๋‹จ๊ณ„๋ณ„๋กœ ์ •๋ฆฌํ•˜๊ณ  ๋ณด๊ณ ยทํ…์ŠคํŠธยท๊ฒ€์‚ฌยท๊ฒฐ๊ณผ ๋ฌถ์Œ์œผ๋กœ ํ”ผ์ฒ˜ 20๊ฐœ ์ด์ƒ ์ •์˜ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `docs/qlt05_logistics_domain_spec.md ๋ฐ ๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ตฐ์ˆ˜ ์‹ค๋ฌด ์—…๋ฌด ํ”„๋กœ์„ธ์Šค์™€์˜ ์ผ์น˜์„ฑ ๋ฐ ๋ถ„์„ ๋ชฉํ‘œ์˜ ๋ช…ํ™•์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์—…๋ฌด ํ”„๋กœ์„ธ์Šค ๋งต ์ž‘์„ฑ ์™„๋ฃŒ, ํ•„์š” ํ”ผ์ฒ˜(Feature) ๋ชฉ๋ก 20๊ฐœ ์ด์ƒ ์ •์˜\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋„๋ฉ”์ธ ๋ถ„์„์„œ ๋ฐ ๋ฐ์ดํ„ฐ ์š”๊ตฌ๋ช…์„ธ์„œ ์Šน์ธ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๊ตฐ์ˆ˜ ๋‹ด๋‹น ์‹ค๋ฌด์ž ์ธํ„ฐ๋ทฐ ๋ฐ ๊ธฐ์กด ์ „์‚ฐ์ฒด๊ณ„ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๋Œ€์กฐ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ๋ฐ์ดํ„ฐ(๋ณด๊ณ  ์ •๋ณด, ๊ฒฐํ•จ ์„œ์ˆ  ํ…์ŠคํŠธ, ์™ธ์ฃผ์ •๋น„ยท๊ฒ€์‚ฌ ์‹ค์ , ์กฐ์น˜ ๊ฒฐ๊ณผ)์˜ ์Šคํ‚ค๋งˆยท๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ(ํ”ผ์ฒ˜ 20๊ฐœ ์ด์ƒ)์™€ ๊ฒฐํ•จ ๋ถ„์„ ์—…๋ฌด ํ”„๋กœ์„ธ์Šค ๋งต์„ ์ •์˜ํ•˜๊ณ , ๊ฐ€์ƒ QDR ํ‘œ๋ณธ์„ ๊ฒ€์ฆํ•˜๋ฉฐ ์˜ค๋ฅ˜ ์ฃผ์ž…์œผ๋กœ ๊ฒ€์ถœ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•œ๋‹ค.\"\"\"\n", "import json\n", "import re\n", "import numpy as np\n", "import pandas as pd\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# 1) ์—…๋ฌด ํ”„๋กœ์„ธ์Šค ๋งต: ๊ฒฐํ•จ ๋ฐœ์ƒ๋ถ€ํ„ฐ ์›์ธ ํ™˜๋ฅ˜๊นŒ์ง€ (๊ฐ ๋‹จ๊ณ„์—์„œ ์ƒ๊ธฐ๋Š” ๋ฐ์ดํ„ฐ)\n", "PROCESS = [(\"๊ฒฐํ•จ ๋ฐœ๊ฒฌ\", \"๋ฐœ๊ฒฌ ๋ถ€๋Œ€, ์žฅ๋น„, ๋ถ€์œ„, ์„œ์ˆ  ํ…์ŠคํŠธ\"), (\"QDR ์ž‘์„ฑ\", \"QDR ID, ์ž‘์„ฑ์ผ, ์น˜๋ช…๋„(Critical/Major/Minor)\"),\n", " (\"์ ‘์ˆ˜ยท๊ฒ€ํ† \", \"ํ…์ŠคํŠธ ์ •์ œ, ๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜• ๋ผ๋ฒจ\"), (\"๊ฒ€์‚ฌยทํŒ์ •\", \"๊ฒ€์‚ฌ ๋Œ€๊ธฐ์ผ, ์žฌ๊ฒ€์‚ฌ ์—ฌ๋ถ€, ์™ธ์ฃผ์ •๋น„ ์—…์ฒด\"),\n", " (\"์›์ธ ๋ถ„์„\", \"์›์ธ ๊ตฌ๋ถ„(์ œ์กฐยท์ •๋น„ยท์šด์šฉยท์„ค๊ณ„ยท๋…ธํ›„), ์ œ์กฐ์‚ฌ\"), (\"์กฐ์น˜\", \"์ˆ˜๋ฆฌยท๊ตํ™˜ยทํ•˜์ž ๊ตฌ์ƒ ์—ฌ๋ถ€, ์กฐ์น˜ ๋น„์šฉ\"),\n", " (\"์‹ค์  ์ง‘๊ณ„\", \"๋ถ€์œ„๋ณ„ยท์›”๋ณ„ ๊ฑด์ˆ˜, ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜\"), (\"๋‹ค๋ฐœ ๊ฒฐํ•จ ์‹๋ณ„\", \"ํŒŒ๋ ˆํ† (ABC), ์ด์ƒ ์‹ ํ˜ธ\"), (\"์‚ฌํ›„๊ฒ€ํ† (AAR)\", \"์›์ธ ์˜ค๋ถ„๋ฅ˜ ์‚ฌ๋ก€, ๋ฃฐ ์กฐ์ • ์ด๋ ฅ\")]\n", "\n", "# 2) ์Šคํ‚ค๋งˆ(๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ): ๊ทธ๋ฃน / ํ•„๋“œ / ์ž๋ฃŒํ˜• / ๊ทœ์น™ / ์„ค๋ช…\n", "PART_NAMES = [p[0] for p in PARTS]\n", "S = {\n", " \"report\": {\n", " \"qdr_id\": (str, {\"pattern\": r\"^Q\\d{7,8}$\"}, \"QDR ID\"),\n", " \"month\": (int, {\"min\": 0, \"max\": 143}, \"๋ณด๊ณ  ์›”(๋ชจ์˜ ์‹œ์ž‘ ๊ธฐ์ค€ ๊ฒฝ๊ณผ ์›”)\"),\n", " \"unit\": (str, {\"pattern\": r\"^VU-\\d{2}$\"}, \"๋ณด๊ณ  ๋ถ€๋Œ€ ์ฝ”๋“œ(๊ฐ€์ƒ)\"),\n", " \"equip_code\": (str, {\"pattern\": r\"^VX-\\d{4}-\\d{4}$\"}, \"์žฅ๋น„ ์‹๋ณ„์ฝ”๋“œ(๊ตญ๋ฐฉ ํ‘œ์ค€ ์ฝ”๋“œ ํ˜•์‹์„ ๋ชจ์‚ฌํ•œ ๊ฐ€์ƒ ์ฝ”๋“œ)\"),\n", " \"fsc_group\": (str, {\"pattern\": r\"^\\d{4}$\"}, \"๋ณด๊ธ‰ ๋ถ„๋ฅ˜๊ตฐ(๊ฐ€์ƒ 4์ž๋ฆฌ)\"),\n", " \"part\": (str, {\"enum\": PART_NAMES}, \"๊ฒฐํ•จ ๋ถ€์œ„(์žฅ๋น„ ๊ณ„ํ†ต)\"),\n", " \"severity\": (str, {\"enum\": SEV}, \"๊ฒฐํ•จ ์น˜๋ช…๋„\"),\n", " },\n", " \"text\": {\n", " \"text\": (str, {\"min_len\": 5}, \"๊ฒฐํ•จ ์„œ์ˆ  ํ…์ŠคํŠธ(๋น„์ •ํ˜•)\"),\n", " \"defect\": (str, {\"enum\": DNAMES + [\"๋ฏธ๋ถ„๋ฅ˜\"]}, \"๊ฒฐํ•จ ์œ ํ˜• ๋ผ๋ฒจ\"),\n", " \"n_tokens\": (int, {\"min\": 1, \"max\": 200}, \"์ •์ œ ํ›„ ํ† ํฐ ์ˆ˜\"),\n", " \"part_label\": (str, {\"enum\": PART_NAMES + [\"๋ฏธ๋ถ„๋ฅ˜\"]}, \"์„œ์ˆ ์—์„œ ์ถ”์ถœํ•œ ๋ถ€์œ„ ๋ผ๋ฒจ\"),\n", " },\n", " \"inspection\": {\n", " \"vendor\": (str, {\"enum\": VENDORS}, \"์ง์ „ ์ •๋น„ ์ˆ˜ํ–‰์ฒ˜(์™ธ์ฃผ ์—…์ฒด ๋˜๋Š” ๋ถ€๋Œ€์ •๋น„)\"),\n", " \"since_maint\": (int, {\"min\": 0, \"max\": 1500}, \"์ง์ „ ์ •๋น„ ํ›„ ๊ฒฝ๊ณผ์ผ\"),\n", " \"inspect_wait\": (float, {\"min\": 0, \"max\": 60}, \"๊ฒ€์‚ฌ ๋Œ€๊ธฐ์ผ(๊ฒ€์‚ฌ ๋ณ‘๋ชฉ ์ง€ํ‘œ)\"),\n", " \"reinspect\": (bool, {}, \"์žฌ๊ฒ€์‚ฌ ์—ฌ๋ถ€\"),\n", " },\n", " \"result\": {\n", " \"cause\": (str, {\"enum\": CAUSES}, \"์›์ธ ๊ตฌ๋ถ„ - ์˜ˆ์ธก๋Œ€์ƒ 1\"),\n", " \"maker\": (str, {\"pattern\": r\"^MK\\d$\"}, \"์ œ์กฐ์‚ฌ ์ฝ”๋“œ(๊ฐ€์ƒ)\"),\n", " \"cost\": (float, {\"min\": 0, \"max\": 100}, \"์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜ - ์˜ˆ์ธก๋Œ€์ƒ 2\"),\n", " \"monthly_count\": (int, {\"min\": 0, \"max\": 500}, \"๋ถ€์œ„๋ณ„ ์›”๊ฐ„ QDR ๊ฑด์ˆ˜ - ์˜ˆ์ธก๋Œ€์ƒ 3\"),\n", " \"vuln_index\": (float, {\"min\": 0, \"max\": 1000}, \"์น˜๋ช…๋„ ๊ฐ€์ค‘ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ - ์˜ˆ์ธก๋Œ€์ƒ 4\"),\n", " \"event_flag\": (bool, {}, \"์ œ์กฐ LOT ๋ถˆ๋Ÿ‰ ๋“ฑ ๊ธ‰์ฆ ์ด๋ฒคํŠธ ์›” ์—ฌ๋ถ€\"),\n", " \"hitl_status\": (str, {\"enum\": [\"PENDING\", \"APPROVED\", \"REJECTED\", \"NO_RESPONSE\"]}, \"ํ’ˆ์งˆ๊ด€ ๊ฒ€ํ†  ์ƒํƒœ\"),\n", " },\n", "}\n", "\n", "\n", "def check(name, v, typ, rule):\n", " if not isinstance(v, typ) or (typ in (int, float) and isinstance(v, bool)):\n", " return [f\"{name}: ์ž๋ฃŒํ˜• ์˜ค๋ฅ˜({type(v).__name__})\"]\n", " e = []\n", " if \"enum\" in rule and v not in rule[\"enum\"]:\n", " e.append(f\"{name}: ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’ '{v}'\")\n", " if \"min\" in rule and v < rule[\"min\"]:\n", " e.append(f\"{name}: ์ตœ์†Œ๊ฐ’ {rule['min']} ๋ฏธ๋งŒ\")\n", " if \"max\" in rule and v > rule[\"max\"]:\n", " e.append(f\"{name}: ์ตœ๋Œ€๊ฐ’ {rule['max']} ์ดˆ๊ณผ\")\n", " if \"min_len\" in rule and len(v) < rule[\"min_len\"]:\n", " e.append(f\"{name}: ์„œ์ˆ ์ด ๋„ˆ๋ฌด ์งง์Œ\")\n", " if \"pattern\" in rule and not re.match(rule[\"pattern\"], v):\n", " e.append(f\"{name}: ํ˜•์‹ ๋ถˆ์ผ์น˜ '{v}'\")\n", " return e\n", "\n", "\n", "def validate(rec):\n", " errs = []\n", " for g, fields in S.items():\n", " for k, (typ, rule, _) in fields.items():\n", " if k not in rec.get(g, {}):\n", " errs.append(f\"{g}.{k}: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\")\n", " else:\n", " errs += check(f\"{g}.{k}\", rec[g][k], typ, rule)\n", " return errs\n", "\n", "\n", "# 3) [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ƒ์„ฑ ๋ฐ์ดํ„ฐ์—์„œ 20๊ฑด์„ ๋ ˆ์ฝ”๋“œ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด ๊ฒ€์ฆ\n", "df, use, events = make_qdr(36, seed=1)\n", "rng = np.random.default_rng(7)\n", "mc = df.groupby([\"part\", \"month\"]).size()\n", "recs = []\n", "for r in df.sample(20, random_state=1).itertuples():\n", " t = r.text\n", " recs.append({\n", " \"report\": {\"qdr_id\": r.qdr_id, \"month\": int(r.month), \"unit\": r.unit, \"equip_code\": f\"VX-{rng.integers(1000, 9999)}-{rng.integers(1000, 9999)}\",\n", " \"fsc_group\": f\"{rng.integers(1000, 9999)}\", \"part\": PART_NAMES[r.part], \"severity\": SEV[r.sev]},\n", " \"text\": {\"text\": t, \"defect\": r.defect, \"n_tokens\": max(len(t.split()), 1), \"part_label\": PART_NAMES[r.part]},\n", " \"inspection\": {\"vendor\": r.vendor, \"since_maint\": int(r.since_maint), \"inspect_wait\": float(r.inspect_wait), \"reinspect\": bool(r.reinspect)},\n", " \"result\": {\"cause\": r.cause, \"maker\": r.maker, \"cost\": float(r.cost), \"monthly_count\": int(mc[(r.part, r.month)]),\n", " \"vuln_index\": float(sum(np.array([1, 3, 9])[df.loc[(df.part == r.part) & (df.month == r.month), \"sev\"]])), \"event_flag\": bool(r.event), \"hitl_status\": \"PENDING\"}})\n", "ok = sum(not validate(r) for r in recs)\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ QDR {len(df)}๊ฑด ์ƒ์„ฑ, ํ‘œ๋ณธ 20๊ฑด ์ค‘ ๊ฒ€์ฆ ํ†ต๊ณผ {ok}๊ฑด, ์˜ค๋ฅ˜ {len(recs) - ok}๊ฑด\")\n", "print(f\"์ „์ฒด ๋ฐ์ดํ„ฐ ๋ถ€์œ„ยท์น˜๋ช…๋„ยท์›์ธ ํ—ˆ์šฉ๊ฐ’ ๊ฒ€์ฆ: {'ํ†ต๊ณผ' if df['part'].between(0, NP - 1).all() and df['sev'].isin([0, 1, 2]).all() and df['cause'].isin(CAUSES).all() else '์‹คํŒจ'}\")\n", "\n", "# 4) ์˜ค๋ฅ˜ ์ฃผ์ž… ์‹œํ—˜\n", "bad = json.loads(json.dumps(recs[0]))\n", "bad[\"report\"][\"qdr_id\"] = \"QDR-7\" # ID ํ˜•์‹ ์˜ค๋ฅ˜\n", "bad[\"report\"][\"severity\"] = \"Severe\" # ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ์น˜๋ช…๋„\n", "bad[\"text\"][\"text\"] = \"๊ณ ์žฅ\" # ์„œ์ˆ  ๋„ˆ๋ฌด ์งง์Œ\n", "del bad[\"inspection\"][\"vendor\"] # ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\n", "bad[\"inspection\"][\"inspect_wait\"] = \"3์ผ\" # ์ž๋ฃŒํ˜• ์˜ค๋ฅ˜\n", "bad[\"result\"][\"cost\"] = 250.0 # ๋ฒ”์œ„ ์ดˆ๊ณผ\n", "print(\"\\n[์˜ค๋ฅ˜ ์ฃผ์ž… ๊ฒฐ๊ณผ]\")\n", "for e in validate(bad):\n", " print(\" -\", e)\n", "\n", "# 5) ํ”„๋กœ์„ธ์Šค ๋งต๊ณผ ๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ\n", "print(\"\\n[์—…๋ฌด ํ”„๋กœ์„ธ์Šค ๋งต]\")\n", "print(\" -> \".join(f\"{i}.{p}\" for i, (p, _) in enumerate(PROCESS, 1)))\n", "for p, d in PROCESS:\n", " print(f\" {p:10s}: {d}\")\n", "rows = [(g, k, t.__name__, str(r)[:34] if r else \"-\", d) for g, f in S.items() for k, (t, r, d) in f.items()]\n", "dd = pd.DataFrame(rows, columns=[\"๊ทธ๋ฃน\", \"ํ•„๋“œ\", \"์ž๋ฃŒํ˜•\", \"๊ทœ์น™\", \"์„ค๋ช…\"])\n", "print(f\"\\n[๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ] ํ”ผ์ฒ˜ {len(dd)}๊ฐœ (๋ชฉํ‘œ 20๊ฐœ ์ด์ƒ: {'๋‹ฌ์„ฑ' if len(dd) >= 20 else '๋ฏธ๋‹ฌ'})\")\n", "print(dd[[\"๊ทธ๋ฃน\", \"ํ•„๋“œ\", \"์ž๋ฃŒํ˜•\", \"์„ค๋ช…\"]].to_string(index=False))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 01 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ QDR 9221๊ฑด ์ƒ์„ฑ, ํ‘œ๋ณธ 20๊ฑด ์ค‘ ๊ฒ€์ฆ ํ†ต๊ณผ 20๊ฑด, ์˜ค๋ฅ˜ 0๊ฑด\n", "์ „์ฒด ๋ฐ์ดํ„ฐ ๋ถ€์œ„ยท์น˜๋ช…๋„ยท์›์ธ ํ—ˆ์šฉ๊ฐ’ ๊ฒ€์ฆ: ํ†ต๊ณผ\n", "\n", "[์˜ค๋ฅ˜ ์ฃผ์ž… ๊ฒฐ๊ณผ]\n", " - report.qdr_id: ํ˜•์‹ ๋ถˆ์ผ์น˜ 'QDR-7'\n", " - report.severity: ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฐ’ 'Severe'\n", " - text.text: ์„œ์ˆ ์ด ๋„ˆ๋ฌด ์งง์Œ\n", " - inspection.vendor: ํ•„์ˆ˜ ํ•„๋“œ ๋ˆ„๋ฝ\n", " - inspection.inspect_wait: ์ž๋ฃŒํ˜• ์˜ค๋ฅ˜(str)\n", " - result.cost: ์ตœ๋Œ€๊ฐ’ 100 ์ดˆ๊ณผ\n", "\n", "[์—…๋ฌด ํ”„๋กœ์„ธ์Šค ๋งต]\n", "1.๊ฒฐํ•จ ๋ฐœ๊ฒฌ -> 2.QDR ์ž‘์„ฑ -> 3.์ ‘์ˆ˜ยท๊ฒ€ํ†  -> 4.๊ฒ€์‚ฌยทํŒ์ • -> 5.์›์ธ ๋ถ„์„ -> 6.์กฐ์น˜ -> 7.์‹ค์  ์ง‘๊ณ„ -> 8.๋‹ค๋ฐœ ๊ฒฐํ•จ ์‹๋ณ„ -> 9.์‚ฌํ›„๊ฒ€ํ† (AAR)\n", " ๊ฒฐํ•จ ๋ฐœ๊ฒฌ : ๋ฐœ๊ฒฌ ๋ถ€๋Œ€, ์žฅ๋น„, ๋ถ€์œ„, ์„œ์ˆ  ํ…์ŠคํŠธ\n", " QDR ์ž‘์„ฑ : QDR ID, ์ž‘์„ฑ์ผ, ์น˜๋ช…๋„(Critical/Major/Minor)\n", " ์ ‘์ˆ˜ยท๊ฒ€ํ†  : ํ…์ŠคํŠธ ์ •์ œ, ๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜• ๋ผ๋ฒจ\n", " ๊ฒ€์‚ฌยทํŒ์ • : ๊ฒ€์‚ฌ ๋Œ€๊ธฐ์ผ, ์žฌ๊ฒ€์‚ฌ ์—ฌ๋ถ€, ์™ธ์ฃผ์ •๋น„ ์—…์ฒด\n", " ์›์ธ ๋ถ„์„ : ์›์ธ ๊ตฌ๋ถ„(์ œ์กฐยท์ •๋น„ยท์šด์šฉยท์„ค๊ณ„ยท๋…ธํ›„), ์ œ์กฐ์‚ฌ\n", " ์กฐ์น˜ : ์ˆ˜๋ฆฌยท๊ตํ™˜ยทํ•˜์ž ๊ตฌ์ƒ ์—ฌ๋ถ€, ์กฐ์น˜ ๋น„์šฉ\n", " ์‹ค์  ์ง‘๊ณ„ : ๋ถ€์œ„๋ณ„ยท์›”๋ณ„ ๊ฑด์ˆ˜, ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜\n", " ๋‹ค๋ฐœ ๊ฒฐํ•จ ์‹๋ณ„ : ํŒŒ๋ ˆํ† (ABC), ์ด์ƒ ์‹ ํ˜ธ\n", " ์‚ฌํ›„๊ฒ€ํ† (AAR) : ์›์ธ ์˜ค๋ถ„๋ฅ˜ ์‚ฌ๋ก€, ๋ฃฐ ์กฐ์ • ์ด๋ ฅ\n", "\n", "[๋ฐ์ดํ„ฐ ๋”•์…”๋„ˆ๋ฆฌ] ํ”ผ์ฒ˜ 22๊ฐœ (๋ชฉํ‘œ 20๊ฐœ ์ด์ƒ: ๋‹ฌ์„ฑ)\n", " ๊ทธ๋ฃน ํ•„๋“œ ์ž๋ฃŒํ˜• ์„ค๋ช…\n", " report qdr_id str QDR ID\n", " report month int ๋ณด๊ณ  ์›”(๋ชจ์˜ ์‹œ์ž‘ ๊ธฐ์ค€ ๊ฒฝ๊ณผ ์›”)\n", " report unit str ๋ณด๊ณ  ๋ถ€๋Œ€ ์ฝ”๋“œ(๊ฐ€์ƒ)\n", " report equip_code str ์žฅ๋น„ ์‹๋ณ„์ฝ”๋“œ(๊ตญ๋ฐฉ ํ‘œ์ค€ ์ฝ”๋“œ ํ˜•์‹์„ ๋ชจ์‚ฌํ•œ ๊ฐ€์ƒ ์ฝ”๋“œ)\n", " report fsc_group str ๋ณด๊ธ‰ ๋ถ„๋ฅ˜๊ตฐ(๊ฐ€์ƒ 4์ž๋ฆฌ)\n", " report part str ๊ฒฐํ•จ ๋ถ€์œ„(์žฅ๋น„ ๊ณ„ํ†ต)\n", " report severity str ๊ฒฐํ•จ ์น˜๋ช…๋„\n", " text text str ๊ฒฐํ•จ ์„œ์ˆ  ํ…์ŠคํŠธ(๋น„์ •ํ˜•)\n", " text defect str ๊ฒฐํ•จ ์œ ํ˜• ๋ผ๋ฒจ\n", " text n_tokens int ์ •์ œ ํ›„ ํ† ํฐ ์ˆ˜\n", " text part_label str ์„œ์ˆ ์—์„œ ์ถ”์ถœํ•œ ๋ถ€์œ„ ๋ผ๋ฒจ\n", "inspection vendor str ์ง์ „ ์ •๋น„ ์ˆ˜ํ–‰์ฒ˜(์™ธ์ฃผ ์—…์ฒด ๋˜๋Š” ๋ถ€๋Œ€์ •๋น„)\n", "inspection since_maint int ์ง์ „ ์ •๋น„ ํ›„ ๊ฒฝ๊ณผ์ผ\n", "inspection inspect_wait float ๊ฒ€์‚ฌ ๋Œ€๊ธฐ์ผ(๊ฒ€์‚ฌ ๋ณ‘๋ชฉ ์ง€ํ‘œ)\n", "inspection reinspect bool ์žฌ๊ฒ€์‚ฌ ์—ฌ๋ถ€\n", "... (์ดํ•˜ ์ƒ๋žต)\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 02. ๋น„์ •ํ˜• ๊ฒฐํ•จ ์„œ์ˆ  ํ…์ŠคํŠธ ์ •์ œ, ์žฅ๋น„ ๋ถ€์œ„๋ณ„/๊ฒฐํ•จ ์œ ํ˜•๋ณ„ ๋ผ๋ฒจ๋ง ์ „์ฒ˜๋ฆฌ ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋น„์ •ํ˜• ๊ฒฐํ•จ ์„œ์ˆ  ํ…์ŠคํŠธ ์ •์ œ ๋ฐ ๋ถ€์œ„๋ณ„/์œ ํ˜•๋ณ„ ๋ผ๋ฒจ๋ง ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๋น„์ •ํ˜• ๊ฒฐํ•จ ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ์ •์ œํ•˜๊ณ  ์žฅ๋น„ ๋ถ€์œ„๋ณ„ยท๊ฒฐํ•จ ์œ ํ˜•๋ณ„๋กœ ๋ผ๋ฒจ๋งํ•˜๋Š” ์ „์ฒ˜๋ฆฌ ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์ค‘๋ณต QDR ์ œ๊ฑฐ, ๋ถ€๋Œ€ ์ฝ”๋“œ์™€ ์ผ์ž ํ‘œ๊ธฐ ํ†ต์ผ, ์ „๊ฐยทํŠน์ˆ˜๋ฌธ์žยท๊ณต๋ฐฑ ์ •๊ทœํ™”, ์•ฝ์–ด(T/M ๋“ฑ) ํ™•์žฅ, ์‚ฌ์ „ ๊ธฐ๋ฐ˜ ์˜คํƒ€ ๊ต์ •, ์Œ์ˆ˜ ๋Œ€๊ธฐ์ผ ๊ฒฐ์ธก ์ฒ˜๋ฆฌ, KNN ๊ฒฐ์ธก ๋ณด์ •, ๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜• ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง ๋“ฑ ์ •์ œ ๊ทœ์น™ 10์ข…์„ ์ ์šฉ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/etl/qlt05_data_cleansing.py ๋ฐ ์ •์ œ ํ’ˆ์งˆ ๋ณด๊ณ ์„œ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ตฐ์ˆ˜ ํŠน์ˆ˜ ์˜ˆ์™ธ(๋น„๊ณ„ํš ์ •๋น„, ๊ธด๊ธ‰ ๋ถˆ์ถœ ๋“ฑ) ๋ฐ์ดํ„ฐ ๋ณด์กด์˜ ํƒ€๋‹น์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋ฐ์ดํ„ฐ ์ •์ œ ๊ทœ์น™ 10์ข… ์ ์šฉ, ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ ํ†ต๊ณผ์œจ 100%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ETL ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌํ˜„ ๋ฐ ์ •์ œ๋œ ๋งˆ์Šคํ„ฐ ๋ฐ์ดํ„ฐ์…‹ ๊ตฌ์ถ•\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ์ „ํ›„ ๋ฐ์ดํ„ฐ ํ†ต๊ณ„๋Ÿ‰(๊ธฐ์ˆ ํ†ต๊ณ„) ๋น„๊ต ๋ฐ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"ํ‘œ๊ธฐ ๋ถˆ์ผ์น˜ยท์ค‘๋ณตยท์ „๊ฐยทํŠน์ˆ˜๋ฌธ์žยท์•ฝ์–ดยท์˜คํƒ€ยท๋ถˆ๊ฐ€๋Šฅ๊ฐ’ยท๊ฒฐ์ธก์ด ์„ž์ธ ๊ฐ€์ƒ QDR ์›์ฒœ ์ž๋ฃŒ์— ์ •์ œ ๊ทœ์น™ 10์ข…(ํ…์ŠคํŠธ ์ •๊ทœํ™”, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •, KNN ๊ฒฐ์ธก ๋ณด์ •, ๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜• ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง ํฌํ•จ)์„ ์ ์šฉํ•˜๊ณ , ๊ธด๊ธ‰ ๋ณด๊ณ ๋Š” ๋ณด์กดํ•˜๋ฉฐ ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ ํ†ต๊ณผ์œจ๊ณผ ๋ผ๋ฒจ๋ง ์ •ํ™•๋„, ์ •์ œ ์ „ํ›„ ํ†ต๊ณ„๋ฅผ ํ™•์ธํ•œ๋‹ค.\"\"\"\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "from sklearn.impute import KNNImputer\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# ---------------------------------------------------------------------------\n", "# ๊ณตํ†ต ํ…์ŠคํŠธ ์กฐ๊ฐ: ์ •๊ทœํ™”, ๊ฐ„์ด ํ† ํฐํ™”(์กฐ์‚ฌ ์ œ๊ฑฐ), ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ยท์›์ธ ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\n", "# ---------------------------------------------------------------------------\n", "TYPO = {\"๋ˆ„์œณ\": \"๋ˆ„์œ \", \"๊ท ๋ ฌ\": \"๊ท ์—ด\", \"์ ‘์ถ• ๋ถˆ๋Ÿ‰\": \"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋ถ€์”ฉ\": \"๋ถ€์‹\", \"๋งˆ๋ฌ˜\": \"๋งˆ๋ชจ\", \"๊ณผ๋ ฌ\": \"๊ณผ์—ด\", \"์˜ค์ž‘๋˜ฅ\": \"์˜ค์ž‘๋™\", \"์žฌ๋™\": \"์ œ๋™\"}\n", "ABBR = {\"T/M\": \"๋ณ€์†๊ธฐ\", \"BAT\": \"๋ฐฐํ„ฐ๋ฆฌ\"}\n", "PART_KW = sorted([(a, i) for i, p in enumerate(PARTS) for a in p[1]], key=lambda x: -len(x[0]))\n", "DEF_KW = {\"๊ท ์—ด\": [\"๊ท ์—ด\", \"ํฌ๋ž™\", \"๊ฐˆ๋ผ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ \", \"์ƒˆ์–ด\", \"๋ˆ„์„ค\"], \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ\", \"๋‹ณ์€\"], \"๋ถ€์‹\": [\"๋ถ€์‹\", \"๋…น์ด\", \"์‚ฐํ™”\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋‹จ์„ \", \"๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ\", \"์ฒด๊ฒฐ\", \"ํ—๊ฑฐ\"], \"๊ณผ์—ด\": [\"๊ณผ์—ด\", \"์˜จ๋„\", \"์—ด์ด\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™\", \"๋ถˆ์•ˆ์ •\", \"๋ฉˆ์ถค\"]}\n", "SEV_KW = [(2, [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€\", \"์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์œ„ํ—˜\"]), (1, [\"์„ฑ๋Šฅ ์ €ํ•˜\", \"์กฐ์†ํ•œ\"]), (0, [\"๊ฒฝ๋ฏธ\", \"์ง€์žฅ์ด ์—†\"])]\n", "CAUSE_KW = {\"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ\", \"์ถœ๊ณ \", \"์ธ์ˆ˜\"], \"์ •๋น„ ํ’ˆ์งˆ\": [\"์ •๋น„ ํ›„\", \"์žฌ์กฐ๋ฆฝ\", \"์ •๋น„ ์งํ›„\"], \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"์ถฉ๊ฒฉ\", \"๊ณผ๋ถ€ํ•˜\", \"์ ๊ฒ€ ์—†์ด\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ\", \"๊ตฌ์กฐ์ \", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€\"], \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ\", \"๋…ธํ›„\", \"์ˆ˜๋ช…\"]}\n", "PARTICLES = (\"์—์„œ\", \"์œผ๋กœ\", \"์ด\", \"๊ฐ€\", \"์€\", \"๋Š”\", \"์„\", \"๋ฅผ\", \"์—\", \"์˜\", \"๋กœ\", \"์™€\", \"๊ณผ\", \"๋„\", \"ํ•จ\", \"์Œ\", \"๋จ\")\n", "\n", "\n", "def norm(t):\n", " \"\"\"์ •๊ทœํ™”: ์ „๊ฐ->๋ฐ˜๊ฐ, ํŠน์ˆ˜๋ฌธ์žยท์ค‘๋ณต ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •\"\"\"\n", " t = unicodedata.normalize(\"NFKC\", str(t))\n", " t = re.sub(r\"[!#~*@^]+\", \" \", t)\n", " for a, b in ABBR.items():\n", " t = t.replace(a, b)\n", " for a, b in TYPO.items():\n", " t = t.replace(a, b)\n", " return re.sub(r\"\\s+\", \" \", t).strip()\n", "\n", "\n", "def tok(t):\n", " out = []\n", " for w in re.split(r\"[\\s.,]+\", norm(t)):\n", " if not w or re.fullmatch(r\"[\\d\\-A-Za-z]+\", w):\n", " continue\n", " for p in PARTICLES:\n", " if len(w) > len(p) + 1 and w.endswith(p):\n", " w = w[:-len(p)]\n", " break\n", " out.append(w)\n", " return out\n", "\n", "\n", "def label_part(t):\n", " for a, i in PART_KW:\n", " if a in t:\n", " return i\n", " return -1\n", "\n", "\n", "def label_defect(t):\n", " for d, ks in DEF_KW.items():\n", " if any(k in t for k in ks):\n", " return d\n", " return \"๋ฏธ๋ถ„๋ฅ˜\"\n", "\n", "\n", "def label_sev(t):\n", " for s, ks in SEV_KW:\n", " if any(k in t for k in ks):\n", " return s\n", " return -1\n", "\n", "\n", "def label_cause(t):\n", " for c, ks in CAUSE_KW.items():\n", " if any(k in t for k in ks):\n", " return c\n", " return \"๋ฏธ์ƒ\"\n", "\n", "# 1) [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์›์ฒœ ์ž๋ฃŒ: ์ƒ์„ฑ ์ž๋ฃŒ์— ์ผ๋ถ€๋Ÿฌ ์˜ค๋ฅ˜๋ฅผ ์„ž๋Š”๋‹ค\n", "df0, use, events = make_qdr(36, seed=1)\n", "rng = np.random.default_rng(2)\n", "raw = df0[[\"qdr_id\", \"month\", \"unit\", \"vendor\", \"since_maint\", \"inspect_wait\", \"text\", \"part\", \"defect\"]].copy()\n", "raw[\"date\"] = [f\"2024-{m % 12 + 1:02d}-{rng.integers(1, 28):02d}\" for m in raw[\"month\"]]\n", "raw[\"urgent\"] = False\n", "n = len(raw)\n", "urg = rng.choice(n, 40, replace=False)\n", "raw.loc[urg, \"inspect_wait\"] = 0.0; raw.loc[urg, \"urgent\"] = True # ๊ธด๊ธ‰ ๋ณด๊ณ : ๋Œ€๊ธฐ์ผ 0์€ ์ •์ƒ ์˜ˆ์™ธ\n", "raw.loc[urg, \"text\"] = \"๊ธด๊ธ‰ ๋ณด๊ณ . \" + raw.loc[urg, \"text\"]\n", "\n", "\n", "def pick(k):\n", " return rng.choice(n, k, replace=False)\n", "\n", "\n", "i2 = pick(300)\n", "raw.loc[i2, \"unit\"] = [u.lower() if j % 3 == 0 else u.replace(\"-\", \"\") if j % 3 == 1 else \"VU-\" + str(int(u[-2:])) for j, u in enumerate(raw.loc[i2, \"unit\"])]\n", "i3 = pick(400); raw.loc[i3, \"date\"] = [d.replace(\"-\", \".\") if j % 2 else d[2:].replace(\"-\", \"/\") for j, d in enumerate(raw.loc[i3, \"date\"])]\n", "i4 = pick(600); raw.loc[i4, \"text\"] = [t.replace(\". \", \". \").replace(\"์—์„œ\", \"์—์„œ \").replace(\"ํ•จ.\", \"ํ•จ!!\") + \" ##\" if j % 2 else unicodedata.normalize(\"NFKC\", t).replace(\".\", \"๏ผŽ\") for j, t in enumerate(raw.loc[i4, \"text\"])]\n", "i5 = pick(300); raw.loc[i5, \"text\"] = [t.replace(\"๋ณ€์†๊ธฐ\", \"T/M\").replace(\"๋ฐฐํ„ฐ๋ฆฌ\", \"BAT\") for t in raw.loc[i5, \"text\"]]\n", "i6 = rng.choice(np.where(raw[\"text\"].str.contains(\"๋ˆ„์œ |๊ท ์—ด|๋ถ€์‹|์ œ๋™|๊ณผ์—ด\"))[0], 400, replace=False); raw.loc[i6, \"text\"] = [t.replace(\"๋ˆ„์œ \", \"๋ˆ„์œณ\").replace(\"๊ท ์—ด\", \"๊ท ๋ ฌ\").replace(\"๋ถ€์‹\", \"๋ถ€์”ฉ\").replace(\"์ œ๋™\", \"์žฌ๋™\").replace(\"๊ณผ์—ด\", \"๊ณผ๋ ฌ\") for t in raw.loc[i6, \"text\"]]\n", "i7 = pick(80); raw.loc[i7, \"inspect_wait\"] = -1.0\n", "i8 = pick(400); raw.loc[i8, \"since_maint\"] = np.nan\n", "dup = raw.sample(150, random_state=3); raw = pd.concat([raw, dup], ignore_index=True)\n", "print(f\"์›์ฒœ ์ž๋ฃŒ {len(raw)}ํ–‰ (์ •์ƒ {n}ํ–‰ + ๊ธด๊ธ‰ ๋ณด๊ณ  {len(urg)}๊ฑด + ์ค‘๋ณต {len(dup)}ํ–‰, ์˜ค๋ฅ˜ ์ฃผ์ž… {len(i2) + len(i3) + len(i4) + len(i5) + len(i6) + len(i7) + len(i8)}ํ–‰)\")\n", "\n", "# 2) ์ •์ œ ๊ทœ์น™ 10์ข…\n", "log = []\n", "\n", "\n", "def rule(name, cnt):\n", " log.append((name, int(cnt)))\n", "\n", "\n", "df = raw.copy()\n", "before_len = len(df)\n", "df = df.drop_duplicates(\"qdr_id\").reset_index(drop=True); rule(\"R1 ์ค‘๋ณต QDR ์ œ๊ฑฐ\", before_len - len(df))\n", "fix = lambda u: \"VU-%02d\" % int(re.sub(r\"\\D\", \"\", u))\n", "bad = ~df[\"unit\"].str.fullmatch(r\"VU-\\d{2}\"); df[\"unit\"] = df[\"unit\"].map(fix); rule(\"R2 ๋ถ€๋Œ€ ์ฝ”๋“œ ํ‘œ๊ธฐ ํ†ต์ผ\", bad.sum())\n", "bad = ~df[\"date\"].str.fullmatch(r\"\\d{4}-\\d{2}-\\d{2}\")\n", "df[\"date\"] = [pd.to_datetime(d.replace(\".\", \"-\"), format=\"%Y-%m-%d\" if len(d) == 10 else \"%y-%m-%d\", errors=\"coerce\") if \"/\" not in d else pd.to_datetime(d, format=\"%y/%m/%d\") for d in df[\"date\"]]\n", "rule(\"R3 ์ผ์ž ํ˜•์‹ ํ†ต์ผ\", bad.sum())\n", "t0 = df[\"text\"].copy()\n", "df[\"text\"] = df[\"text\"].map(lambda t: re.sub(r\"\\s+\", \" \", re.sub(r\"[!#~*@^]+\", \" \", unicodedata.normalize(\"NFKC\", t))).strip()); rule(\"R4 ํ…์ŠคํŠธ ์ •๊ทœํ™”(์ „๊ฐยทํŠน์ˆ˜๋ฌธ์žยท๊ณต๋ฐฑ)\", (df[\"text\"] != t0).sum())\n", "c = df[\"text\"].str.contains(\"T/M|BAT\"); df[\"text\"] = df[\"text\"].map(norm); rule(\"R5 ์•ฝ์–ด ํ™•์žฅ(T/M, BAT)\", c.sum())\n", "c = t0.map(lambda t: any(k in t for k in TYPO))\n", "rule(\"R6 ์˜คํƒ€ ๊ต์ •(์‚ฌ์ „ ๊ธฐ๋ฐ˜)\", c.sum())\n", "bad = df[\"inspect_wait\"] < 0; df.loc[bad, \"inspect_wait\"] = np.nan; rule(\"R7 ๋ถˆ๊ฐ€๋Šฅ๊ฐ’(์Œ์ˆ˜ ๋Œ€๊ธฐ์ผ) ๊ฒฐ์ธก ์ฒ˜๋ฆฌ\", bad.sum())\n", "miss = df[\"since_maint\"].isna() | df[\"inspect_wait\"].isna()\n", "X = pd.DataFrame({\"part\": df[\"part\"], \"vendor\": df[\"vendor\"].astype(\"category\").cat.codes * 5, \"wait\": df[\"inspect_wait\"], \"since\": df[\"since_maint\"], \"urgent\": df[\"urgent\"] * 20})\n", "df[[\"inspect_wait\", \"since_maint\"]] = KNNImputer(n_neighbors=5).fit_transform(X)[:, 2:4]; rule(\"R8 ๋Œ€๊ธฐ์ผยท์ •๋น„ํ›„๊ฒฝ๊ณผ์ผ KNN ๋ณด์ •\", miss.sum())\n", "df[\"part_label\"] = df[\"text\"].map(label_part); df[\"defect_label\"] = df[\"text\"].map(label_defect); rule(\"R9 ๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜• ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\", len(df))\n", "flag = (df[\"inspect_wait\"] > 40) | ((df[\"inspect_wait\"] == 0) & ~df[\"urgent\"])\n", "df[\"flag\"] = flag\n", "rule(\"R10 ๊ธด๊ธ‰ ๋ณด๊ณ  ๋ณด์กด(์ผ๋ฐ˜ ์ด์ƒ๊ฐ’์€ ํ‘œ์‹œ๋งŒ)\", int(df[\"urgent\"].sum()))\n", "print(\"\\n[์ ์šฉ ๊ทœ์น™๊ณผ ์ฒ˜๋ฆฌ ๊ฑด์ˆ˜]\")\n", "for nm, c in log:\n", " print(f\" {nm:40s} {c:6d}๊ฑด\")\n", "\n", "# 3) ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ + ๋ผ๋ฒจ๋ง ์ •ํ™•๋„\n", "truth = df.merge(df0[[\"qdr_id\", \"part\", \"defect\"]], on=\"qdr_id\", suffixes=(\"\", \"_t\"))\n", "rawlab = raw.drop_duplicates(\"qdr_id\").assign(pl=lambda d: d[\"text\"].map(label_part), dl=lambda d: d[\"text\"].map(label_defect))\n", "rawacc_p, rawacc_d = (rawlab[\"pl\"] == rawlab[\"part\"]).mean(), (rawlab[\"dl\"] == rawlab[\"defect\"]).mean()\n", "acc_p, acc_d = (truth[\"part_label\"] == truth[\"part_t\"]).mean(), (truth[\"defect_label\"] == truth[\"defect_t\"]).mean()\n", "checks = {\n", " \"R1 ID ์œ ์ผ\": df[\"qdr_id\"].is_unique, \"R2 ๋ถ€๋Œ€ ์ฝ”๋“œ ํ˜•์‹\": df[\"unit\"].str.fullmatch(r\"VU-\\d{2}\").all(), \"R3 ์ผ์ž ๋ณ€ํ™˜\": df[\"date\"].notna().all(),\n", " \"R4 ํŠน์ˆ˜๋ฌธ์žยท์ด์ค‘ ๊ณต๋ฐฑ ์—†์Œ\": not df[\"text\"].str.contains(r\"[!#]|\\s\\s|๏ผŽ\").any(), \"R5 ์•ฝ์–ด ์—†์Œ\": not df[\"text\"].str.contains(\"T/M|BAT\").any(),\n", " \"R6 ์˜คํƒ€ ์—†์Œ\": not df[\"text\"].map(lambda t: any(k in t for k in TYPO)).any(), \"R7 ์Œ์ˆ˜ ๋Œ€๊ธฐ์ผ ์—†์Œ\": (df[\"inspect_wait\"] >= 0).all(),\n", " \"R8 ๊ฒฐ์ธก ์—†์Œ\": df[[\"inspect_wait\", \"since_maint\"]].notna().all().all(), \"R9 ๋ถ€์œ„ ๋ผ๋ฒจ ๋ถ€์—ฌ์œจ 100%\": (df[\"part_label\"] >= 0).all(),\n", " \"R10 ๊ธด๊ธ‰ ๋ณด๊ณ  40๊ฑด ๋ณด์กด\": int(df[\"urgent\"].sum()) == 40}\n", "npass = sum(checks.values())\n", "print(f\"\\n์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ {npass}/10 ํ†ต๊ณผ, ํ†ต๊ณผ์œจ {npass / 10:.0%} (๋ชฉํ‘œ 100%: {'๋‹ฌ์„ฑ' if npass == 10 else '๋ฏธ๋‹ฌ'})\")\n", "print(f\"๋ผ๋ฒจ๋ง ์ •ํ™•๋„: ๋ถ€์œ„ {rawacc_p:.1%} -> {acc_p:.1%}, ๊ฒฐํ•จ ์œ ํ˜• {rawacc_d:.1%} -> {acc_d:.1%} (์ •์ œ ์ „ -> ํ›„)\")\n", "print(f\"๊ธด๊ธ‰ ๋ณด๊ณ  {int(df['urgent'].sum())}๊ฑด ๋ณด์กด, ์ผ๋ฐ˜ ์ด์ƒ ํ‘œ์‹œ {int((df['flag'] & ~df['urgent']).sum())}๊ฑด\")\n", "\n", "# 4) ์ •์ œ ์ „ํ›„ ๊ธฐ์ˆ ํ†ต๊ณ„\n", "def stats(d, after):\n", " return {\"ํ–‰ ์ˆ˜\": len(d), \"๋ถ€๋Œ€ ํ‘œ๊ธฐ ์ข…๋ฅ˜ ์ˆ˜\": d[\"unit\"].nunique(), \"์„œ์ˆ  ํ‰๊ท  ๊ธธ์ด(์ž)\": round(d[\"text\"].str.len().mean(), 1),\n", " \"๋Œ€๊ธฐ์ผ ์ตœ์†Œ\": round(d[\"inspect_wait\"].min(), 1), \"๋Œ€๊ธฐ์ผ ๊ฒฐ์ธก ์ˆ˜\": int(d[\"inspect_wait\"].isna().sum()), \"์ •๋น„ํ›„๊ฒฝ๊ณผ์ผ ๊ฒฐ์ธก ์ˆ˜\": int(d[\"since_maint\"].isna().sum())}\n", "print(\"\\n[์ •์ œ ์ „ํ›„ ๊ธฐ์ˆ ํ†ต๊ณ„]\")\n", "print(pd.DataFrame({\"์ •์ œ ์ „\": stats(raw, False), \"์ •์ œ ํ›„\": stats(df, True)}).to_string())\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 02 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "์›์ฒœ ์ž๋ฃŒ 9371ํ–‰ (์ •์ƒ 9221ํ–‰ + ๊ธด๊ธ‰ ๋ณด๊ณ  40๊ฑด + ์ค‘๋ณต 150ํ–‰, ์˜ค๋ฅ˜ ์ฃผ์ž… 2480ํ–‰)\n", "\n", "[์ ์šฉ ๊ทœ์น™๊ณผ ์ฒ˜๋ฆฌ ๊ฑด์ˆ˜]\n", " R1 ์ค‘๋ณต QDR ์ œ๊ฑฐ 150๊ฑด\n", " R2 ๋ถ€๋Œ€ ์ฝ”๋“œ ํ‘œ๊ธฐ ํ†ต์ผ 238๊ฑด\n", " R3 ์ผ์ž ํ˜•์‹ ํ†ต์ผ 400๊ฑด\n", " R4 ํ…์ŠคํŠธ ์ •๊ทœํ™”(์ „๊ฐยทํŠน์ˆ˜๋ฌธ์žยท๊ณต๋ฐฑ) 600๊ฑด\n", " R5 ์•ฝ์–ด ํ™•์žฅ(T/M, BAT) 294๊ฑด\n", " R6 ์˜คํƒ€ ๊ต์ •(์‚ฌ์ „ ๊ธฐ๋ฐ˜) 400๊ฑด\n", " R7 ๋ถˆ๊ฐ€๋Šฅ๊ฐ’(์Œ์ˆ˜ ๋Œ€๊ธฐ์ผ) ๊ฒฐ์ธก ์ฒ˜๋ฆฌ 80๊ฑด\n", " R8 ๋Œ€๊ธฐ์ผยท์ •๋น„ํ›„๊ฒฝ๊ณผ์ผ KNN ๋ณด์ • 474๊ฑด\n", " R9 ๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜• ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง 9221๊ฑด\n", " R10 ๊ธด๊ธ‰ ๋ณด๊ณ  ๋ณด์กด(์ผ๋ฐ˜ ์ด์ƒ๊ฐ’์€ ํ‘œ์‹œ๋งŒ) 40๊ฑด\n", "\n", "์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ 10/10 ํ†ต๊ณผ, ํ†ต๊ณผ์œจ 100% (๋ชฉํ‘œ 100%: ๋‹ฌ์„ฑ)\n", "๋ผ๋ฒจ๋ง ์ •ํ™•๋„: ๋ถ€์œ„ 98.5% -> 100.0%, ๊ฒฐํ•จ ์œ ํ˜• 96.8% -> 100.0% (์ •์ œ ์ „ -> ํ›„)\n", "๊ธด๊ธ‰ ๋ณด๊ณ  40๊ฑด ๋ณด์กด, ์ผ๋ฐ˜ ์ด์ƒ ํ‘œ์‹œ 1๊ฑด\n", "\n", "[์ •์ œ ์ „ํ›„ ๊ธฐ์ˆ ํ†ต๊ณ„]\n", " ์ •์ œ ์ „ ์ •์ œ ํ›„\n", "ํ–‰ ์ˆ˜ 9371.0 9221.0\n", "๋ถ€๋Œ€ ํ‘œ๊ธฐ ์ข…๋ฅ˜ ์ˆ˜ 98.0 30.0\n", "์„œ์ˆ  ํ‰๊ท  ๊ธธ์ด(์ž) 56.4 56.1\n", "๋Œ€๊ธฐ์ผ ์ตœ์†Œ -1.0 0.0\n", "๋Œ€๊ธฐ์ผ ๊ฒฐ์ธก ์ˆ˜ 0.0 0.0\n", "์ •๋น„ํ›„๊ฒฝ๊ณผ์ผ ๊ฒฐ์ธก ์ˆ˜ 406.0 0.0\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 03. ๋นˆ๋„ ๋ถ„์„ ๊ธฐ๋ฐ˜ 1์ฐจ ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€ํ’ˆ ๋„์ถœ Baseline ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 2.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋นˆ๋„ ๋ถ„์„ ๊ธฐ๋ฐ˜ ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€ํ’ˆ ๋„์ถœ Baseline ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๋นˆ๋„ ๋ถ„์„ ๊ธฐ๋ฐ˜ 1์ฐจ ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€์œ„ ๋„์ถœ Baseline์„ ๊ตฌํ˜„ํ•˜๊ณ , ํƒ์ƒ‰์  ๋ถ„์„์œผ๋กœ ๋นˆ๋„ ๊ธฐ์ค€์˜ ํ•œ๊ณ„์™€ ์™ธ์ฃผ์ •๋น„ ๊ฒ€์‚ฌ ๋ณ‘๋ชฉ, ํ•ต์‹ฌ ๊ด€๋ฆฌ ๋Œ€์ƒ์„ ์ฐพ๋Š”๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **ํ•™์Šต ๊ธฐ๊ฐ„ ๊ฑด์ˆ˜ ์ƒ์œ„ 5๊ฐœ ๋ถ€์œ„๋ฅผ ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€์œ„๋กœ ์ง€์ •ํ•ด ๊ฒ€์ฆ ๊ธฐ๊ฐ„ ์ ์ค‘๋ฅ ๊ณผ ์น˜๋ช…๋„ ๊ฐ€์ค‘ ์ง€์ˆ˜ ์ˆœ์œ„์™€์˜ ์ฐจ์ด ํ™•์ธํ•˜๊ณ  ์œ ํšจ์„ฑ์„ ์ ๊ฒ€ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `notebooks/qlt05_eda_analysis.ipynb ๋ฐ EDA ์š”์•ฝ ๋ฆฌํฌํŠธ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ตฐ์ˆ˜ ์˜์‚ฌ๊ฒฐ์ •์— ์ง๊ด€์ ์ธ ์ธ์‚ฌ์ดํŠธ ์ œ๊ณต ์ˆ˜์ค€\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: EDA ์‹œ๊ฐํ™” ์ฐจํŠธ 8์ข… ์ด์ƒ ์ƒ์„ฑ, ์œ ์˜๋ฏธํ•œ ์ƒ๊ด€๊ด€๊ณ„ ํ”ผ์ฒ˜ 5๊ฐœ ์ด์ƒ ๋„์ถœ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: EDA ๋…ธํŠธ๋ถ ์‹คํ–‰ ์™„๋ฃŒ ๋ฐ ํ•ต์‹ฌ ๋ฐœ๊ฒฌ์ (Key Findings) ๋ฌธ์„œํ™”\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ํ†ต๊ณ„์  ๊ฐ€์„ค ๊ฒ€์ •(p-value, ์ƒ๊ด€๊ณ„์ˆ˜)์„ ํ†ตํ•œ ์œ ์˜์„ฑ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"๋นˆ๋„ ๋ถ„์„ ๊ธฐ๋ฐ˜ 1์ฐจ ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€์œ„ ๋„์ถœ Baseline(ํ•™์Šต ๊ธฐ๊ฐ„ ๊ฑด์ˆ˜ ์ƒ์œ„ 5๊ฐœ ๋ถ€์œ„)์˜ ์ ์ค‘๋ฅ ๊ณผ ํ•œ๊ณ„(์น˜๋ช…๋„ยท๋น„์šฉ ์ˆœ์œ„์™€์˜ ์ฐจ์ด)๋ฅผ ํ™•์ธํ•˜๊ณ , ํƒ์ƒ‰์  ๋ถ„์„(EDA ์ฐจํŠธ 8์ข…, ์›”๊ฐ„ QDR ๊ฑด์ˆ˜์™€ ํ”ผ์ฒ˜์˜ ์ƒ๊ด€๊ณ„์ˆ˜ยทp-value, ์™ธ์ฃผ์ •๋น„ ์—…์ฒด๋ณ„ ๊ฒ€์‚ฌ ๋Œ€๊ธฐยท์›์ธ, ๊ฑด์ˆ˜ยท๋น„์šฉ ํŒŒ๋ ˆํ†  ABC ๋ถ„๋ฅ˜)์œผ๋กœ ํ•ต์‹ฌ ๊ด€๋ฆฌ ๋Œ€์ƒ์„ ์‹๋ณ„ํ•œ๋‹ค.\"\"\"\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "from scipy.stats import spearmanr\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "df, use, events = make_qdr(36, seed=1)\n", "names = [p[0] for p in PARTS]\n", "tr, va = df[df[\"month\"] < 24], df[df[\"month\"] >= 24]\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR {len(df)}๊ฑด, ๋ถ€์œ„ {NP}์ข…, 36๊ฐœ์›” (ํ•™์Šต 24๊ฐœ์›” {len(tr)}๊ฑด, ๊ฒ€์ฆ 12๊ฐœ์›” {len(va)}๊ฑด)\")\n", "\n", "# 1) Baseline: ํ•™์Šต ๊ธฐ๊ฐ„ ๊ฑด์ˆ˜ ์ƒ์œ„ 5๊ฐœ ๋ถ€์œ„๋ฅผ ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€์œ„๋กœ ์ง€์ •\n", "cnt_tr = tr.groupby(\"part\").size().sort_values(ascending=False)\n", "top_base = list(cnt_tr.index[:5])\n", "cnt_va = va.groupby(\"part\").size().sort_values(ascending=False)\n", "vuln = lambda d: d.groupby(\"part\")[\"sev\"].apply(lambda s: np.array([1, 3, 9])[s].sum()).sort_values(ascending=False)\n", "v_va = vuln(va)\n", "monthly_hit = np.mean([len(set(top_base) & set(g.groupby(\"part\").size().sort_values(ascending=False).index[:5])) / 5 for _, g in va.groupby(\"month\")])\n", "print(f\"Baseline ์ƒ์œ„ 5 ๋ถ€์œ„: {', '.join(names[i] for i in top_base)}\")\n", "print(f\" ๊ฒ€์ฆ ๊ธฐ๊ฐ„ ๊ฑด์ˆ˜ ์ƒ์œ„ 5์™€ ์ผ์น˜ {len(set(top_base) & set(cnt_va.index[:5]))}/5, ์›”๋ณ„ ํ‰๊ท  ์ ์ค‘๋ฅ  {monthly_hit:.0%}\")\n", "print(f\" ์น˜๋ช…๋„ ๊ฐ€์ค‘ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์ƒ์œ„ 5์™€ ์ผ์น˜ {len(set(top_base) & set(v_va.index[:5]))}/5 -> ๊ฑด์ˆ˜๋งŒ ๋ณด๋ฉด ๋†“์น˜๋Š” ๋ถ€์œ„: \" + \", \".join(names[i] for i in set(v_va.index[:5]) - set(top_base)))\n", "crit = va[va[\"sev\"] == 2]\n", "miss = (~crit[\"part\"].isin(top_base)).mean()\n", "print(f\" Critical ๊ฒฐํ•จ {len(crit)}๊ฑด ์ค‘ Baseline ๋Œ€์ƒ ๋ฐ– ๋ถ€์œ„ ๋น„์ค‘ {miss:.0%}\")\n", "\n", "# 2) ํŒŒ๋ ˆํ† (ABC): ๊ฑด์ˆ˜์™€ ์กฐ์น˜ ๋น„์šฉ\n", "par = pd.DataFrame({\"๋ถ€์œ„\": [names[i] for i in cnt_tr.index], \"๊ฑด์ˆ˜\": cnt_tr.values}).set_index(\"๋ถ€์œ„\")\n", "par[\"๋น„์šฉ์ง€์ˆ˜\"] = [df.loc[df[\"part\"] == i, \"cost\"].sum() for i in cnt_tr.index]\n", "pc = par.sort_values(\"๋น„์šฉ์ง€์ˆ˜\", ascending=False)\n", "pc[\"๋ˆ„์ ๋น„์œจ\"] = pc[\"๋น„์šฉ์ง€์ˆ˜\"].cumsum() / pc[\"๋น„์šฉ์ง€์ˆ˜\"].sum()\n", "pc[\"๋“ฑ๊ธ‰\"] = np.where(pc[\"๋ˆ„์ ๋น„์œจ\"] <= 0.7, \"A\", np.where(pc[\"๋ˆ„์ ๋น„์œจ\"] <= 0.9, \"B\", \"C\")); pc.iloc[0, 3] = \"A\"\n", "print(\"\\n[ํŒŒ๋ ˆํ† ] ์กฐ์น˜ ๋น„์šฉ ๊ธฐ์ค€(๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜)\")\n", "print(pc.assign(๋น„์šฉ์ง€์ˆ˜=lambda d: d[\"๋น„์šฉ์ง€์ˆ˜\"].round(0), ๋ˆ„์ ๋น„์œจ=lambda d: d[\"๋ˆ„์ ๋น„์œจ\"].map(\"{:.0%}\".format)).head(6).to_string())\n", "top20 = pc.head(int(np.ceil(NP * 0.2)))\n", "print(f\"์ƒ์œ„ 20% ๋ถ€์œ„({', '.join(top20.index)})๊ฐ€ ์ „์ฒด ์กฐ์น˜ ๋น„์šฉ์˜ {top20['๋น„์šฉ์ง€์ˆ˜'].sum() / pc['๋น„์šฉ์ง€์ˆ˜'].sum():.0%}, A๋“ฑ๊ธ‰ {int((pc['๋“ฑ๊ธ‰'] == 'A').sum())}์ข…\")\n", "\n", "# 3) ์ƒ๊ด€๋ถ„์„: ์›”๊ฐ„ QDR ๊ฑด์ˆ˜์™€ ์›”๋ณ„ ํ”ผ์ฒ˜\n", "mon = df.groupby(\"month\").agg(n=(\"qdr_id\", \"size\"), wait=(\"inspect_wait\", \"mean\"), re=(\"reinspect\", \"mean\"), crit=(\"sev\", lambda s: (s == 2).mean()),\n", " ven_b=(\"vendor\", lambda s: (s == \"์™ธ์ฃผB\").mean()), short=(\"since_maint\", lambda s: (s < 60).mean()),\n", " mfg=(\"cause\", lambda s: (s == \"์ œ์กฐ ๋ถˆ๋Ÿ‰\").mean()), cost=(\"cost\", \"mean\"), units=(\"unit\", \"nunique\"),\n", " short_n=(\"since_maint\", lambda s: (s < 60).sum()))\n", "mon[\"usage\"] = use; mon[\"trend\"] = np.arange(36); mon[\"season\"] = np.cos(2 * np.pi * (mon[\"trend\"] % 12 - 7) / 12); mon[\"events\"] = [sum(1 for e in events if e[1] == m) for m in range(36)]\n", "rows = []\n", "for c, lab in [(\"usage\", \"ํ›ˆ๋ จ ๊ฐ•๋„\"), (\"trend\", \"๊ฒฝ๊ณผ ์›”(์ถ”์„ธ)\"), (\"season\", \"๊ณ„์ ˆ ์š”์ธ\"), (\"wait\", \"ํ‰๊ท  ๊ฒ€์‚ฌ ๋Œ€๊ธฐ์ผ\"), (\"re\", \"์žฌ๊ฒ€์‚ฌ์œจ\"), (\"short\", \"์ •๋น„ ํ›„ 60์ผ ์ด๋‚ด ๋น„์ค‘\"),\n", " (\"mfg\", \"์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘\"), (\"cost\", \"๊ฑด๋‹น ํ‰๊ท  ์กฐ์น˜ ๋น„์šฉ\"), (\"ven_b\", \"์™ธ์ฃผB ๋น„์ค‘\"), (\"crit\", \"Critical ๋น„์ค‘\"), (\"units\", \"๋ณด๊ณ  ๋ถ€๋Œ€ ์ˆ˜\"), (\"short_n\", \"์ •๋น„ ํ›„ 60์ผ ์ด๋‚ด ๊ฑด์ˆ˜\")]:\n", " r, p = spearmanr(mon[c], mon[\"n\"]); rows.append((lab, r, p))\n", "cor = pd.DataFrame(rows, columns=[\"ํ”ผ์ฒ˜\", \"rho\", \"p\"]); cor[\"์œ ์˜\"] = (cor[\"p\"] < 0.05) & (cor[\"rho\"].abs() >= 0.3)\n", "print(\"\\n[์ƒ๊ด€๋ถ„์„] ํ”ผ์ฒ˜ | ์Šคํ”ผ์–ด๋งŒ rho | p-value | ์œ ์˜(p<0.05, |rho|>=0.3)\")\n", "for x in cor.sort_values(\"rho\", key=abs, ascending=False).head(7).itertuples():\n", " print(f\" {x.ํ”ผ์ฒ˜:16s} {x.rho:+.2f} {x.p:.4f} {'์œ ์˜' if x.์œ ์˜ else '-'}\")\n", "nsig = int(cor[\"์œ ์˜\"].sum())\n", "\n", "# 4) ์™ธ์ฃผ์ •๋น„ ์—…์ฒด๋ณ„ ๊ฒ€์‚ฌ ๋ณ‘๋ชฉ๊ณผ ์›์ธ\n", "ven = df.groupby(\"vendor\").agg(๊ฑด์ˆ˜=(\"qdr_id\", \"size\"), ๋Œ€๊ธฐ์ผ=(\"inspect_wait\", \"mean\"), ์žฌ๊ฒ€์‚ฌ์œจ=(\"reinspect\", \"mean\"), ์ •๋น„ํ’ˆ์งˆ์›์ธ=(\"cause\", lambda s: (s == \"์ •๋น„ ํ’ˆ์งˆ\").mean()))\n", "print(\"\\n[์—…์ฒด๋ณ„ ๊ฒ€์‚ฌ ์‹ค์ ] ์ง์ „ ์ •๋น„ ์ˆ˜ํ–‰์ฒ˜ ๊ธฐ์ค€\")\n", "print(ven.round(2).to_string())\n", "b, o = df[df[\"vendor\"] == \"์™ธ์ฃผB\"], df[~df[\"vendor\"].isin([\"์™ธ์ฃผB\"])]\n", "print(f\"์™ธ์ฃผB: ๋Œ€๊ธฐ์ผ {b['inspect_wait'].mean():.1f}์ผ(ํƒ€ ์ˆ˜ํ–‰์ฒ˜ {o['inspect_wait'].mean():.1f}์ผ), '์ •๋น„ ํ’ˆ์งˆ' ์›์ธ {np.mean(b['cause'] == '์ •๋น„ ํ’ˆ์งˆ'):.0%}(ํƒ€ {np.mean(o['cause'] == '์ •๋น„ ํ’ˆ์งˆ'):.0%}), ์ •๋น„ ํ›„ 60์ผ ์ด๋‚ด ์žฌ๋ฐœ {np.mean(b['since_maint'] < 60):.0%}\")\n", "\n", "# 5) EDA ์ฐจํŠธ 8์ข…\n", "fig, ax = plt.subplots(2, 4, figsize=(16, 7))\n", "H = df.pivot_table(index=\"part\", columns=\"month\", values=\"qdr_id\", aggfunc=\"size\", fill_value=0)\n", "ax[0, 0].imshow(H.values, aspect=\"auto\", cmap=\"Reds\"); ax[0, 0].set_title(\"1 QDR heatmap (part x month)\")\n", "ax[0, 1].plot(mon[\"n\"].values, label=\"QDR\"); ax[0, 1].plot(mon[\"usage\"].values * mon[\"n\"].mean(), label=\"usage x mean\"); ax[0, 1].legend(fontsize=6); ax[0, 1].set_title(\"2 Monthly QDR vs usage\")\n", "ax[0, 2].bar(range(NP), pc[\"๋น„์šฉ์ง€์ˆ˜\"].values); ax[0, 2].plot(range(NP), pc[\"๋ˆ„์ ๋น„์œจ\"].values * pc[\"๋น„์šฉ์ง€์ˆ˜\"].max(), \"r-o\"); ax[0, 2].set_title(\"3 Pareto of repair cost\")\n", "sv = pd.crosstab(df[\"part\"], df[\"sev\"], normalize=\"index\"); ax[0, 3].bar(range(NP), sv[0], label=\"Minor\"); ax[0, 3].bar(range(NP), sv[1], bottom=sv[0], label=\"Major\"); ax[0, 3].bar(range(NP), sv[2], bottom=sv[0] + sv[1], label=\"Critical\"); ax[0, 3].legend(fontsize=6); ax[0, 3].set_title(\"4 Severity share by part\")\n", "ax[1, 0].boxplot([df.loc[df[\"vendor\"] == v, \"inspect_wait\"] for v in VENDORS], labels=[v[-1] for v in VENDORS]); ax[1, 0].set_title(\"5 Inspection wait by vendor\")\n", "cv = pd.crosstab(df[\"vendor\"], df[\"cause\"], normalize=\"index\"); ax[1, 1].imshow(cv.values, cmap=\"Blues\"); ax[1, 1].set_title(\"6 Cause share by vendor\")\n", "ax[1, 2].hist([df.loc[df[\"cause\"] == c, \"since_maint\"].clip(0, 400) for c in CAUSES[:2]], bins=20, label=[\"mfg\", \"maint\"]); ax[1, 2].legend(fontsize=6); ax[1, 2].set_title(\"7 Days since maintenance\")\n", "ax[1, 3].scatter(mon[\"wait\"], mon[\"n\"], s=12); ax[1, 3].set_title(\"8 Wait vs monthly QDR\")\n", "plt.tight_layout(); plt.show()\n", "\n", "print(f\"\\nEDA ์ฐจํŠธ 8์ข… ์ƒ์„ฑ(๋ชฉํ‘œ 8์ข… ์ด์ƒ: ๋‹ฌ์„ฑ), ์œ ์˜ํ•œ ํ”ผ์ฒ˜ {nsig}๊ฐœ (๋ชฉํ‘œ 5๊ฐœ ์ด์ƒ: {'๋‹ฌ์„ฑ' if nsig >= 5 else '๋ฏธ๋‹ฌ'})\")\n", "print(\"ํ•ต์‹ฌ ๋ฐœ๊ฒฌ: 1) ๊ฑด์ˆ˜ ์ƒ์œ„๋งŒ ๋ณด๋ฉด ์กฐํ–ฅ ์žฅ์น˜์ฒ˜๋Ÿผ ์น˜๋ช…๋„ ๋†’์€ ๋ถ€์œ„๋ฅผ ๋†“์นจ 2) ์™ธ์ฃผB๋Š” ๊ฒ€์‚ฌ ๋Œ€๊ธฐ๊ฐ€ ๊ธธ๊ณ  ์ •๋น„ ํ›„ ์กฐ๊ธฐ ์žฌ๋ฐœ ๋น„์ค‘์ด ๋†’์Œ 3) ์›”๋ณ„ QDR๋Š” ํ›ˆ๋ จ ๊ฐ•๋„์™€ ๊ณ„์ ˆ์— ์—ฐ๋™\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 03 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR 9221๊ฑด, ๋ถ€์œ„ 12์ข…, 36๊ฐœ์›” (ํ•™์Šต 24๊ฐœ์›” 5906๊ฑด, ๊ฒ€์ฆ 12๊ฐœ์›” 3315๊ฑด)\n", "Baseline ์ƒ์œ„ 5 ๋ถ€์œ„: ์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ, ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต, ๋ƒ‰๊ฐ ๊ณ„ํ†ต, ์ œ๋™ ์žฅ์น˜, ๋ณ€์†๊ธฐ\n", " ๊ฒ€์ฆ ๊ธฐ๊ฐ„ ๊ฑด์ˆ˜ ์ƒ์œ„ 5์™€ ์ผ์น˜ 5/5, ์›”๋ณ„ ํ‰๊ท  ์ ์ค‘๋ฅ  80%\n", " ์น˜๋ช…๋„ ๊ฐ€์ค‘ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์ƒ์œ„ 5์™€ ์ผ์น˜ 4/5 -> ๊ฑด์ˆ˜๋งŒ ๋ณด๋ฉด ๋†“์น˜๋Š” ๋ถ€์œ„: ์กฐํ–ฅ ์žฅ์น˜\n", " Critical ๊ฒฐํ•จ 366๊ฑด ์ค‘ Baseline ๋Œ€์ƒ ๋ฐ– ๋ถ€์œ„ ๋น„์ค‘ 46%\n", "\n", "[ํŒŒ๋ ˆํ† ] ์กฐ์น˜ ๋น„์šฉ ๊ธฐ์ค€(๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜)\n", " ๊ฑด์ˆ˜ ๋น„์šฉ์ง€์ˆ˜ ๋ˆ„์ ๋น„์œจ ๋“ฑ๊ธ‰\n", "๋ถ€์œ„ \n", "๋ณ€์†๊ธฐ 558 13464.0 18% A\n", "์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต 770 11550.0 33% A\n", "์ œ๋™ ์žฅ์น˜ 596 10220.0 47% A\n", "์กฐํ–ฅ ์žฅ์น˜ 397 7155.0 56% A\n", "์ฐจ์ฒด ์šฉ์ ‘๋ถ€ 277 6776.0 65% A\n", "์œ ์•• ๊ณ„ํ†ต 462 5607.0 73% B\n", "์ƒ์œ„ 20% ๋ถ€์œ„(๋ณ€์†๊ธฐ, ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต, ์ œ๋™ ์žฅ์น˜)๊ฐ€ ์ „์ฒด ์กฐ์น˜ ๋น„์šฉ์˜ 47%, A๋“ฑ๊ธ‰ 5์ข…\n", "\n", "[์ƒ๊ด€๋ถ„์„] ํ”ผ์ฒ˜ | ์Šคํ”ผ์–ด๋งŒ rho | p-value | ์œ ์˜(p<0.05, |rho|>=0.3)\n", " ์ •๋น„ ํ›„ 60์ผ ์ด๋‚ด ๊ฑด์ˆ˜ +0.94 0.0000 ์œ ์˜\n", " ํ‰๊ท  ๊ฒ€์‚ฌ ๋Œ€๊ธฐ์ผ +0.83 0.0000 ์œ ์˜\n", " ํ›ˆ๋ จ ๊ฐ•๋„ +0.82 0.0000 ์œ ์˜\n", " ๊ณ„์ ˆ ์š”์ธ +0.78 0.0000 ์œ ์˜\n", " ๊ฒฝ๊ณผ ์›”(์ถ”์„ธ) +0.39 0.0200 ์œ ์˜\n", " ์™ธ์ฃผB ๋น„์ค‘ -0.22 0.1928 -\n", " Critical ๋น„์ค‘ +0.16 0.3583 -\n", "\n", "[์—…์ฒด๋ณ„ ๊ฒ€์‚ฌ ์‹ค์ ] ์ง์ „ ์ •๋น„ ์ˆ˜ํ–‰์ฒ˜ ๊ธฐ์ค€\n", " ๊ฑด์ˆ˜ ๋Œ€๊ธฐ์ผ ์žฌ๊ฒ€์‚ฌ์œจ ์ •๋น„ํ’ˆ์งˆ์›์ธ\n", "vendor \n", "๋ถ€๋Œ€์ •๋น„ 1811 3.11 0.18 0.18\n", "์™ธ์ฃผA 2231 5.00 0.17 0.18\n", "์™ธ์ฃผB 1916 8.44 0.27 0.36\n", "์™ธ์ฃผC 1864 5.08 0.16 0.17\n", "์™ธ์ฃผD 1399 4.98 0.17 0.17\n", "์™ธ์ฃผB: ๋Œ€๊ธฐ์ผ 8.4์ผ(ํƒ€ ์ˆ˜ํ–‰์ฒ˜ 4.5์ผ), '์ •๋น„ ํ’ˆ์งˆ' ์›์ธ 36%(ํƒ€ 18%), ์ •๋น„ ํ›„ 60์ผ ์ด๋‚ด ์žฌ๋ฐœ 49%\n", "\n", "EDA ์ฐจํŠธ 8์ข… ์ƒ์„ฑ(๋ชฉํ‘œ 8์ข… ์ด์ƒ: ๋‹ฌ์„ฑ), ์œ ์˜ํ•œ ํ”ผ์ฒ˜ 5๊ฐœ (๋ชฉํ‘œ 5๊ฐœ ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "ํ•ต์‹ฌ ๋ฐœ๊ฒฌ: 1) ๊ฑด์ˆ˜ ์ƒ์œ„๋งŒ ๋ณด๋ฉด ์กฐํ–ฅ ์žฅ์น˜์ฒ˜๋Ÿผ ์น˜๋ช…๋„ ๋†’์€ ๋ถ€์œ„๋ฅผ ๋†“์นจ 2) ์™ธ์ฃผB๋Š” ๊ฒ€์‚ฌ ๋Œ€๊ธฐ๊ฐ€ ๊ธธ๊ณ  ์ •๋น„ ํ›„ ์กฐ๊ธฐ ์žฌ๋ฐœ ๋น„์ค‘์ด ๋†’์Œ 3) ์›”๋ณ„ QDR๋Š” ํ›ˆ๋ จ ๊ฐ•๋„์™€ ๊ณ„์ ˆ์— ์—ฐ๋™\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 04. ํ…์ŠคํŠธ ์ž„๋ฒ ๋”ฉ ๋ฐ ํ† ํ”ฝ ๋ชจ๋ธ๋ง ๊ธฐ๋ฐ˜ ์žฅ๋น„ ๊ตฌ์กฐ์  ํ’ˆ์งˆ ์ทจ์•ฝ์  ์ž๋™ ๊ตฐ์ง‘ํ™” ๋ชจ๋ธ ๊ณ ๋„ํ™”\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "ํ† ํ”ฝ ๋ชจ๋ธ๋ง ๊ธฐ๋ฐ˜ ์žฅ๋น„ ๊ตฌ์กฐ์  ํ’ˆ์งˆ ์ทจ์•ฝ์  ์ž๋™ ๊ตฐ์ง‘ํ™” ๋ชจ๋ธ์„ ๊ณ ๋„ํ™”ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **ํ…์ŠคํŠธ ์ž„๋ฒ ๋”ฉ(TF-IDF)๊ณผ ํ† ํ”ฝ ๋ชจ๋ธ๋ง(NMF)์œผ๋กœ ์žฅ๋น„ ๊ตฌ์กฐ์  ํ’ˆ์งˆ ์ทจ์•ฝ์ ์„ ์ž๋™ ๊ตฐ์ง‘ํ™”ํ•˜๊ณ , ๋ถ€์œ„๋ณ„ ์›”๊ฐ„ QDR ๊ฑด์ˆ˜ ๊ธฐ์ค€ ๋ชจ๋ธ์„ ํŒŒ์ƒ ํ”ผ์ฒ˜๋กœ ๊ณ ๋„ํ™”ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **๊ฒฐํ•จ ์„œ์ˆ ์˜ ํ•ต์‹ฌ ๋ฌธ์žฅ์„ TF-IDF๋กœ ๋ฒกํ„ฐํ™”ํ•˜๊ณ  NMF ํ† ํ”ฝ 36๊ฐœ๋กœ ๊ตฐ์ง‘ํ™”ํ•ด ๋ถ€์œ„ยท๊ฒฐํ•จ ๋ผ๋ฒจ๊ณผ์˜ ์ผ์น˜๋„(NMI) ํ™•์ธํ•˜๊ณ  ์œ ํšจ์„ฑ์„ ์ ๊ฒ€ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/features/qlt05_feature_engineering.py ๋ฐ ๊ธฐ์ค€ ๋ชจ๋ธ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ตฐ์ˆ˜ ์‹ค๋ฌด์—์„œ ๋‚ฉ๋“ ๊ฐ€๋Šฅํ•œ ํ”ผ์ฒ˜ ์„ค๊ณ„์˜ ๋…ผ๋ฆฌ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ํŒŒ์ƒ ํ”ผ์ฒ˜ 10๊ฐœ ์ด์ƒ ์ƒ์„ฑ, ๊ธฐ์ค€ ๋ชจ๋ธ MAPE 25% ์ดํ•˜ ๋‹ฌ์„ฑ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ํ”ผ์ฒ˜ ํŒŒ์ดํ”„๋ผ์ธ ๋ฐ ๋ฒ ์ด์Šค๋ผ์ธ ๋ชจ๋ธ ์„ฑ๋Šฅ ์ง€ํ‘œ ๋„์ถœ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๊ต์ฐจ ๊ฒ€์ฆ(Time-series Cross Validation)์„ ํ†ตํ•œ ๊ณผ์ ํ•ฉ ๋ฐฉ์ง€\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"QDR ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ TF-IDF๋กœ ๋ฒกํ„ฐํ™”ํ•˜๊ณ  NMF ํ† ํ”ฝ ๋ชจ๋ธ๋ง์œผ๋กœ ์žฅ๋น„ ๊ตฌ์กฐ์  ํ’ˆ์งˆ ์ทจ์•ฝ์ ์„ ์ž๋™ ๊ตฐ์ง‘ํ™”ํ•ด ๋ถ€์œ„ยท๊ฒฐํ•จ ๋ผ๋ฒจ๊ณผ์˜ ์ผ์น˜๋„(NMI)์™€ ์ทจ์•ฝ ๊ตฐ์ง‘(์น˜๋ช…๋„ยท์„ค๊ณ„ ์ทจ์•ฝ ๋น„์ค‘)์„ ํ™•์ธํ•˜๊ณ , ํ† ํ”ฝ ๋น„์ค‘ ๋“ฑ ํŒŒ์ƒ ํ”ผ์ฒ˜ 14๊ฐœ๋กœ ๋ถ€์œ„๋ณ„ ์›”๊ฐ„ QDR ๊ฑด์ˆ˜ ๊ธฐ์ค€ ๋ชจ๋ธ์„ ๋งŒ๋“ค์–ด ์‹œ๊ณ„์—ด ๊ต์ฐจ๊ฒ€์ฆ MAPE๋ฅผ ํ‰๊ฐ€ํ•˜๊ณ  ์ˆœ์—ด ์ค‘์š”๋„๋กœ ํ”ผ์ฒ˜๋ฅผ ์ค„์ธ๋‹ค.\"\"\"\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "from sklearn.decomposition import NMF\n", "from sklearn.ensemble import GradientBoostingRegressor\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.inspection import permutation_importance\n", "from sklearn.metrics import normalized_mutual_info_score\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# ---------------------------------------------------------------------------\n", "# ๊ณตํ†ต ํ…์ŠคํŠธ ์กฐ๊ฐ: ์ •๊ทœํ™”, ๊ฐ„์ด ํ† ํฐํ™”(์กฐ์‚ฌ ์ œ๊ฑฐ), ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ยท์›์ธ ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\n", "# ---------------------------------------------------------------------------\n", "TYPO = {\"๋ˆ„์œณ\": \"๋ˆ„์œ \", \"๊ท ๋ ฌ\": \"๊ท ์—ด\", \"์ ‘์ถ• ๋ถˆ๋Ÿ‰\": \"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋ถ€์”ฉ\": \"๋ถ€์‹\", \"๋งˆ๋ฌ˜\": \"๋งˆ๋ชจ\", \"๊ณผ๋ ฌ\": \"๊ณผ์—ด\", \"์˜ค์ž‘๋˜ฅ\": \"์˜ค์ž‘๋™\", \"์žฌ๋™\": \"์ œ๋™\"}\n", "ABBR = {\"T/M\": \"๋ณ€์†๊ธฐ\", \"BAT\": \"๋ฐฐํ„ฐ๋ฆฌ\"}\n", "PART_KW = sorted([(a, i) for i, p in enumerate(PARTS) for a in p[1]], key=lambda x: -len(x[0]))\n", "DEF_KW = {\"๊ท ์—ด\": [\"๊ท ์—ด\", \"ํฌ๋ž™\", \"๊ฐˆ๋ผ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ \", \"์ƒˆ์–ด\", \"๋ˆ„์„ค\"], \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ\", \"๋‹ณ์€\"], \"๋ถ€์‹\": [\"๋ถ€์‹\", \"๋…น์ด\", \"์‚ฐํ™”\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋‹จ์„ \", \"๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ\", \"์ฒด๊ฒฐ\", \"ํ—๊ฑฐ\"], \"๊ณผ์—ด\": [\"๊ณผ์—ด\", \"์˜จ๋„\", \"์—ด์ด\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™\", \"๋ถˆ์•ˆ์ •\", \"๋ฉˆ์ถค\"]}\n", "SEV_KW = [(2, [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€\", \"์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์œ„ํ—˜\"]), (1, [\"์„ฑ๋Šฅ ์ €ํ•˜\", \"์กฐ์†ํ•œ\"]), (0, [\"๊ฒฝ๋ฏธ\", \"์ง€์žฅ์ด ์—†\"])]\n", "CAUSE_KW = {\"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ\", \"์ถœ๊ณ \", \"์ธ์ˆ˜\"], \"์ •๋น„ ํ’ˆ์งˆ\": [\"์ •๋น„ ํ›„\", \"์žฌ์กฐ๋ฆฝ\", \"์ •๋น„ ์งํ›„\"], \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"์ถฉ๊ฒฉ\", \"๊ณผ๋ถ€ํ•˜\", \"์ ๊ฒ€ ์—†์ด\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ\", \"๊ตฌ์กฐ์ \", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€\"], \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ\", \"๋…ธํ›„\", \"์ˆ˜๋ช…\"]}\n", "PARTICLES = (\"์—์„œ\", \"์œผ๋กœ\", \"์ด\", \"๊ฐ€\", \"์€\", \"๋Š”\", \"์„\", \"๋ฅผ\", \"์—\", \"์˜\", \"๋กœ\", \"์™€\", \"๊ณผ\", \"๋„\", \"ํ•จ\", \"์Œ\", \"๋จ\")\n", "\n", "\n", "def norm(t):\n", " \"\"\"์ •๊ทœํ™”: ์ „๊ฐ->๋ฐ˜๊ฐ, ํŠน์ˆ˜๋ฌธ์žยท์ค‘๋ณต ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •\"\"\"\n", " t = unicodedata.normalize(\"NFKC\", str(t))\n", " t = re.sub(r\"[!#~*@^]+\", \" \", t)\n", " for a, b in ABBR.items():\n", " t = t.replace(a, b)\n", " for a, b in TYPO.items():\n", " t = t.replace(a, b)\n", " return re.sub(r\"\\s+\", \" \", t).strip()\n", "\n", "\n", "def tok(t):\n", " out = []\n", " for w in re.split(r\"[\\s.,]+\", norm(t)):\n", " if not w or re.fullmatch(r\"[\\d\\-A-Za-z]+\", w):\n", " continue\n", " for p in PARTICLES:\n", " if len(w) > len(p) + 1 and w.endswith(p):\n", " w = w[:-len(p)]\n", " break\n", " out.append(w)\n", " return out\n", "\n", "\n", "def label_part(t):\n", " for a, i in PART_KW:\n", " if a in t:\n", " return i\n", " return -1\n", "\n", "\n", "def label_defect(t):\n", " for d, ks in DEF_KW.items():\n", " if any(k in t for k in ks):\n", " return d\n", " return \"๋ฏธ๋ถ„๋ฅ˜\"\n", "\n", "\n", "def label_sev(t):\n", " for s, ks in SEV_KW:\n", " if any(k in t for k in ks):\n", " return s\n", " return -1\n", "\n", "\n", "def label_cause(t):\n", " for c, ks in CAUSE_KW.items():\n", " if any(k in t for k in ks):\n", " return c\n", " return \"๋ฏธ์ƒ\"\n", "\n", "df, use, events = make_qdr(36, seed=1, scale=3.0)\n", "names = [p[0] for p in PARTS]\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR {len(df)}๊ฑด, 36๊ฐœ์›”\")\n", "print(\"(ํ† ํ”ฝ ๋ชจ๋ธ์€ ๊ฒฐํ•จ ์„œ์ˆ  ํ•ต์‹ฌ ๋ฌธ์žฅ '๋ถ€์œ„์—์„œ ์ฆ์ƒ'์— ์ ์šฉํ•˜๊ณ , ์น˜๋ช…๋„ยท์›์ธ์€ ๊ตฐ์ง‘ ํ•ด์„์— ์‚ฌ์šฉ)\")\n", "\n", "# 1) ํ…์ŠคํŠธ ์ž„๋ฒ ๋”ฉ(TF-IDF) + NMF ํ† ํ”ฝ ๋ชจ๋ธ๋ง\n", "STOP = set(w for a in ACTIONS for w in tok(a)) | {\"์†Œ์†\"}\n", "vec = TfidfVectorizer(tokenizer=lambda s: [w for w in tok(s) if w not in STOP], lowercase=False, token_pattern=None, min_df=5)\n", "df[\"core\"] = df[\"text\"].map(lambda s: [x for x in s.split(\". \") if \"์—์„œ\" in x][0]) # ๊ฒฐํ•จ ์„œ์ˆ  ํ•ต์‹ฌ ๋ฌธ์žฅ(๋ถ€์œ„+์ฆ์ƒ)\n", "X = vec.fit_transform(df[\"core\"])\n", "K = 36\n", "nmf = NMF(n_components=K, init=\"nndsvd\", random_state=0, max_iter=400).fit(X)\n", "Wd = nmf.transform(X)\n", "df[\"topic\"] = Wd.argmax(axis=1)\n", "vocab = np.array(vec.get_feature_names_out())\n", "nmi_p = normalized_mutual_info_score(df[\"part\"], df[\"topic\"]); nmi_d = normalized_mutual_info_score(df[\"defect\"], df[\"topic\"]); nmi_pd = normalized_mutual_info_score(df[\"part\"].astype(str) + df[\"defect\"], df[\"topic\"])\n", "print(f\"TF-IDF ์–ดํœ˜ {X.shape[1]}๊ฐœ, ํ† ํ”ฝ {K}๊ฐœ: ๊ตฐ์ง‘-๋ผ๋ฒจ ์ผ์น˜๋„(NMI) ๋ถ€์œ„ {nmi_p:.2f}, ๊ฒฐํ•จ ์œ ํ˜• {nmi_d:.2f}, ๋ถ€์œ„+๊ฒฐํ•จ {nmi_pd:.2f}\")\n", "base_acc = np.mean([label_part(t) == p for t, p in zip(df[\"text\"], df[\"part\"])])\n", "print(f\" ์ฐธ๊ณ : ํ‚ค์›Œ๋“œ ๊ทœ์น™ ๋ถ€์œ„ ๋ผ๋ฒจ ์ •ํ™•๋„ {base_acc:.1%}\")\n", "g = df.groupby(\"topic\").agg(๊ฑด์ˆ˜=(\"qdr_id\", \"size\"), critical=(\"sev\", lambda s: (s == 2).mean()), ์„ค๊ณ„์ทจ์•ฝ=(\"cause\", lambda s: (s == \"์„ค๊ณ„ ์ทจ์•ฝ\").mean()),\n", " ์ฃผ๋ถ€์œ„=(\"part\", lambda s: names[s.mode()[0]]), ์ฃผ๊ฒฐํ•จ=(\"defect\", lambda s: s.mode()[0]))\n", "g[\"ํ•ต์‹ฌ์–ด\"] = [\", \".join(vocab[np.argsort(-nmf.components_[k])[:4]]) for k in g.index]\n", "g[\"์ทจ์•ฝ์ง€์ˆ˜\"] = g[\"๊ฑด์ˆ˜\"] * (g[\"critical\"] * 9 + (1 - g[\"critical\"]) * 1.5)\n", "print(\"\\n[์ทจ์•ฝ ๊ตฐ์ง‘ ์ƒ์œ„ 5] ํ† ํ”ฝ | ๊ฑด์ˆ˜ | Critical ๋น„์ค‘ | ์ฃผ ๋ถ€์œ„ | ์ฃผ ๊ฒฐํ•จ | ํ•ต์‹ฌ์–ด\")\n", "for k, r in g.sort_values(\"์ทจ์•ฝ์ง€์ˆ˜\", ascending=False).head(5).iterrows():\n", " print(f\" T{k:02d} {r['๊ฑด์ˆ˜']:4d} {r['critical']:.0%} {r['์ฃผ๋ถ€์œ„']} / {r['์ฃผ๊ฒฐํ•จ']} [{r['ํ•ต์‹ฌ์–ด']}]\")\n", "struct = list(g[\"์„ค๊ณ„์ทจ์•ฝ\"].sort_values(ascending=False).index[:8])\n", "print(f\"์„ค๊ณ„ ์ทจ์•ฝ ๋น„์ค‘ ์ƒ์œ„ ํ† ํ”ฝ {len(struct)}๊ฐœ(๊ตฌ์กฐ์  ์ทจ์•ฝ ํ›„๋ณด): \" + \", \".join(f\"T{k:02d}\" for k in struct) + f\" -> ํ•ด๋‹น ํ† ํ”ฝ QDR์˜ ์„ค๊ณ„ ์ทจ์•ฝ ๋น„์ค‘ {df.loc[df['topic'].isin(struct), 'cause'].eq('์„ค๊ณ„ ์ทจ์•ฝ').mean():.0%} (์ „์ฒด {df['cause'].eq('์„ค๊ณ„ ์ทจ์•ฝ').mean():.0%})\")\n", "\n", "# 2) ๋ถ€์œ„๋ณ„ ์›”๊ฐ„ QDR ๊ฑด์ˆ˜ ๊ธฐ์ค€ ๋ชจ๋ธ: ํŒŒ์ƒ ํ”ผ์ฒ˜ 13๊ฐœ\n", "top8 = list(df.groupby(\"part\").size().sort_values(ascending=False).index[:8])\n", "mon = df.groupby([\"part\", \"month\"]).agg(n=(\"qdr_id\", \"size\"), crit=(\"sev\", lambda s: (s == 2).mean()), wait=(\"inspect_wait\", \"mean\"), vb=(\"vendor\", lambda s: (s == \"์™ธ์ฃผB\").mean()),\n", " re=(\"reinspect\", \"mean\"), st=(\"topic\", lambda s: s.isin(struct).mean())).reset_index()\n", "rows = []\n", "for p in top8:\n", " d = mon[mon[\"part\"] == p].set_index(\"month\").reindex(range(36)).fillna(0)\n", " f = pd.DataFrame({\"part\": p, \"month\": range(36), \"n\": d[\"n\"].values})\n", " f[\"lag1\"], f[\"lag2\"] = f[\"n\"].shift(1), f[\"n\"].shift(2)\n", " f[\"roll3\"] = f[\"n\"].shift(1).rolling(3).mean()\n", " f[\"exp_count\"] = (f[\"n\"] / use).shift(1).rolling(6, min_periods=3).mean() * use # ์ตœ๊ทผ 6๊ฐœ์›” ํ‰๊ท  ๋ฐœ์ƒ๋ฅ  x ์ด๋ฒˆ ๋‹ฌ ํ›ˆ๋ จ ๊ฐ•๋„\n", " f[\"usage\"], f[\"usage_lag1\"] = use, np.r_[use[0], use[:-1]]\n", " f[\"season_c\"], f[\"season_s\"] = np.cos(2 * np.pi * f[\"month\"] / 12), np.sin(2 * np.pi * f[\"month\"] / 12)\n", " f[\"trend\"] = f[\"month\"]\n", " for c in (\"crit\", \"wait\", \"vb\", \"re\", \"st\"):\n", " f[c + \"_lag1\"] = d[c].shift(1).values\n", " rows.append(f)\n", "F = pd.concat(rows, ignore_index=True).dropna()\n", "DER = [\"lag1\", \"lag2\", \"roll3\", \"exp_count\", \"usage\", \"usage_lag1\", \"season_c\", \"season_s\", \"trend\", \"crit_lag1\", \"wait_lag1\", \"vb_lag1\", \"re_lag1\", \"st_lag1\"]\n", "print(f\"\\n์›”๊ฐ„ ๊ฑด์ˆ˜ ์˜ˆ์ธก: ํŒŒ์ƒ ํ”ผ์ฒ˜ {len(DER)}๊ฐœ (๋ชฉํ‘œ 10๊ฐœ ์ด์ƒ: {'๋‹ฌ์„ฑ' if len(DER) >= 10 else '๋ฏธ๋‹ฌ'}), ๋Œ€์ƒ {len(top8)}๊ฐœ ๋ถ€์œ„ x {F['month'].nunique()}๊ฐœ์›”\")\n", "\n", "\n", "def fit(d, cols):\n", " return GradientBoostingRegressor(n_estimators=120, max_depth=3, learning_rate=0.08, subsample=0.8, random_state=0).fit(d[cols], d[\"n\"])\n", "\n", "\n", "mape = lambda y, p: float(np.mean(np.abs(y - p) / y))\n", "folds, naive = [], []\n", "for c in (17, 23, 29): # ํ™•์žฅ ์œˆ๋„: ๊ณผ๊ฑฐ๋กœ๋งŒ ํ•™์Šต, ๋’ค 6๊ฐœ์›” ๊ฒ€์ฆ\n", " trn, val = F[F[\"month\"] <= c], F[(F[\"month\"] > c) & (F[\"month\"] <= c + 6)]\n", " folds.append(mape(val[\"n\"], fit(trn, DER).predict(val[DER]))); naive.append(mape(val[\"n\"], val[\"lag1\"]))\n", "print(f\"์‹œ๊ณ„์—ด ๊ต์ฐจ๊ฒ€์ฆ ํด๋“œ๋ณ„ MAPE: {', '.join(f'{x:.1%}' for x in folds)} -> ํ‰๊ท  {np.mean(folds):.1%} (๋ชฉํ‘œ 25% ์ดํ•˜: {'๋‹ฌ์„ฑ' if np.mean(folds) <= 0.25 else '๋ฏธ๋‹ฌ'}), ์ „์›”๊ฐ’ ๊ธฐ์ค€ ํ‰๊ท  {np.mean(naive):.1%}\")\n", "\n", "# 3) ํ”ผ์ฒ˜ ์ค‘์š”๋„๋กœ ์ฐจ์› ์ถ•์†Œ\n", "trn, val = F[F[\"month\"] <= 29], F[F[\"month\"] > 29]\n", "full = fit(trn, DER)\n", "pi = permutation_importance(full, val[DER], val[\"n\"], n_repeats=5, random_state=0, scoring=\"neg_mean_absolute_error\")\n", "imp = pd.Series(pi.importances_mean, DER).sort_values(ascending=False)\n", "print(\"ํ”ผ์ฒ˜ ์ค‘์š”๋„ ์ƒ์œ„ 5: \" + \", \".join(f\"{k}({v:.2f})\" for k, v in imp.head(5).items()))\n", "top = list(imp.index[:7])\n", "print(f\"์ฐจ์› ์ถ•์†Œ {len(DER)}๊ฐœ -> {len(top)}๊ฐœ: ๊ฒ€์ฆ MAPE {mape(val['n'], full.predict(val[DER])):.1%} -> {mape(val['n'], fit(trn, top).predict(val[top])):.1%}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 04 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR 27675๊ฑด, 36๊ฐœ์›”\n", "(ํ† ํ”ฝ ๋ชจ๋ธ์€ ๊ฒฐํ•จ ์„œ์ˆ  ํ•ต์‹ฌ ๋ฌธ์žฅ '๋ถ€์œ„์—์„œ ์ฆ์ƒ'์— ์ ์šฉํ•˜๊ณ , ์น˜๋ช…๋„ยท์›์ธ์€ ๊ตฐ์ง‘ ํ•ด์„์— ์‚ฌ์šฉ)\n", "TF-IDF ์–ดํœ˜ 90๊ฐœ, ํ† ํ”ฝ 36๊ฐœ: ๊ตฐ์ง‘-๋ผ๋ฒจ ์ผ์น˜๋„(NMI) ๋ถ€์œ„ 0.45, ๊ฒฐํ•จ ์œ ํ˜• 0.60, ๋ถ€์œ„+๊ฒฐํ•จ 0.63\n", " ์ฐธ๊ณ : ํ‚ค์›Œ๋“œ ๊ทœ์น™ ๋ถ€์œ„ ๋ผ๋ฒจ ์ •ํ™•๋„ 100.0%\n", "\n", "[์ทจ์•ฝ ๊ตฐ์ง‘ ์ƒ์œ„ 5] ํ† ํ”ฝ | ๊ฑด์ˆ˜ | Critical ๋น„์ค‘ | ์ฃผ ๋ถ€์œ„ | ์ฃผ ๊ฒฐํ•จ | ํ•ต์‹ฌ์–ด\n", " T03 1711 10% ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต / ๋ˆ„์œ  [๋‚˜์˜ด, ์ƒˆ์–ด, ์˜ค์ผ, ๋ผ๋””์—์ดํ„ฐ]\n", " T24 1631 11% ๋ณ€์†๊ธฐ / ๋งˆ๋ชจ [๋‹ณ์€, ํ”์ , ์žˆ์Œ, ์‚ฐํ™”]\n", " T28 935 30% ์กฐํ–ฅ ์žฅ์น˜ / ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰ [์กฐํ–ฅ, ๋กœ๋“œ, ์žฅ์น˜, ํฌ๋ž™]\n", " T02 1430 12% ๋ƒ‰๊ฐ ๊ณ„ํ†ต / ๋ˆ„์œ  [๋ˆ„์œ , ๋ฐœ์ƒ, ๋…น์ด, ๋ผ๋””์—์ดํ„ฐ]\n", " T25 1009 18% ์กฐํ–ฅ ์žฅ์น˜ / ์ ‘์ด‰๋ถˆ๋Ÿ‰ [์—ฐ๋™๋ถ€, ํ•ธ๋“ค, ๋ฌด์ „๊ธฐ, ์ ‘์ด‰]\n", "์„ค๊ณ„ ์ทจ์•ฝ ๋น„์ค‘ ์ƒ์œ„ ํ† ํ”ฝ 8๊ฐœ(๊ตฌ์กฐ์  ์ทจ์•ฝ ํ›„๋ณด): T05, T28, T35, T13, T11, T25, T30, T02 -> ํ•ด๋‹น ํ† ํ”ฝ QDR์˜ ์„ค๊ณ„ ์ทจ์•ฝ ๋น„์ค‘ 21% (์ „์ฒด 17%)\n", "\n", "์›”๊ฐ„ ๊ฑด์ˆ˜ ์˜ˆ์ธก: ํŒŒ์ƒ ํ”ผ์ฒ˜ 14๊ฐœ (๋ชฉํ‘œ 10๊ฐœ ์ด์ƒ: ๋‹ฌ์„ฑ), ๋Œ€์ƒ 8๊ฐœ ๋ถ€์œ„ x 33๊ฐœ์›”\n", "์‹œ๊ณ„์—ด ๊ต์ฐจ๊ฒ€์ฆ ํด๋“œ๋ณ„ MAPE: 27.8%, 13.9%, 16.9% -> ํ‰๊ท  19.5% (๋ชฉํ‘œ 25% ์ดํ•˜: ๋‹ฌ์„ฑ), ์ „์›”๊ฐ’ ๊ธฐ์ค€ ํ‰๊ท  18.9%\n", "ํ”ผ์ฒ˜ ์ค‘์š”๋„ ์ƒ์œ„ 5: lag1(6.77), usage(1.86), exp_count(1.78), st_lag1(0.81), lag2(0.68)\n", "์ฐจ์› ์ถ•์†Œ 14๊ฐœ -> 7๊ฐœ: ๊ฒ€์ฆ MAPE 16.9% -> 15.3%\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 05. ๊ฒฐํ•จ ์น˜๋ช…๋„(Critical/Major/Minor) ๊ธฐ๋ฐ˜ ์ข…ํ•ฉ ํ’ˆ์งˆ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์‚ฐ์ถœ ์—”์ง„ ๊ฐœ๋ฐœ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๊ฒฐํ•จ ์น˜๋ช…๋„ ๊ธฐ๋ฐ˜ ์ข…ํ•ฉ ํ’ˆ์งˆ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์‚ฐ์ถœ ์—”์ง„์„ ๊ฐœ๋ฐœํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **๊ฒฐํ•จ ์น˜๋ช…๋„(Critical/Major/Minor)๋ฅผ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์น˜๋ช…๋„ ๊ฐ€์ค‘ ์ข…ํ•ฉ ํ’ˆ์งˆ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜๋ฅผ ๋ถ€์œ„๋ณ„๋กœ ์‚ฐ์ถœยท์˜ˆ์ธกํ•˜๋Š” ์—”์ง„์„ ๊ฐœ๋ฐœํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **ํ‚ค์›Œ๋“œ ๊ทœ์น™๊ณผ TF-IDF ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€์˜ ์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ์ •ํ™•๋„์™€ Critical ์žฌํ˜„์œจ์„ ๋น„๊ตํ•˜๊ณ  ์‹ ๋ขฐ๋„ ๊ตฌ๊ฐ„๋ณ„ ์‹ค์ œ ์ •ํ™•๋„๋ฅผ ์ ๊ฒ€ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/models/qlt05_advanced_model.py` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๋น„์ •ํ˜•/๋ถˆ๊ทœ์น™ ์ˆ˜์š”์— ๋Œ€ํ•œ ๋ชจ๋ธ์˜ ์ ์‘๋ ฅ ๋ฐ ์˜ˆ์ธก ์•ˆ์ •์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๊ธฐ์ค€ ๋ชจ๋ธ ๋Œ€๋น„ ์˜ค์ฐจ์œจ(MAE/RMSE) 20% ์ด์ƒ ๊ฐœ์„ , R2 Score 0.85 ์ด์ƒ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ๋ชจ๋ธ ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ์™„๋ฃŒ ๋ฐ ์ตœ์  ๋ชจ๋ธ ๊ฐ€์ค‘์น˜ ์ €์žฅ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: Hold-out ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ์…‹ ๋Œ€์ƒ ์ตœ์ข… ์„ฑ๋Šฅ ํ‰๊ฐ€\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"QDR ์„œ์ˆ ยท๋ถ€์œ„ยท๊ฒฐํ•จ ์ •๋ณด๋กœ ๊ฐœ๋ณ„ ๊ฒฐํ•จ์˜ ์น˜๋ช…๋„(Critical/Major/Minor)๋ฅผ ๋ถ„๋ฅ˜ํ•˜๋Š” ๋ชจ๋ธ(ํ‚ค์›Œ๋“œ ๊ทœ์น™ ๊ธฐ์ค€ ๋Œ€๋น„)๊ณผ ์‹ ๋ขฐ๋„ ๋ณด์ •์„ ํ™•์ธํ•˜๊ณ , ์น˜๋ช…๋„ ๊ฐ€์ค‘ ๋ถ€์œ„๋ณ„ ์›”๊ฐ„ ์ข…ํ•ฉ ํ’ˆ์งˆ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜๋ฅผ ์•™์ƒ๋ธ”(๋ฆฟ์ง€์— ๊ทธ๋ž˜๋””์–ธํŠธ ๋ถ€์ŠคํŒ…ยท๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ ์ž”์ฐจ ๋ณด์ •์„ ๋”ํ•œ ๋ชจ๋ธ)๋กœ ์˜ˆ์ธกํ•ด ํ•™์Šต ๊ธฐ๊ฐ„ ํ‰๊ท  ๊ธฐ์ค€ ๋ชจ๋ธ๊ณผ MAEยทRMSEยทR2๋ฅผ ๋น„๊ตํ•˜๋Š” ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์‚ฐ์ถœ ์—”์ง„์„ ๊ตฌํ˜„ํ•œ๋‹ค.\"\"\"\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import scipy.sparse as sp\n", "from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.linear_model import LogisticRegression, Ridge\n", "from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n", "from sklearn.pipeline import make_pipeline\n", "from sklearn.preprocessing import StandardScaler\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# ---------------------------------------------------------------------------\n", "# ๊ณตํ†ต ํ…์ŠคํŠธ ์กฐ๊ฐ: ์ •๊ทœํ™”, ๊ฐ„์ด ํ† ํฐํ™”(์กฐ์‚ฌ ์ œ๊ฑฐ), ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ยท์›์ธ ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\n", "# ---------------------------------------------------------------------------\n", "TYPO = {\"๋ˆ„์œณ\": \"๋ˆ„์œ \", \"๊ท ๋ ฌ\": \"๊ท ์—ด\", \"์ ‘์ถ• ๋ถˆ๋Ÿ‰\": \"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋ถ€์”ฉ\": \"๋ถ€์‹\", \"๋งˆ๋ฌ˜\": \"๋งˆ๋ชจ\", \"๊ณผ๋ ฌ\": \"๊ณผ์—ด\", \"์˜ค์ž‘๋˜ฅ\": \"์˜ค์ž‘๋™\", \"์žฌ๋™\": \"์ œ๋™\"}\n", "ABBR = {\"T/M\": \"๋ณ€์†๊ธฐ\", \"BAT\": \"๋ฐฐํ„ฐ๋ฆฌ\"}\n", "PART_KW = sorted([(a, i) for i, p in enumerate(PARTS) for a in p[1]], key=lambda x: -len(x[0]))\n", "DEF_KW = {\"๊ท ์—ด\": [\"๊ท ์—ด\", \"ํฌ๋ž™\", \"๊ฐˆ๋ผ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ \", \"์ƒˆ์–ด\", \"๋ˆ„์„ค\"], \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ\", \"๋‹ณ์€\"], \"๋ถ€์‹\": [\"๋ถ€์‹\", \"๋…น์ด\", \"์‚ฐํ™”\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋‹จ์„ \", \"๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ\", \"์ฒด๊ฒฐ\", \"ํ—๊ฑฐ\"], \"๊ณผ์—ด\": [\"๊ณผ์—ด\", \"์˜จ๋„\", \"์—ด์ด\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™\", \"๋ถˆ์•ˆ์ •\", \"๋ฉˆ์ถค\"]}\n", "SEV_KW = [(2, [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€\", \"์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์œ„ํ—˜\"]), (1, [\"์„ฑ๋Šฅ ์ €ํ•˜\", \"์กฐ์†ํ•œ\"]), (0, [\"๊ฒฝ๋ฏธ\", \"์ง€์žฅ์ด ์—†\"])]\n", "CAUSE_KW = {\"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ\", \"์ถœ๊ณ \", \"์ธ์ˆ˜\"], \"์ •๋น„ ํ’ˆ์งˆ\": [\"์ •๋น„ ํ›„\", \"์žฌ์กฐ๋ฆฝ\", \"์ •๋น„ ์งํ›„\"], \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"์ถฉ๊ฒฉ\", \"๊ณผ๋ถ€ํ•˜\", \"์ ๊ฒ€ ์—†์ด\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ\", \"๊ตฌ์กฐ์ \", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€\"], \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ\", \"๋…ธํ›„\", \"์ˆ˜๋ช…\"]}\n", "PARTICLES = (\"์—์„œ\", \"์œผ๋กœ\", \"์ด\", \"๊ฐ€\", \"์€\", \"๋Š”\", \"์„\", \"๋ฅผ\", \"์—\", \"์˜\", \"๋กœ\", \"์™€\", \"๊ณผ\", \"๋„\", \"ํ•จ\", \"์Œ\", \"๋จ\")\n", "\n", "\n", "def norm(t):\n", " \"\"\"์ •๊ทœํ™”: ์ „๊ฐ->๋ฐ˜๊ฐ, ํŠน์ˆ˜๋ฌธ์žยท์ค‘๋ณต ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •\"\"\"\n", " t = unicodedata.normalize(\"NFKC\", str(t))\n", " t = re.sub(r\"[!#~*@^]+\", \" \", t)\n", " for a, b in ABBR.items():\n", " t = t.replace(a, b)\n", " for a, b in TYPO.items():\n", " t = t.replace(a, b)\n", " return re.sub(r\"\\s+\", \" \", t).strip()\n", "\n", "\n", "def tok(t):\n", " out = []\n", " for w in re.split(r\"[\\s.,]+\", norm(t)):\n", " if not w or re.fullmatch(r\"[\\d\\-A-Za-z]+\", w):\n", " continue\n", " for p in PARTICLES:\n", " if len(w) > len(p) + 1 and w.endswith(p):\n", " w = w[:-len(p)]\n", " break\n", " out.append(w)\n", " return out\n", "\n", "\n", "def label_part(t):\n", " for a, i in PART_KW:\n", " if a in t:\n", " return i\n", " return -1\n", "\n", "\n", "def label_defect(t):\n", " for d, ks in DEF_KW.items():\n", " if any(k in t for k in ks):\n", " return d\n", " return \"๋ฏธ๋ถ„๋ฅ˜\"\n", "\n", "\n", "def label_sev(t):\n", " for s, ks in SEV_KW:\n", " if any(k in t for k in ks):\n", " return s\n", " return -1\n", "\n", "\n", "def label_cause(t):\n", " for c, ks in CAUSE_KW.items():\n", " if any(k in t for k in ks):\n", " return c\n", " return \"๋ฏธ์ƒ\"\n", "\n", "df, use, events = make_qdr(48, seed=1, scale=4.0)\n", "names = [p[0] for p in PARTS]\n", "W_SEV = np.array([1, 3, 9]) # ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ๊ฐ€์ค‘์น˜(Minor 1, Major 3, Critical 9)\n", "tr, va = df[df[\"month\"] < 36], df[df[\"month\"] >= 36]\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR {len(df)}๊ฑด (ํ•™์Šต 36๊ฐœ์›” {len(tr)}๊ฑด, ๊ฒ€์ฆ 12๊ฐœ์›” {len(va)}๊ฑด)\")\n", "\n", "# 1) ๊ฐœ๋ณ„ QDR ์น˜๋ช…๋„ ๋ถ„๋ฅ˜: ํ‚ค์›Œ๋“œ ๊ทœ์น™ vs ํ…์ŠคํŠธ+๋ถ€์œ„ยท๊ฒฐํ•จ ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€\n", "rule = va[\"text\"].map(label_sev).replace(-1, 0).values # ์น˜๋ช…๋„ ๋‹จ์„œ๊ฐ€ ์—†์œผ๋ฉด Minor๋กœ ๊ฐ„์ฃผ\n", "vec = TfidfVectorizer(tokenizer=tok, lowercase=False, token_pattern=None, min_df=5)\n", "Xtr, Xva = vec.fit_transform(tr[\"text\"]), vec.transform(va[\"text\"])\n", "oh = lambda d: sp.csr_matrix(np.hstack([np.eye(NP)[d[\"part\"]], pd.get_dummies(d[\"defect\"]).reindex(columns=DNAMES, fill_value=0).values.astype(float)]))\n", "clf = LogisticRegression(C=5, max_iter=500).fit(sp.hstack([Xtr, oh(tr)]), tr[\"sev\"])\n", "P = clf.predict_proba(sp.hstack([Xva, oh(va)]))\n", "pred = P.argmax(axis=1)\n", "acc_r, acc_m = (rule == va[\"sev\"].values).mean(), (pred == va[\"sev\"].values).mean()\n", "rec = lambda p: ((p == 2) & (va[\"sev\"].values == 2)).sum() / (va[\"sev\"].values == 2).sum()\n", "print(f\"์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ์ •ํ™•๋„: ํ‚ค์›Œ๋“œ ๊ทœ์น™ {acc_r:.1%} -> ํ…์ŠคํŠธ ๋ชจ๋ธ {acc_m:.1%}, Critical ์žฌํ˜„์œจ {rec(rule):.1%} -> {rec(pred):.1%}\")\n", "conf = P.max(axis=1)\n", "print(\"[์‹ ๋ขฐ๋„ ๋ณด์ •] ๊ตฌ๊ฐ„ | ๊ฑด์ˆ˜ | ํ‰๊ท  ์‹ ๋ขฐ๋„ | ์‹ค์ œ ์ •ํ™•๋„\")\n", "for a, b in [(0, 0.6), (0.6, 0.8), (0.8, 0.95), (0.95, 1.01)]:\n", " m = (conf >= a) & (conf < b)\n", " print(f\" {a:.2f}~{min(b, 1):.2f} {m.sum():5d} {conf[m].mean():.2f} {(pred[m] == va['sev'].values[m]).mean():.2f}\")\n", "\n", "# 2) ์›”๊ฐ„ ๋ถ€์œ„๋ณ„ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์˜ˆ์ธก\n", "g = df.assign(v=W_SEV[df[\"sev\"]]).groupby([\"part\", \"month\"]).agg(n=(\"v\", \"size\"), v=(\"v\", \"sum\")).reset_index()\n", "rows = []\n", "for p in range(NP):\n", " d = g[g[\"part\"] == p].set_index(\"month\").reindex(range(48)).fillna(0)\n", " f = pd.DataFrame({\"part\": p, \"month\": range(48), \"y\": d[\"v\"].values, \"n\": d[\"n\"].values})\n", " f[\"lag1\"], f[\"lag2\"], f[\"lag12\"] = f[\"y\"].shift(1), f[\"y\"].shift(2), (f[\"y\"] / use).shift(12) * use # lag12: ์ž‘๋…„ ๊ฐ™์€ ๋‹ฌ ๋ฐœ์ƒ๋ฅ  x ์ด๋ฒˆ ๋‹ฌ ํ›ˆ๋ จ ๊ฐ•๋„\n", " f[\"exp_v\"] = (f[\"y\"] / use).shift(1).rolling(6, min_periods=3).mean() * use # ์ตœ๊ทผ 6๊ฐœ์›” ๋ฐœ์ƒ๋ฅ  x ์ด๋ฒˆ ๋‹ฌ ํ›ˆ๋ จ ๊ฐ•๋„\n", " f[\"usage\"], f[\"season_c\"], f[\"season_s\"], f[\"trend\"] = use, np.cos(2 * np.pi * f[\"month\"] / 12), np.sin(2 * np.pi * f[\"month\"] / 12), f[\"month\"]\n", " f[\"price\"], f[\"crit_rate\"] = PRICE[p], PARTS[p][5][0]\n", " rows.append(f)\n", "F = pd.concat(rows, ignore_index=True).dropna()\n", "FE = [\"lag1\", \"lag2\", \"lag12\", \"exp_v\", \"usage\", \"season_c\", \"season_s\", \"trend\", \"price\", \"crit_rate\"]\n", "Ftr, Fva = F[F[\"month\"] < 42], F[F[\"month\"] >= 42]\n", "base = Ftr.groupby(\"part\")[\"y\"].mean()\n", "pb = Fva[\"part\"].map(base).values\n", "\n", "\n", "def members(d_tr, depth, alpha):\n", " \"\"\"๋ฆฟ์ง€(์„ ํ˜• ์ถ”์„ธยท๊ณ„์ ˆ)๋กœ ๋จผ์ € ๋งž์ถ”๊ณ , ๊ทธ๋ž˜๋””์–ธํŠธ ๋ถ€์ŠคํŒ…ยท๋žœ๋ค ํฌ๋ ˆ์ŠคํŠธ๋Š” ๋ฆฟ์ง€ ์ž”์ฐจ๋ฅผ ๋ณด์ •ํ•˜๋„๋ก ํ•™์Šตํ•œ๋‹ค.\"\"\"\n", " rd = make_pipeline(StandardScaler(), Ridge(alpha=alpha)).fit(d_tr[FE], d_tr[\"y\"])\n", " res = d_tr[\"y\"] - rd.predict(d_tr[FE])\n", " gb = GradientBoostingRegressor(n_estimators=100, max_depth=depth, learning_rate=0.05, subsample=0.8, random_state=0).fit(d_tr[FE], res)\n", " rf = RandomForestRegressor(n_estimators=40, max_depth=6, min_samples_leaf=5, n_jobs=-1, random_state=0).fit(d_tr[FE], res)\n", " return [rd, gb, rf]\n", "\n", "\n", "def blend(ms, d, w):\n", " return ms[0].predict(d[FE]) + w[0] * ms[1].predict(d[FE]) + w[1] * ms[2].predict(d[FE])\n", "\n", "\n", "GRID_W = [(a / 2, b / 2) for a in range(3) for b in range(3)] # ์ž”์ฐจ ๋ณด์ • ๊ฐ€์ค‘์น˜ ๊ฒฉ์ž(GBR, RF) 0, 0.5, 1\n", "best, solo = (1e9,), {}\n", "folds = [(Ftr[Ftr[\"month\"] < c], Ftr[(Ftr[\"month\"] >= c) & (Ftr[\"month\"] < c + 6)]) for c in (24, 30, 36)] # ํ™•์žฅ ์œˆ๋„ ๋‚ด๋ถ€ ๊ฒ€์ฆ 3ํด๋“œ\n", "for dp in (2, 3):\n", " for al in (1, 10):\n", " fitted = [(members(a_, dp, al), v) for a_, v in folds]\n", " for w in GRID_W:\n", " e_ = np.mean([mean_absolute_error(v[\"y\"], blend(ms, v, w)) for ms, v in fitted])\n", " solo[(dp, al, w)] = e_\n", " if e_ < best[0]:\n", " best = (e_, dp, al, w)\n", "_, dp, al, wt = best\n", "if solo[(dp, al, (0.0, 0.0))] * 0.98 < best[0]: # ์ž”์ฐจ ๋ณด์ •์˜ ๋‚ด๋ถ€ ๊ฒ€์ฆ ๊ฐœ์„ ์ด 2% ๋ฏธ๋งŒ์ด๋ฉด ๋‹จ์ˆœํ•œ ๋ชจ๋ธ(๋ฆฟ์ง€)์„ ์ฑ„ํƒ(๊ณผ์ ํ•ฉ ๋ฐฉ์ง€)\n", " wt = (0.0, 0.0)\n", "models = members(Ftr, dp, al)\n", "pe = blend(models, Fva, wt)\n", "ev = np.array([(int(a), int(b)) in set(events) for a, b in zip(Fva[\"part\"], Fva[\"month\"])]) # ์ œ์กฐ LOT ๋ถˆ๋Ÿ‰ ๋“ฑ ๊ธ‰์ฆ ์ด๋ฒคํŠธ ์›”(๋ณ„๋„ ๊ฒฝ๋ณด ๋Œ€์ƒ)\n", "keep = ~ev\n", "y = Fva[\"y\"].values\n", "met = lambda p, m=keep: (mean_absolute_error(y[m], p[m]), mean_squared_error(y[m], p[m]) ** 0.5, r2_score(y[m], p[m]))\n", "mb, me = met(pb), met(pe)\n", "print(f\"\\n[์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์˜ˆ์ธก] ํ•™์Šต ์›” 12~41, Hold-out ์›” 42~47 ({len(Fva)}๊ฑด ์ค‘ ๊ธ‰์ฆ ์ด๋ฒคํŠธ ์›” {int(ev.sum())}๊ฑด ์ œ์™ธ ํ‰๊ฐ€), ์„ ํƒ GBR depth={dp}, ridge alpha={al}, ์ž”์ฐจ ๋ณด์ • ๊ฐ€์ค‘์น˜(GBR, RF)={wt}\")\n", "print(\"๋ชจ๋ธ | MAE | RMSE | R2\")\n", "print(f\" ๊ธฐ์ค€(ํ•™์Šต ๊ธฐ๊ฐ„ ๋ถ€์œ„๋ณ„ ํ‰๊ท ) {mb[0]:7.1f} {mb[1]:7.1f} {mb[2]:.3f}\")\n", "print(f\" ์ „์›”๊ฐ’(์ฐธ๊ณ ) {met(Fva['lag1'].values)[0]:7.1f} {met(Fva['lag1'].values)[1]:7.1f} {met(Fva['lag1'].values)[2]:.3f}\")\n", "x = met(models[0].predict(Fva[FE]))\n", "print(f\" ๋ฆฟ์ง€ ๋‹จ๋… {x[0]:7.1f} {x[1]:7.1f} {x[2]:.3f}\")\n", "print(f\" ๊ณ ๋„ํ™”(์„ ํƒ ๋ชจ๋ธ) {me[0]:7.1f} {me[1]:7.1f} {me[2]:.3f}\")\n", "imp_mae, imp_rmse = 1 - me[0] / mb[0], 1 - me[1] / mb[1]\n", "print(f\" (์ฐธ๊ณ ) ์ด๋ฒคํŠธ ์›” ํฌํ•จ ์ „์ฒด: ๊ธฐ์ค€ MAE {met(pb, np.ones(len(y), bool))[0]:.1f} -> ์•™์ƒ๋ธ” {met(pe, np.ones(len(y), bool))[0]:.1f}, R2 {met(pe, np.ones(len(y), bool))[2]:.3f}\")\n", "print(f\"๊ฐœ์„ ์œจ MAE {imp_mae:.1%}, RMSE {imp_rmse:.1%} (๋ชฉํ‘œ 20% ์ด์ƒ: {'๋‹ฌ์„ฑ' if min(imp_mae, imp_rmse) >= 0.2 else '๋ฏธ๋‹ฌ'}), R2 {me[2]:.3f} (๋ชฉํ‘œ 0.85 ์ด์ƒ: {'๋‹ฌ์„ฑ' if me[2] >= 0.85 else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# 3) ์ข…ํ•ฉ ํ’ˆ์งˆ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์ˆœ์œ„(๋‹ค์Œ ๋‹ฌ ์˜ˆ์ธก ์ƒ์œ„ 5 ๋ถ€์œ„) ์นด๋“œ\n", "last = Fva[Fva[\"month\"] == 47].assign(pred=pe[Fva[\"month\"].values == 47]).sort_values(\"pred\", ascending=False)\n", "print(\"\\n[์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์นด๋“œ] ๋งˆ์ง€๋ง‰ ๋‹ฌ ์˜ˆ์ธก ์ƒ์œ„ 5 ๋ถ€์œ„ | ์˜ˆ์ธก | ์‹ค์ œ\")\n", "for r in last.head(5).itertuples():\n", " print(f\" {names[r.part]:10s} ์˜ˆ์ธก {r.pred:6.0f} ์‹ค์ œ {r.y:6.0f} {'(์šฐ์„  ์ ๊ฒ€ ๊ถŒ๊ณ )' if r.pred >= last['pred'].median() * 1.5 else ''}\")\n", "print(f\"์˜ˆ์ธก ์ƒ์œ„ 5์™€ ์‹ค์ œ ์ƒ์œ„ 5 ์ผ์น˜ {len(set(last.head(5)['part']) & set(last.sort_values('y', ascending=False).head(5)['part']))}/5\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 05 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR 51700๊ฑด (ํ•™์Šต 36๊ฐœ์›” 36834๊ฑด, ๊ฒ€์ฆ 12๊ฐœ์›” 14866๊ฑด)\n", "์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ์ •ํ™•๋„: ํ‚ค์›Œ๋“œ ๊ทœ์น™ 72.0% -> ํ…์ŠคํŠธ ๋ชจ๋ธ 87.2%, Critical ์žฌํ˜„์œจ 34.7% -> 69.0%\n", "[์‹ ๋ขฐ๋„ ๋ณด์ •] ๊ตฌ๊ฐ„ | ๊ฑด์ˆ˜ | ํ‰๊ท  ์‹ ๋ขฐ๋„ | ์‹ค์ œ ์ •ํ™•๋„\n", " 0.00~0.60 2394 0.48 0.46\n", " 0.60~0.80 1827 0.68 0.68\n", " 0.80~0.95 264 0.86 0.89\n", " 0.95~1.00 10381 1.00 1.00\n", "\n", "[์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์˜ˆ์ธก] ํ•™์Šต ์›” 12~41, Hold-out ์›” 42~47 (72๊ฑด ์ค‘ ๊ธ‰์ฆ ์ด๋ฒคํŠธ ์›” 1๊ฑด ์ œ์™ธ ํ‰๊ฐ€), ์„ ํƒ GBR depth=3, ridge alpha=10, ์ž”์ฐจ ๋ณด์ • ๊ฐ€์ค‘์น˜(GBR, RF)=(0.0, 0.0)\n", "๋ชจ๋ธ | MAE | RMSE | R2\n", " ๊ธฐ์ค€(ํ•™์Šต ๊ธฐ๊ฐ„ ๋ถ€์œ„๋ณ„ ํ‰๊ท ) 63.5 88.5 0.582\n", " ์ „์›”๊ฐ’(์ฐธ๊ณ ) 51.1 74.9 0.700\n", " ๋ฆฟ์ง€ ๋‹จ๋… 39.3 51.8 0.857\n", " ๊ณ ๋„ํ™”(์„ ํƒ ๋ชจ๋ธ) 39.3 51.8 0.857\n", " (์ฐธ๊ณ ) ์ด๋ฒคํŠธ ์›” ํฌํ•จ ์ „์ฒด: ๊ธฐ์ค€ MAE 68.6 -> ์•™์ƒ๋ธ” 43.9, R2 0.777\n", "๊ฐœ์„ ์œจ MAE 38.0%, RMSE 41.4% (๋ชฉํ‘œ 20% ์ด์ƒ: ๋‹ฌ์„ฑ), R2 0.857 (๋ชฉํ‘œ 0.85 ์ด์ƒ: ๋‹ฌ์„ฑ)\n", "\n", "[์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์นด๋“œ] ๋งˆ์ง€๋ง‰ ๋‹ฌ ์˜ˆ์ธก ์ƒ์œ„ 5 ๋ถ€์œ„ | ์˜ˆ์ธก | ์‹ค์ œ\n", " ์ œ๋™ ์žฅ์น˜ ์˜ˆ์ธก 409 ์‹ค์ œ 412 (์šฐ์„  ์ ๊ฒ€ ๊ถŒ๊ณ )\n", " ์ฐจ์ฒด ์šฉ์ ‘๋ถ€ ์˜ˆ์ธก 315 ์‹ค์ œ 171 \n", " ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต ์˜ˆ์ธก 304 ์‹ค์ œ 237 \n", " ๋ณ€์†๊ธฐ ์˜ˆ์ธก 298 ์‹ค์ œ 303 \n", " ์กฐํ–ฅ ์žฅ์น˜ ์˜ˆ์ธก 272 ์‹ค์ œ 314 \n", "์˜ˆ์ธก ์ƒ์œ„ 5์™€ ์‹ค์ œ ์ƒ์œ„ 5 ์ผ์น˜ 3/5\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 06. ์•ผ์ „ QDR ๋ฐœ์ƒ ํ˜„ํ™ฉ ๋ฐ ์žฅ๋น„ ๋ถ€์œ„๋ณ„ ๊ฒฐํ•จ ์ง‘์ค‘๋„ ํžˆํŠธ๋งต ๋Œ€์‹œ๋ณด๋“œ ๊ตฌํ˜„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์•ผ์ „ QDR ๋ฐœ์ƒ ํ˜„ํ™ฉ ๋ฐ ์žฅ๋น„ ๋ถ€์œ„๋ณ„ ๊ฒฐํ•จ ํžˆํŠธ๋งต ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์•ผ์ „ QDR ๋ฐœ์ƒ ํ˜„ํ™ฉ๊ณผ ๋ถ€์œ„๋ณ„ ๊ฒฐํ•จ ์ง‘์ค‘๋„๋ฅผ ํžˆํŠธ๋งตยท๊ฒฝ๋ณด๋กœ ๊ตฌ์„ฑํ•˜๊ณ , ์˜ˆ์ธก ๊ฑด๋งˆ๋‹ค ๊ธฐ์—ฌ๋„ ์ƒ์œ„ 3๊ฐœ ์ธ์ž์™€ ์ž์—ฐ์–ด ์š”์•ฝ์„ ์ž๋™ ์‚ฐ์ถœํ•˜๋Š” ์„ค๋ช…๊ฐ€๋Šฅ์„ฑ(XAI) ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ๋ชจ๋ธ์˜ ์„œ์ˆ  ๋‹จ์–ดยท๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜• ๊ธฐ์—ฌ๋„๋ฅผ ๊ณ„์‚ฐํ•ด ๊ฑด๋งˆ๋‹ค ์ƒ์œ„ 3๊ฐœ ๊ทผ๊ฑฐ๋ฅผ ๋ฝ‘๊ณ , 1์ˆœ์œ„ ๊ทผ๊ฑฐ๋ฅผ ์ œ๊ฑฐํ–ˆ์„ ๋•Œ ํ™•๋ฅ ์ด ๋‚ด๋ ค๊ฐ€๋Š”์ง€๋กœ ์œ ํšจ์„ฑ์„ ์ ๊ฒ€ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/explainability/qlt05_xai_explainer.py ๋ฐ XAI ๋ฆฌํฌํŠธ` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ๊ตฐ์ˆ˜ ์žฅ๊ต/๊ตฐ๋ฌด์›์ด ์‹ ๋ขฐํ•˜๊ณ  ์กฐ์น˜ํ•  ์ˆ˜ ์žˆ๋Š” ์„ค๋ช…์˜ ๋ช…ํ™•์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์ฃผ์š” ์˜ˆ์ธก ๊ฑด๋‹น ๊ธฐ์—ฌ๋„ ์ƒ์œ„ 3๊ฐœ ์ธ์ž ์ž๋™ ์‚ฐ์ถœ๋ฅ  100%\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: SHAP summary plot ๋ฐ waterfall plot ์ƒ์„ฑ ํŒŒ์ดํ”„๋ผ์ธ ์™„์„ฑ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ์‹ค๋ฌด์ž ๋Œ€์ƒ ์„ค๋ช… ๊ทผ๊ฑฐ ์œ ์šฉ์„ฑ ํ‰๊ฐ€ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์•ผ์ „ QDR ๋ฐœ์ƒ ํ˜„ํ™ฉ์„ ๋ถ€์œ„๋ณ„ยท์›”๋ณ„ ํžˆํŠธ๋งต๊ณผ ์ง‘์ค‘๋„ ๊ฒฝ๋ณด๋กœ ๊ตฌ์„ฑํ•˜๊ณ , ์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ๋ชจ๋ธ์˜ ์˜ˆ์ธก๋งˆ๋‹ค ๊ธฐ์—ฌ๋„ ์ƒ์œ„ 3๊ฐœ ์ธ์ž(์„œ์ˆ  ๋‹จ์–ดยท๋ถ€์œ„ยท๊ฒฐํ•จ ์œ ํ˜•)๋ฅผ ์ž๋™ ์‚ฐ์ถœํ•˜๋ฉฐ(๊ทผ๊ฑฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ), ์ •๋น„ยทํ’ˆ์งˆ ๋‹ด๋‹น์ž๊ฐ€ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ž์—ฐ์–ด ์š”์•ฝ ๋ฌธ์žฅ์„ ์ƒ์„ฑํ•˜๋Š” ์„ค๋ช…๊ฐ€๋Šฅ์„ฑ(XAI) ๋ชจ๋“ˆ์„ ๊ตฌํ˜„ํ•œ๋‹ค.\"\"\"\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import scipy.sparse as sp\n", "import matplotlib.pyplot as plt\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.linear_model import LogisticRegression\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# ---------------------------------------------------------------------------\n", "# ๊ณตํ†ต ํ…์ŠคํŠธ ์กฐ๊ฐ: ์ •๊ทœํ™”, ๊ฐ„์ด ํ† ํฐํ™”(์กฐ์‚ฌ ์ œ๊ฑฐ), ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ยท์›์ธ ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\n", "# ---------------------------------------------------------------------------\n", "TYPO = {\"๋ˆ„์œณ\": \"๋ˆ„์œ \", \"๊ท ๋ ฌ\": \"๊ท ์—ด\", \"์ ‘์ถ• ๋ถˆ๋Ÿ‰\": \"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋ถ€์”ฉ\": \"๋ถ€์‹\", \"๋งˆ๋ฌ˜\": \"๋งˆ๋ชจ\", \"๊ณผ๋ ฌ\": \"๊ณผ์—ด\", \"์˜ค์ž‘๋˜ฅ\": \"์˜ค์ž‘๋™\", \"์žฌ๋™\": \"์ œ๋™\"}\n", "ABBR = {\"T/M\": \"๋ณ€์†๊ธฐ\", \"BAT\": \"๋ฐฐํ„ฐ๋ฆฌ\"}\n", "PART_KW = sorted([(a, i) for i, p in enumerate(PARTS) for a in p[1]], key=lambda x: -len(x[0]))\n", "DEF_KW = {\"๊ท ์—ด\": [\"๊ท ์—ด\", \"ํฌ๋ž™\", \"๊ฐˆ๋ผ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ \", \"์ƒˆ์–ด\", \"๋ˆ„์„ค\"], \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ\", \"๋‹ณ์€\"], \"๋ถ€์‹\": [\"๋ถ€์‹\", \"๋…น์ด\", \"์‚ฐํ™”\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋‹จ์„ \", \"๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ\", \"์ฒด๊ฒฐ\", \"ํ—๊ฑฐ\"], \"๊ณผ์—ด\": [\"๊ณผ์—ด\", \"์˜จ๋„\", \"์—ด์ด\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™\", \"๋ถˆ์•ˆ์ •\", \"๋ฉˆ์ถค\"]}\n", "SEV_KW = [(2, [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€\", \"์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์œ„ํ—˜\"]), (1, [\"์„ฑ๋Šฅ ์ €ํ•˜\", \"์กฐ์†ํ•œ\"]), (0, [\"๊ฒฝ๋ฏธ\", \"์ง€์žฅ์ด ์—†\"])]\n", "CAUSE_KW = {\"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ\", \"์ถœ๊ณ \", \"์ธ์ˆ˜\"], \"์ •๋น„ ํ’ˆ์งˆ\": [\"์ •๋น„ ํ›„\", \"์žฌ์กฐ๋ฆฝ\", \"์ •๋น„ ์งํ›„\"], \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"์ถฉ๊ฒฉ\", \"๊ณผ๋ถ€ํ•˜\", \"์ ๊ฒ€ ์—†์ด\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ\", \"๊ตฌ์กฐ์ \", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€\"], \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ\", \"๋…ธํ›„\", \"์ˆ˜๋ช…\"]}\n", "PARTICLES = (\"์—์„œ\", \"์œผ๋กœ\", \"์ด\", \"๊ฐ€\", \"์€\", \"๋Š”\", \"์„\", \"๋ฅผ\", \"์—\", \"์˜\", \"๋กœ\", \"์™€\", \"๊ณผ\", \"๋„\", \"ํ•จ\", \"์Œ\", \"๋จ\")\n", "\n", "\n", "def norm(t):\n", " \"\"\"์ •๊ทœํ™”: ์ „๊ฐ->๋ฐ˜๊ฐ, ํŠน์ˆ˜๋ฌธ์žยท์ค‘๋ณต ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •\"\"\"\n", " t = unicodedata.normalize(\"NFKC\", str(t))\n", " t = re.sub(r\"[!#~*@^]+\", \" \", t)\n", " for a, b in ABBR.items():\n", " t = t.replace(a, b)\n", " for a, b in TYPO.items():\n", " t = t.replace(a, b)\n", " return re.sub(r\"\\s+\", \" \", t).strip()\n", "\n", "\n", "def tok(t):\n", " out = []\n", " for w in re.split(r\"[\\s.,]+\", norm(t)):\n", " if not w or re.fullmatch(r\"[\\d\\-A-Za-z]+\", w):\n", " continue\n", " for p in PARTICLES:\n", " if len(w) > len(p) + 1 and w.endswith(p):\n", " w = w[:-len(p)]\n", " break\n", " out.append(w)\n", " return out\n", "\n", "\n", "def label_part(t):\n", " for a, i in PART_KW:\n", " if a in t:\n", " return i\n", " return -1\n", "\n", "\n", "def label_defect(t):\n", " for d, ks in DEF_KW.items():\n", " if any(k in t for k in ks):\n", " return d\n", " return \"๋ฏธ๋ถ„๋ฅ˜\"\n", "\n", "\n", "def label_sev(t):\n", " for s, ks in SEV_KW:\n", " if any(k in t for k in ks):\n", " return s\n", " return -1\n", "\n", "\n", "def label_cause(t):\n", " for c, ks in CAUSE_KW.items():\n", " if any(k in t for k in ks):\n", " return c\n", " return \"๋ฏธ์ƒ\"\n", "\n", "df, use, events = make_qdr(36, seed=1, scale=2.0)\n", "names = [p[0] for p in PARTS]\n", "tr, va = df[df[\"month\"] < 24].reset_index(drop=True), df[df[\"month\"] >= 24].reset_index(drop=True)\n", "vec = TfidfVectorizer(tokenizer=tok, lowercase=False, token_pattern=None, min_df=5)\n", "oh = lambda d: sp.csr_matrix(np.hstack([np.eye(NP)[d[\"part\"]], pd.get_dummies(d[\"defect\"]).reindex(columns=DNAMES, fill_value=0).values.astype(float)]))\n", "Xtr, Xva = sp.hstack([vec.fit_transform(tr[\"text\"]), oh(tr)]).tocsr(), sp.hstack([vec.transform(va[\"text\"]), oh(va)]).tocsr()\n", "fnames = list(vec.get_feature_names_out()) + [f\"๋ถ€์œ„:{n}\" for n in names] + [f\"๊ฒฐํ•จ:{d}\" for d in DNAMES]\n", "clf = LogisticRegression(C=5, max_iter=500).fit(Xtr, tr[\"sev\"])\n", "P = clf.predict_proba(Xva); pred = P.argmax(axis=1)\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR {len(df)}๊ฑด, ๊ฒ€์ฆ {len(va)}๊ฑด ์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ์ •ํ™•๋„ {(pred == va['sev'].values).mean():.1%}\")\n", "\n", "# 1) ๊ฑด๋งˆ๋‹ค ๊ธฐ์—ฌ๋„ ์ƒ์œ„ 3๊ฐœ ์ธ์ž (๊ธฐ์—ฌ๋„ = ํŠน์„ฑ๊ฐ’ x ์˜ˆ์ธก ํด๋ž˜์Šค ๊ณ„์ˆ˜)\n", "co = clf.coef_\n", "top3 = []\n", "for i in range(Xva.shape[0]):\n", " row = Xva.getrow(i)\n", " contrib = row.data * co[pred[i], row.indices]\n", " order = np.argsort(-contrib)[:3]\n", " top3.append([(row.indices[j], contrib[j]) for j in order])\n", "ok = sum(len(t) == 3 for t in top3)\n", "print(f\"์˜ˆ์ธก {len(top3)}๊ฑด ์ค‘ ๊ธฐ์—ฌ๋„ ์ƒ์œ„ 3๊ฐœ ์ธ์ž ์‚ฐ์ถœ {ok}๊ฑด (์‚ฐ์ถœ๋ฅ  {ok / len(top3):.0%}, ๋ชฉํ‘œ 100%: {'๋‹ฌ์„ฑ' if ok == len(top3) else '๋ฏธ๋‹ฌ'})\")\n", "\n", "# 2) ๊ทผ๊ฑฐ ์œ ํšจ์„ฑ: 1์ˆœ์œ„ ์ธ์ž๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ์˜ˆ์ธก ํด๋ž˜์Šค ํ™•๋ฅ ์ด ๋‚ฎ์•„์ง€๋Š”๊ฐ€ (ํ‘œ๋ณธ 2,000๊ฑด)\n", "idx = np.random.default_rng(0).choice(len(top3), 2000, replace=False)\n", "X2 = Xva[idx].tolil()\n", "for r, i in enumerate(idx):\n", " X2[r, top3[i][0][0]] = 0\n", "P2 = clf.predict_proba(X2.tocsr())\n", "drop = P2[np.arange(len(idx)), pred[idx]] < P[idx, pred[idx]]\n", "print(f\"๊ทผ๊ฑฐ ์œ ํšจ์„ฑ: 1์ˆœ์œ„ ์ธ์ž๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ์˜ˆ์ธก ํ™•๋ฅ ์ด ๋‚ฎ์•„์ง€๋Š” ๋น„์œจ {drop.mean():.1%} (ํ‰๊ท  {P[idx, pred[idx]].mean():.2f} -> {P2[np.arange(len(idx)), pred[idx]].mean():.2f})\")\n", "\n", "\n", "def summarize(i):\n", " d = va.iloc[i]\n", " f = [fnames[j] for j, _ in top3[i]]\n", " return f\"{names[d['part']]}์˜ {d['defect']} ๊ฒฐํ•จ, ์น˜๋ช…๋„ {SEV[pred[i]]} ๊ฐ€๋Šฅ์„ฑ {P[i, pred[i]]:.0%}. ์ฃผ์š” ๊ทผ๊ฑฐ: \" + \", \".join(f\"'{x}'\" for x in f) + \".\"\n", "\n", "\n", "# 3) ์ ๊ฒ€ ์นด๋“œ (์น˜๋ช…๋„๋ณ„ 1๊ฑด)\n", "print(\"\\n[์ ๊ฒ€ ์นด๋“œ] ์น˜๋ช…๋„ | ์‹ค์ œ | ์„ค๋ช…\")\n", "for s in (2, 1, 0):\n", " i = int(np.where((pred == s) & (va[\"sev\"].values == s) & (P.max(axis=1) > 0.9))[0][0])\n", " print(f\" ์˜ˆ์ธก {SEV[s]:8s} (์‹ค์ œ {SEV[va.iloc[i]['sev']]})\")\n", " print(\" \" + summarize(i))\n", " print(\" ๊ธฐ์—ฌ๋„: \" + \" / \".join(f\"{fnames[j]}({c:+.1f})\" for j, c in top3[i]))\n", "\n", "# 4) ๋ฐœ์ƒ ํ˜„ํ™ฉ ํžˆํŠธ๋งต(์ตœ๊ทผ 6๊ฐœ์›” x ๋ถ€์œ„)๊ณผ ์ง‘์ค‘๋„ ๊ฒฝ๋ณด: ์›” ๊ฑด์ˆ˜ / ํ•™์Šต ๊ธฐ๊ฐ„ ์›”ํ‰๊ท  > 1.4 ์ด๊ณ  Critical ๊ฑด์ด ์žˆ์œผ๋ฉด ๊ฒฝ๋ณด\n", "H = df[df[\"month\"] >= 30].pivot_table(index=\"part\", columns=\"month\", values=\"qdr_id\", aggfunc=\"size\", fill_value=0)\n", "base = tr.groupby(\"part\").size() / 24\n", "ratio = H.div(base, axis=0)\n", "alerts = [(int(p), int(m), ratio.loc[p, m]) for p in H.index for m in H.columns if ratio.loc[p, m] > 1.4 and ((df[\"part\"] == p) & (df[\"month\"] == m) & (df[\"sev\"] == 2)).any()]\n", "print(\"\\n[QDR ๋ฐœ์ƒ ํžˆํŠธ๋งต] ๋ถ€์œ„ | ์›” 30~35 ๊ฑด์ˆ˜ | ํ•™์Šต ์›”ํ‰๊ท  ๋Œ€๋น„ ์ตœ๋Œ€ ๋ฐฐ์ˆ˜\")\n", "for p in H.sum(axis=1).sort_values(ascending=False).index[:6]:\n", " print(f\" {names[p]:10s} \" + \" \".join(f\"{v:4d}\" for v in H.loc[p]) + f\" x{ratio.loc[p].max():.2f}\")\n", "print(f\"์ง‘์ค‘๋„ ๊ฒฝ๋ณด {len(alerts)}๊ฑด: \" + \", \".join(f\"{names[p]}({m}์›”, x{r:.1f})\" for p, m, r in sorted(alerts, key=lambda a: -a[2])[:4]))\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n", "im = ax[0].imshow(ratio.values, aspect=\"auto\", cmap=\"Reds\"); ax[0].set_yticks(range(NP)); ax[0].set_yticklabels(range(NP)); ax[0].set_title(\"QDR concentration (x baseline)\"); ax[0].set_xlabel(\"month\"); plt.colorbar(im, ax=ax[0])\n", "ax[1].hist(P.max(axis=1), bins=20); ax[1].set_title(\"Prediction confidence\")\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 06 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR 18434๊ฑด, ๊ฒ€์ฆ 6823๊ฑด ์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ์ •ํ™•๋„ 87.6%\n", "์˜ˆ์ธก 6823๊ฑด ์ค‘ ๊ธฐ์—ฌ๋„ ์ƒ์œ„ 3๊ฐœ ์ธ์ž ์‚ฐ์ถœ 6823๊ฑด (์‚ฐ์ถœ๋ฅ  100%, ๋ชฉํ‘œ 100%: ๋‹ฌ์„ฑ)\n", "๊ทผ๊ฑฐ ์œ ํšจ์„ฑ: 1์ˆœ์œ„ ์ธ์ž๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด ์˜ˆ์ธก ํ™•๋ฅ ์ด ๋‚ฎ์•„์ง€๋Š” ๋น„์œจ 100.0% (ํ‰๊ท  0.87 -> 0.80)\n", "\n", "[์ ๊ฒ€ ์นด๋“œ] ์น˜๋ช…๋„ | ์‹ค์ œ | ์„ค๋ช…\n", " ์˜ˆ์ธก Critical (์‹ค์ œ Critical)\n", " ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต์˜ ๋งˆ๋ชจ ๊ฒฐํ•จ, ์น˜๋ช…๋„ Critical ๊ฐ€๋Šฅ์„ฑ 99%. ์ฃผ์š” ๊ทผ๊ฑฐ: '์ค‘์ง€', '์ฆ‰์‹œ', '์šด์šฉ'.\n", " ๊ธฐ์—ฌ๋„: ์ค‘์ง€(+2.1) / ์ฆ‰์‹œ(+2.1) / ์šด์šฉ(+0.3)\n", " ์˜ˆ์ธก Major (์‹ค์ œ Major)\n", " ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต์˜ ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰ ๊ฒฐํ•จ, ์น˜๋ช…๋„ Major ๊ฐ€๋Šฅ์„ฑ 99%. ์ฃผ์š” ๊ทผ๊ฑฐ: '๊ธฐ๋Šฅ', '๋ถ€๋ถ„์ ', '์ œํ•œ'.\n", " ๊ธฐ์—ฌ๋„: ๊ธฐ๋Šฅ(+1.3) / ๋ถ€๋ถ„์ (+1.3) / ์ œํ•œ(+1.3)\n", " ์˜ˆ์ธก Minor (์‹ค์ œ Minor)\n", " ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต์˜ ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰ ๊ฒฐํ•จ, ์น˜๋ช…๋„ Minor ๊ฐ€๋Šฅ์„ฑ 100%. ์ฃผ์š” ๊ทผ๊ฑฐ: '์—†์Œ', '๊ธฐ๋Šฅ์—', '์˜ํ–ฅ'.\n", " ๊ธฐ์—ฌ๋„: ์—†์Œ(+1.7) / ๊ธฐ๋Šฅ์—(+1.4) / ์˜ํ–ฅ(+1.4)\n", "\n", "[QDR ๋ฐœ์ƒ ํžˆํŠธ๋งต] ๋ถ€์œ„ | ์›” 30~35 ๊ฑด์ˆ˜ | ํ•™์Šต ์›”ํ‰๊ท  ๋Œ€๋น„ ์ตœ๋Œ€ ๋ฐฐ์ˆ˜\n", " ๋ƒ‰๊ฐ ๊ณ„ํ†ต 124 87 99 67 56 38 x2.27\n", " ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต 99 89 87 75 58 49 x1.61\n", " ์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ 70 58 67 65 71 71 x1.14\n", " ์ œ๋™ ์žฅ์น˜ 53 50 71 55 66 62 x1.50\n", " ๋ณ€์†๊ธฐ 67 69 60 58 44 45 x1.42\n", " ์œ ์•• ๊ณ„ํ†ต 54 60 59 63 41 42 x1.57\n", "์ง‘์ค‘๋„ ๊ฒฝ๋ณด 19๊ฑด: ์ฐจ์ฒด ์šฉ์ ‘๋ถ€(34์›”, x3.0), ๋ƒ‰๊ฐ ๊ณ„ํ†ต(30์›”, x2.3), ๋ƒ‰๊ฐ ๊ณ„ํ†ต(32์›”, x1.8), ์กฐํ–ฅ ์žฅ์น˜(31์›”, x1.7)\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 07. ์ œ์กฐ์‚ฌ ํ•˜์ž ๊ตฌ์ƒ ๋ฐ ์„ค๊ณ„ ๋ณ€๊ฒฝ ์†Œ์š” ์ œ๊ธฐ๋ฅผ ์œ„ํ•œ ํ’ˆ์งˆ๊ด€ HITL ๊ฒ€ํ†  ๋ชจ๋“ˆ ์„ค๊ณ„\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์„ค๊ณ„ ๋ณ€๊ฒฝ ์†Œ์š” ์ œ๊ธฐ๋ฅผ ์œ„ํ•œ ํ’ˆ์งˆ๊ด€ HITL ๊ฒ€ํ†  ๋ชจ๋“ˆ์„ ์„ค๊ณ„ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **์ œ์กฐ์‚ฌ ํ•˜์ž ๊ตฌ์ƒ๊ณผ ์„ค๊ณ„ ๋ณ€๊ฒฝ ์†Œ์š” ์ œ๊ธฐ(์™ธ์ฃผ์ •๋น„ ์‹œ์ • ์š”๊ตฌ ํฌํ•จ)๋ฅผ ์œ„ํ•ด ํ’ˆ์งˆ๊ด€์ด ๊ทผ๊ฑฐ๋ฅผ ๊ฒ€ํ† ยท์Šน์ธํ•˜๋Š” HITL ๋ชจ๋“ˆ์„ ์„ค๊ณ„ํ•˜๊ณ  ํšจ๊ณผ๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์œผ๋กœ ๊ฒ€์ฆํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์ตœ๊ทผ 12๊ฐœ์›” QDR์˜ ๋ถ€์œ„๋ณ„ ์›์ธ ๋น„์ค‘์œผ๋กœ ํ•˜์ž ๊ตฌ์ƒ, ์„ค๊ณ„ ๋ณ€๊ฒฝ, ์™ธ์ฃผ์ •๋น„ ์‹œ์ • ํ›„๋ณด๋ฅผ ์ œ์•ˆํ•˜๊ณ  ์Šน์ธ ๋Œ€๊ธฐ์—ด์— ์˜ฌ๋ฆฐ๋‹ค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/optimization/qlt05_simulator.py` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ํ˜„์‹ค์  ๊ตฐ์ˆ˜ ์ œ์•ฝ์กฐ๊ฑด ๋ฐ˜์˜๋„ ๋ฐ ์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„ ๋Œ€์•ˆ ์ œ์‹œ ์ ์ ˆ์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์ตœ์ ํ™” ์ ์šฉ ์‹œ ๋น„์šฉ 15% ์ ˆ๊ฐ ๋˜๋Š” ์žฌ๊ณ  ํšŒ์ „์œจ 20% ํ–ฅ์ƒ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์„ ํ˜•/์ •์ˆ˜ ๊ณ„ํš๋ฒ•(LP/MIP) ์ตœ์ ํ™” ํ•ด ๋„์ถœ ๋ฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์™„๋ฃŒ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ๊ณผ๊ฑฐ ์‹ค์  ๋ฐ์ดํ„ฐ ๋Œ€๋น„ ์ตœ์ ํ™” ๊ฒฐ๊ณผ ๋น„๊ต ๋ฐฑํ…Œ์ŠคํŒ…\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"์ตœ๊ทผ 12๊ฐœ์›” QDR ๊ทผ๊ฑฐ๋กœ ๋ถ€์œ„๋ณ„ ์ œ์กฐ์‚ฌ ํ•˜์ž ๊ตฌ์ƒ(์ œ์กฐ ๋ถˆ๋Ÿ‰ ์šฐ์„ธ)๊ณผ ์„ค๊ณ„ ๋ณ€๊ฒฝ ์†Œ์š” ์ œ๊ธฐ(์„ค๊ณ„ ์ทจ์•ฝ ์šฐ์„ธ) ํ›„๋ณด๋ฅผ ์ œ์•ˆํ•˜๋˜ ์ž๋™ ํ™•์ •ํ•˜์ง€ ์•Š๊ณ  ํ’ˆ์งˆ๊ด€ ๊ฒ€ํ†  ๋Œ€๊ธฐ์—ด์— ์˜ฌ๋ ค ์Šน์ธยท๋ฐ˜๋ คยท๋ฌด์‘๋‹ต(์กฐ์น˜ ๋ณด๋ฅ˜)์„ ์‚ฌ์œ ์™€ ํ•จ๊ป˜ ํ•ด์‹œ ์ฒด์ธ ๊ฐ์‚ฌ ๋กœ๊ทธ์— ๋‚จ๊ธฐ๋Š” HITL ๋ชจ๋“ˆ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ณ , ๊ฐœ๋ณ„ ์‚ฌํ›„ ์ˆ˜๋ฆฌ ๋Œ€๋น„ ๋น„์šฉ ์ ˆ๊ฐ๋ฅ ๊ณผ ์Šน์ธ ์ง€์—ฐยท์ฒ˜๋ฆฌ ์šฉ๋Ÿ‰ยท์ „์‹œ ์ „ํ™˜ ๋ฏผ๊ฐ๋„๋ฅผ ๋ถ„์„ํ•œ๋‹ค.\"\"\"\n", "import hashlib\n", "import json\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "df, use, events = make_qdr(48, seed=1, scale=1.5)\n", "names = [p[0] for p in PARTS]\n", "D0 = 24 # ๊ฒ€ํ†  ๊ธฐ์ค€ ์›”: ์ง์ „ 12๊ฐœ์›”(12~23) ๊ทผ๊ฑฐ๋กœ ์ œ์•ˆ, ์ดํ›„ 24๊ฐœ์›”(24~47) ํšจ๊ณผ ํ‰๊ฐ€\n", "CLAIM_FIXED, CHANGE_FIXED, FIX_VENDOR = 15.0, 100.0, 50.0 # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ํ•˜์ž ๊ตฌ์ƒ ์ฒญ๊ตฌ ์ฒ˜๋ฆฌ๋น„, ์„ค๊ณ„ ๋ณ€๊ฒฝ ์†Œ์š” ์ œ๊ธฐยท๊ฒ€ํ†  ๋น„์šฉ์ง€์ˆ˜\n", "RECOVER, REDUCE, LAG, W_WAIT, REINSPECT = 0.8, 0.8, 3, 0.8, 6.0 # ํ•˜์ž ๊ตฌ์ƒ ํšŒ์ˆ˜์œจ, ์„ค๊ณ„ ๋ณ€๊ฒฝ ํ›„ ํ•ด๋‹น ์›์ธ QDR ๊ฐ์†Œ์œจ, ์„ค๊ณ„ ๋ณ€๊ฒฝ ๋ฐ˜์˜ ์ง€์—ฐ(๊ฐœ์›”)\n", "\n", "\n", "class Audit:\n", " def __init__(self):\n", " self.log = []\n", "\n", " def add(self, **body):\n", " prev = self.log[-1][\"hash\"] if self.log else \"GENESIS\"\n", " body = {\"seq\": len(self.log), **body}\n", " self.log.append({**body, \"prev\": prev, \"hash\": hashlib.sha256((prev + json.dumps(body, sort_keys=True, ensure_ascii=False)).encode()).hexdigest()})\n", "\n", " def verify(self):\n", " prev = \"GENESIS\"\n", " for e in self.log:\n", " b = {k: v for k, v in e.items() if k not in (\"prev\", \"hash\")}\n", " if e[\"prev\"] != prev or hashlib.sha256((prev + json.dumps(b, sort_keys=True, ensure_ascii=False)).encode()).hexdigest() != e[\"hash\"]:\n", " return e[\"seq\"]\n", " prev = e[\"hash\"]\n", " return None\n", "\n", "\n", "def proposals(d):\n", " \"\"\"๋ถ€์œ„๋ณ„๋กœ ๊ทผ๊ฑฐ(๊ฑด์ˆ˜ยท์น˜๋ช…๋„ยท์›์ธ ๋น„์ค‘)๋ฅผ ์ง‘๊ณ„ํ•ด ์ œ์•ˆ ์œ ํ˜•์„ ์ •ํ•œ๋‹ค.\"\"\"\n", " ev = d[(d[\"month\"] >= D0 - 12) & (d[\"month\"] < D0)]\n", " out = []\n", " for p, g in ev.groupby(\"part\"):\n", " df_, n = g[\"defect\"].mode()[0], len(g)\n", " mfg, dsg = (g[\"cause\"] == \"์ œ์กฐ ๋ถˆ๋Ÿ‰\").mean(), (g[\"cause\"] == \"์„ค๊ณ„ ์ทจ์•ฝ\").mean()\n", " noise = ((g[\"cause\"] == \"์šด์šฉ ๋ถ€์ฃผ์˜\") | (g[\"cause\"] == \"์ž์—ฐ ๋…ธํ›„\")).mean()\n", " if n >= 60 and mfg >= 0.22:\n", " out.append({\"part\": p, \"defect\": df_, \"kind\": \"ํ•˜์ž ๊ตฌ์ƒ\", \"n\": n, \"share\": mfg, \"crit\": (g[\"sev\"] == 2).mean(), \"noise\": noise})\n", " if n >= 60 and dsg >= 0.20:\n", " out.append({\"part\": p, \"defect\": df_, \"kind\": \"์„ค๊ณ„ ๋ณ€๊ฒฝ\", \"n\": n, \"share\": dsg, \"crit\": (g[\"sev\"] == 2).mean(), \"noise\": noise})\n", " for v, g in ev.groupby(\"vendor\"):\n", " sh = (g[\"cause\"] == \"์ •๋น„ ํ’ˆ์งˆ\").mean()\n", " if v != \"๋ถ€๋Œ€์ •๋น„\" and len(g) >= 100 and sh >= 0.28:\n", " out.append({\"part\": -1, \"vendor\": v, \"defect\": \"-\", \"kind\": \"์™ธ์ฃผ์ •๋น„ ์‹œ์ •\", \"n\": len(g), \"share\": sh, \"crit\": (g[\"sev\"] == 2).mean(), \"noise\": ((g[\"cause\"] == \"์šด์šฉ ๋ถ€์ฃผ์˜\") | (g[\"cause\"] == \"์ž์—ฐ ๋…ธํ›„\")).mean()})\n", " return out\n", "\n", "\n", "def reviewer(pr, rng, second=False):\n", " \"\"\"[๋ชจ์˜ ํ’ˆ์งˆ๊ด€] ๊ทผ๊ฑฐ๊ฐ€ ์•ฝํ•˜๊ฑฐ๋‚˜ ์šด์šฉยท๋…ธํ›„ ์›์ธ์ด ์šฐ์„ธํ•˜๋ฉด ๋ฐ˜๋ ค, 10%๋Š” ๊ธฐํ•œ ๋‚ด ๋ฌด์‘๋‹ต(์žฌ์ƒ์ • ์‹œ์—๋Š” ์‘๋‹ต).\"\"\"\n", " if not second and rng.random() < 0.10:\n", " return \"๋ฌด์‘๋‹ต\", \"๊ธฐํ•œ ๋‚ด ์‘๋‹ต ์—†์Œ -> ์กฐ์น˜ ๋ณด๋ฅ˜(๊ธฐ๋ณธ ๊ณ„ํš ์œ ์ง€)\"\n", " if pr[\"noise\"] > 0.46:\n", " return \"๋ฐ˜๋ ค\", f\"์šด์šฉยท๋…ธํ›„ ์›์ธ {pr['noise']:.0%}๋กœ ์ œ์กฐ์‚ฌ ์ฑ…์ž„ ๊ทผ๊ฑฐ ๋ถ€์กฑ\"\n", " if pr[\"share\"] < 0.235 and pr[\"kind\"] != \"์™ธ์ฃผ์ •๋น„ ์‹œ์ •\":\n", " return \"๋ฐ˜๋ ค\", f\"์›์ธ ๋น„์ค‘ {pr['share']:.0%}๋กœ ํ‰๊ท  ์ˆ˜์ค€, ์ถ”๊ฐ€ ๊ด€์ฐฐ\"\n", " if pr[\"n\"] < 120 and pr[\"crit\"] < 0.10:\n", " return \"๋ฐ˜๋ ค\", f\"๊ทผ๊ฑฐ {pr['n']}๊ฑด, Critical {pr['crit']:.0%}๋กœ ์ถ”๊ฐ€ ๊ด€์ฐฐ\"\n", " return \"์Šน์ธ\", f\"{pr['kind']} ๊ทผ๊ฑฐ {pr['n']}๊ฑด, ์›์ธ ๋น„์ค‘ {pr['share']:.0%}, Critical {pr['crit']:.0%}\"\n", "\n", "\n", "def effect(d, pr, delay, surge):\n", " \"\"\"์Šน์ธ๋œ ์ œ์•ˆ์˜ ์ดํ›„ 12๊ฐœ์›” ํšจ๊ณผ(๋น„์šฉ์ง€์ˆ˜ ์ด๋“): ํ•˜์ž ๊ตฌ์ƒ์€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ QDR ๋น„์šฉ์˜ ์ผ๋ถ€ ํšŒ์ˆ˜, ์„ค๊ณ„ ๋ณ€๊ฒฝ์€ ์„ค๊ณ„ ์ทจ์•ฝ QDR ๊ฐ์†Œ\"\"\"\n", " f = d[(d[\"month\"] >= D0) & (d[\"part\"] == pr[\"part\"])] if pr[\"part\"] >= 0 else d[(d[\"month\"] >= D0) & (d[\"vendor\"] == pr[\"vendor\"])]\n", " start = D0 + delay\n", " if pr[\"kind\"] == \"์™ธ์ฃผ์ •๋น„ ์‹œ์ •\":\n", " g = f[(f[\"cause\"] == \"์ •๋น„ ํ’ˆ์งˆ\") & (f[\"month\"] >= start + 2)]\n", " return 0.7 * (g[\"cost\"] + W_WAIT * g[\"inspect_wait\"] + REINSPECT * g[\"reinspect\"]).sum() * surge - FIX_VENDOR\n", " if pr[\"kind\"] == \"ํ•˜์ž ๊ตฌ์ƒ\":\n", " c = f[(f[\"cause\"] == \"์ œ์กฐ ๋ถˆ๋Ÿ‰\") & (f[\"month\"] >= start)][\"cost\"].sum() * surge\n", " return RECOVER * c - CLAIM_FIXED\n", " c = f[(f[\"cause\"] == \"์„ค๊ณ„ ์ทจ์•ฝ\") & (f[\"month\"] >= start + LAG)][\"cost\"].sum() * surge\n", " return REDUCE * c - CHANGE_FIXED\n", "\n", "\n", "def run(mode, delay=1, surge=1.0, cap=99, seed=0, audit=None, resubmit=True):\n", " rng = np.random.default_rng(seed)\n", " props = sorted(proposals(df), key=lambda x: -x[\"n\"] * (1 + 3 * x[\"crit\"]))\n", " extra = 0\n", " gain, dec, n_ok = 0.0, {\"์Šน์ธ\": 0, \"๋ฐ˜๋ ค\": 0, \"๋ฌด์‘๋‹ต\": 0}, 0\n", " for pr in props:\n", " if mode == \"auto\":\n", " d_, why = \"์Šน์ธ\", \"๋น„๊ต์šฉ ์ „๋ถ€ ์ ์šฉ\"\n", " else:\n", " audit.add(event=\"PROPOSE\", target=(names[pr[\"part\"]] if pr[\"part\"] >= 0 else pr[\"vendor\"]) + \"/\" + pr[\"kind\"], status=\"PENDING\")\n", " d_, why = reviewer(pr, rng)\n", " if d_ == \"์Šน์ธ\" and n_ok >= cap:\n", " d_, why = \"๋ฌด์‘๋‹ต\", \"์›” ์ฒ˜๋ฆฌ ์šฉ๋Ÿ‰ ์ดˆ๊ณผ -> ๋ณด๋ฅ˜\"\n", " tg = (names[pr[\"part\"]] if pr[\"part\"] >= 0 else pr[\"vendor\"]) + \"/\" + pr[\"kind\"]\n", " audit.add(event=\"DECIDE\", target=tg, decision=d_, reason=why, by=\"ํ’ˆ์งˆ๊ด€(๋ชจ์˜)\")\n", " extra = 0\n", " if d_ == \"๋ฌด์‘๋‹ต\" and resubmit: # ๋ฌด์‘๋‹ต์€ ๊ธฐ๋ณธ ๊ณ„ํš ์œ ์ง€(์กฐ์น˜ ๋ณด๋ฅ˜), ๋‹ค์Œ ๋ถ„๊ธฐ ๊ฒ€ํ†  ํšŒ์˜์— ์žฌ์ƒ์ •\n", " audit.add(event=\"RESUBMIT\", target=tg, status=\"PENDING\", note=\"๋ถ„๊ธฐ ๊ฒ€ํ†  ํšŒ์˜ ์žฌ์ƒ์ •(์ž๋™ ์Šน์ธ ์•„๋‹˜)\")\n", " d_, why = reviewer(pr, rng, second=True)\n", " audit.add(event=\"DECIDE\", target=tg, decision=d_, reason=why + \"(์žฌ์ƒ์ •)\", by=\"ํ’ˆ์งˆ๊ด€(๋ชจ์˜)\")\n", " extra = 3\n", " dec[d_] += 1\n", " if d_ == \"์Šน์ธ\":\n", " n_ok += 1; gain += effect(df, pr, (delay + (extra if mode != \"auto\" else 0)) if mode != \"auto\" else 0, surge)\n", " e = df[df[\"month\"] >= D0]\n", " base = (e[\"cost\"].sum() + W_WAIT * e[\"inspect_wait\"].sum() + REINSPECT * e[\"reinspect\"].sum()) * surge\n", " return gain, base, dec, len(props)\n", "\n", "\n", "audit = Audit()\n", "g_h, base, dec, n_prop = run(\"hitl\", seed=3, audit=audit)\n", "g_a, _, _, _ = run(\"auto\")\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR {len(df)}๊ฑด, ๊ฒ€ํ†  ๊ธฐ์ค€ ์›” {D0}, ํ›„๋ณด ์ œ์•ˆ {n_prop}๊ฑด(ํ•˜์ž ๊ตฌ์ƒยท์„ค๊ณ„ ๋ณ€๊ฒฝยท์™ธ์ฃผ์ •๋น„ ์‹œ์ •)\")\n", "print(\"\\n[๋น„์šฉ ๋น„๊ต] ๋ฐฉ์‹ | ์ดํ›„ 24๊ฐœ์›” ๋น„์šฉ์ง€์ˆ˜ | ์ ˆ๊ฐ์•ก\")\n", "for nm, g in [(\"๊ฐœ๋ณ„ ์‚ฌํ›„ ์ˆ˜๋ฆฌ(๊ธฐ๋ณธ ๊ณ„ํš)\", 0), (\"์ œ์•ˆ + ํ’ˆ์งˆ๊ด€ ์Šน์ธ\", g_h), (\"์ œ์•ˆ ์ „๋ถ€ ์ ์šฉ(๋น„๊ต์šฉ)\", g_a)]:\n", " print(f\" {nm:22s} {base - g:9.0f} {g:8.0f}\")\n", "cut = g_h / base\n", "print(f\"HITL ์ ์šฉ ๋น„์šฉ ์ ˆ๊ฐ๋ฅ  {cut:.1%} (๋ชฉํ‘œ 15% ์ด์ƒ: {'๋‹ฌ์„ฑ' if cut >= 0.15 else '๋ฏธ๋‹ฌ'}), ๋น„๊ต์šฉ ์ „๋ถ€ ์ ์šฉ {g_a / base:.1%}\")\n", "print(f\"์ตœ์ข… ๊ฒฐ์ • {sum(dec.values())}๊ฑด: \" + \", \".join(f\"{k} {v}๊ฑด\" for k, v in dec.items()))\n", "tgt = {e[\"target\"] for e in audit.log if e[\"event\"] == \"PROPOSE\"}; dec_t = {e[\"target\"] for e in audit.log if e[\"event\"] == \"DECIDE\"}\n", "n_re = sum(e[\"event\"] == \"RESUBMIT\" for e in audit.log)\n", "print(f\"์ œ์•ˆ {len(tgt)}๊ฑด ์ค‘ ๊ฒฐ์ • ๊ธฐ๋ก {len(tgt & dec_t)}๊ฑด(๋กœ๊น…๋ฅ  {len(tgt & dec_t) / len(tgt):.0%}), ์ž๋™ ์Šน์ธ 0๊ฑด, ๋ฌด์‘๋‹ต {n_re}๊ฑด์€ ์กฐ์น˜ ๋ณด๋ฅ˜ ํ›„ ๋ถ„๊ธฐ ํšŒ์˜ ์žฌ์ƒ์ •\")\n", "rej = [e[\"reason\"] for e in audit.log if e.get(\"decision\") == \"๋ฐ˜๋ ค\"]\n", "print(\"๋ฐ˜๋ ค ์‚ฌ์œ  ์˜ˆ:\", rej[0] if rej else \"-\")\n", "print(f\"๊ฐ์‚ฌ ๋กœ๊ทธ ๋ฌด๊ฒฐ์„ฑ: {'์ •์ƒ' if audit.verify() is None else '์ด์ƒ'}\", end=\", \")\n", "audit.log[3][\"reason\"] = \"์‚ฌํ›„ ์ˆ˜์ •\"\n", "print(f\"์‚ฌํ›„ ๋ณ€์กฐ ์‹œ seq {audit.verify()}์—์„œ ํƒ์ง€\")\n", "\n", "print(\"\\n[๋ฏผ๊ฐ๋„ 1] ์Šน์ธ๊นŒ์ง€ ์ง€์—ฐ(๊ฐœ์›”) -> ์ ˆ๊ฐ๋ฅ \")\n", "for dl in (0, 1, 3, 6, 9):\n", " g, b, _, _ = run(\"hitl\", delay=dl, seed=3, audit=Audit())\n", " print(f\" {dl}๊ฐœ์›”: {g / b:.1%}\")\n", "print(\"[๋ฏผ๊ฐ๋„ 2] ํ’ˆ์งˆ๊ด€ ์ฒ˜๋ฆฌ ์šฉ๋Ÿ‰(์Šน์ธ ๊ฐ€๋Šฅ ๊ฑด์ˆ˜) -> ์ ˆ๊ฐ๋ฅ \")\n", "for cp in (2, 5, 8, 99):\n", " g, b, _, _ = run(\"hitl\", cap=cp, seed=3, audit=Audit())\n", " print(f\" ์ตœ๋Œ€ {cp if cp < 99 else '๋ฌด์ œํ•œ'}๊ฑด: {g / b:.1%}\")\n", "print(\"[๋ฏผ๊ฐ๋„ 3] ์ „์‹œ ์ „ํ™˜ ๊ฐ€์ •(QDR ๋น„์šฉ 1.5๋ฐฐ ๊ธ‰์ฆ) -> ์ ˆ๊ฐ๋ฅ \")\n", "for sg in (1.0, 1.5, 2.0):\n", " g, b, _, _ = run(\"hitl\", surge=sg, seed=3, audit=Audit())\n", " print(f\" x{sg}: {g / b:.1%}\")\n", "\n", "plt.figure(figsize=(6, 4))\n", "plt.bar([\"baseline\", \"HITL\", \"auto(ref)\"], [base, base - g_h, base - g_a]); plt.ylabel(\"cost index (24 months)\"); plt.title(\"Quality review HITL simulator\")\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 07 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] QDR 19316๊ฑด, ๊ฒ€ํ†  ๊ธฐ์ค€ ์›” 24, ํ›„๋ณด ์ œ์•ˆ 13๊ฑด(ํ•˜์ž ๊ตฌ์ƒยท์„ค๊ณ„ ๋ณ€๊ฒฝยท์™ธ์ฃผ์ •๋น„ ์‹œ์ •)\n", "\n", "[๋น„์šฉ ๋น„๊ต] ๋ฐฉ์‹ | ์ดํ›„ 24๊ฐœ์›” ๋น„์šฉ์ง€์ˆ˜ | ์ ˆ๊ฐ์•ก\n", " ๊ฐœ๋ณ„ ์‚ฌํ›„ ์ˆ˜๋ฆฌ(๊ธฐ๋ณธ ๊ณ„ํš) 144670 0\n", " ์ œ์•ˆ + ํ’ˆ์งˆ๊ด€ ์Šน์ธ 122929 21741\n", " ์ œ์•ˆ ์ „๋ถ€ ์ ์šฉ(๋น„๊ต์šฉ) 119586 25084\n", "HITL ์ ์šฉ ๋น„์šฉ ์ ˆ๊ฐ๋ฅ  15.0% (๋ชฉํ‘œ 15% ์ด์ƒ: ๋‹ฌ์„ฑ), ๋น„๊ต์šฉ ์ „๋ถ€ ์ ์šฉ 17.3%\n", "์ตœ์ข… ๊ฒฐ์ • 13๊ฑด: ์Šน์ธ 12๊ฑด, ๋ฐ˜๋ ค 1๊ฑด, ๋ฌด์‘๋‹ต 0๊ฑด\n", "์ œ์•ˆ 13๊ฑด ์ค‘ ๊ฒฐ์ • ๊ธฐ๋ก 13๊ฑด(๋กœ๊น…๋ฅ  100%), ์ž๋™ ์Šน์ธ 0๊ฑด, ๋ฌด์‘๋‹ต 2๊ฑด์€ ์กฐ์น˜ ๋ณด๋ฅ˜ ํ›„ ๋ถ„๊ธฐ ํšŒ์˜ ์žฌ์ƒ์ •\n", "๋ฐ˜๋ ค ์‚ฌ์œ  ์˜ˆ: ์›์ธ ๋น„์ค‘ 23%๋กœ ํ‰๊ท  ์ˆ˜์ค€, ์ถ”๊ฐ€ ๊ด€์ฐฐ\n", "๊ฐ์‚ฌ ๋กœ๊ทธ ๋ฌด๊ฒฐ์„ฑ: ์ •์ƒ, ์‚ฌํ›„ ๋ณ€์กฐ ์‹œ seq 3์—์„œ ํƒ์ง€\n", "\n", "[๋ฏผ๊ฐ๋„ 1] ์Šน์ธ๊นŒ์ง€ ์ง€์—ฐ(๊ฐœ์›”) -> ์ ˆ๊ฐ๋ฅ \n", " 0๊ฐœ์›”: 15.9%\n", " 1๊ฐœ์›”: 15.0%\n", " 3๊ฐœ์›”: 13.6%\n", " 6๊ฐœ์›”: 11.4%\n", " 9๊ฐœ์›”: 9.2%\n", "[๋ฏผ๊ฐ๋„ 2] ํ’ˆ์งˆ๊ด€ ์ฒ˜๋ฆฌ ์šฉ๋Ÿ‰(์Šน์ธ ๊ฐ€๋Šฅ ๊ฑด์ˆ˜) -> ์ ˆ๊ฐ๋ฅ \n", " ์ตœ๋Œ€ 2๊ฑด: 14.2%\n", " ์ตœ๋Œ€ 5๊ฑด: 14.4%\n", " ์ตœ๋Œ€ 8๊ฑด: 14.8%\n", " ์ตœ๋Œ€ ๋ฌด์ œํ•œ๊ฑด: 15.0%\n", "[๋ฏผ๊ฐ๋„ 3] ์ „์‹œ ์ „ํ™˜ ๊ฐ€์ •(QDR ๋น„์šฉ 1.5๋ฐฐ ๊ธ‰์ฆ) -> ์ ˆ๊ฐ๋ฅ \n", " x1.0: 15.0%\n", " x1.5: 15.1%\n", " x2.0: 15.2%\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 08. 10๋…„์น˜ ๋Œ€์šฉ๋Ÿ‰ ์•ผ์ „ QDR ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ผ๊ด„ ๋ถ„์„ ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "๋Œ€์šฉ๋Ÿ‰ ์•ผ์ „ QDR ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ผ๊ด„ ๋ถ„์„ ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **10๋…„(120๊ฐœ์›”)์น˜ ๋Œ€์šฉ๋Ÿ‰ ์•ผ์ „ QDR ํ…์ŠคํŠธ๋ฅผ ์ผ๊ด„ ๋ถ„์„ํ•˜๋Š” ์ŠคํŠธ๋ ˆ์Šค ์‹œํ—˜์„ ์ˆ˜ํ–‰ํ•ด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„, ํ™”๋ฉด ๊ตฌ์„ฑ ์‹œ๊ฐ„, ๊ฒฝ๋ณด ์ •ํ™•๋„๋ฅผ ํ™•์ธํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **๊ฐ€์ƒ QDR ์•ฝ 6๋งŒ ๊ฑด์— ๋นˆ ์„œ์ˆ , ์ธ์ฝ”๋”ฉ ๊นจ์ง, ๊ณผ๋Œ€ ๊ธธ์ด ๋ ˆ์ฝ”๋“œ๋ฅผ ์„ž์–ด 1~10๋…„ ๋‹จ์œ„๋กœ ์ฃผ์ž…ํ•ด ๊ฒฉ๋ฆฌยท์œ ์‹ค ๊ฑด์ˆ˜์™€ ๋ถ„์„ ์‹œ๊ฐ„์„ ์„ผ๋‹ค ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `src/dashboard/qlt05_app.py (Streamlit/Dash ๋“ฑ)` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์˜์‚ฌ๊ฒฐ์ • ์ง€์›์— ์ตœ์ ํ™”๋œ ์‹œ๊ฐ์  ์œ„๊ณ„ ๋ฐ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋Œ€์‹œ๋ณด๋“œ ๋กœ๋”ฉ ์‹œ๊ฐ„ 2์ดˆ ์ด๋‚ด, ๊ฒฝ๋ณด ๋ฐœ๋ น ์ •ํ™•๋„ 95% ์ด์ƒ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์›น ๊ธฐ๋ฐ˜ ๋ถ„์„ ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ๋™ ๋ฐ ์‹ค์‹œ๊ฐ„ ํ•„ํ„ฐ๋ง ๊ธฐ๋Šฅ ๊ฒ€์ฆ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ์‹ค์ œ ๊ตฐ์ˆ˜ ๋ฐ์ดํ„ฐ ์—ฐ๋™ ํ™”๋ฉด ํ…Œ์ŠคํŠธ ๋ฐ UI ๋™์ž‘ ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"10๋…„(120๊ฐœ์›”)์น˜ ๋Œ€์šฉ๋Ÿ‰ ์•ผ์ „ QDR ํ…์ŠคํŠธ๋ฅผ ํ•œ๊บผ๋ฒˆ์— ์ฃผ์ž…ํ•ด ์ •๊ทœํ™”ยท๋ถ€์œ„/๊ฒฐํ•จ/์น˜๋ช…๋„ ๋ผ๋ฒจ๋งยท์›”๋ณ„ ์ง‘๊ณ„ยท์ง‘์ค‘๋„ ๊ฒฝ๋ณด๋ฅผ ์ผ๊ด„ ๋ถ„์„ํ•˜๊ณ , ๊ทœ๋ชจ๋ณ„ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„, ๋ถˆ๋Ÿ‰ ๋ ˆ์ฝ”๋“œ(๋นˆ ์„œ์ˆ ยท์ธ์ฝ”๋”ฉ ๊นจ์งยท๊ณผ๋Œ€ ๊ธธ์ด) ๊ฒฉ๋ฆฌ์™€ ์œ ์‹ค 0๊ฑด, ๋Œ€์‹œ๋ณด๋“œ ํ™”๋ฉด ๊ตฌ์„ฑยทํ•„ํ„ฐ ์‘๋‹ต ์‹œ๊ฐ„, ๊ธ‰์ฆ ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ๊ฒฝ๋ณด ์ •ํ™•๋„๋ฅผ ํ™•์ธํ•˜๋Š” ์ŠคํŠธ๋ ˆ์Šค ์‹œํ—˜์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.\"\"\"\n", "import re\n", "import time\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# ---------------------------------------------------------------------------\n", "# ๊ณตํ†ต ํ…์ŠคํŠธ ์กฐ๊ฐ: ์ •๊ทœํ™”, ๊ฐ„์ด ํ† ํฐํ™”(์กฐ์‚ฌ ์ œ๊ฑฐ), ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ยท์›์ธ ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\n", "# ---------------------------------------------------------------------------\n", "TYPO = {\"๋ˆ„์œณ\": \"๋ˆ„์œ \", \"๊ท ๋ ฌ\": \"๊ท ์—ด\", \"์ ‘์ถ• ๋ถˆ๋Ÿ‰\": \"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋ถ€์”ฉ\": \"๋ถ€์‹\", \"๋งˆ๋ฌ˜\": \"๋งˆ๋ชจ\", \"๊ณผ๋ ฌ\": \"๊ณผ์—ด\", \"์˜ค์ž‘๋˜ฅ\": \"์˜ค์ž‘๋™\", \"์žฌ๋™\": \"์ œ๋™\"}\n", "ABBR = {\"T/M\": \"๋ณ€์†๊ธฐ\", \"BAT\": \"๋ฐฐํ„ฐ๋ฆฌ\"}\n", "PART_KW = sorted([(a, i) for i, p in enumerate(PARTS) for a in p[1]], key=lambda x: -len(x[0]))\n", "DEF_KW = {\"๊ท ์—ด\": [\"๊ท ์—ด\", \"ํฌ๋ž™\", \"๊ฐˆ๋ผ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ \", \"์ƒˆ์–ด\", \"๋ˆ„์„ค\"], \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ\", \"๋‹ณ์€\"], \"๋ถ€์‹\": [\"๋ถ€์‹\", \"๋…น์ด\", \"์‚ฐํ™”\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋‹จ์„ \", \"๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ\", \"์ฒด๊ฒฐ\", \"ํ—๊ฑฐ\"], \"๊ณผ์—ด\": [\"๊ณผ์—ด\", \"์˜จ๋„\", \"์—ด์ด\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™\", \"๋ถˆ์•ˆ์ •\", \"๋ฉˆ์ถค\"]}\n", "SEV_KW = [(2, [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€\", \"์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์œ„ํ—˜\"]), (1, [\"์„ฑ๋Šฅ ์ €ํ•˜\", \"์กฐ์†ํ•œ\"]), (0, [\"๊ฒฝ๋ฏธ\", \"์ง€์žฅ์ด ์—†\"])]\n", "CAUSE_KW = {\"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ\", \"์ถœ๊ณ \", \"์ธ์ˆ˜\"], \"์ •๋น„ ํ’ˆ์งˆ\": [\"์ •๋น„ ํ›„\", \"์žฌ์กฐ๋ฆฝ\", \"์ •๋น„ ์งํ›„\"], \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"์ถฉ๊ฒฉ\", \"๊ณผ๋ถ€ํ•˜\", \"์ ๊ฒ€ ์—†์ด\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ\", \"๊ตฌ์กฐ์ \", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€\"], \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ\", \"๋…ธํ›„\", \"์ˆ˜๋ช…\"]}\n", "PARTICLES = (\"์—์„œ\", \"์œผ๋กœ\", \"์ด\", \"๊ฐ€\", \"์€\", \"๋Š”\", \"์„\", \"๋ฅผ\", \"์—\", \"์˜\", \"๋กœ\", \"์™€\", \"๊ณผ\", \"๋„\", \"ํ•จ\", \"์Œ\", \"๋จ\")\n", "\n", "\n", "def norm(t):\n", " \"\"\"์ •๊ทœํ™”: ์ „๊ฐ->๋ฐ˜๊ฐ, ํŠน์ˆ˜๋ฌธ์žยท์ค‘๋ณต ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •\"\"\"\n", " t = unicodedata.normalize(\"NFKC\", str(t))\n", " t = re.sub(r\"[!#~*@^]+\", \" \", t)\n", " for a, b in ABBR.items():\n", " t = t.replace(a, b)\n", " for a, b in TYPO.items():\n", " t = t.replace(a, b)\n", " return re.sub(r\"\\s+\", \" \", t).strip()\n", "\n", "\n", "def tok(t):\n", " out = []\n", " for w in re.split(r\"[\\s.,]+\", norm(t)):\n", " if not w or re.fullmatch(r\"[\\d\\-A-Za-z]+\", w):\n", " continue\n", " for p in PARTICLES:\n", " if len(w) > len(p) + 1 and w.endswith(p):\n", " w = w[:-len(p)]\n", " break\n", " out.append(w)\n", " return out\n", "\n", "\n", "def label_part(t):\n", " for a, i in PART_KW:\n", " if a in t:\n", " return i\n", " return -1\n", "\n", "\n", "def label_defect(t):\n", " for d, ks in DEF_KW.items():\n", " if any(k in t for k in ks):\n", " return d\n", " return \"๋ฏธ๋ถ„๋ฅ˜\"\n", "\n", "\n", "def label_sev(t):\n", " for s, ks in SEV_KW:\n", " if any(k in t for k in ks):\n", " return s\n", " return -1\n", "\n", "\n", "def label_cause(t):\n", " for c, ks in CAUSE_KW.items():\n", " if any(k in t for k in ks):\n", " return c\n", " return \"๋ฏธ์ƒ\"\n", "\n", "names = [p[0] for p in PARTS]\n", "t0 = time.perf_counter()\n", "full, use, events = make_qdr(120, seed=3, scale=1.5)\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] 10๋…„(120๊ฐœ์›”) QDR {len(full)}๊ฑด ์ƒ์„ฑ({time.perf_counter() - t0:.1f}์ดˆ, ๋ถ„์„ ์‹œ๊ฐ„์—๋Š” ํฌํ•จํ•˜์ง€ ์•Š์Œ), ๋ถ€์œ„ {NP}์ข…, ๊ธ‰์ฆ ์ด๋ฒคํŠธ {len(events)}๊ฑด\")\n", "rng = np.random.default_rng(0)\n", "RE_SPEC, RE_SPACE = re.compile(r\"[!#~*@^]+\"), re.compile(r\"\\s+\")\n", "PART_RE = [(re.compile(\"|\".join(map(re.escape, p[1]))), i) for i, p in enumerate(PARTS)]\n", "DEF_RE = [(d, re.compile(\"|\".join(ks))) for d, ks in DEF_KW.items()]\n", "\n", "\n", "def analyze(d):\n", " \"\"\"์ผ๊ด„ ๋ถ„์„: ๊ฒ€์ฆ(๊ฒฉ๋ฆฌ) -> ์ •๊ทœํ™” -> ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ ๋ผ๋ฒจ๋ง. ๋ฐ˜ํ™˜: (์ •์ƒ ๊ฒฐ๊ณผ, ๊ฒฉ๋ฆฌ ๊ฑด์ˆ˜)\"\"\"\n", " ok = d[\"text\"].str.len().between(5, 400) & ~d[\"text\"].str.contains(\"๏ฟฝ\", regex=False)\n", " g = d[ok].copy()\n", " g[\"t\"] = g[\"text\"].str.replace(RE_SPEC, \" \", regex=True).str.replace(RE_SPACE, \" \", regex=True).str.strip()\n", " g[\"p_lab\"] = -1\n", " for rx, i in PART_RE:\n", " g.loc[(g[\"p_lab\"] == -1) & g[\"t\"].str.contains(rx), \"p_lab\"] = i\n", " g[\"d_lab\"] = \"๋ฏธ๋ถ„๋ฅ˜\"\n", " for dn, rx in DEF_RE:\n", " g.loc[(g[\"d_lab\"] == \"๋ฏธ๋ถ„๋ฅ˜\") & g[\"t\"].str.contains(rx), \"d_lab\"] = dn\n", " g[\"s_lab\"] = np.where(g[\"t\"].str.contains(\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€|์‚ฌ์šฉ ๋ถˆ๊ฐ€|์œ„ํ—˜|์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€|์œ„ํ˜‘\"), 2, np.where(g[\"t\"].str.contains(\"์„ฑ๋Šฅ ์ €ํ•˜|์กฐ์†ํ•œ|์ถœ๋ ฅ์ด|๋ถ€๋ถ„์ ์œผ๋กœ\"), 1, 0))\n", " return g, int((~ok).sum())\n", "\n", "\n", "def alarms(g, months):\n", " \"\"\"์›”ยท๋ถ€์œ„ ์ง‘๊ณ„ ํ›„ ์ง์ „ 12๊ฐœ์›” ๋ฐœ์ƒ๋ฅ (ํ›ˆ๋ จ ๊ฐ•๋„ ๋ณด์ •) ๋Œ€๋น„ ๊ธ‰์ฆ ๊ฒฝ๋ณด\"\"\"\n", " M = g.pivot_table(index=\"p_lab\", columns=\"month\", values=\"qdr_id\", aggfunc=\"size\", fill_value=0).reindex(index=range(NP), columns=range(months), fill_value=0)\n", " rate = (M / use[:months]).T.rolling(12, min_periods=6).mean().shift(1).T\n", " exp = rate * use[:months]\n", " z = (M - exp) / np.sqrt(exp.clip(lower=1))\n", " return M, (M / exp.clip(lower=1) > 1.9) & (z > 4)\n", "\n", "\n", "def make_input(n_months, bad=0.01):\n", " d = full[full[\"month\"] < n_months].copy()\n", " k = int(len(d) * bad)\n", " idx = rng.choice(len(d), k, replace=False)\n", " d.iloc[idx[: k // 3], d.columns.get_loc(\"text\")] = \"\"\n", " d.iloc[idx[k // 3: 2 * k // 3], d.columns.get_loc(\"text\")] = \"๏ฟฝ๏ฟฝ๏ฟฝ ์˜ค๋ฅ˜\"\n", " d.iloc[idx[2 * k // 3:], d.columns.get_loc(\"text\")] = \"๊ฐ€\" * 900\n", " return d, k\n", "\n", "\n", "print(\"\\n[๊ธฐ๊ฐ„๋ณ„ ์ผ๊ด„ ๋ถ„์„] ๊ธฐ๊ฐ„ | ์ž…๋ ฅ | ์ •์ƒ | ๊ฒฉ๋ฆฌ | ์œ ์‹ค | ๋ถ„์„ ์‹œ๊ฐ„ | ์ฒ˜๋ฆฌ๋Ÿ‰\")\n", "res = {}\n", "for months in (12, 36, 72, 120):\n", " d, k = make_input(months)\n", " t = time.perf_counter()\n", " g, q = analyze(d)\n", " M, A = alarms(g, months)\n", " sec = time.perf_counter() - t\n", " res[months] = (g, M, A, sec)\n", " print(f\" {months // 12:2d}๋…„ {len(d):7d} {len(g):7d} {q:5d} {len(d) - len(g) - q:3d} {sec:6.2f}์ดˆ {len(d) / sec:8.0f}๊ฑด/์ดˆ\")\n", "g, M, A, sec = res[120]\n", "\n", "# ๋ผ๋ฒจ๋ง ํ’ˆ์งˆ๊ณผ ๊ฒฝ๋ณด ์ •ํ™•๋„\n", "tr_ = full.set_index(\"qdr_id\").loc[g[\"qdr_id\"]]\n", "print(f\"\\n๋ผ๋ฒจ๋ง ์ •ํ™•๋„(10๋…„์น˜): ๋ถ€์œ„ {(g['p_lab'].values == tr_['part'].values).mean():.1%}, ๊ฒฐํ•จ ์œ ํ˜• {(g['d_lab'].values == tr_['defect'].values).mean():.1%}, ์น˜๋ช…๋„ {(g['s_lab'].values == tr_['sev'].values).mean():.1%} (๋‹จ์„œ ์—†๋Š” ๊ฑด์€ Minor ์ฒ˜๋ฆฌ)\")\n", "truth = np.zeros((NP, 120), bool)\n", "for p, m in events:\n", " truth[p, m] = True\n", "valid = ~np.isnan(A.values.astype(float)) & (np.arange(120) >= 12)[None, :]\n", "al = A.values & valid\n", "tp, fp, fn = (al & truth).sum(), (al & ~truth).sum(), (~al & truth & valid).sum()\n", "acc = ((al == truth) & valid).sum() / valid.sum()\n", "print(f\"๊ธ‰์ฆ ๊ฒฝ๋ณด(12๊ฐœ์›” ์ดํ›„ {int(valid.sum())}๊ฐœ ๋ถ€์œ„-์›” ์…€): ๊ฒฝ๋ณด ์ •ํ™•๋„ {acc:.1%} (๋ชฉํ‘œ 95% ์ด์ƒ: {'๋‹ฌ์„ฑ' if acc >= 0.95 else '๋ฏธ๋‹ฌ'}), ์žฌํ˜„์œจ {tp / max(tp + fn, 1):.0%}, ์ •๋ฐ€๋„ {tp / max(tp + fp, 1):.0%}, ์‹ค์ œ ์ด๋ฒคํŠธ {int((truth & valid).sum())}๊ฑด\")\n", "\n", "# ๋Œ€์‹œ๋ณด๋“œ ํ™”๋ฉด ๊ตฌ์„ฑ: 10๋…„์น˜ ์ง‘๊ณ„ํ‘œ์—์„œ ํžˆํŠธ๋งตยท์ƒ์œ„ 5 ๋ถ€์œ„ยท๊ฒฝ๋ณด ๋ชฉ๋ก์„ ๋งŒ๋“œ๋Š” ์‹œ๊ฐ„, ํ•„ํ„ฐ(๋ถ€์œ„ยท๊ธฐ๊ฐ„) ์‘๋‹ต ์‹œ๊ฐ„\n", "t = time.perf_counter()\n", "heat = M.values.reshape(NP, 10, 12).sum(axis=2)\n", "top5 = M.sum(axis=1).sort_values(ascending=False).head(5)\n", "alert_list = [(names[p], m) for p, m in zip(*np.where(al))]\n", "load = time.perf_counter() - t\n", "t = time.perf_counter()\n", "for p in range(NP):\n", " sub = g[(g[\"p_lab\"] == p) & (g[\"month\"] >= 84)]\n", " _ = sub.groupby(\"d_lab\").size()\n", "flt = (time.perf_counter() - t) / NP\n", "print(f\"ํ™”๋ฉด ๊ตฌ์„ฑ(์—ฐ๋„๋ณ„ ํžˆํŠธ๋งตยท์ƒ์œ„ 5ยท๊ฒฝ๋ณด {len(alert_list)}๊ฑด) {load * 1000:.0f}ms, ํ•„ํ„ฐ 1ํšŒ ์‘๋‹ต {flt * 1000:.0f}ms (๋ชฉํ‘œ 2์ดˆ ์ด๋‚ด: {'๋‹ฌ์„ฑ' if max(load, flt) <= 2 else '๋ฏธ๋‹ฌ'})\")\n", "print(\"์ƒ์œ„ 5 ๋ถ€์œ„: \" + \", \".join(f\"{names[p]} {v}\" for p, v in top5.items()))\n", "print(\"์šด์šฉ ๊ธฐ์ค€: ์ผ๊ด„ ๋ถ„์„์€ ์•ผ๊ฐ„ ๋ฐฐ์น˜๋กœ ์ง‘๊ณ„ํ‘œ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ณ  ํ™”๋ฉด์€ ์ง‘๊ณ„ํ‘œ๋งŒ ์ฝ๋Š”๋‹ค. ์ฒ˜๋ฆฌ๊ฐ€ ๋А๋ ค์ง€๋ฉด ์ตœ๊ทผ 36๊ฐœ์›”๋งŒ ์žฌ๊ณ„์‚ฐํ•˜๊ณ  ๊ฒฉ๋ฆฌ์œจ์ด 5%๋ฅผ ๋„˜์œผ๋ฉด ์„œ์ˆ  ์ž…๋ ฅ ์ฒด๊ณ„๋ฅผ ์ ๊ฒ€ํ•œ๋‹ค.\")\n", "\n", "plt.figure(figsize=(6, 4))\n", "ms = sorted(res)\n", "plt.plot([m // 12 for m in ms], [res[m][3] for m in ms], marker=\"o\"); plt.xlabel(\"years of QDR\"); plt.ylabel(\"analysis seconds\"); plt.title(\"Bulk analysis time\")\n", "plt.tight_layout(); plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 08 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] 10๋…„(120๊ฐœ์›”) QDR 62293๊ฑด ์ƒ์„ฑ(6.5์ดˆ, ๋ถ„์„ ์‹œ๊ฐ„์—๋Š” ํฌํ•จํ•˜์ง€ ์•Š์Œ), ๋ถ€์œ„ 12์ข…, ๊ธ‰์ฆ ์ด๋ฒคํŠธ 20๊ฑด\n", "\n", "[๊ธฐ๊ฐ„๋ณ„ ์ผ๊ด„ ๋ถ„์„] ๊ธฐ๊ฐ„ | ์ž…๋ ฅ | ์ •์ƒ | ๊ฒฉ๋ฆฌ | ์œ ์‹ค | ๋ถ„์„ ์‹œ๊ฐ„ | ์ฒ˜๋ฆฌ๋Ÿ‰\n", " 1๋…„ 4177 4136 41 0 0.14์ดˆ 30805๊ฑด/์ดˆ\n", " 3๋…„ 14068 13928 140 0 0.36์ดˆ 39039๊ฑด/์ดˆ\n", " 6๋…„ 32040 31720 320 0 0.77์ดˆ 41671๊ฑด/์ดˆ\n", " 10๋…„ 62293 61671 622 0 1.78์ดˆ 34950๊ฑด/์ดˆ\n", "\n", "๋ผ๋ฒจ๋ง ์ •ํ™•๋„(10๋…„์น˜): ๋ถ€์œ„ 100.0%, ๊ฒฐํ•จ ์œ ํ˜• 100.0%, ์น˜๋ช…๋„ 87.0% (๋‹จ์„œ ์—†๋Š” ๊ฑด์€ Minor ์ฒ˜๋ฆฌ)\n", "๊ธ‰์ฆ ๊ฒฝ๋ณด(12๊ฐœ์›” ์ดํ›„ 1296๊ฐœ ๋ถ€์œ„-์›” ์…€): ๊ฒฝ๋ณด ์ •ํ™•๋„ 99.8% (๋ชฉํ‘œ 95% ์ด์ƒ: ๋‹ฌ์„ฑ), ์žฌํ˜„์œจ 90%, ์ •๋ฐ€๋„ 100%, ์‹ค์ œ ์ด๋ฒคํŠธ 20๊ฑด\n", "ํ™”๋ฉด ๊ตฌ์„ฑ(์—ฐ๋„๋ณ„ ํžˆํŠธ๋งตยท์ƒ์œ„ 5ยท๊ฒฝ๋ณด 18๊ฑด) 1ms, ํ•„ํ„ฐ 1ํšŒ ์‘๋‹ต 4ms (๋ชฉํ‘œ 2์ดˆ ์ด๋‚ด: ๋‹ฌ์„ฑ)\n", "์ƒ์œ„ 5 ๋ถ€์œ„: ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต 7848, ์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ 7816, ๋ƒ‰๊ฐ ๊ณ„ํ†ต 7095, ๋ณ€์†๊ธฐ 6301, ์ œ๋™ ์žฅ์น˜ 5667\n", "์šด์šฉ ๊ธฐ์ค€: ์ผ๊ด„ ๋ถ„์„์€ ์•ผ๊ฐ„ ๋ฐฐ์น˜๋กœ ์ง‘๊ณ„ํ‘œ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ณ  ํ™”๋ฉด์€ ์ง‘๊ณ„ํ‘œ๋งŒ ์ฝ๋Š”๋‹ค. ์ฒ˜๋ฆฌ๊ฐ€ ๋А๋ ค์ง€๋ฉด ์ตœ๊ทผ 36๊ฐœ์›”๋งŒ ์žฌ๊ณ„์‚ฐํ•˜๊ณ  ๊ฒฉ๋ฆฌ์œจ์ด 5%๋ฅผ ๋„˜์œผ๋ฉด ์„œ์ˆ  ์ž…๋ ฅ ์ฒด๊ณ„๋ฅผ ์ ๊ฒ€ํ•œ๋‹ค.\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 09. AAR ๊ธฐ๋ฐ˜ ๊ฒฐํ•จ ์›์ธ ์˜ค๋ถ„๋ฅ˜ ๋ถ„์„ ๋ฐ ํ…์ŠคํŠธ ๋งˆ์ด๋‹ ๋ฃฐ์…‹ ์žฌํŠœ๋‹\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.0์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "AAR ๊ธฐ๋ฐ˜ ๊ฒฐํ•จ ์›์ธ ์˜ค๋ถ„๋ฅ˜๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๋งˆ์ด๋‹ ๋ฃฐ์…‹์„ ์žฌํŠœ๋‹ํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **AAR ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฒฐํ•จ ์›์ธ ์˜ค๋ถ„๋ฅ˜ ์‚ฌ๋ก€๋ฅผ ๋ถ„์„ํ•˜๊ณ  ํ…์ŠคํŠธ ๋งˆ์ด๋‹ ๋ฃฐ์…‹์„ ๋ณด์กฐ ๊ทœ์น™์œผ๋กœ ์žฌํŠœ๋‹ํ•ด ํšจ๊ณผ๋ฅผ ์ง€ํ‘œ๋กœ ์‚ฐ์ถœํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **AAR ๊ธฐ๊ฐ„ ์›์ธ ๋ถ„๋ฅ˜ ์˜ค๋ฅ˜๋ฅผ ์›์ธ ๋‹จ์„œ ์—†์Œ๊ณผ ๋‹จ์„œ-์‹ค์ œ ์›์ธ ๋ถˆ์ผ์น˜๋กœ ๋‚˜๋ˆ„๊ณ  ๋Œ€ํ‘œ ์‚ฌ๋ก€ ์ •๋ฆฌํ•˜๊ณ  ๊ทœ๊ฒฉํ™”ํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `reports/qlt05_roi_analysis.md` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ถ”์ • ์‚ฐ์‹์˜ ๊ฐ๊ด€์„ฑ ๋ฐ ๊ตฐ ์žฌ์ •/๊ตฐ์ˆ˜ ๊ธฐํš ๋ถ€์„œ ์„ค๋“๋ ฅ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ๋น„์šฉ ์ ˆ๊ฐ์•ก, ์‹œ๊ฐ„ ๋‹จ์ถ•๋ฅ  ๋“ฑ 3๋Œ€ ์ •๋Ÿ‰ ํšจ๊ณผ ์ง€ํ‘œ ์‚ฐ์ถœ\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ROI ๋ถ„์„ ๋ณด๊ณ ์„œ ์™„์„ฑ ๋ฐ ์ฃผ์š” ์ง€ํ‘œ ๋ฏผ๊ฐ๋„ ๋ถ„์„ ์ˆ˜๋ก\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: ํšŒ๊ณ„/๊ธฐํš ๊ธฐ์ค€ ๋ชจ๋ธ๊ณผ์˜ ๋น„๊ต ๊ฒ€์ฆ\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"AAR ๊ธฐ๊ฐ„(12~23๊ฐœ์›”) QDR์˜ ๊ฒฐํ•จ ์›์ธ ๋ถ„๋ฅ˜ ์˜ค๋ฅ˜๋ฅผ ์›์ธ๋ณ„๋กœ ๋ถ„์„ํ•˜๊ณ , ํ‚ค์›Œ๋“œ ๊ธฐ๋ฐ˜ ํ…์ŠคํŠธ ๋งˆ์ด๋‹ ๋ฃฐ์…‹์— ์™ธ์ฃผ ์ •๋น„ ํ›„ ๊ฒฝ๊ณผ์ผยท์—…์ฒดยท๊ฒฐํ•จ ์œ ํ˜• ๋“ฑ ๋ณด์กฐ ๊ทœ์น™(๊ฒฐ์ • ํŠธ๋ฆฌ๋กœ ๋„์ถœ)์„ ๋”ํ•ด ์žฌํŠœ๋‹ํ•œ ๋’ค, ๊ฒ€์ฆ ๊ธฐ๊ฐ„(24~35๊ฐœ์›”)์—์„œ ์ •ํ™•๋„์™€ ์˜ค๋ถ„๋ฅ˜ ๋น„์šฉ ๋ณ€ํ™”๋ฅผ ํ™•์ธํ•˜๊ณ  3๋Œ€ ์ •๋Ÿ‰ ํšจ๊ณผ ์ง€ํ‘œ(๋น„์šฉ ์ ˆ๊ฐ, ํ™•์ธ ์‹œ๊ฐ„ ๋‹จ์ถ•, ์ œ์กฐยท์ •๋น„ ์ฑ…์ž„ ๊ฑด ์‹๋ณ„ ์ฆ๊ฐ€)์™€ ๋„์ž… ๋กœ๋“œ๋งต์„ ์‚ฐ์ถœํ•œ๋‹ค.\"\"\"\n", "import re\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "from sklearn.tree import DecisionTreeClassifier, export_text\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# ---------------------------------------------------------------------------\n", "# ๊ณตํ†ต ํ…์ŠคํŠธ ์กฐ๊ฐ: ์ •๊ทœํ™”, ๊ฐ„์ด ํ† ํฐํ™”(์กฐ์‚ฌ ์ œ๊ฑฐ), ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ยท์›์ธ ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\n", "# ---------------------------------------------------------------------------\n", "TYPO = {\"๋ˆ„์œณ\": \"๋ˆ„์œ \", \"๊ท ๋ ฌ\": \"๊ท ์—ด\", \"์ ‘์ถ• ๋ถˆ๋Ÿ‰\": \"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋ถ€์”ฉ\": \"๋ถ€์‹\", \"๋งˆ๋ฌ˜\": \"๋งˆ๋ชจ\", \"๊ณผ๋ ฌ\": \"๊ณผ์—ด\", \"์˜ค์ž‘๋˜ฅ\": \"์˜ค์ž‘๋™\", \"์žฌ๋™\": \"์ œ๋™\"}\n", "ABBR = {\"T/M\": \"๋ณ€์†๊ธฐ\", \"BAT\": \"๋ฐฐํ„ฐ๋ฆฌ\"}\n", "PART_KW = sorted([(a, i) for i, p in enumerate(PARTS) for a in p[1]], key=lambda x: -len(x[0]))\n", "DEF_KW = {\"๊ท ์—ด\": [\"๊ท ์—ด\", \"ํฌ๋ž™\", \"๊ฐˆ๋ผ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ \", \"์ƒˆ์–ด\", \"๋ˆ„์„ค\"], \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ\", \"๋‹ณ์€\"], \"๋ถ€์‹\": [\"๋ถ€์‹\", \"๋…น์ด\", \"์‚ฐํ™”\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋‹จ์„ \", \"๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ\", \"์ฒด๊ฒฐ\", \"ํ—๊ฑฐ\"], \"๊ณผ์—ด\": [\"๊ณผ์—ด\", \"์˜จ๋„\", \"์—ด์ด\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™\", \"๋ถˆ์•ˆ์ •\", \"๋ฉˆ์ถค\"]}\n", "SEV_KW = [(2, [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€\", \"์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์œ„ํ—˜\"]), (1, [\"์„ฑ๋Šฅ ์ €ํ•˜\", \"์กฐ์†ํ•œ\"]), (0, [\"๊ฒฝ๋ฏธ\", \"์ง€์žฅ์ด ์—†\"])]\n", "CAUSE_KW = {\"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ\", \"์ถœ๊ณ \", \"์ธ์ˆ˜\"], \"์ •๋น„ ํ’ˆ์งˆ\": [\"์ •๋น„ ํ›„\", \"์žฌ์กฐ๋ฆฝ\", \"์ •๋น„ ์งํ›„\"], \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"์ถฉ๊ฒฉ\", \"๊ณผ๋ถ€ํ•˜\", \"์ ๊ฒ€ ์—†์ด\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ\", \"๊ตฌ์กฐ์ \", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€\"], \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ\", \"๋…ธํ›„\", \"์ˆ˜๋ช…\"]}\n", "PARTICLES = (\"์—์„œ\", \"์œผ๋กœ\", \"์ด\", \"๊ฐ€\", \"์€\", \"๋Š”\", \"์„\", \"๋ฅผ\", \"์—\", \"์˜\", \"๋กœ\", \"์™€\", \"๊ณผ\", \"๋„\", \"ํ•จ\", \"์Œ\", \"๋จ\")\n", "\n", "\n", "def norm(t):\n", " \"\"\"์ •๊ทœํ™”: ์ „๊ฐ->๋ฐ˜๊ฐ, ํŠน์ˆ˜๋ฌธ์žยท์ค‘๋ณต ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •\"\"\"\n", " t = unicodedata.normalize(\"NFKC\", str(t))\n", " t = re.sub(r\"[!#~*@^]+\", \" \", t)\n", " for a, b in ABBR.items():\n", " t = t.replace(a, b)\n", " for a, b in TYPO.items():\n", " t = t.replace(a, b)\n", " return re.sub(r\"\\s+\", \" \", t).strip()\n", "\n", "\n", "def tok(t):\n", " out = []\n", " for w in re.split(r\"[\\s.,]+\", norm(t)):\n", " if not w or re.fullmatch(r\"[\\d\\-A-Za-z]+\", w):\n", " continue\n", " for p in PARTICLES:\n", " if len(w) > len(p) + 1 and w.endswith(p):\n", " w = w[:-len(p)]\n", " break\n", " out.append(w)\n", " return out\n", "\n", "\n", "def label_part(t):\n", " for a, i in PART_KW:\n", " if a in t:\n", " return i\n", " return -1\n", "\n", "\n", "def label_defect(t):\n", " for d, ks in DEF_KW.items():\n", " if any(k in t for k in ks):\n", " return d\n", " return \"๋ฏธ๋ถ„๋ฅ˜\"\n", "\n", "\n", "def label_sev(t):\n", " for s, ks in SEV_KW:\n", " if any(k in t for k in ks):\n", " return s\n", " return -1\n", "\n", "\n", "def label_cause(t):\n", " for c, ks in CAUSE_KW.items():\n", " if any(k in t for k in ks):\n", " return c\n", " return \"๋ฏธ์ƒ\"\n", "\n", "df, use, events = make_qdr(36, seed=1, scale=1.5)\n", "df[\"t\"] = df[\"text\"].map(norm)\n", "aar, val = df[(df[\"month\"] >= 12) & (df[\"month\"] < 24)].copy(), df[df[\"month\"] >= 24].copy()\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] AAR ๊ธฐ๊ฐ„ {len(aar)}๊ฑด, ๊ฒ€์ฆ ๊ธฐ๊ฐ„ {len(val)}๊ฑด\")\n", "\n", "# 1) ๊ธฐ์กด ๋ฃฐ์…‹: ์„œ์ˆ  ์† ์›์ธ ๋‹จ์„œ ํ‚ค์›Œ๋“œ(์ฒซ ์ผ์น˜)\n", "for d in (aar, val):\n", " d[\"rule0\"] = d[\"t\"].map(label_cause)\n", "wrong = aar[aar[\"rule0\"] != aar[\"cause\"]]\n", "no_cue = wrong[wrong[\"rule0\"] == \"๋ฏธ์ƒ\"]\n", "conflict = wrong[wrong[\"rule0\"] != \"๋ฏธ์ƒ\"]\n", "print(f\"๊ธฐ์กด ๋ฃฐ์…‹ ์ •ํ™•๋„: AAR {1 - len(wrong) / len(aar):.1%}, ๊ฒ€์ฆ {(val['rule0'] == val['cause']).mean():.1%}\")\n", "print(f\"AAR ์˜ค๋ถ„๋ฅ˜ {len(wrong)}๊ฑด ({len(wrong) / len(aar):.1%}): ์›์ธ ๋‹จ์„œ ์—†์Œ(๋ฏธ์ƒ) {len(no_cue)}๊ฑด ({len(no_cue) / len(wrong):.0%}), ๋‹จ์„œ์™€ ์‹ค์ œ ์›์ธ ๋ถˆ์ผ์น˜ {len(conflict)}๊ฑด ({len(conflict) / len(wrong):.0%})\")\n", "print(\" ๋ฏธ์ƒ ๊ฑด์˜ ์‹ค์ œ ์›์ธ: \" + \", \".join(f\"{k} {v}\" for k, v in no_cue[\"cause\"].value_counts().items()))\n", "c0 = conflict.groupby([\"rule0\", \"cause\"]).size().sort_values(ascending=False).head(2)\n", "print(\" ๋ถˆ์ผ์น˜ ์ƒ์œ„: \" + \", \".join(f\"๋ฃฐ '{a}' -> ์‹ค์ œ '{b}' {v}๊ฑด\" for (a, b), v in c0.items()))\n", "ex = no_cue[no_cue[\"cause\"] == \"์ •๋น„ ํ’ˆ์งˆ\"].iloc[0]\n", "print(f\" ์‚ฌ๋ก€: '{ex['text']}' (์—…์ฒด {ex['vendor']}, ์ •๋น„ ํ›„ {ex['since_maint']}์ผ) -> ๋ฃฐ ๋ฏธ์ƒ, ์‹ค์ œ ์ •๋น„ ํ’ˆ์งˆ\")\n", "\n", "# 2) ์žฌํŠœ๋‹: ๋ฏธ์ƒ ๊ฑด์— ๋ณด์กฐ ๊ทœ์น™ ์ ์šฉ(AAR ๊ธฐ๊ฐ„ ๋ฏธ์ƒ ๊ฑด๋งŒ์œผ๋กœ ๊ฒฐ์ • ํŠธ๋ฆฌ ํ•™์Šต, ๊นŠ์ด 3)\n", "def meta(d):\n", " X = pd.DataFrame({\"since_maint\": d[\"since_maint\"], \"ext_vendor\": (d[\"vendor\"] != \"๋ถ€๋Œ€์ •๋น„\").astype(int), \"vendor_B\": (d[\"vendor\"] == \"์™ธ์ฃผB\").astype(int),\n", " \"wait\": d[\"inspect_wait\"], \"reinspect\": d[\"reinspect\"].astype(int), \"sev\": d[\"sev\"], \"wear\": d[\"defect\"].isin([\"๋งˆ๋ชจ\", \"๋ถ€์‹\"]).astype(int),\n", " \"leak\": (d[\"defect\"] == \"๋ˆ„์œ \").astype(int)})\n", " for p_ in (2, 3, 7):\n", " X[f\"part{p_}\"] = (d[\"part\"] == p_).astype(int)\n", " for p_ in (4, 5, 11):\n", " X[f\"part{p_}\"] = (d[\"part\"] == p_).astype(int)\n", " return X\n", "\n", "\n", "tree = DecisionTreeClassifier(max_depth=3, min_samples_leaf=40, random_state=0).fit(meta(no_cue.assign(cause=no_cue[\"cause\"])), no_cue[\"cause\"])\n", "print(\"\\n[์žฌํŠœ๋‹ ๋ณด์กฐ ๊ทœ์น™(๊ฒฐ์ • ํŠธ๋ฆฌ, ๋ฏธ์ƒ ๊ฑด ๋Œ€์ƒ)]\")\n", "for line in export_text(tree, feature_names=list(meta(no_cue).columns), max_depth=3).splitlines()[:9]:\n", " print(\" \" + line)\n", "\n", "\n", "CONF = 0.45\n", "\n", "\n", "def rule1(d):\n", " out = d[\"rule0\"].copy()\n", " unk = out == \"๋ฏธ์ƒ\"\n", " if unk.any():\n", " pr = tree.predict_proba(meta(d[unk]))\n", " out[unk] = np.where(pr.max(axis=1) >= CONF, tree.classes_[pr.argmax(axis=1)], \"๋ฏธ์ƒ\") # ํ™•์‹ ์ด ๋‚ฎ์œผ๋ฉด ๋ฏธ์ƒ์œผ๋กœ ๋‚จ๊ฒจ ์‚ฌ๋žŒ์ด ํ™•์ธ\n", " return out\n", "\n", "\n", "for d in (aar, val):\n", " d[\"rule1\"] = rule1(d)\n", "acc = lambda d, c: (d[c] == d[\"cause\"]).mean()\n", "print(f\"\\n์ •ํ™•๋„ ๋ณ€ํ™”: AAR(ํŠœ๋‹) {acc(aar, 'rule0'):.1%} -> {acc(aar, 'rule1'):.1%}, ๊ฒ€์ฆ(๋ฏธ์‚ฌ์šฉ) {acc(val, 'rule0'):.1%} -> {acc(val, 'rule1'):.1%}\")\n", "for c in (\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\"):\n", " m = val[\"cause\"] == c\n", " print(f\" {c} ์žฌํ˜„์œจ(๊ฒ€์ฆ): {(val.loc[m, 'rule0'] == c).mean():.0%} -> {(val.loc[m, 'rule1'] == c).mean():.0%}\")\n", "\n", "# 3) 3๋Œ€ ์ •๋Ÿ‰ ํšจ๊ณผ ์ง€ํ‘œ (๊ฒ€์ฆ 12๊ฐœ์›”, ๊ฐ€์ • ๋‹จ๊ฐ€)\n", "REWORK, HOURS = 0.5, 0.8 # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์˜ค๋ถ„๋ฅ˜ ๊ฑด๋‹น ์žฌ์ž‘์—… ๋น„์šฉ = ์กฐ์น˜ ๋น„์šฉ x 0.5, ๋ฏธ์ƒ ๊ฑด ์ˆ˜์ž‘์—… ์›์ธ ํ™•์ธ 0.8์‹œ๊ฐ„\n", "cost0, cost1 = (val.loc[val[\"rule0\"] != val[\"cause\"], \"cost\"].sum() * REWORK), (val.loc[val[\"rule1\"] != val[\"cause\"], \"cost\"].sum() * REWORK)\n", "h0, h1 = (val[\"rule0\"] == \"๋ฏธ์ƒ\").sum() * HOURS, (val[\"rule1\"] == \"๋ฏธ์ƒ\").sum() * HOURS\n", "ext0, ext1 = ((val[\"rule0\"].isin([\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\"])) & (val[\"cause\"].isin([\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\"])) & (val[\"rule0\"] == val[\"cause\"])).sum(), \\\n", " ((val[\"rule1\"].isin([\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\"])) & (val[\"cause\"].isin([\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\"])) & (val[\"rule1\"] == val[\"cause\"])).sum()\n", "print(\"\\n[3๋Œ€ ์ •๋Ÿ‰ ํšจ๊ณผ ์ง€ํ‘œ] (๊ฒ€์ฆ 12๊ฐœ์›”, ๊ฐ€์ • ๋‹จ๊ฐ€)\")\n", "print(f\" 1) ๋น„์šฉ ์ ˆ๊ฐ: ์˜ค๋ถ„๋ฅ˜ ์žฌ์ž‘์—… ๋น„์šฉ์ง€์ˆ˜ {cost0:.0f} -> {cost1:.0f} ({1 - cost1 / cost0:.1%} ์ ˆ๊ฐ), ์—ฐ ํ™˜์‚ฐ ์•ฝ {cost0 - cost1:.0f}\")\n", "print(f\" 2) ์‹œ๊ฐ„ ๋‹จ์ถ•: ์›์ธ ์ˆ˜์ž‘์—… ํ™•์ธ {h0:.0f}h -> {h1:.0f}h ({1 - h1 / h0:.1%} ๋‹จ์ถ•, ํ™•์‹  {CONF:.0%} ๋ฏธ๋งŒ์€ ๋ฏธ์ƒ์œผ๋กœ ๋‚จ๊ฒจ ์‚ฌ๋žŒ์ด ํ™•์ธ)\")\n", "print(f\" 3) ์ œ์กฐยท์ •๋น„ ์ฑ…์ž„ ๊ฑด ์ •ํ™• ์‹๋ณ„: {ext0}๊ฑด -> {ext1}๊ฑด (+{ext1 / ext0 - 1:.0%}), ํ•˜์ž ๊ตฌ์ƒยท์‹œ์ • ์š”๊ตฌ ๊ทผ๊ฑฐ ํ™•๋ณด ์ฆ๊ฐ€\")\n", "print(\"[๋„์ž… ๋กœ๋“œ๋งต] ๋‹จ๊ธฐ(1~3์›”): ๋ณด์กฐ ๊ทœ์น™ ์ ์šฉ + ํ’ˆ์งˆ๊ด€ ํ™•์ธ ๋ณ‘ํ–‰ / ์ค‘๊ธฐ(3~12์›”): ๋ถ„๊ธฐ AAR๋กœ ๊ทœ์น™ ์žฌํ•™์Šต, ๋‹จ์„œ ์šฉ์–ด ์‚ฌ์ „ ํ™•์žฅ / ์žฅ๊ธฐ(1๋…„~): ๊ตฐ์ˆ˜ ์ •๋ณด์ฒด๊ณ„ ์ •๋น„ ์ด๋ ฅ๊ณผ ์›์ธ ๋ผ๋ฒจ ํ™˜๋ฅ˜\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 09 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] AAR ๊ธฐ๊ฐ„ 4568๊ฑด, ๊ฒ€์ฆ ๊ธฐ๊ฐ„ 5103๊ฑด\n", "๊ธฐ์กด ๋ฃฐ์…‹ ์ •ํ™•๋„: AAR 60.3%, ๊ฒ€์ฆ 61.7%\n", "AAR ์˜ค๋ถ„๋ฅ˜ 1815๊ฑด (39.7%): ์›์ธ ๋‹จ์„œ ์—†์Œ(๋ฏธ์ƒ) 1617๊ฑด (89%), ๋‹จ์„œ์™€ ์‹ค์ œ ์›์ธ ๋ถˆ์ผ์น˜ 198๊ฑด (11%)\n", " ๋ฏธ์ƒ ๊ฑด์˜ ์‹ค์ œ ์›์ธ: ์ œ์กฐ ๋ถˆ๋Ÿ‰ 407, ์ž์—ฐ ๋…ธํ›„ 354, ์ •๋น„ ํ’ˆ์งˆ 331, ์„ค๊ณ„ ์ทจ์•ฝ 266, ์šด์šฉ ๋ถ€์ฃผ์˜ 259\n", " ๋ถˆ์ผ์น˜ ์ƒ์œ„: ๋ฃฐ '์„ค๊ณ„ ์ทจ์•ฝ' -> ์‹ค์ œ '์ •๋น„ ํ’ˆ์งˆ' 15๊ฑด, ๋ฃฐ '์ œ์กฐ ๋ถˆ๋Ÿ‰' -> ์‹ค์ œ '์ •๋น„ ํ’ˆ์งˆ' 15๊ฑด\n", " ์‚ฌ๋ก€: '์—ฐ๋ฃŒ๊ณ„ํ†ต์—์„œ ๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ. ์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ.' (์—…์ฒด ์™ธ์ฃผB, ์ •๋น„ ํ›„ 6์ผ) -> ๋ฃฐ ๋ฏธ์ƒ, ์‹ค์ œ ์ •๋น„ ํ’ˆ์งˆ\n", "\n", "[์žฌํŠœ๋‹ ๋ณด์กฐ ๊ทœ์น™(๊ฒฐ์ • ํŠธ๋ฆฌ, ๋ฏธ์ƒ ๊ฑด ๋Œ€์ƒ)]\n", " |--- reinspect <= 0.50\n", " | |--- since_maint <= 62.50\n", " | | |--- vendor_B <= 0.50\n", " | | | |--- class: ์ œ์กฐ ๋ถˆ๋Ÿ‰\n", " | | |--- vendor_B > 0.50\n", " | | | |--- class: ์ •๋น„ ํ’ˆ์งˆ\n", " | |--- since_maint > 62.50\n", " | | |--- part5 <= 0.50\n", " | | | |--- class: ์ž์—ฐ ๋…ธํ›„\n", "\n", "์ •ํ™•๋„ ๋ณ€ํ™”: AAR(ํŠœ๋‹) 60.3% -> 65.4%, ๊ฒ€์ฆ(๋ฏธ์‚ฌ์šฉ) 61.7% -> 66.2%\n", " ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์žฌํ˜„์œจ(๊ฒ€์ฆ): 63% -> 67%\n", " ์ •๋น„ ํ’ˆ์งˆ ์žฌํ˜„์œจ(๊ฒ€์ฆ): 62% -> 79%\n", "\n", "[3๋Œ€ ์ •๋Ÿ‰ ํšจ๊ณผ ์ง€ํ‘œ] (๊ฒ€์ฆ 12๊ฐœ์›”, ๊ฐ€์ • ๋‹จ๊ฐ€)\n", " 1) ๋น„์šฉ ์ ˆ๊ฐ: ์˜ค๋ถ„๋ฅ˜ ์žฌ์ž‘์—… ๋น„์šฉ์ง€์ˆ˜ 8127 -> 7253 (10.8% ์ ˆ๊ฐ), ์—ฐ ํ™˜์‚ฐ ์•ฝ 874\n", " 2) ์‹œ๊ฐ„ ๋‹จ์ถ•: ์›์ธ ์ˆ˜์ž‘์—… ํ™•์ธ 1409h -> 1126h (20.1% ๋‹จ์ถ•, ํ™•์‹  45% ๋ฏธ๋งŒ์€ ๋ฏธ์ƒ์œผ๋กœ ๋‚จ๊ฒจ ์‚ฌ๋žŒ์ด ํ™•์ธ)\n", " 3) ์ œ์กฐยท์ •๋น„ ์ฑ…์ž„ ๊ฑด ์ •ํ™• ์‹๋ณ„: 1454๊ฑด -> 1685๊ฑด (+16%), ํ•˜์ž ๊ตฌ์ƒยท์‹œ์ • ์š”๊ตฌ ๊ทผ๊ฑฐ ํ™•๋ณด ์ฆ๊ฐ€\n", "[๋„์ž… ๋กœ๋“œ๋งต] ๋‹จ๊ธฐ(1~3์›”): ๋ณด์กฐ ๊ทœ์น™ ์ ์šฉ + ํ’ˆ์งˆ๊ด€ ํ™•์ธ ๋ณ‘ํ–‰ / ์ค‘๊ธฐ(3~12์›”): ๋ถ„๊ธฐ AAR๋กœ ๊ทœ์น™ ์žฌํ•™์Šต, ๋‹จ์„œ ์šฉ์–ด ์‚ฌ์ „ ํ™•์žฅ / ์žฅ๊ธฐ(1๋…„~): ๊ตฐ์ˆ˜ ์ •๋ณด์ฒด๊ณ„ ์ •๋น„ ์ด๋ ฅ๊ณผ ์›์ธ ๋ผ๋ฒจ ํ™˜๋ฅ˜\n", "```\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## ๐ŸŽฏ ๋ฏธ์…˜ 10. ์•ผ์ „ ์žฅ๋น„ ๊ฒฐํ•จ ๋ณด๊ณ (QDR) ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋ฐ ํ’ˆ์งˆ ์ทจ์•ฝ์  ์‹๋ณ„ ๋„๊ตฌ ํ†ตํ•ฉ ์™„์„ฑ\n", "**์†Œ์š”์‹œ๊ฐ„**: ์•ฝ 3.5์‹œ๊ฐ„\n", "\n", "### ๐Ÿ“‹ ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ์ˆ˜ํ–‰ ์ง€์นจ\n", "#### ๋ฏธ์…˜ ๋ฐฐ๊ฒฝ ๋ฐ ๋‹น์œ„์„ฑ\n", "\n", "์•ผ์ „ ์žฅ๋น„ ๊ฒฐํ•จ ๋ณด๊ณ (QDR) ๋ฐ์ดํ„ฐ ๋ถ„์„ ๋ฐ ํ’ˆ์งˆ ์ทจ์•ฝ์  ์‹๋ณ„ ๋„๊ตฌ๋ฅผ ์™„์„ฑํ•œ๋‹ค. ์‹ค์ œ ์ „์ˆ  ํ˜„์žฅ์—์„œ๋Š” ๋‹จ์ˆœ ํƒ์ง€์— ๊ทธ์น˜์ง€ ์•Š๊ณ  ์‹ ์†ํ•œ ์œ„ํ˜‘ ํŒ๋‹จ๊ณผ ์‹ ๋ขฐ๋„ ๋†’์€ ์˜์‚ฌ๊ฒฐ์ •์ด ํ•„์ˆ˜์ ์ด๋ฏ€๋กœ, **QDR ๊ฒ€์ฆยท์ •์ œ๋ถ€ํ„ฐ ์น˜๋ช…๋„ ๋ถ„๋ฅ˜, ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ยท๊ฒฝ๋ณด, ์›์ธ ๋ถ„๋ฅ˜, ์Šน์ธ ๋Œ€๊ธฐ ํ›„๋ณด ์ œ์•ˆ, ์žฌํ•™์Šต๊นŒ์ง€ ํ•œ ํŒŒ์ผ๋กœ ํ†ตํ•ฉํ•˜๊ณ  ์ž์ฒด ์ ๊ฒ€๊ณผ ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ์„ ์ถœ๋ ฅํ•˜๋Š” ๋„๊ตฌ๋ฅผ ์™„์„ฑํ•œ๋‹ค.** ๊ณผ์—…์„ ์™„์ˆ˜ํ•˜์—ฌ ์˜คํŒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ž‘์ „ ๋Œ€์‘๋ ฅ์„ ๊ทน๋Œ€ํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ณธ ๋ฏธ์…˜์—์„œ๋Š” **์ง€๋‚œ๋‹ฌยท์ด๋ฒˆ ๋‹ฌ ์‹ค์ ์œผ๋กœ ์ฑ”ํ”ผ์–ธ ๋ชจ๋ธ๊ณผ ๋„์ „์ž ๋ชจ๋ธ์„ ํ•™์Šตํ•ด ๊ฒ€์ฆ ์ •ํ™•๋„๋ฅผ ๋น„๊ตํ•˜๊ณ  ๋น„์Šทํ•˜๊ฑฐ๋‚˜ ๋‚˜์„ ๋•Œ๋งŒ ๊ต์ฒด ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋Š”** ์‹ค์Šต์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "ํ•™์Šต์ž๋Š” ์‹ค์Šต๊ฐ€์ด๋“œ์— ์ œ์‹œ๋œ ์ด 4๊ฐœ ์„ธ๋ถ€ ํ™œ๋™(๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ โž” ๊ฒฐ๊ณผ ์ง€ํ‘œ ๊ฒ€์ฆ โž” ํŒŒ๋ผ๋ฏธํ„ฐ ํŠœ๋‹ ๋ฐ ๋„์ „๊ณผ์ œ)์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•ต์‹ฌ ํŒ์ • ๊ธฐ์ค€์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.\n", "\n", "**์ด๋ฒˆ ๊ฒฐ๊ณผ๋ฌผ:** `docs/qlt05_operations_guide.md ๋ฐ pipeline/qlt05_retrain.py` ํ˜•ํƒœ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์™„์„ฑํ•œ๋‹ค.\n", "\n", "### ๐Ÿ” ํ‰๊ฐ€ ๋ฐ ๊ฒ€์ฆ ๊ธฐ์ค€ (PASS ์กฐ๊ฑด)\n", "#### ๋ฏธ์…˜ ํŒ์ •\n", "\n", "**ํ‰๊ฐ€ ๊ตฌ๋ถ„:** ํ•„์ˆ˜\n", "\n", "**ํ•ต์‹ฌ ํ‰๊ฐ€์˜์—ญ:** ์ง€์† ๊ฐ€๋Šฅํ•œ ์šด์˜ ๊ฐ€๋Šฅ์„ฑ ๋ฐ ๊ตญ๋ฐฉ ๊ตฐ์ˆ˜ ์ •๋ณด์ฒด๊ณ„์™€์˜ ์—ฐ๋™ ์šฉ์ด์„ฑ\n", "\n", "#### ๋ฏธ์…˜ PASS ์กฐ๊ฑด\n", "\n", "- ์ •๋Ÿ‰ ๊ธฐ์ค€: ์ž๋™ ์žฌํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ ์ •์ƒ ์‹คํ–‰, ์ตœ์ข… ๋ฌธ์„œ 100% ์™„๋น„\n", "\n", "\n", "- ์ง„ํ–‰ ๊ธฐ์ค€: ์ตœ์ข… ์ธ๋„๋ฌผ ๊ฒ€์ˆ˜ ๋ฐ ํ”„๋กœ์ ํŠธ ์™„๋ฃŒ ๋ฐœํ‘œ์ž๋ฃŒ ์™„์„ฑ\n", "\n", "\n", "- ๊ฒ€์ฆ ๋ฐฉ๋ฒ•: End-to-End ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ์ž๋™ํ™” ์‹คํ–‰ ๋ฐ ์ตœ์ข… ๊ฒ€์ˆ˜\n", "\n", "\n", "#### ์ฑ„์ ์ž ํŒ์ • ๋ฉ”๋ชจ\n", "\n", "> ๋‹จ์ˆœ ์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ถœ๋ ฅ๋œ ์ˆ˜์น˜์™€ ๊ทผ๊ฑฐ ์ž๋ฃŒ๊ฐ€ ๊ตฐ ์‹ค๋ฌด ์š”๊ตฌ์‚ฌํ•ญ์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ํ™•์ธํ•œ๋‹ค.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"QDR ์„œ์ˆ  ๊ฒ€์ฆยท์ •์ œยท๋ผ๋ฒจ๋ง, ์น˜๋ช…๋„ ๋ถ„๋ฅ˜ ๋ชจ๋ธ์˜ ์ž๋™ ์žฌํ•™์Šต(์ฑ”ํ”ผ์–ธยท๋„์ „์ž ๋น„๊ต ํ›„ ๊ต์ฒด), ๋ถ€์œ„๋ณ„ ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜์™€ ๊ธ‰์ฆ ๊ฒฝ๋ณด, ์žฌํŠœ๋‹ ๊ทœ์น™์˜ ์›์ธ ๋ถ„๋ฅ˜, ํ•˜์ž ๊ตฌ์ƒยท์„ค๊ณ„ ๋ณ€๊ฒฝยท์™ธ์ฃผ์ •๋น„ ์‹œ์ • ํ›„๋ณด์˜ ํ’ˆ์งˆ๊ด€ ์Šน์ธ ๋Œ€๊ธฐ ๋ชฉ๋ก๊นŒ์ง€ ์•ผ์ „ QDR ํ’ˆ์งˆ ์ทจ์•ฝ์  ์‹๋ณ„ ๋„๊ตฌ๋ฅผ ํ•œ ํŒŒ์ผ๋กœ ํ†ตํ•ฉ ์‹คํ–‰ํ•˜๊ณ , ์ž์ฒด ์ ๊ฒ€ยท์šด์˜ ๋ฌธ์„œ ์™„๋น„ ์ ๊ฒ€ยทํ’ˆ์งˆ ๋‹ด๋‹น ์žฅ๊ต ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ์„ ์ถœ๋ ฅํ•œ๋‹ค.\"\"\"\n", "import re\n", "import time\n", "import unicodedata\n", "import numpy as np\n", "import pandas as pd\n", "import scipy.sparse as sp\n", "import matplotlib.pyplot as plt\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.linear_model import LogisticRegression\n", "\n", "# ---------------------------------------------------------------------------\n", "# [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ๊ฐ€์ƒ ์•ผ์ „ ์žฅ๋น„ ํ’ˆ์งˆ๊ฒฐํ•จ๋ณด๊ณ (QDR) ์ƒ์„ฑ๊ธฐ (์‹ค์ œ ๋ถ€๋Œ€ยท์žฅ๋น„ยท์—…์ฒดยท์ •๋น„ ์‹ค์  ์—†์Œ)\n", "# ๊ฐ€์ƒ ์žฅ๋น„ ๋ถ€์œ„ 12์ข…, ๊ฒฐํ•จ ์œ ํ˜• 8์ข…, ์น˜๋ช…๋„(Critical/Major/Minor), ์›์ธ 5์ข…, ๊ฐ€์ƒ ์™ธ์ฃผ์ •๋น„ ์—…์ฒด 4๊ณณ + ๋ถ€๋Œ€์ •๋น„.\n", "# QDR 1๊ฑด๋งˆ๋‹ค ํ•œ๊ตญ์–ด ์„œ์ˆ  ํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ , ์›”๋ณ„ ๊ฑด์ˆ˜๋Š” ๊ณ„์ ˆยท์ถ”์„ธยทํ›ˆ๋ จ ๊ฐ•๋„(usage)ยท์ผ๋ถ€ ์ œ์กฐ ๋ถˆ๋Ÿ‰ ์ด๋ฒคํŠธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค.\n", "# ---------------------------------------------------------------------------\n", "PARTS = [ # (์ด๋ฆ„, ๋ณ„์นญ, ์›” ๊ธฐ์ค€ ๊ฑด์ˆ˜, ๊ณ„์ ˆ ์ง„ํญ, ์ •์  ์›”, (Critical, Major ๋น„์œจ), ๊ฐ€์ƒ ๋‹จ๊ฐ€์ง€์ˆ˜, ์ œ์กฐ์‚ฌ, ์ฃผ์š” ๊ฒฐํ•จ ๊ฐ€์ค‘์น˜)\n", " (\"์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต\", [\"์—ฐ๋ฃŒ๊ณ„ํ†ต\", \"์—ฐ๋ฃŒ ๋ผ์ธ\", \"์—ฐ๋ฃŒ ํŽŒํ”„\"], 26, 0.15, 7, (0.10, 0.35), 6.0, \"MK1\", \"๋ˆ„์œ :4 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ์˜ค์ž‘๋™:2\"),\n", " (\"๋ณ€์†๊ธฐ\", [\"๋ณ€์†๊ธฐ\", \"T/M\", \"๋ฏธ์…˜\"], 22, 0.10, 8, (0.12, 0.38), 9.0, \"MK1\", \"๋งˆ๋ชจ:4 ๋ˆ„์œ :2 ๊ณผ์—ด:3 ์˜ค์ž‘๋™:1\"),\n", " (\"์ œ๋™ ์žฅ์น˜\", [\"์ œ๋™ ์žฅ์น˜\", \"๋ธŒ๋ ˆ์ดํฌ\", \"์ œ๋™ ๋ผ์ธ\"], 20, 0.20, 12, (0.26, 0.40), 5.0, \"MK2\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:2 ๋ˆ„์œ :2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2\"),\n", " (\"์กฐํ–ฅ ์žฅ์น˜\", [\"์กฐํ–ฅ ์žฅ์น˜\", \"ํ•ธ๋“ค ์—ฐ๋™๋ถ€\", \"์กฐํ–ฅ ๋กœ๋“œ\"], 14, 0.10, 6, (0.30, 0.40), 5.0, \"MK2\", \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:4 ๊ท ์—ด:2 ๋งˆ๋ชจ:3 ์˜ค์ž‘๋™:1\"),\n", " (\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", [\"๋ƒ‰๊ฐ ๊ณ„ํ†ต\", \"๋ผ๋””์—์ดํ„ฐ\", \"๋ƒ‰๊ฐ์ˆ˜ ๋ผ์ธ\"], 24, 0.30, 7, (0.05, 0.30), 3.0, \"MK3\", \"๋ˆ„์œ :4 ๊ณผ์—ด:4 ๋ถ€์‹:2\"),\n", " (\"์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ\", [\"๋ฐฐํ„ฐ๋ฆฌ\", \"์ „๊ธฐ ๊ณ„ํ†ต\", \"๋ฐœ์ „๊ธฐ\"], 28, 0.35, 1, (0.04, 0.28), 2.5, \"MK3\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:4 ๋ถ€์‹:2 ๊ณผ์—ด:2 ์˜ค์ž‘๋™:2\"),\n", " (\"ํ†ต์‹  ๋ชจ๋“ˆ\", [\"ํ†ต์‹  ๋ชจ๋“ˆ\", \"๋ฌด์ „๊ธฐ ์—ฐ๋™๋ถ€\", \"์•ˆํ…Œ๋‚˜ ์ ‘์†๋ถ€\"], 16, 0.05, 4, (0.03, 0.25), 4.0, \"MK4\", \"์ ‘์ด‰๋ถˆ๋Ÿ‰:5 ์˜ค์ž‘๋™:4 ๋ถ€์‹:1\"),\n", " (\"์œ ์•• ๊ณ„ํ†ต\", [\"์œ ์•• ๊ณ„ํ†ต\", \"์œ ์•• ํ˜ธ์Šค\", \"์œ ์•• ์‹ค๋ฆฐ๋”\"], 18, 0.10, 9, (0.10, 0.35), 4.5, \"MK2\", \"๋ˆ„์œ :5 ๊ท ์—ด:2 ๋งˆ๋ชจ:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"ํ˜„๊ฐ€ ์žฅ์น˜\", [\"ํ˜„๊ฐ€ ์žฅ์น˜\", \"์„œ์ŠคํŽœ์…˜\", \"์™„์ถฉ๊ธฐ\"], 15, 0.08, 5, (0.06, 0.30), 3.5, \"MK4\", \"๋งˆ๋ชจ:4 ๊ท ์—ด:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:2 ๋ถ€์‹:1\"),\n", " (\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", [\"์ฐจ์ฒด ์šฉ์ ‘๋ถ€\", \"ํ”„๋ ˆ์ž„ ์šฉ์ ‘๋ถ€\", \"์ฐจ์ฒด ์ด์Œ๋ถ€\"], 10, 0.05, 10, (0.28, 0.40), 7.0, \"MK1\", \"๊ท ์—ด:6 ๋ถ€์‹:3 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ด‘ํ•™ยท์กฐ์ค€ ์žฅ๋น„\", [\"๊ด‘ํ•™ ์žฅ๋น„\", \"์กฐ์ค€๊ฒฝ\", \"๊ด€์ธก ์žฅ๋น„\"], 8, 0.05, 3, (0.03, 0.22), 8.0, \"MK4\", \"์˜ค์ž‘๋™:4 ์ ‘์ด‰๋ถˆ๋Ÿ‰:3 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:1\"),\n", " (\"๊ณต๊ธฐ ์ •ํ™” ํ•„ํ„ฐ\", [\"๊ณต๊ธฐ ํ•„ํ„ฐ\", \"ํก๊ธฐ ํ•„ํ„ฐ\", \"์ •ํ™” ์žฅ์น˜\"], 12, 0.25, 4, (0.01, 0.12), 1.0, \"MK3\", \"๋งˆ๋ชจ:5 ๋ถ€์‹:2 ์ฒด๊ฒฐ๋ถˆ๋Ÿ‰:3\")]\n", "NP = len(PARTS)\n", "DEFECTS = { # ๊ฒฐํ•จ ์œ ํ˜•: ์ฆ์ƒ ํ‘œํ˜„(๋™์˜์–ด)\n", " \"๊ท ์—ด\": [\"๊ท ์—ด์ด ๋ฐœ๊ฒฌ๋จ\", \"ํฌ๋ž™์ด ํ™•์ธ๋จ\", \"๊ฐˆ๋ผ์ง์ด ์žˆ์Œ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜ค์ผ์ด ์ƒˆ์–ด ๋‚˜์˜ด\", \"๋ˆ„์„ค ํ”์ ์ด ์žˆ์Œ\"],\n", " \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ๊ฐ€ ์‹ฌํ•จ\", \"๊ณผ๋„ํ•œ ๋งˆ๋ชจ๊ฐ€ ํ™•์ธ๋จ\", \"๋‹ณ์€ ํ”์ ์ด ์žˆ์Œ\"], \"๋ถ€์‹\": [\"๋ถ€์‹์ด ์ง„ํ–‰๋จ\", \"๋…น์ด ๋ฐœ์ƒํ•จ\", \"์‚ฐํ™” ํ”์ ์ด ์žˆ์Œ\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰์ด ๋ฐœ์ƒํ•จ\", \"์ปค๋„ฅํ„ฐ ๋‹จ์„ ์ด ์˜์‹ฌ๋จ\", \"์‹ ํ˜ธ๊ฐ€ ๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ ํ’€๋ฆผ์ด ํ™•์ธ๋จ\", \"์ฒด๊ฒฐ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"๊ณ ์ •๋ถ€๊ฐ€ ํ—๊ฑฐ์›€\"],\n", " \"๊ณผ์—ด\": [\"๊ณผ์—ด ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•จ\", \"์˜จ๋„๊ฐ€ ๊ณผ๋„ํ•˜๊ฒŒ ์ƒ์Šนํ•จ\", \"์—ด์ด ์‹ฌํ•˜๊ฒŒ ๋‚จ\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™์ด ๋ฐ˜๋ณต๋จ\", \"์ž‘๋™์ด ๋ถˆ์•ˆ์ •ํ•จ\", \"๊ฐ„ํ—์ ์œผ๋กœ ๋ฉˆ์ถค\"]}\n", "DNAMES = list(DEFECTS)\n", "SEV = [\"Minor\", \"Major\", \"Critical\"]\n", "SEV_CUE = {0: [\"๊ฒฝ๋ฏธํ•œ ์ˆ˜์ค€์ž„\", \"์šด์šฉ์—๋Š” ์ง€์žฅ์ด ์—†์Œ\", \"์œก์•ˆ ์ ๊ฒ€์ƒ ์‚ฌ์†Œํ•œ ์ด์ƒ์ž„\", \"๊ธฐ๋Šฅ์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ\"],\n", " 1: [\"์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์žˆ์Œ\", \"์กฐ์†ํ•œ ์ •๋น„๊ฐ€ ํ•„์š”ํ•จ\", \"์ถœ๋ ฅ์ด ๋–จ์–ด์ง€๋Š” ์ƒํƒœ์ž„\", \"๊ธฐ๋Šฅ์ด ๋ถ€๋ถ„์ ์œผ๋กœ ์ œํ•œ๋จ\"],\n", " 2: [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€ํ•จ\", \"์•ˆ์ „์— ์œ„ํ—˜์ด ์žˆ์–ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์ž‘๋™ ์ •์ง€๋กœ ์ž„๋ฌด ์ˆ˜ํ–‰์ด ์–ด๋ ค์›€\", \"์ธ๋ช… ์•ˆ์ „ ์œ„ํ˜‘์ด ์šฐ๋ ค๋จ\"]}\n", "CAUSES = [\"์ œ์กฐ ๋ถˆ๋Ÿ‰\", \"์ •๋น„ ํ’ˆ์งˆ\", \"์šด์šฉ ๋ถ€์ฃผ์˜\", \"์„ค๊ณ„ ์ทจ์•ฝ\", \"์ž์—ฐ ๋…ธํ›„\"]\n", "CAUSE_CUE = {\n", " \"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ ์ธ์ˆ˜ ์งํ›„ ๋ฐœ์ƒํ•จ\", \"์ถœ๊ณ  ์ƒํƒœ์—์„œ ์ด๋ฏธ ์ด์ƒ์ด ์žˆ์—ˆ์Œ\", \"์ธ์ˆ˜ ํ›„ ์–ผ๋งˆ ์ง€๋‚˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•จ\"],\n", " \"์ •๋น„ ํ’ˆ์งˆ\": [\"์™ธ์ฃผ ์ •๋น„ ํ›„ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•„ ์žฌ๋ฐœํ•จ\", \"์ •๋น„ ํ›„ ์žฌ์กฐ๋ฆฝ ์ƒํƒœ๊ฐ€ ๋ถˆ๋Ÿ‰ํ•จ\", \"์ •๋น„ ์งํ›„ ๊ฐ™์€ ๋ถ€์œ„์—์„œ ๋‹ค์‹œ ๋ฐœ์ƒํ•จ\"],\n", " \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"ํ—˜์ง€ ๊ธฐ๋™ ์ค‘ ์ถฉ๊ฒฉ ์ดํ›„ ๋ฐœ์ƒํ•จ\", \"๊ณผ๋ถ€ํ•˜ ์šด์šฉ ํ›„ ๋ฐœ์ƒํ•จ\", \"์ ๊ฒ€ ์—†์ด ์—ฐ์† ์šด์šฉํ•œ ๋’ค ๋ฐœ์ƒํ•จ\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ์ด ๋‹ค๋ฅธ ์ฐจ๋Ÿ‰์—์„œ๋„ ๋ฐ˜๋ณต๋จ\", \"๊ตฌ์กฐ์ ์œผ๋กœ ์ทจ์•ฝํ•œ ๋ถ€์œ„๋กœ ๋ณด์ž„\", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€์—์„œ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ณด๊ณ ๋จ\"],\n", " \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ์ด ๋„๋ž˜ํ•จ\", \"์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉ์œผ๋กœ ๋…ธํ›„๋จ\", \"์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ง๊ธฐ์— ๋ฐœ์ƒํ•จ\"]}\n", "VENDORS = [\"์™ธ์ฃผA\", \"์™ธ์ฃผB\", \"์™ธ์ฃผC\", \"์™ธ์ฃผD\", \"๋ถ€๋Œ€์ •๋น„\"]\n", "ACTIONS = [\"์ •๋น„์†Œ ์ž…๊ณ ๋ฅผ ์š”์ฒญํ•จ\", \"ํ˜„์žฅ ์ž„์‹œ ์กฐ์น˜ ํ›„ ์šด์šฉ ์ค‘์ž„\", \"๋ถ€ํ’ˆ ๊ตํ™˜์„ ์š”์ฒญํ•จ\", \"์žฌ๊ฒ€์‚ฌ๋ฅผ ์š”์ฒญํ•จ\", \"์›์ธ ํ™•์ธ์ด ํ•„์š”ํ•จ\"]\n", "MAKER_OF = [p[7] for p in PARTS]\n", "PRICE = np.array([p[6] for p in PARTS])\n", "\n", "\n", "def usage_index(months, seed=0):\n", " \"\"\"์›”๋ณ„ ํ›ˆ๋ จ ๊ฐ•๋„(๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„ํš ๊ฐ’): ์—ฐ 1ํšŒ ์ •์ ์„ ๊ฐ–๋Š” ๊ณ„์ ˆ + ์ž‘์€ ์žก์Œ\"\"\"\n", " r = np.random.default_rng(seed + 99)\n", " t = np.arange(months)\n", " return 1 + 0.25 * np.sin(2 * np.pi * (t - 3) / 12) + r.normal(0, 0.04, months)\n", "\n", "\n", "def make_qdr(months=36, seed=1, scale=1.0, surge=True):\n", " \"\"\"๋ฐ˜ํ™˜: QDR DataFrame(๊ฑด๋‹น 1ํ–‰), ์›”๋ณ„ usage, ๊ธ‰์ฆ ์ด๋ฒคํŠธ ๋ชฉ๋ก\"\"\"\n", " rng = np.random.default_rng(seed)\n", " use = usage_index(months, seed)\n", " events = []\n", " if surge:\n", " for _ in range(max(2, months // 12 * 2)):\n", " events.append((int(rng.integers(NP)), int(rng.integers(6, months))))\n", " rows = []\n", " wdef = [np.array([float(x.split(\":\")[1]) for x in p[8].split()]) for p in PARTS]\n", " ddef = [[x.split(\":\")[0] for x in p[8].split()] for p in PARTS]\n", " n = 0\n", " for m in range(months):\n", " for pi, p in enumerate(PARTS):\n", " lam = p[2] * scale * (1 + 0.010 * m) * use[m] * (1 + p[3] * np.cos(2 * np.pi * (m % 12 - p[4]) / 12))\n", " ev = (pi, m) in events\n", " k = rng.poisson(lam * (2.6 if ev else 1.0))\n", " for _ in range(k):\n", " rows.append((n, m, pi, ev)); n += 1\n", " N = len(rows)\n", " A = np.array(rows)\n", " month, part, ev = A[:, 1], A[:, 2], A[:, 3].astype(bool)\n", " defect = np.array([rng.choice(ddef[i], p=wdef[i] / wdef[i].sum()) for i in part])\n", " cr = np.array([PARTS[i][5][0] for i in part]); ma = np.array([PARTS[i][5][1] for i in part])\n", " u = rng.random(N)\n", " sev = np.where(u < cr, 2, np.where(u < cr + ma, 1, 0))\n", " vendor = rng.choice(VENDORS, N, p=[0.25, 0.2, 0.2, 0.15, 0.2])\n", " # ์›์ธ: ๊ธฐ๋ณธ ๋น„์œจ์— ๋ถ€์œ„(์ œ์กฐ์‚ฌ)ยท์—…์ฒดยท์ด๋ฒคํŠธ ํšจ๊ณผ๋ฅผ ๊ณฑํ•œ๋‹ค\n", " base = np.array([0.22, 0.20, 0.20, 0.16, 0.22])\n", " P = np.tile(base, (N, 1))\n", " P[vendor == \"์™ธ์ฃผB\", 1] *= 2.6\n", " P[ev, 0] *= 5.0\n", " design = np.isin(part, [2, 3, 7]); P[design, 3] *= 2.2\n", " P[np.isin(part, [4, 5, 11]), 0] *= 2.0 # ์ œ์กฐ์‚ฌ MK3 ๋‚ฉํ’ˆ ๋ถ€์œ„๋Š” ์ œ์กฐ ๋ถˆ๋Ÿ‰ ๋น„์ค‘์ด ๋†’๋‹ค\n", " P[defect == \"๋งˆ๋ชจ\", 4] *= 1.6; P[defect == \"๋ˆ„์œ \", 4] *= 1.3\n", " P /= P.sum(axis=1, keepdims=True)\n", " cause = np.array([CAUSES[rng.choice(5, p=P[i])] for i in range(N)])\n", " since = np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", rng.exponential(35, N), rng.exponential(160, N)).clip(1, 720).round().astype(int)\n", " wait = (rng.lognormal(1.3, 0.5, N) * np.where(vendor == \"์™ธ์ฃผB\", 1.7, np.where(vendor == \"๋ถ€๋Œ€์ •๋น„\", 0.6, 1.0)) * (0.7 + 0.5 * use[month])).round(1)\n", " reins = rng.random(N) < np.where(cause == \"์ •๋น„ ํ’ˆ์งˆ\", 0.6, 0.08)\n", " texts = []\n", " for i in range(N):\n", " pt = PARTS[part[i]]\n", " cl = [f\"{rng.choice(pt[1])}์—์„œ {rng.choice(DEFECTS[defect[i]])}\"]\n", " if rng.random() < 0.7: cl.append(rng.choice(SEV_CUE[sev[i]]))\n", " if rng.random() < 0.65: # ์›์ธ ๋‹จ์„œ: 8%๋Š” ์‹ค์ œ ์›์ธ๊ณผ ๋‹ค๋ฅธ ๋‹จ์„œ๊ฐ€ ์ ํžŒ๋‹ค(๋ณด๊ณ ์ž์˜ ์ถ”์ •)\n", " cc = cause[i] if rng.random() > 0.08 else rng.choice(CAUSES)\n", " cl.append(rng.choice(CAUSE_CUE[cc]))\n", " cl.append(rng.choice(ACTIONS))\n", " if rng.random() < 0.5: cl.insert(0, f\"VU-{rng.integers(1, 31):02d} ์†Œ์†\")\n", " texts.append(\". \".join(cl) + \".\")\n", " df = pd.DataFrame({\"qdr_id\": [f\"Q{seed}{i:06d}\" for i in range(N)], \"month\": month, \"unit\": [f\"VU-{x:02d}\" for x in rng.integers(1, 31, N)],\n", " \"part\": part, \"defect\": defect, \"sev\": sev, \"cause\": cause, \"vendor\": vendor, \"maker\": [MAKER_OF[i] for i in part],\n", " \"since_maint\": since, \"inspect_wait\": wait, \"reinspect\": reins, \"event\": ev, \"text\": texts})\n", " df[\"cost\"] = PRICE[df[\"part\"]] * np.array([1.0, 2.0, 4.0])[df[\"sev\"]] # [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์ˆ˜๋ฆฌยท์กฐ์น˜ ๋น„์šฉ์ง€์ˆ˜\n", " return df, use, events\n", "\n", "# ---------------------------------------------------------------------------\n", "# ๊ณตํ†ต ํ…์ŠคํŠธ ์กฐ๊ฐ: ์ •๊ทœํ™”, ๊ฐ„์ด ํ† ํฐํ™”(์กฐ์‚ฌ ์ œ๊ฑฐ), ๋ถ€์œ„ยท๊ฒฐํ•จยท์น˜๋ช…๋„ยท์›์ธ ํ‚ค์›Œ๋“œ ๋ผ๋ฒจ๋ง\n", "# ---------------------------------------------------------------------------\n", "TYPO = {\"๋ˆ„์œณ\": \"๋ˆ„์œ \", \"๊ท ๋ ฌ\": \"๊ท ์—ด\", \"์ ‘์ถ• ๋ถˆ๋Ÿ‰\": \"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋ถ€์”ฉ\": \"๋ถ€์‹\", \"๋งˆ๋ฌ˜\": \"๋งˆ๋ชจ\", \"๊ณผ๋ ฌ\": \"๊ณผ์—ด\", \"์˜ค์ž‘๋˜ฅ\": \"์˜ค์ž‘๋™\", \"์žฌ๋™\": \"์ œ๋™\"}\n", "ABBR = {\"T/M\": \"๋ณ€์†๊ธฐ\", \"BAT\": \"๋ฐฐํ„ฐ๋ฆฌ\"}\n", "PART_KW = sorted([(a, i) for i, p in enumerate(PARTS) for a in p[1]], key=lambda x: -len(x[0]))\n", "DEF_KW = {\"๊ท ์—ด\": [\"๊ท ์—ด\", \"ํฌ๋ž™\", \"๊ฐˆ๋ผ\"], \"๋ˆ„์œ \": [\"๋ˆ„์œ \", \"์ƒˆ์–ด\", \"๋ˆ„์„ค\"], \"๋งˆ๋ชจ\": [\"๋งˆ๋ชจ\", \"๋‹ณ์€\"], \"๋ถ€์‹\": [\"๋ถ€์‹\", \"๋…น์ด\", \"์‚ฐํ™”\"],\n", " \"์ ‘์ด‰๋ถˆ๋Ÿ‰\": [\"์ ‘์ด‰ ๋ถˆ๋Ÿ‰\", \"๋‹จ์„ \", \"๋Š๊น€\"], \"์ฒด๊ฒฐ๋ถˆ๋Ÿ‰\": [\"๋ณผํŠธ\", \"์ฒด๊ฒฐ\", \"ํ—๊ฑฐ\"], \"๊ณผ์—ด\": [\"๊ณผ์—ด\", \"์˜จ๋„\", \"์—ด์ด\"], \"์˜ค์ž‘๋™\": [\"์˜ค์ž‘๋™\", \"๋ถˆ์•ˆ์ •\", \"๋ฉˆ์ถค\"]}\n", "SEV_KW = [(2, [\"์ฆ‰์‹œ ์šด์šฉ ์ค‘์ง€\", \"์‚ฌ์šฉ ๋ถˆ๊ฐ€\", \"์œ„ํ—˜\"]), (1, [\"์„ฑ๋Šฅ ์ €ํ•˜\", \"์กฐ์†ํ•œ\"]), (0, [\"๊ฒฝ๋ฏธ\", \"์ง€์žฅ์ด ์—†\"])]\n", "CAUSE_KW = {\"์ œ์กฐ ๋ถˆ๋Ÿ‰\": [\"์‹ ํ’ˆ\", \"์ถœ๊ณ \", \"์ธ์ˆ˜\"], \"์ •๋น„ ํ’ˆ์งˆ\": [\"์ •๋น„ ํ›„\", \"์žฌ์กฐ๋ฆฝ\", \"์ •๋น„ ์งํ›„\"], \"์šด์šฉ ๋ถ€์ฃผ์˜\": [\"์ถฉ๊ฒฉ\", \"๊ณผ๋ถ€ํ•˜\", \"์ ๊ฒ€ ์—†์ด\"],\n", " \"์„ค๊ณ„ ์ทจ์•ฝ\": [\"๋™์ผ ์ฆ์ƒ\", \"๊ตฌ์กฐ์ \", \"์—ฌ๋Ÿฌ ๋ถ€๋Œ€\"], \"์ž์—ฐ ๋…ธํ›„\": [\"์‚ฌ์šฉ ์—ฐํ•œ\", \"๋…ธํ›„\", \"์ˆ˜๋ช…\"]}\n", "PARTICLES = (\"์—์„œ\", \"์œผ๋กœ\", \"์ด\", \"๊ฐ€\", \"์€\", \"๋Š”\", \"์„\", \"๋ฅผ\", \"์—\", \"์˜\", \"๋กœ\", \"์™€\", \"๊ณผ\", \"๋„\", \"ํ•จ\", \"์Œ\", \"๋จ\")\n", "\n", "\n", "def norm(t):\n", " \"\"\"์ •๊ทœํ™”: ์ „๊ฐ->๋ฐ˜๊ฐ, ํŠน์ˆ˜๋ฌธ์žยท์ค‘๋ณต ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์•ฝ์–ด ํ™•์žฅ, ์˜คํƒ€ ๊ต์ •\"\"\"\n", " t = unicodedata.normalize(\"NFKC\", str(t))\n", " t = re.sub(r\"[!#~*@^]+\", \" \", t)\n", " for a, b in ABBR.items():\n", " t = t.replace(a, b)\n", " for a, b in TYPO.items():\n", " t = t.replace(a, b)\n", " return re.sub(r\"\\s+\", \" \", t).strip()\n", "\n", "\n", "def tok(t):\n", " out = []\n", " for w in re.split(r\"[\\s.,]+\", norm(t)):\n", " if not w or re.fullmatch(r\"[\\d\\-A-Za-z]+\", w):\n", " continue\n", " for p in PARTICLES:\n", " if len(w) > len(p) + 1 and w.endswith(p):\n", " w = w[:-len(p)]\n", " break\n", " out.append(w)\n", " return out\n", "\n", "\n", "def label_part(t):\n", " for a, i in PART_KW:\n", " if a in t:\n", " return i\n", " return -1\n", "\n", "\n", "def label_defect(t):\n", " for d, ks in DEF_KW.items():\n", " if any(k in t for k in ks):\n", " return d\n", " return \"๋ฏธ๋ถ„๋ฅ˜\"\n", "\n", "\n", "def label_sev(t):\n", " for s, ks in SEV_KW:\n", " if any(k in t for k in ks):\n", " return s\n", " return -1\n", "\n", "\n", "def label_cause(t):\n", " for c, ks in CAUSE_KW.items():\n", " if any(k in t for k in ks):\n", " return c\n", " return \"๋ฏธ์ƒ\"\n", "\n", "CONFIG = {\"retrain_tolerance\": 0.005, \"alarm_ratio\": 1.9, \"alarm_z\": 4.0, \"since_short\": 62, \"min_cases\": 60, \"mfg_share\": 0.22, \"design_share\": 0.20, \"vendor_share\": 0.28}\n", "OPS_GUIDE = { # docs/qlt05_operations_guide.md ์š”์•ฝ\n", " \"1. ๋ชฉ์ ๊ณผ ๋ฒ”์œ„\": \"์•ผ์ „ QDR ์„œ์ˆ ยท์‹ค์ ์œผ๋กœ ๋‹ค๋ฐœ ๊ฒฐํ•จ ๋ถ€์œ„์™€ ํ’ˆ์งˆ ์ทจ์•ฝ์ ์„ ์‹๋ณ„ํ•˜๋Š” ๋ณด์กฐ ๋„๊ตฌ. ํ•˜์ž ๊ตฌ์ƒยท์„ค๊ณ„ ๋ณ€๊ฒฝ ์†Œ์š” ์ œ๊ธฐ์˜ ๊ฒฐ์ •์€ ํ’ˆ์งˆ๊ด€.\",\n", " \"2. ์ž…๋ ฅ ๋ฐ์ดํ„ฐ\": \"QDR(๋ถ€๋Œ€ยท์žฅ๋น„ยท๋ถ€์œ„ยท์„œ์ˆ ยท์น˜๋ช…๋„), ์ •๋น„ ์ˆ˜ํ–‰์ฒ˜์™€ ๊ฒฝ๊ณผ์ผ, ๊ฒ€์‚ฌ ๋Œ€๊ธฐยท์žฌ๊ฒ€์‚ฌ. ๊ฐ€์ƒ ์‹๋ณ„์ฝ”๋“œ ์‚ฌ์šฉ, ์‹ค์ œ ๋ถ€๋Œ€ ์ž๋ฃŒ๋Š” ๋ถ€๋Œ€ ๋‚ด์—์„œ๋งŒ ์ฒ˜๋ฆฌ.\",\n", " \"3. ์ผ์ผ ์šด์šฉ ์ ˆ์ฐจ\": \"์ ‘์ˆ˜ -> ๊ฒ€์ฆยท๊ฒฉ๋ฆฌ -> ์ •์ œยท๋ผ๋ฒจ๋ง -> ์น˜๋ช…๋„ ๋ถ„๋ฅ˜ -> ์›”๋ณ„ ์ง‘๊ณ„ยท๊ฒฝ๋ณด -> ํ›„๋ณด ์ œ์•ˆ(๋Œ€๊ธฐ) -> ํ’ˆ์งˆ๊ด€ ๊ฒฐ์ • -> ๊ฒฐ๊ณผ ๊ธฐ๋ก.\",\n", " \"4. ์Šน์ธ(HITL) ๊ธฐ์ค€\": \"์ œ์กฐ์‚ฌ ํ•˜์ž ๊ตฌ์ƒยท์„ค๊ณ„ ๋ณ€๊ฒฝยท์™ธ์ฃผ์ •๋น„ ์‹œ์ •์€ ํ’ˆ์งˆ๊ด€ ์Šน์ธ ํ›„ ํ™•์ •, ๋ฌด์‘๋‹ต์€ ์กฐ์น˜ ๋ณด๋ฅ˜ ํ›„ ๋ถ„๊ธฐ ํšŒ์˜ ์žฌ์ƒ์ •, ์‚ฌ์œ  ๊ธฐ๋ก.\",\n", " \"5. ์žฌํ•™์Šต ์ ˆ์ฐจ\": \"๋ถ„๊ธฐ 1ํšŒ ์‹ ๊ทœ QDR๋กœ ๋„์ „์ž ํ•™์Šต, ๊ฒ€์ฆ ์ •ํ™•๋„๊ฐ€ ๋‚˜๋น ์ง€์ง€ ์•Š์„ ๋•Œ๋งŒ ๊ต์ฒด.\",\n", " \"6. ์žฅ์•  ๋Œ€์‘\": \"๋ชจ๋ธ ์˜ค๋ฅ˜ ์‹œ ํ‚ค์›Œ๋“œ ๊ทœ์น™์œผ๋กœ ๋Œ€์ฒด, ์†์ƒยท๋น„์ •์ƒ ์„œ์ˆ ์€ ์ˆ˜๋™ ํ™•์ธ ๋ชฉ๋ก์œผ๋กœ ๋ถ„๋ฆฌ.\",\n", " \"7. ์—ฐ๋™ ์ธํ„ฐํŽ˜์ด์Šค\": \"๊ตญ๋ฐฉ ๊ตฐ์ˆ˜ ์ •๋ณด์ฒด๊ณ„์™€ QDRยท์ •๋น„ ์ด๋ ฅ ํ‘œ์ค€ ํ•„๋“œ(์žฅ๋น„์ฝ”๋“œยท๋ถ„๋ฅ˜๊ตฐยท์ผ์‹œ)๋ฅผ CSV/API๋กœ ๊ตํ™˜ํ•˜๋Š” ์•ˆ์„ ์ฐจ๊ธฐ ๊ณผ์ œ๋กœ ๋‘”๋‹ค.\",\n", "}\n", "names = [p[0] for p in PARTS]\n", "oh = lambda d: sp.csr_matrix(np.hstack([np.eye(NP)[d[\"part\"]], pd.get_dummies(d[\"defect\"]).reindex(columns=DNAMES, fill_value=0).values.astype(float)]))\n", "\n", "\n", "def validate(d):\n", " ok = d[\"text\"].str.len().between(5, 400) & ~d[\"text\"].str.contains(\"๏ฟฝ\", regex=False)\n", " return d[ok].copy(), d[~ok].copy()\n", "\n", "\n", "def fit_sev(d):\n", " vec = TfidfVectorizer(tokenizer=tok, lowercase=False, token_pattern=None, min_df=5)\n", " X = sp.hstack([vec.fit_transform(d[\"text\"]), oh(d)]).tocsr()\n", " return vec, LogisticRegression(C=5, max_iter=500).fit(X, d[\"sev\"])\n", "\n", "\n", "def predict_sev(model, d):\n", " vec, clf = model\n", " return clf.predict(sp.hstack([vec.transform(d[\"text\"]), oh(d)]).tocsr())\n", "\n", "\n", "def retrain(champion, old, new):\n", " \"\"\"๋„์ „์ž(์ง€๋‚œ๋‹ฌ+์ด๋ฒˆ ๋‹ฌ ์•ž 2/3) vs ์ฑ”ํ”ผ์–ธ(์ง€๋‚œ๋‹ฌ) -> ์ด๋ฒˆ ๋‹ฌ ๋’ค 1/3 ๊ฒ€์ฆ ์ •ํ™•๋„ ๋น„๊ต, ๋น„์Šทํ•˜๊ฑฐ๋‚˜ ๋‚˜์„ ๋•Œ๋งŒ ๊ต์ฒด\"\"\"\n", " cut = new[\"month\"].quantile(2 / 3)\n", " hold, new_tr = new[new[\"month\"] > cut], new[new[\"month\"] <= cut]\n", " challenger = fit_sev(pd.concat([old, new_tr], ignore_index=True))\n", " a_old, a_new = (predict_sev(champion, hold) == hold[\"sev\"].values).mean(), (predict_sev(challenger, hold) == hold[\"sev\"].values).mean()\n", " swap = a_new >= a_old - CONFIG[\"retrain_tolerance\"]\n", " return (challenger if swap else champion), {\"์ฑ”ํ”ผ์–ธ\": round(float(a_old), 3), \"๋„์ „์ž\": round(float(a_new), 3), \"๊ต์ฒด\": bool(swap)}\n", "\n", "\n", "def sev_safe(model, d):\n", " try:\n", " return predict_sev(model, d), \"๋ชจ๋ธ\"\n", " except Exception:\n", " return d[\"text\"].map(label_sev).replace(-1, 0).values, \"ํ‚ค์›Œ๋“œ ๊ทœ์น™ ๋Œ€์ฒด\"\n", "\n", "\n", "def cause_rule(d):\n", " c = d[\"text\"].map(norm).map(label_cause)\n", " short = (c == \"๋ฏธ์ƒ\") & (d[\"vendor\"] == \"์™ธ์ฃผB\") & (d[\"since_maint\"] <= CONFIG[\"since_short\"])\n", " return c.where(~short, \"์ •๋น„ ํ’ˆ์งˆ\")\n", "\n", "\n", "# ---------------- 1. ํ†ตํ•ฉ ์‹คํ–‰ ----------------\n", "t_start = time.perf_counter()\n", "old, _, _ = make_qdr(36, seed=11, scale=1.5) # ์ง€๋‚œ๋‹ฌ ์‹ค์ \n", "new, _, _ = make_qdr(36, seed=1, scale=1.5) # ์ด๋ฒˆ ๋‹ฌ ์‹ค์ \n", "ops, use, events = make_qdr(24, seed=21, scale=1.5) # ๋‹ค์Œ ๋‹ฌ ์šด์šฉ(์‹ ๊ทœ QDR)\n", "ops.iloc[[10, 300], ops.columns.get_loc(\"text\")] = [\"\", \"๏ฟฝ๏ฟฝ ์˜ค๋ฅ˜\"] # ์†์ƒ ์„œ์ˆ  2๊ฑด ์ฃผ์ž…\n", "champion = fit_sev(old)\n", "model, rt = retrain(champion, old, new)\n", "print(f\"[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์žฌํ•™์Šต: {rt} -> {'๋„์ „์ž ๊ต์ฒด' if rt['๊ต์ฒด'] else '์ฑ”ํ”ผ์–ธ ์œ ์ง€'}\")\n", "good, bad = validate(ops)\n", "good = good.copy()\n", "good[\"sev_pred\"], src = sev_safe(model, good)\n", "good[\"cause_pred\"] = cause_rule(good)\n", "sec = time.perf_counter() - t_start\n", "acc = (good[\"sev_pred\"] == good[\"sev\"]).mean()\n", "print(f\"์šด์šฉ QDR {len(ops)}๊ฑด: ๋ถ„์„ {len(good)}๊ฑด, ์ˆ˜๋™ ํ™•์ธ {len(bad)}๊ฑด, ์น˜๋ช…๋„ ์ถœ์ฒ˜ '{src}', ์ •ํ™•๋„ {acc:.1%}, ์ „์ฒด ์‹คํ–‰ {sec:.1f}์ดˆ\")\n", "\n", "# ์›”๋ณ„ ์ง‘๊ณ„ยท๊ฒฝ๋ณดยท์ทจ์•ฝ์„ฑ ์ง€์ˆ˜\n", "M = good.pivot_table(index=\"part\", columns=\"month\", values=\"qdr_id\", aggfunc=\"size\", fill_value=0).reindex(index=range(NP), columns=range(24), fill_value=0)\n", "rate = (M / use[:24]).T.rolling(12, min_periods=12).mean().shift(1).T\n", "exp = rate * use[:24]\n", "Z = (M - exp) / np.sqrt(exp.clip(lower=1))\n", "A = (M / exp.clip(lower=1) > CONFIG[\"alarm_ratio\"]) & (Z > CONFIG[\"alarm_z\"])\n", "W_SEV = np.array([1, 3, 9])\n", "V = good.assign(v=W_SEV[good[\"sev_pred\"]]).groupby(\"part\")[\"v\"].sum()\n", "recent = good[good[\"month\"] >= 21].assign(v=lambda d: W_SEV[d[\"sev_pred\"]]).groupby(\"part\")[\"v\"].sum().sort_values(ascending=False)\n", "\n", "# ํ›„๋ณด ์ œ์•ˆ(์Šน์ธ ๋Œ€๊ธฐ): ์ตœ๊ทผ 12๊ฐœ์›” ๊ทผ๊ฑฐ\n", "ev = good[good[\"month\"] >= 12]\n", "cands = []\n", "for p, g in ev.groupby(\"part\"):\n", " mf, ds = (g[\"cause_pred\"] == \"์ œ์กฐ ๋ถˆ๋Ÿ‰\").mean(), (g[\"cause_pred\"] == \"์„ค๊ณ„ ์ทจ์•ฝ\").mean()\n", " if len(g) >= CONFIG[\"min_cases\"] and mf >= CONFIG[\"mfg_share\"]: cands.append((names[p], \"ํ•˜์ž ๊ตฌ์ƒ\", len(g), mf))\n", " if len(g) >= CONFIG[\"min_cases\"] and ds >= CONFIG[\"design_share\"]: cands.append((names[p], \"์„ค๊ณ„ ๋ณ€๊ฒฝ\", len(g), ds))\n", "for v, g in ev.groupby(\"vendor\"):\n", " sh = (g[\"cause_pred\"] == \"์ •๋น„ ํ’ˆ์งˆ\").mean()\n", " if v != \"๋ถ€๋Œ€์ •๋น„\" and len(g) >= 100 and sh >= CONFIG[\"vendor_share\"]: cands.append((v, \"์™ธ์ฃผ์ •๋น„ ์‹œ์ •\", len(g), sh))\n", "C = pd.DataFrame(cands, columns=[\"๋Œ€์ƒ\", \"์œ ํ˜•\", \"๊ทผ๊ฑฐ ๊ฑด์ˆ˜\", \"์›์ธ ๋น„์ค‘\"])\n", "C[\"status\"] = \"PENDING\" # ํ’ˆ์งˆ๊ด€ ๊ฒฐ์ • ์ „๊นŒ์ง€ ํ™•์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค\n", "\n", "# ---------------- 2. ์ž์ฒด ์ ๊ฒ€ ----------------\n", "_, bad_src = sev_safe(None, good.head(3))\n", "checks = {\n", " \"QDR ์œ ์‹ค 0๊ฑด\": len(good) + len(bad) == len(ops),\n", " \"์†์ƒ ์„œ์ˆ  2๊ฑด ์ˆ˜๋™ ํ™•์ธ ๋ถ„๋ฆฌ\": len(bad) == 2,\n", " \"์žฌํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰\": \"๊ต์ฒด\" in rt,\n", " \"์น˜๋ช…๋„ ์ •ํ™•๋„ 80% ์ด์ƒ\": bool(acc >= 0.80),\n", " \"๋ชจ๋ธ ์žฅ์•  ์‹œ ํ‚ค์›Œ๋“œ ๊ทœ์น™ ๋Œ€์ฒด\": bad_src == \"ํ‚ค์›Œ๋“œ ๊ทœ์น™ ๋Œ€์ฒด\",\n", " \"๊ฒฝ๋ณด ์…€ ๋ชจ๋‘ 12๊ฐœ์›” ์ดํ›„\": not bool(A.iloc[:, :12].values.any()),\n", " \"ํ›„๋ณด ์ œ์•ˆ 1๊ฑด ์ด์ƒ ์ƒ์„ฑ\": len(C) >= 1,\n", " \"์ž๋™ ์Šน์ธ 0๊ฑด(ํ›„๋ณด๋Š” ๋Œ€๊ธฐ)\": bool((C[\"status\"] == \"PENDING\").all()),\n", "}\n", "print(f\"\\n์ž์ฒด ์ ๊ฒ€ {sum(checks.values())}/{len(checks)} ํ†ต๊ณผ, ์ž๋™ ์žฌํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ {'์ •์ƒ ์‹คํ–‰' if checks['์žฌํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰'] else '์‹คํŒจ'}\")\n", "for k, v in checks.items():\n", " if not v:\n", " print(\" ์‹คํŒจ:\", k)\n", "doc = sum(bool(v.strip()) for v in OPS_GUIDE.values()) / len(OPS_GUIDE)\n", "print(f\"์šด์˜ ๋ฌธ์„œ {len(OPS_GUIDE)}๊ฐœ ์žฅ ์ž‘์„ฑ๋ฅ  {doc:.0%} (๋ชฉํ‘œ 100%: {'๋‹ฌ์„ฑ' if doc == 1 else '๋ฏธ๋‹ฌ'}) - \" + \", \".join(k.split(\". \")[1] for k in OPS_GUIDE))\n", "\n", "# ---------------- 3. ํ™”๋ฉด๊ณผ ๋ธŒ๋ฆฌํ•‘ ----------------\n", "fig, ax = plt.subplots(1, 2, figsize=(11, 4))\n", "ax[0].imshow((M / exp.clip(lower=1)).clip(upper=3).values, aspect=\"auto\", cmap=\"Reds\"); ax[0].set_title(\"QDR concentration (x expected)\"); ax[0].set_xlabel(\"month\"); ax[0].set_ylabel(\"part\")\n", "ax[1].bar(range(NP), V.reindex(range(NP)).fillna(0).values); ax[1].set_title(\"Vulnerability index by part\"); ax[1].set_xlabel(\"part id\")\n", "plt.tight_layout(); plt.show()\n", "\n", "fa = [(names[p], int(m)) for p, m in zip(*np.where(A.values))]\n", "print(\"\\n[ํ’ˆ์งˆ ๋‹ด๋‹น ์žฅ๊ต ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ] [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\")\n", "print(f\"1. ์šด์šฉ 24๊ฐœ์›” QDR {len(ops)}๊ฑด ์ค‘ ์†์ƒ {len(bad)}๊ฑด ์ˆ˜๋™ ํ™•์ธ, ์น˜๋ช…๋„ Critical {int((good['sev_pred'] == 2).sum())}๊ฑด({(good['sev_pred'] == 2).mean():.0%})\")\n", "print(\"2. ์ตœ๊ทผ 3๊ฐœ์›” ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์ƒ์œ„ ๋ถ€์œ„: \" + \", \".join(f\"{names[p]} {v:.0f}\" for p, v in recent.head(3).items()))\n", "print(f\"3. ์›”๋ณ„ ๊ธ‰์ฆ ๊ฒฝ๋ณด {len(fa)}๊ฑด\" + (\": \" + \", \".join(f\"{n}({m}์›”)\" for n, m in fa[:3]) if fa else \"\"))\n", "print(f\"4. ํ›„๋ณด ์ œ์•ˆ {len(C)}๊ฑด์€ ํ’ˆ์งˆ๊ด€ ์Šน์ธ ๋Œ€๊ธฐ(์ž๋™ ์Šน์ธ 0๊ฑด): \" + \", \".join(f\"{r['๋Œ€์ƒ']} {r['์œ ํ˜•']}\" for _, r in C.head(4).iterrows()))\n", "vb = good[good[\"vendor\"] == \"์™ธ์ฃผB\"]\n", "print(f\"5. ์™ธ์ฃผB ๊ฒ€์‚ฌ ๋Œ€๊ธฐ ํ‰๊ท  {vb['inspect_wait'].mean():.1f}์ผ(ํƒ€ ์ˆ˜ํ–‰์ฒ˜ {good.loc[good['vendor'] != '์™ธ์ฃผB', 'inspect_wait'].mean():.1f}์ผ), ์žฌ๊ฒ€์‚ฌ์œจ {vb['reinspect'].mean():.0%}\")\n", "print(f\"6. ์น˜๋ช…๋„ ๋ชจ๋ธ: {'์‹ ๊ทœ ์‹ค์ ์œผ๋กœ ๊ต์ฒด' if rt['๊ต์ฒด'] else '๊ธฐ์กด ์œ ์ง€'}(๊ฒ€์ฆ ์ •ํ™•๋„ {max(rt['์ฑ”ํ”ผ์–ธ'], rt['๋„์ „์ž']):.1%})\")\n", "print(\"7. ๊ฑด์˜: ์™ธ์ฃผ์ •๋น„ ์—…์ฒด ์‹œ์ • ์š”๊ตฌ์™€ ์ œ์กฐ์‚ฌ ํ•˜์ž ๊ตฌ์ƒ ๊ทผ๊ฑฐ ์ •๋ฆฌ, ๊ตญ๋ฐฉ ๊ตฐ์ˆ˜ ์ •๋ณด์ฒด๊ณ„์™€ QDR ํ‘œ์ค€ ํ•„๋“œ ์—ฐ๋™ ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "๐Ÿ’ก [์ฐธ๊ณ ] ๋ฏธ์…˜ 10 ์˜ˆ์ƒ ์‹คํ–‰ ๊ฒฐ๊ณผ (Output)\n", "\n", "```text\n", "[๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ] ์žฌํ•™์Šต: {'์ฑ”ํ”ผ์–ธ': 0.867, '๋„์ „์ž': 0.865, '๊ต์ฒด': True} -> ๋„์ „์ž ๊ต์ฒด\n", "์šด์šฉ QDR 8923๊ฑด: ๋ถ„์„ 8921๊ฑด, ์ˆ˜๋™ ํ™•์ธ 2๊ฑด, ์น˜๋ช…๋„ ์ถœ์ฒ˜ '๋ชจ๋ธ', ์ •ํ™•๋„ 87.8%, ์ „์ฒด ์‹คํ–‰ 7.3์ดˆ\n", "\n", "์ž์ฒด ์ ๊ฒ€ 8/8 ํ†ต๊ณผ, ์ž๋™ ์žฌํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ ์ •์ƒ ์‹คํ–‰\n", "์šด์˜ ๋ฌธ์„œ 7๊ฐœ ์žฅ ์ž‘์„ฑ๋ฅ  100% (๋ชฉํ‘œ 100%: ๋‹ฌ์„ฑ) - ๋ชฉ์ ๊ณผ ๋ฒ”์œ„, ์ž…๋ ฅ ๋ฐ์ดํ„ฐ, ์ผ์ผ ์šด์šฉ ์ ˆ์ฐจ, ์Šน์ธ(HITL) ๊ธฐ์ค€, ์žฌํ•™์Šต ์ ˆ์ฐจ, ์žฅ์•  ๋Œ€์‘, ์—ฐ๋™ ์ธํ„ฐํŽ˜์ด์Šค\n", "\n", "[ํ’ˆ์งˆ ๋‹ด๋‹น ์žฅ๊ต ๋ธŒ๋ฆฌํ•‘ ์š”์•ฝ] [๊ต์œก์šฉ๊ฐ€์ƒ์ž๋ฃŒ]\n", "1. ์šด์šฉ 24๊ฐœ์›” QDR 8923๊ฑด ์ค‘ ์†์ƒ 2๊ฑด ์ˆ˜๋™ ํ™•์ธ, ์น˜๋ช…๋„ Critical 717๊ฑด(8%)\n", "2. ์ตœ๊ทผ 3๊ฐœ์›” ์ทจ์•ฝ์„ฑ ์ง€์ˆ˜ ์ƒ์œ„ ๋ถ€์œ„: ์ œ๋™ ์žฅ์น˜ 378, ์กฐํ–ฅ ์žฅ์น˜ 268, ์—”์ง„ ์—ฐ๋ฃŒ๊ณ„ํ†ต 244\n", "3. ์›”๋ณ„ ๊ธ‰์ฆ ๊ฒฝ๋ณด 3๊ฑด: ์กฐํ–ฅ ์žฅ์น˜(20์›”), ๋ƒ‰๊ฐ ๊ณ„ํ†ต(16์›”), ์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ(18์›”)\n", "4. ํ›„๋ณด ์ œ์•ˆ 3๊ฑด์€ ํ’ˆ์งˆ๊ด€ ์Šน์ธ ๋Œ€๊ธฐ(์ž๋™ ์Šน์ธ 0๊ฑด): ๋ƒ‰๊ฐ ๊ณ„ํ†ต ํ•˜์ž ๊ตฌ์ƒ, ์ „๊ธฐยท๋ฐฐํ„ฐ๋ฆฌ ํ•˜์ž ๊ตฌ์ƒ, ์™ธ์ฃผB ์™ธ์ฃผ์ •๋น„ ์‹œ์ •\n", "5. ์™ธ์ฃผB ๊ฒ€์‚ฌ ๋Œ€๊ธฐ ํ‰๊ท  8.7์ผ(ํƒ€ ์ˆ˜ํ–‰์ฒ˜ 4.5์ผ), ์žฌ๊ฒ€์‚ฌ์œจ 26%\n", "6. ์น˜๋ช…๋„ ๋ชจ๋ธ: ์‹ ๊ทœ ์‹ค์ ์œผ๋กœ ๊ต์ฒด(๊ฒ€์ฆ ์ •ํ™•๋„ 86.7%)\n", "7. ๊ฑด์˜: ์™ธ์ฃผ์ •๋น„ ์—…์ฒด ์‹œ์ • ์š”๊ตฌ์™€ ์ œ์กฐ์‚ฌ ํ•˜์ž ๊ตฌ์ƒ ๊ทผ๊ฑฐ ์ •๋ฆฌ, ๊ตญ๋ฐฉ ๊ตฐ์ˆ˜ ์ •๋ณด์ฒด๊ณ„์™€ QDR ํ‘œ์ค€ ํ•„๋“œ ์—ฐ๋™ ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜\n", "```\n", "
\n" ] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 0 }