NumPy 인덱싱 소개

NumPyBeginner
지금 연습하기

소개

다시 오신 것을 환영합니다! NumPy 배열을 생성하는 방법을 이해했으니, 이제 배열 내의 데이터를 액세스하고 조작하는 방법을 배울 차례입니다. 이 실습에서는 NumPy 배열 내에서 데이터를 액세스하고 수정하는 주요 방법인 **인덱싱 (indexing)**을 탐구합니다.

배운 내용 복습

이전 실습에서 다음 내용을 배웠습니다.

  • 다양한 NumPy 함수를 사용하여 배열 생성 방법
  • 배열과 Python 리스트의 차이점
  • 배열 차원 (1D, 2D, 3D) 및 데이터 유형

이제 다음 내용을 배우게 됩니다.

  • 개별 요소 또는 요소 그룹 액세스 방법
  • 배열에서 특정 행, 열 또는 영역 추출 방법
  • 복잡한 데이터 필터링을 위한 고급 선택 기법 사용 방법
  • 인덱싱을 통한 배열 데이터 수정 방법

인덱싱이 중요한 이유

인덱싱은 다음과 같은 작업을 가능하게 하므로 데이터 조작의 기본입니다.

  • 분석을 위한 데이터 부분 집합 추출
  • 대규모 데이터셋의 특정 값 수정
  • 조건에 기반한 데이터 필터링
  • 선택된 요소에 대한 벡터화된 연산 수행

이러한 기술은 Python 에서의 모든 데이터 분석 또는 과학 컴퓨팅 작업에 필수적입니다.

1D 배열의 기본 인덱싱 및 슬라이싱

배열을 생성하는 방법을 알았으니, 이제 배열의 내용을 액세스하는 방법을 배워보겠습니다. 이 실습의 모든 작업은 indexing_practice.py 파일에서 진행됩니다.

배열 인덱싱 이해하기

0 기반 인덱싱

Python 리스트와 마찬가지로 NumPy 배열도 0 기반 인덱싱을 사용합니다. 이는 다음을 의미합니다.

  • 첫 번째 요소는 인덱스 0에 있습니다.
  • 두 번째 요소는 인덱스 1에 있습니다.
  • 이와 같이 계속됩니다...

배열 인덱스 시각화

x = [10, 20, 30, 40, 50] 배열의 경우:

Index:  0   1   2   3   4
Value: 10  20  30  40  50

따라서 x[0]10을, x[2]30을 반환합니다.

슬라이싱 구문

슬라이싱을 사용하면 start:stop:step 구문으로 요소 범위를 선택할 수 있습니다.

  • start: 슬라이스가 시작되는 인덱스 (포함)
  • stop: 슬라이스가 끝나는 인덱스 (제외)
  • step: 건너뛸 요소 수 (선택 사항, 기본값은 1)

일반적인 슬라이싱 패턴:

  • x[1:4]: 인덱스 1, 2, 3 의 요소
  • x[:3]: 처음 3 개 요소 (인덱스 0, 1, 2)
  • x[2:]: 인덱스 2 부터 끝까지
  • x[::2]: 인덱스 0 부터 시작하여 두 번째 요소마다
  • x[::-1]: 전체 배열 반전

먼저 편집기에서 indexing_practice.py 파일을 엽니다. 그런 다음 파일의 전체 내용을 다음 코드로 바꿉니다. 이 코드는 1D 배열을 생성하고 단일 요소 및 요소 슬라이스를 액세스하는 방법을 보여줍니다.

import numpy as np

## 0 부터 9 까지의 숫자로 1D 배열 생성
x = np.arange(10)
print("Original array:", x)

## 인덱스 2 의 단일 요소 액세스
element = x[2]
print("Element at index 2:", element)

## 인덱스 1 부터 인덱스 7 까지 (7 은 제외) 2 씩 건너뛰며 슬라이싱
a_slice = x[1:7:2]
print("Slice from 1 to 7 with step 2:", a_slice)

indexing_practice.py에 코드를 추가한 후 파일을 저장합니다. 이제 터미널에서 다음 명령을 실행하여 스크립트를 실행합니다.

python indexing_practice.py

다음과 같은 출력이 표시되어야 합니다. 지정된 인덱스의 요소와 결과 슬라이스가 포함된 원본 배열이 표시됩니다.

Original array: [0 1 2 3 4 5 6 7 8 9]
Element at index 2: 2
Slice from 1 to 7 with step 2: [1 3 5]

다차원 배열 인덱싱

이제 여러 차원을 가진 배열을 다뤄보겠습니다. NumPy 가 Python 리스트에 비해 진가를 발휘하는 부분입니다!

