{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0f0ed137",
   "metadata": {},
   "source": [
    "# Part 3: 파이썬 컬렉션 심화\n",
    "\n",
    "**⬅️ 이전 시간: [Part 2: 파이썬 핵심 문법 마스터하기](../02_python_core_syntax/part_2_python_core_syntax.md)**\n",
    "**➡️ 다음 시간: [Part 4: 객체 지향 프로그래밍 (OOP)](../04_object_oriented_programming/part_4_object_oriented_programming.md)**\n",
    "\n",
    "---\n",
    "\n",
    "## 1. 학습 목표 (Learning Objectives)\n",
    "\n",
    "이번 파트가 끝나면, 여러분은 다음을 할 수 있게 됩니다.\n",
    "\n",
    "- 리스트의 다양한 메서드를 활용하여 데이터를 자유자재로 조작할 수 있습니다.\n",
    "- `sort()`와 `sorted()`의 차이점을 명확히 구분하여 사용합니다.\n",
    "- `Counter`, `defaultdict`, `deque`, `namedtuple` 등 특수 목적 컬렉션의 장점을 이해하고 적재적소에 활용할 수 있습니다.\n",
    "- [튜플 언패킹](../../glossary.md#튜플-언패킹-tuple-unpacking)을 사용하여 간결하고 가독성 높은 코드를 작성합니다.\n",
    "- 문제 상황에 가장 적합한 [자료구조](../../glossary.md#자료구조-data-structure)를 선택하여 코드의 효율성을 높일 수 있습니다.\n",
    "\n",
    "## 2. 핵심 요약 (Key Summary)\n",
    "이 파트에서는 파이썬의 기본 컬렉션(리스트, 튜플, 딕셔너리)을 더 깊이 있게 다룹니다. 리스트의 `sort()`와 `sorted()` 함수의 차이점을 배우고, `collections` 모듈의 특수 목적 컬렉션인 `Counter`, `defaultdict`, `deque`, `namedtuple`을 학습합니다. 이러한 자료구조들을 언제, 어떻게 사용해야 코드의 효율성과 가독성을 높일 수 있는지 이해하고, 튜플 언패킹과 같은 파이썬다운 기술을 익힙니다.\n",
    "\n",
    "- **핵심 키워드**: `리스트 메서드(List Methods)`, `sort() vs sorted()`, `collections.Counter`, `collections.defaultdict`, `collections.deque`, `collections.namedtuple`, `튜플 언패킹(Tuple Unpacking)`\n",
    "\n",
    "## 3. 전문가의 공구함, 컬렉션 깊이 알기 (Introduction)\n",
    "\n",
    "지난 시간에 파이썬의 기본 뼈대를 세웠다면, 이번 주차에는 그 연장들을 넣어둘 **'전문가용 공구함(컬렉션)'**을 깊이 있게 탐구합니다. 단순히 데이터를 담는 것을 넘어, **AI 데이터 분석 및 처리**라는 특정 용도에 따라 최적화된 특수 공구들을 꺼내 쓰는 법을 배우게 됩니다. 상황에 맞는 최적의 '데이터 그릇'을 선택하는 능력을 길러, 코드의 효율성과 가독성을 한 단계 끌어올려 보겠습니다.\n",
    "\n",
    "> [!TIP]\n",
    "> 본 파트의 모든 예제 코드는 `../../source_code/part_2_3_python_syntax_collections.py` 파일에서 직접 실행해볼 수 있습니다.\n",
    "\n",
    "---\n",
    "\n",
    "## 4. 리스트(List) 다시 보기\n",
    "\n",
    "> **🎯 심화 목표 1:** 리스트의 핵심 메서드를 능숙하게 사용하고, `sort()`와 `sorted()`의 차이를 명확히 이해하며, `Counter`로 데이터 빈도를 효율적으로 계산합니다.\n",
    "\n",
    "### 4.1. `sort()` vs `sorted()`: 원본을 바꿀 것인가, 새것을 얻을 것인가?\n",
    "\n",
    "데이터 분석 과정에서 **원본 데이터를 훼손하지 않고 보존하는 것은 매우 중요**합니다. 이 둘의 차이를 명확히 이해해야 의도치 않은 데이터 유실을 막을 수 있습니다.\n",
    "\n",
    "- **`list.sort()` 메서드**: **원본 리스트 자체를** 정렬합니다. 반환값은 `None`입니다. (내 방 정리)\n",
    "- **`sorted()` 내장 함수**: 정렬된 **새로운 리스트를 반환**합니다. 원본은 바뀌지 않습니다. (정리된 모습 촬영)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f5791902",
   "metadata": {},
   "outputs": [],
   "source": [
    "my_list = [3, 1, 4, 2]\n",
    "# wrong_list = my_list.sort() #! 잘못된 사용! wrong_list는 None이 됩니다.\n",
    "new_list = sorted(my_list) # 올바른 사용\n",
    "print(f\"원본: {my_list}, 정렬된 사본: {new_list}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "feeef5b1",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 4.2. 스마트한 개수 세기: `collections.Counter`\n",
    "리스트나 문자열에 포함된 각 항목이 몇 번 등장하는지 세어주는 '똑똑한 재고 관리인'입니다. 특히 **자연어 처리(NLP)에서 텍스트 데이터의 단어 빈도를 계산**하는 가장 기초적이면서도 핵심적인 작업에 사용됩니다. 이를 통해 어떤 단어가 중요한지 파악하는 등 데이터 분석의 첫걸음을 뗄 수 있습니다.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5b0d889",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import Counter\n",
    "\n",
    "fruit_counts = Counter(['apple', 'banana', 'apple'])\n",
    "print(f\"과일 개수: {fruit_counts}\")\n",
    "# 출력: Counter({'apple': 2, 'banana': 1})\n",
    "print(f\"가장 흔한 과일: {fruit_counts.most_common(1)}\")\n",
    "# 출력: [('apple', 2)]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73e82d59",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "## 5. 튜플(Tuple)과 딕셔너리(Dictionary) 깊이 알기\n",
    "\n",
    "> **🎯 심화 목표 2:** 튜플 언패킹을 자유자재로 활용하고, `defaultdict`로 딕셔너리 초기화 코드를 간소화합니다.\n",
    "\n",
    "### 5.1. 튜플의 힘: [언패킹(Unpacking)](../../glossary.md#튜플-언패킹-tuple-unpacking)\n",
    "튜플의 각 항목을 여러 변수에 한 번에 할당하는 '우아한 소포 전달' 기능입니다.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b25f8ce4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 변수 값 교환(swap)\n",
    "a, b = 10, 20\n",
    "a, b = b, a # a=20, b=10\n",
    "\n",
    "# `*`를 이용한 언패킹\n",
    "first, *rest, last = (1, 2, 3, 4, 5)\n",
    "# first: 1, rest: [2, 3, 4], last: 5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b251502",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 5.2. 키가 없을 때의 구원투수: `collections.defaultdict`\n",
    "존재하지 않는 키에 접근할 때 `KeyError` 대신 지정된 기본값을 자동으로 생성하는 '자동 완성 노트'입니다. `if`문을 사용해 키의 존재 여부를 매번 확인할 필요가 없어 코드가 매우 간결해집니다. **흩어져 있는 데이터를 특정 기준(e.g., 영화 장르, 사용자 그룹)에 따라 묶어서(Grouping) 분석**할 때 폭발적인 효율성을 보여줍니다.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aa90ac2c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "# 키가 없을 때 빈 리스트(list)를 기본값으로 생성\n",
    "d = defaultdict(list)\n",
    "d['a'].append('apple')\n",
    "d['b'].append('banana')\n",
    "d['a'].append('ant')\n",
    "print(d) # defaultdict(<class 'list'>, {'a': ['apple', 'ant'], 'b': ['banana']})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38744d0d",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "## 6. 특수 목적 컬렉션\n",
    "\n",
    "> **🎯 심화 목표 3:** 양방향 큐 `deque`와 이름으로 접근하는 `namedtuple`의 편리함을 학습합니다.\n",
    "\n",
    "### 6.1. 고속 큐(Queue): `collections.deque`\n",
    "데이터의 **양쪽 끝에서 항목을 추가(`append`, `appendleft`)하거나 제거(`pop`, `popleft`)하는 속도가 매우 빠른** '효율적인 대기줄'입니다. 최근 사용 목록(LRU 캐시) 등을 구현할 때 유용합니다.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d997895",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import deque\n",
    "\n",
    "# 최대 3개까지만 저장되는 deque\n",
    "recent_plays = deque(maxlen=3)\n",
    "recent_plays.append(\"Hype Boy\")\n",
    "recent_plays.append(\"Ditto\")\n",
    "recent_plays.append(\"OMG\")\n",
    "print(recent_plays) # deque(['Hype Boy', 'Ditto', 'OMG'], maxlen=3)\n",
    "recent_plays.append(\"Super Shy\") # 'Hype Boy'는 자동으로 삭제됨\n",
    "print(recent_plays) # deque(['Ditto', 'OMG', 'Super Shy'], maxlen=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e9637ad",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 6.2. 이름을 가진 튜플: `collections.namedtuple`\n",
    "튜플의 각 항목에 이름을 붙여, 인덱스(`p[0]`)뿐만 아니라 **이름(`p.x`)으로도 접근**할 수 있게 만든 '이름표가 붙은 칸막이 상자'입니다. 코드의 가독성을 획기적으로 높여줍니다.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eafcfbc3",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import namedtuple\n",
    "\n",
    "Point = namedtuple('Point', ['x', 'y'])\n",
    "p1 = Point(10, 20)\n",
    "print(f\"좌표: ({p1.x}, {p1.y})\") # 인덱스보다 훨씬 명확함"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16e0849f",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "## 7. 연습 문제 (Exercises)\n",
    "지금까지 배운 여러 자료구조를 활용하여 다음 문제들을 해결해보세요.\n",
    "\n",
    "### 문제 1: 최빈 단어 분석기\n",
    "**요구사항**: 주어진 영어 텍스트에서 가장 많이 등장하는 단어 3개를 찾아 출력하세요. 단, 대소문자는 구분하지 않고, 구두점은 무시합니다. (`collections.Counter` 활용)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb626a33",
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "from collections import Counter\n",
    "\n",
    "text = \"\"\"Python is an interpreted, high-level, general-purpose programming language.\"\"\"\n",
    "words = re.findall(r'\\w+', text.lower())\n",
    "word_counts = Counter(words)\n",
    "print(f\"가장 많이 나온 단어 3개: {word_counts.most_common(3)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6eb4340",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 문제 2: 연락처 그룹 관리\n",
    "**요구사항**: 친구들의 이름을 그룹('가족', '회사' 등)별로 관리하고 출력하는 `ContactManager` 클래스를 만드세요. (`collections.defaultdict` 활용)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fcee3dbe",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "class ContactManager:\n",
    "    def __init__(self):\n",
    "        self.contacts = defaultdict(list)\n",
    "    def add_contact(self, name, group):\n",
    "        self.contacts[group].append(name)\n",
    "    def display_contacts(self):\n",
    "        for group, names in self.contacts.items():\n",
    "            print(f\"- {group}: {', '.join(names)}\")\n",
    "\n",
    "# 사용 예시\n",
    "manager = ContactManager()\n",
    "manager.add_contact(\"엄마\", \"가족\"); manager.add_contact(\"아빠\", \"가족\")\n",
    "manager.add_contact(\"김대리\", \"회사\"); manager.add_contact(\"이과장\", \"회사\")\n",
    "manager.display_contacts()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "661639f0",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 문제 3: 최근 재생 목록\n",
    "**요구사항**: 노래를 재생하고 최근 5곡의 재생 목록을 보여주는 `Playlist` 클래스를 만드세요. 5개를 초과하면 가장 오래된 노래가 자동으로 삭제되어야 합니다. (`collections.deque` 활용)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21a1c08d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import deque\n",
    "\n",
    "class Playlist:\n",
    "    def __init__(self, max_size=5):\n",
    "        self.playlist = deque(maxlen=max_size)\n",
    "    def play(self, song):\n",
    "        self.playlist.append(song)\n",
    "    def show_recent(self):\n",
    "        print(f\"최근 재생 목록: {list(self.playlist)}\")\n",
    "\n",
    "# 사용 예시\n",
    "playlist = Playlist(max_size=3)\n",
    "playlist.play(\"라일락\"); playlist.play(\"Dynamite\"); playlist.play(\"뚜두뚜두\")\n",
    "playlist.play(\"Hype Boy\") # '라일락' 삭제됨\n",
    "playlist.show_recent()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a391346",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "---\n",
    "\n",
    "## 8. 트러블슈팅 (Troubleshooting)\n",
    "\n",
    "- **`AttributeError: 'list' object has no attribute 'appendleft'`가 발생했나요?**\n",
    "  - `appendleft`는 `deque` 객체의 메서드입니다. 일반 리스트에서는 사용할 수 없습니다. `from collections import deque`를 통해 `deque`를 생성했는지 확인하세요.\n",
    "- **`KeyError`가 자꾸 발생하나요?**\n",
    "  - `dict`에서 존재하지 않는 키를 조회할 때 발생합니다. 키가 항상 존재한다고 보장할 수 없다면 `my_dict.get('key', 'default_value')`를 사용하거나, 데이터를 그룹핑하는 경우라면 `defaultdict`를 사용하는 것이 좋습니다.\n",
    "- **어떤 자료구조를 써야 할지 모르겠나요?**\n",
    "  - 아래 '컬렉션 선택 가이드' 표를 다시 한번 살펴보세요. 데이터의 특징(순서, 중복, 변경 가능성)과 내가 하려는 작업(검색, 추가/삭제)을 기준으로 가장 적합한 것을 선택하는 연습이 중요합니다.\n",
    "\n",
    "더 자세한 문제 해결 가이드나 다른 동료들이 겪은 문제에 대한 해결책이 궁금하다면, 아래 문서를 참고해주세요.\n",
    "\n",
    "- **➡️ [Geumdo-Docs: TROUBLESHOOTING.md](../../../TROUBLESHOOTING.md)**\n",
    "\n",
    "---\n",
    "\n",
    "## 9. 되짚어보기 (Summary)\n",
    "\n",
    "### 컬렉션 선택 가이드: 어떤 공구를 언제 쓸까?\n",
    "어떤 상황에 어떤 [자료구조](../../glossary.md#자료구조-data-structure)를 선택하는 것이 가장 효율적인지 한눈에 정리해봅시다.\n",
    "\n",
    "| 자료구조 | 주요 특징 | 핵심 사용 사례 |\n",
    "|---|---|---|\n",
    "| **[`list`](../02_python_core_syntax/glossary.md#리스트-list)** | 순서 O, 변경 O | 순서가 중요하고, 데이터 추가/삭제/변경이 잦을 때 |\n",
    "| **[`tuple`](../02_python_core_syntax/glossary.md#튜플-tuple)** | 순서 O, 변경 X | 내용이 바뀌면 안 되는 값의 묶음 (e.g., 좌표, DB 레코드) |\n",
    "| **[`dict`](../02_python_core_syntax/glossary.md#딕셔너리-dictionary-dict)** | Key-Value, 순서 X | 의미 있는 키(Key)로 데이터를 빠르게 찾고 싶을 때 (e.g., 사용자 정보) |\n",
    "| **`set`** | 순서 X, 중복 X | 중복 제거, 항목의 존재 여부 확인, 집합 연산이 필요할 때 |\n",
    "| **`Counter`** | `dict`의 자식 | 항목 별 개수를 세거나, 가장 빈번한 항목을 찾을 때 (e.g., 단어 빈도수) |\n",
    "| **`defaultdict`** | `dict`의 자식 | 키가 없을 때 기본값을 자동으로 생성하여 그룹핑 등을 간소화할 때 |\n",
    "| **`deque`** | 양방향 큐 | 양쪽 끝에서의 추가/삭제가 빈번할 때 (e.g., 최근 목록 관리) |\n",
    "| **`namedtuple`** | `tuple`의 자식 | 인덱스 대신 이름으로 데이터에 접근하여 가독성을 높이고 싶을 때 |\n",
    "\n",
    "이러한 자료구조의 특징을 잘 이해하고 적재적소에 사용하면, 여러분의 파이썬 코드는 훨씬 더 효율적이고 읽기 좋아질 것입니다.\n",
    "\n",
    "---\n",
    "\n",
    "## 10. 더 깊이 알아보기 (Further Reading)\n",
    "\n",
    "- [파이썬 공식 문서: `collections` — 컨테이너 데이터형](https://docs.python.org/ko/3/library/collections.html)\n",
    "- [파이썬 공식 튜토리얼: 자료구조 심화](https://docs.python.org/ko/3/tutorial/datastructures.html)\n",
    "\n",
    "---\n",
    "\n",
    "## 11. 심화 프로젝트: 전자상거래 주문 데이터 분석\n",
    "\n",
    "이번 파트에서 배운 전문가용 컬렉션들을 활용하여, 좀 더 현실적인 데이터 분석 문제를 해결해봅시다. 여러 건의 전자상거래 주문 데이터를 분석하여, 제품별 매출 현황과 인기 상품을 파악하는 스크립트를 작성하는 것이 목표입니다.\n",
    "\n",
    "이 프로젝트를 통해 `namedtuple`로 가독성 높은 데이터 구조를 만들고, `defaultdict`와 `Counter`로 복잡한 데이터를 효율적으로 집계하는 실전 감각을 익힐 수 있습니다.\n",
    "\n",
    "### 프로젝트 목표\n",
    "\n",
    "주어진 주문 데이터(튜플의 리스트)를 처리하여 다음과 같은 분석 리포트를 출력하는 Python 스크립트(`order_analyzer.py`)를 완성합니다.\n",
    "\n",
    "**샘플 주문 데이터:**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4778762",
   "metadata": {},
   "outputs": [],
   "source": [
    "# (주문 ID, 제품명, 수량, 개당 가격)\n",
    "orders_data = [\n",
    "    (1, \"Laptop\", 1, 1500000),\n",
    "    (2, \"Mouse\", 2, 25000),\n",
    "    (1, \"Keyboard\", 1, 45000),\n",
    "    (3, \"Monitor\", 1, 300000),\n",
    "    (2, \"Keyboard\", 1, 45000),\n",
    "    (3, \"Mouse\", 1, 25000),\n",
    "    (4, \"Laptop\", 1, 1500000),\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e52b0aea",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "**예상 출력 결과:**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bf05dcab",
   "metadata": {},
   "outputs": [],
   "source": [
    "E-commerce Order Analysis Report\n",
    "---------------------------------\n",
    "Total Revenue: 3,440,000 KRW\n",
    "\n",
    "Product Sales Summary:\n",
    "- Laptop: Total Items: 2, Total Revenue: 3,000,000 KRW\n",
    "- Mouse: Total Items: 3, Total Revenue: 75,000 KRW\n",
    "- Keyboard: Total Items: 2, Total Revenue: 90,000 KRW\n",
    "- Monitor: Total Items: 1, Total Revenue: 300,000 KRW\n",
    "\n",
    "Top 3 Most Sold Products:\n",
    "1. Mouse (3 items)\n",
    "2. Laptop (2 items)\n",
    "3. Keyboard (2 items)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6927f3f7",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 단계별 구현 가이드\n",
    "\n",
    "**1. `namedtuple`로 주문 구조 정의하기**\n",
    "- `collections.namedtuple`을 사용하여 'Order'라는 이름의 튜플을 만드세요.\n",
    "- 필드는 `order_id`, `product_name`, `quantity`, `price_per_item` 으로 구성합니다.\n",
    "\n",
    "**2. 데이터 분석 함수 구현하기**\n",
    "- `analyze_orders(orders)` 라는 함수를 정의하고, 그 안에 다음 분석 로직을 구현하세요.\n",
    "\n",
    "**2-1. 제품별 매출 및 판매량 집계**\n",
    "- `collections.defaultdict`를 사용하여 제품별 총 매출액(`total_revenue_per_product`)을 계산하세요. 키는 제품명, 값은 총 매출액입니다.\n",
    "- `collections.Counter`를 사용하여 제품별 총 판매 수량(`items_sold_per_product`)을 집계하세요.\n",
    "  - (힌트) `Counter`는 `counter[key] += value` 형식으로 값을 누적할 수 있습니다.\n",
    "\n",
    "**2-2. 전체 총 매출 계산**\n",
    "- `defaultdict`에 저장된 제품별 매출액을 모두 더하여 전체 총 매출(`total_revenue`)을 계산합니다.\n",
    "\n",
    "**2-3. 가장 많이 팔린 상품 Top 3 찾기**\n",
    "- `Counter`의 `most_common(3)` 메서드를 사용하여 가장 많이 팔린 상품 3개를 찾습니다.\n",
    "\n",
    "**3. 리포트 출력 함수 구현하기**\n",
    "- `print_report(total_revenue, revenue_per_product, items_per_product)` 함수를 만들어, 위 '예상 출력 결과'와 같은 형식으로 분석 내용을 깔끔하게 출력하세요.\n",
    "- 숫자는 세 자리마다 콤마(,)를 넣어주면 가독성이 더 좋습니다. (`f\"{value:,}\"` 포맷팅 사용)\n",
    "\n",
    "**4. 전체 로직 통합**\n",
    "\n",
    "아래는 위 기능들을 통합한 전체 코드의 뼈대입니다. `...` 부분을 채워 완성해보세요.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "61d28a66",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import namedtuple, defaultdict, Counter\n",
    "\n",
    "# 1. namedtuple 정의\n",
    "Order = namedtuple('Order', ['order_id', 'product_name', 'quantity', 'price_per_item'])\n",
    "\n",
    "# 샘플 데이터\n",
    "orders_data = [\n",
    "    (1, \"Laptop\", 1, 1500000), (2, \"Mouse\", 2, 25000), (1, \"Keyboard\", 1, 45000),\n",
    "    (3, \"Monitor\", 1, 300000), (2, \"Keyboard\", 1, 45000), (3, \"Mouse\", 1, 25000),\n",
    "    (4, \"Laptop\", 1, 1500000),\n",
    "]\n",
    "\n",
    "# Order namedtuple 리스트로 변환\n",
    "orders = [Order(*o) for o in orders_data]\n",
    "\n",
    "def analyze_orders(order_list):\n",
    "    # 2-1. 제품별 매출 및 판매량 집계\n",
    "    revenue_per_product = defaultdict(float)\n",
    "    items_per_product = Counter()\n",
    "    for order in order_list:\n",
    "        revenue_per_product[order.product_name] += order.quantity * order.price_per_item\n",
    "        items_per_product[order.product_name] += order.quantity\n",
    "\n",
    "    # 2-2. 전체 총 매출 계산\n",
    "    total_revenue = sum(revenue_per_product.values())\n",
    "\n",
    "    # 2-3. 가장 많이 팔린 상품 Top 3 찾기\n",
    "    top_3_products = items_per_product.most_common(3)\n",
    "\n",
    "    return total_revenue, revenue_per_product, items_per_product, top_3_products\n",
    "\n",
    "def print_report(total_revenue, revenue_per_product, items_per_product, top_3_products):\n",
    "    print(\"E-commerce Order Analysis Report\")\n",
    "    print(\"---------------------------------\")\n",
    "    print(f\"Total Revenue: {total_revenue:,.0f} KRW\\n\")\n",
    "    print(\"Product Sales Summary:\")\n",
    "    for product, total_rev in revenue_per_product.items():\n",
    "        total_items = items_per_product[product]\n",
    "        print(f\"- {product}: Total Items: {total_items}, Total Revenue: {total_rev:,.0f} KRW\")\n",
    "    \n",
    "    print(\"\\nTop 3 Most Sold Products:\")\n",
    "    for i, (product, count) in enumerate(top_3_products, 1):\n",
    "        print(f\"{i}. {product} ({count} items)\")\n",
    "\n",
    "# 4. 분석 및 리포트 출력\n",
    "if __name__ == \"__main__\":\n",
    "    total_revenue, revenue_per_product, items_per_product, top_3_products = analyze_orders(orders)\n",
    "    print_report(total_revenue, revenue_per_product, items_per_product, top_3_products)"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}
