{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f38c518c",
   "metadata": {},
   "source": [
    "# 2. NumPy 배열 인덱싱과 슬라이싱\n",
    "\n",
    "## 📚 학습 목표\n",
    "- NumPy 배열의 인덱싱 방법 이해\n",
    "- 슬라이싱을 통한 부분 배열 추출\n",
    "- 불리언 인덱싱과 팬시 인덱싱 활용\n",
    "- 다차원 배열의 인덱싱 기법 습득\n",
    "\n",
    "## 🔍 문제 설명\n",
    "\n",
    "NumPy 배열에서 특정 원소나 부분 배열을 추출하는 것은 데이터 분석의 핵심 기술입니다. 인덱싱과 슬라이싱을 통해 원하는 데이터만 선택적으로 처리할 수 있습니다.\n",
    "\n",
    "### 실전 활용 사례\n",
    "- **데이터 전처리**: 특정 조건을 만족하는 데이터만 선택\n",
    "- **이미지 처리**: 특정 영역의 픽셀 값 추출\n",
    "- **시계열 분석**: 특정 기간의 데이터만 분석\n",
    "- **머신러닝**: 특성 선택 및 데이터 분할\n",
    "\n",
    "## 📝 문제\n",
    "\n",
    "### 문제 2-1: 기본 인덱싱\n",
    "1. 1차원 배열 `[10, 20, 30, 40, 50]`에서 세 번째 원소를 추출하세요.\n",
    "2. 2차원 배열 `[[1, 2, 3], [4, 5, 6], [7, 8, 9]]`에서 (1, 2) 위치의 원소를 추출하세요.\n",
    "3. 3차원 배열에서 특정 원소에 접근하는 방법을 연습하세요.\n",
    "\n",
    "### 문제 2-2: 슬라이싱\n",
    "4. 1차원 배열에서 인덱스 1부터 4까지의 원소를 추출하세요.\n",
    "5. 2차원 배열에서 첫 번째 행의 모든 원소를 추출하세요.\n",
    "6. 2차원 배열에서 마지막 열의 모든 원소를 추출하세요.\n",
    "\n",
    "### 문제 2-3: 고급 인덱싱\n",
    "7. 불리언 인덱싱을 사용하여 5보다 큰 원소만 추출하세요.\n",
    "8. 팬시 인덱싱을 사용하여 특정 인덱스의 원소들을 추출하세요.\n",
    "9. 조건부 인덱싱을 사용하여 짝수 원소만 추출하세요.\n",
    "\n",
    "## 💡 해답 및 설명\n",
    "\n",
    "### 해답 2-1: 기본 인덱싱\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f139a161",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "# 1. 1차원 배열에서 세 번째 원소 추출\n",
    "arr_1d = np.array([10, 20, 30, 40, 50])\n",
    "third_element = arr_1d[2]  # 인덱스는 0부터 시작\n",
    "print(\"세 번째 원소:\", third_element)  # 출력: 30\n",
    "\n",
    "# 2. 2차원 배열에서 (1, 2) 위치의 원소 추출\n",
    "arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "element_1_2 = arr_2d[1, 2]  # 또는 arr_2d[1][2]\n",
    "print(\"(1, 2) 위치의 원소:\", element_1_2)  # 출력: 6\n",
    "\n",
    "# 3. 3차원 배열 인덱싱\n",
    "arr_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])\n",
    "print(\"3차원 배열 형태:\", arr_3d.shape)  # 출력: (2, 2, 2)\n",
    "element_3d = arr_3d[0, 1, 0]\n",
    "print(\"(0, 1, 0) 위치의 원소:\", element_3d)  # 출력: 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a677cb0c",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 해답 2-2: 슬라이싱\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac15e003",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 4. 1차원 배열 슬라이싱\n",
    "arr_1d = np.array([10, 20, 30, 40, 50, 60, 70])\n",
    "slice_1d = arr_1d[1:5]  # 인덱스 1부터 4까지 (5는 포함되지 않음)\n",
    "print(\"인덱스 1~4의 원소:\", slice_1d)  # 출력: [20 30 40 50]\n",
    "\n",
    "# 5. 2차원 배열에서 첫 번째 행 추출\n",
    "arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "first_row = arr_2d[0, :]  # 또는 arr_2d[0]\n",
    "print(\"첫 번째 행:\", first_row)  # 출력: [1 2 3]\n",
    "\n",
    "# 6. 2차원 배열에서 마지막 열 추출\n",
    "last_column = arr_2d[:, -1]\n",
    "print(\"마지막 열:\", last_column)  # 출력: [3 6 9]\n",
    "\n",
    "# 추가 슬라이싱 예제\n",
    "print(\"\\n슬라이싱 추가 예제:\")\n",
    "print(\"첫 번째 행과 두 번째 행:\", arr_2d[0:2, :])\n",
    "print(\"첫 번째 열과 두 번째 열:\", arr_2d[:, 0:2])\n",
    "print(\"2x2 부분 배열 (좌상단):\", arr_2d[0:2, 0:2])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f792870a",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 해답 2-3: 고급 인덱싱\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "59c9b941",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 7. 불리언 인덱싱\n",
    "arr = np.array([1, 3, 5, 7, 9, 2, 4, 6, 8, 10])\n",
    "boolean_mask = arr > 5\n",
    "elements_greater_than_5 = arr[boolean_mask]\n",
    "print(\"5보다 큰 원소:\", elements_greater_than_5)  # 출력: [7 9 6 8 10]\n",
    "\n",
    "# 8. 팬시 인덱싱\n",
    "fancy_indices = [0, 2, 4, 6, 8]\n",
    "selected_elements = arr[fancy_indices]\n",
    "print(\"선택된 인덱스의 원소:\", selected_elements)  # 출력: [1 5 9 4 8]\n",
    "\n",
    "# 9. 조건부 인덱싱 (짝수 원소만)\n",
    "even_elements = arr[arr % 2 == 0]\n",
    "print(\"짝수 원소:\", even_elements)  # 출력: [2 4 6 8 10]\n",
    "\n",
    "# 2차원 배열에서의 불리언 인덱싱\n",
    "arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "mask_2d = arr_2d > 5\n",
    "print(\"\\n2차원 배열에서 5보다 큰 원소:\")\n",
    "print(\"불리언 마스크:\")\n",
    "print(mask_2d)\n",
    "print(\"조건을 만족하는 원소:\", arr_2d[mask_2d])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a21ceae",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 🔧 주요 인덱싱 기법\n",
    "\n",
    "### 기본 인덱싱\n",
    "- `array[index]`: 1차원 배열에서 특정 위치의 원소\n",
    "- `array[row, col]`: 2차원 배열에서 특정 위치의 원소\n",
    "- `array[i, j, k]`: 3차원 배열에서 특정 위치의 원소\n",
    "\n",
    "### 슬라이싱\n",
    "- `array[start:stop:step]`: 범위 지정 슬라이싱\n",
    "- `array[start:]`: 시작 위치부터 끝까지\n",
    "- `array[:stop]`: 처음부터 끝 위치까지\n",
    "- `array[::step]`: 스텝 간격으로 슬라이싱\n",
    "- `array[::-1]`: 역순으로 슬라이싱\n",
    "\n",
    "### 불리언 인덱싱\n",
    "- `array[boolean_array]`: 조건을 만족하는 원소만 선택\n",
    "- `array[array > value]`: 특정 값보다 큰 원소 선택\n",
    "- `array[(array > min_val) & (array < max_val)]`: 범위 조건\n",
    "\n",
    "### 팬시 인덱싱\n",
    "- `array[index_array]`: 인덱스 배열로 원소 선택\n",
    "- `array[[0, 2, 4]]`: 특정 인덱스의 원소들 선택\n",
    "\n",
    "## 📊 추가 연습문제\n",
    "\n",
    "### 연습문제 1: 기본 인덱싱\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42eec4f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 다음 배열을 생성하고 문제를 풀어보세요:\n",
    "arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])\n",
    "\n",
    "# 1. 두 번째 행의 세 번째 원소를 추출하세요.\n",
    "# 2. 첫 번째 열의 모든 원소를 추출하세요.\n",
    "# 3. 마지막 행의 마지막 원소를 추출하세요."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e42d3b3a",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 연습문제 2: 슬라이싱\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88a0ff61",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 다음 배열을 사용하여 문제를 풀어보세요:\n",
    "arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "\n",
    "# 1. 인덱스 2부터 7까지의 원소를 추출하세요.\n",
    "# 2. 짝수 인덱스의 원소만 추출하세요 (0, 2, 4, 6, 8).\n",
    "# 3. 배열을 역순으로 출력하세요."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36033eb4",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 연습문제 3: 고급 인덱싱\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b90e1f02",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 다음 배열을 사용하여 문제를 풀어보세요:\n",
    "arr = np.array([15, 23, 8, 42, 17, 31, 5, 19, 28, 12])\n",
    "\n",
    "# 1. 20보다 큰 원소만 추출하세요.\n",
    "# 2. 10의 배수인 원소만 추출하세요.\n",
    "# 3. 인덱스 1, 3, 5, 7, 9의 원소를 추출하세요."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19aaa904",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## ⚠️ 주의사항 및 팁\n",
    "\n",
    "1. **인덱스 범위**: Python 인덱스는 0부터 시작하며, 슬라이싱에서 끝 인덱스는 포함되지 않습니다.\n",
    "2. **뷰(View) vs 복사(Copy)**: 슬라이싱은 원본 배열의 뷰를 반환하지만, 팬시 인덱싱은 복사를 반환합니다.\n",
    "3. **불리언 인덱싱**: 조건을 만족하는 원소만 선택하므로 결과 배열의 크기가 달라질 수 있습니다.\n",
    "4. **다차원 인덱싱**: 각 차원별로 인덱스를 지정할 수 있습니다.\n",
    "5. **음수 인덱스**: -1은 마지막 원소, -2는 뒤에서 두 번째 원소를 의미합니다.\n",
    "\n",
    "## 🎯 실전 활용 예제\n",
    "\n",
    "### 예제 1: 이미지 데이터 처리\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff369d44",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 28x28 이미지 데이터에서 특정 영역 추출\n",
    "image = np.random.randint(0, 256, (28, 28))\n",
    "print(\"전체 이미지 형태:\", image.shape)\n",
    "\n",
    "# 중앙 14x14 영역 추출\n",
    "center_region = image[7:21, 7:21]\n",
    "print(\"중앙 영역 형태:\", center_region.shape)\n",
    "\n",
    "# 특정 밝기 이상의 픽셀만 추출\n",
    "bright_pixels = image[image > 200]\n",
    "print(\"밝은 픽셀 개수:\", len(bright_pixels))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6db89df8",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 예제 2: 시계열 데이터 분석\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d86a1d1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 100일간의 주가 데이터\n",
    "np.random.seed(42)\n",
    "stock_prices = np.random.normal(100, 10, 100).cumsum()\n",
    "\n",
    "# 최근 30일 데이터만 추출\n",
    "recent_30_days = stock_prices[-30:]\n",
    "print(\"최근 30일 데이터 형태:\", recent_30_days.shape)\n",
    "\n",
    "# 상승한 날의 데이터만 추출\n",
    "up_days = stock_prices[1:][stock_prices[1:] > stock_prices[:-1]]\n",
    "print(\"상승한 날의 수:\", len(up_days))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72edfcf5",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 예제 3: 조건부 데이터 필터링\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c42257d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 학생들의 점수 데이터\n",
    "scores = np.array([85, 92, 78, 96, 88, 75, 91, 83, 79, 94])\n",
    "grades = np.array(['A', 'A', 'C', 'A', 'B', 'C', 'A', 'B', 'C', 'A'])\n",
    "\n",
    "# A등급 학생들의 점수만 추출\n",
    "a_grade_scores = scores[grades == 'A']\n",
    "print(\"A등급 학생들의 점수:\", a_grade_scores)\n",
    "\n",
    "# 90점 이상 학생들의 등급\n",
    "high_scores_grades = grades[scores >= 90]\n",
    "print(\"90점 이상 학생들의 등급:\", high_scores_grades)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0658ac85",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 📚 다음 단계\n",
    "\n",
    "이제 NumPy 배열의 인덱싱과 슬라이싱을 익혔습니다. 다음 단계에서는:\n",
    "- 배열 연산과 브로드캐스팅\n",
    "- 조건부 필터링과 마스킹\n",
    "- 배열 변형과 재구성\n",
    "- 집계 함수와 통계 계산\n",
    "\n",
    "을 학습하게 됩니다.\n",
    "\n",
    "---\n",
    "\n",
    "**참고 자료:**\n",
    "- [NumPy 인덱싱 가이드](https://numpy.org/doc/stable/user/basics.indexing.html)\n",
    "- [NumPy 슬라이싱 튜토리얼](https://numpy.org/doc/stable/reference/arrays.indexing.html) "
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}