다차원 사고방식

2D 배열을 표 (Table) 처럼 생각하기

2D 배열은 스프레드시트나 표와 같습니다.

  • **행 (Rows)**은 첫 번째 차원 (가로) 입니다.
  • **열 (Columns)**은 두 번째 차원 (세로) 입니다.
  • 행과 열 인덱스를 모두 지정합니다: array[row, column]

2D 인덱싱 시각화

2D 배열의 경우:

array = [[10, 20, 30],
         [40, 50, 60],
         [70, 80, 90]]

Indices:     0,0  0,1  0,2
             1,0  1,1  1,2
             2,0  2,1  2,2
  • array[0, 0] → 10 (첫 번째 행, 첫 번째 열)
  • array[1, 2] → 60 (두 번째 행, 세 번째 열)
  • array[2, 1] → 80 (세 번째 행, 두 번째 열)

전체 행 또는 열 선택하기

  • array[0] 또는 array[0, :] → 전체 첫 번째 행 [10, 20, 30]
  • array[:, 1] → 전체 두 번째 열 [20, 50, 80]
  • 이는 중첩된 Python 리스트보다 훨씬 편리합니다!

2 차원 (2D) 배열로 이를 연습해 보겠습니다. indexing_practice.py 파일을 아래 코드로 업데이트하세요. 이 스크립트는 3x4 배열을 생성하고 단일 요소와 전체 행을 액세스하는 방법을 보여줍니다.

import numpy as np

## 2D 배열 생성 (3 행, 4 열)
x = np.arange(12).reshape(3, 4)
print("Original 2D array:\n", x)

## 1 행 2 열의 요소 액세스
element = x[1, 2]
print("\nElement at (1, 2):", element)

## 전체 첫 번째 행 (행 인덱스 0) 액세스
first_row = x[0]
print("\nFirst row:", first_row)

파일을 저장하고 터미널에서 다시 실행합니다.

python indexing_practice.py

출력에는 2D 배열과 선택한 특정 부분이 표시됩니다.

Original 2D array:
 [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

Element at (1, 2): 6

First row: [0 1 2 3]

고급 인덱싱

기본 슬라이싱은 연속된 영역에 잘 작동하지만, 때로는 더 복잡한 선택이 필요합니다. NumPy 는 두 가지 강력한 고급 인덱싱 기법을 제공합니다.

정수 배열 인덱싱 (Integer Array Indexing)

인덱스 배열을 제공하여 임의의 요소를 선택합니다. 이는 위치를 사용하여 리스트에서 특정 항목을 선택하는 것과 같습니다.

실제 예시: 시험 점수가 있고 3, 7, 12 번 학생들의 점수를 확인하고 싶다고 가정해 봅시다.

scores = np.array([85, 92, 78, 95, 88, 76, 91, 89, 84, 93, 87, 90, 82])
student_positions = [3, 7, 12]  ## 관심 있는 학생들의 위치
selected_scores = scores[student_positions]  ## 결과: [95, 89, 82]

불리언 배열 인덱싱 (Boolean Array Indexing, 마스킹)

조건에 따라 요소를 선택합니다. True/False 값으로 "마스크"를 생성한 다음 이를 사용하여 배열을 필터링합니다.

실제 예시: 반에서 합격 점수 (80 점 이상) 를 필터링합니다.

scores = np.array([85, 92, 78, 95, 88, 76, 91, 89, 84, 93])
passing_mask = scores >= 80  ## 결과: [True, True, False, True, True, False, True, True, True, True]
passing_scores = scores[passing_mask]  ## 결과: [85, 92, 95, 88, 91, 89, 84, 93]

왜 이것이 중요한가

  • 정수 인덱싱: 특정 데이터 포인트를 샘플링하는 데 완벽합니다.
  • 불리언 인덱싱: 데이터 필터링 및 조건부 선택에 이상적입니다.
  • 둘 다 복사본을 생성하며 (뷰가 아님), 따라서 수정이 원본 배열에 영향을 미치지 않습니다.

둘 다 시도해 보겠습니다. indexing_practice.py 파일의 내용을 다음 코드로 바꾸세요.

import numpy as np

## --- 정수 배열 인덱싱 ---
x = np.arange(10, 0, -1)
print("Array for integer indexing:", x)

## 인덱스 3, 3, 1, 8 의 요소 선택
selected_elements = x[np.array([3, 3, 1, 8])]
print("Selected elements with integer array:", selected_elements)


## --- 불리언 배열 인덱싱 ---
y = np.array([1., -1., -2., 3.])
print("\nArray for boolean indexing:", y)

## 음수 요소에 대한 불리언 마스크 생성
mask = y < 0
print("Boolean mask (y < 0):", mask)

## 조건이 True 인 요소 선택
negative_elements = y[mask]
print("Elements where y < 0:", negative_elements)

파일을 저장하고 스크립트를 실행합니다.

python indexing_practice.py

출력은 정수 및 불리언 인덱싱이 배열에서 특정 데이터를 선택하는 데 어떻게 작동하는지 보여줍니다.

Array for integer indexing: [10  9  8  7  6  5  4  3  2  1]
Selected elements with integer array: [7 7 9 2]

Array for boolean indexing: [ 1. -1. -2.  3.]
Boolean mask (y < 0): [False  True  True False]
Elements where y < 0: [-1. -2.]

인덱싱된 배열에 값 할당

인덱싱은 데이터를 읽는 데만 사용되는 것이 아니라 데이터를 수정하는 데도 강력합니다. 할당 연산자 (=) 의 왼쪽에 어떤 인덱싱 방법이든 사용하여 특정 요소를 변경할 수 있습니다.

브로드캐스팅 (Broadcasting): 형태 (Shape) 호환성 맞추기

인덱싱된 배열에 값을 할당할 때, NumPy 는 형태를 호환시키기 위해 브로드캐스팅을 사용합니다. 이것은 NumPy 의 가장 강력한 기능 중 하나입니다!

브로드캐스팅 규칙

NumPy 는 할당 중에 다음 규칙에 따라 작은 배열을 자동으로 확장하여 큰 배열과 일치시킬 수 있습니다.

  1. 단일 값을 여러 요소에 할당: 하나의 값을 여러 위치에 할당할 수 있습니다.
  2. 작은 배열을 더 큰 선택 영역에 할당: 차원이 호환되는 한 가능합니다.

할당 시 브로드캐스팅 예시

## 슬라이스에 단일 값 할당
arr = np.array([1, 2, 3, 4, 5])
arr[1:4] = 99  ## 결과: [1, 99, 99, 99, 5]

## 일치하는 슬라이스에 배열 할당
arr = np.array([1, 2, 3, 4, 5])
arr[1:4] = [10, 20, 30]  ## 결과: [1, 10, 20, 30, 5]

## 브로드캐스팅을 사용한 불리언 인덱싱
arr = np.array([1, 2, 3, 4, 5])
arr[arr % 2 == 0] = -1  ## 모든 짝수를 -1 로 대체

중요 참고 사항

  • 브로드캐스팅은 형태가 호환될 때만 작동합니다.
  • 할당되는 값의 형태는 인덱싱된 선택 영역에 "맞아야" 합니다.
  • 이는 요소를 수동으로 반복하는 것보다 훨씬 효율적입니다.

이 기능을 실제로 확인하기 위해 indexing_practice.py 파일을 다음 코드로 업데이트하세요.

import numpy as np

## --- 슬라이스에 단일 값 할당 ---
x = np.arange(10)
print("Original array:", x)

## 인덱스 2 부터 4 까지의 요소에 값 99 할당
x[2:5] = 99
print("After assigning 99 to slice [2:5]:", x)


## --- 불리언 조건을 기반으로 값 할당 ---
y = np.arange(10)
print("\nOriginal array:", y)

## 모든 짝수에 값 -1 할당
y[y % 2 == 0] = -1
print("After assigning -1 to even numbers:", y)

파일을 저장하고 터미널에서 실행합니다.

python indexing_practice.py

출력은 수정 전후의 배열을 보여주며, 이 기능이 데이터 조작에 얼마나 강력한지 보여줍니다.

Original array: [0 1 2 3 4 5 6 7 8 9]
After assigning 99 to slice [2:5]: [ 0  1 99 99 99  5  6  7  8  9]

Original array: [0 1 2 3 4 5 6 7 8 9]
After assigning -1 to even numbers: [-1  1 -1  3 -1  5 -1  7 -1  9]

요약

이 실습에서는 NumPy 배열 인덱싱의 필수 기법을 배웠습니다. Python 리스트와 유사하게 1D 배열에서 기본적인 단일 요소 접근 및 슬라이싱으로 시작했습니다. 그런 다음 다차원 배열 인덱싱으로 진행하여 특정 요소와 하위 배열을 선택했습니다. 마지막으로 정수 및 불리언 배열을 사용한 고급 인덱싱을 탐색했으며, 이러한 강력한 선택 방법을 사용하여 배열 내의 데이터를 수정하는 방법을 배웠습니다. 이러한 기술은 NumPy 를 사용한 Python 에서의 효과적인 데이터 조작 및 분석에 필수적입니다.