왜 Numpy를 배워야 할까? -2 (넘파이 인덱싱, ...)
2024.05.21 - [PYTHON/MachineLearning] - 왜 Numpy를 배워야 할까? -1
왜 Numpy를 배워야 할까? -1 (배열 생성, 차원 변환, ...)
1. Numpy 소개* Numpy 란? 넘파이(Numpy, Numerical Python)는 파이썬에서 과학적 계산을 위한 핵심 라이브러리 중 하나이다. 단순한 문법과 폭넓은 호환성으로 널리 사용되고 있는 파이썬이지만, 대규모
luvtako.tistory.com
- 넘파이 기본 개념과 배열 생성은 이전 글 참고해주세요 :D
1. 넘파이의 ndarray의 데이터 세트 선택하기 - 인덱싱(Indexing)
인덱싱 : 넘파이에서 ndarray 내의 일부 데이터 세트나 특정 데이터만을 선택할 수 있도록 하는 것이다.
① 단일 값 추출 : 원하는 위치의 인덱스 값을 지정하면 해당 위치의 데이터가 반환된다.
② 슬라이싱(Slicing) : 연속된 인덱스상의 ndarray를 추출하는 방식이다. ':' 기호 사이에 시작 인덱스와 종료 인덱스를 표시하면 시작 인덱스에서 종료 인덱스-1 위치에 있는 데이터의 ndarray를 반환한다.
③ 팬시 인덱싱(Fancy Indexing) : 일정한 인덱싱 집합을 리스트 또는 ndarray 형태로 지정해 해당 위치에 있는 데이터의 ndarray를 반환한다.
④ 불린 인덱싱(Boolean Indexing) : 특정 조건에 해당하는지 여부인 True/False 값 인덱싱 집합을 기반으로 True에 해당하는 인덱스 위치에 있는 데이터의 ndarray를 반환한다.
2. 단일 값 추출
# 1부터 9까지의 1차원 ndarray 생성
array1 = np.arange(1, 10)
print(array1)
# index는 0부터 시작하므로 array1[2]는 3번째 index 위치의 데이터값을 의미
value = array1[2]
print(value)
print(type(value))
[output]
[1 2 3 4 5 6 7 8 9]
3
<class 'numpy.int64'>
- 인덱스는 0부터 시작하므로 array1[2]는 3번째 위치의 데이터값을 의미한다.
- array1[2](= value)의 타입은 더 이상 ndarray의 타입이 아니고 ndarray 내의 데이터값을 의미한다.
print(f'맨 뒤의 값 : {array1[-1]}, 맨 뒤에서 두 번째 값 : {array1[-2]}')
# print('맨 뒤의 값 : {}, 맨 뒤에서 두 번째 값 : {}'.format(array1[-1], array1[-2])) 같은 표현
[output]
맨 뒤의 값 : 9, 맨 뒤에서 두 번째 값 : 8
- 인덱스에 마이너스 기호를 이용하면 맨 뒤에서부터 데이터를 추출할 수 있다.
- 인덱스 -1은 맨 뒤의 데이터값을 의미하고 -2는 맨 뒤에서 두 번째에 있는 데이터값을 의미한다.
- 단일 인덱스를 이용해 ndarray 내의 데이터값도 수정 가능하다.
array1[0] = 9
array1[8] = 0
print(array1)
[output]
[9 2 3 4 5 6 7 8 0]
- 0번 인덱스의 데이터값(1)을 9로 수정
- 8번 인덱스의 데이터값(9)을 0으로 수정
- 2차원의 경우 콤마(,)로 분리된 row와 column 위치의 인덱스를 통해 접근한다. [row, column]
array1d = np.arange(1, 10) # 1차원 배열 생성
array2d = array1d.reshape(3, 3) # reshape를 이용해 array1을 2차원 배열로 변환
print(array2d) # 출력
print(array2d[0, 0])
print(array2d[0, 1])
print(array2d[1, 0])
print(array2d[2, 2])
[output]
[[1 2 3]
[4 5 6]
[7 8 9]]
1
2
4
9
- 정확한 표현은 [row = 0, column = 1]이 아닌 [axis 0 = 0, axis 1 = 1]이다.
3. 슬라이싱(Slicing)
- ':' 기호를 이용해 연속한 데이터를 슬라이싱해서 추출할 수 있다.
- 단일 데이터값 추출을 제외하고 슬라이싱, 팬시 인덱싱, 불린 인덱싱으로 추출된 데이터 세트는 모두 ndarray 타입이다.
- ':' 왼쪽에 시작 인덱스와 오른쪽에 종료 인덱스를 표시하면 시작 인덱스에서 종료 인덱스-1의 위치에 있는 데이터의 ndarray를 반환한다.
array1 = np.arange(1, 10)
array3 = array1[0:3]
print(array1)
print(array3)
print(type(array3))
[output]
[1 2 3 4 5 6 7 8 9]
[1 2 3]
<class 'numpy.ndarray'>
- 시작, 종료 인덱스는 생략 가능하다.
① ':' 기호 앞에 시작 인덱스를 생략하면 자동으로 맨 처음 인덱스인 0으로 간주한다.
② ':' 기호 뒤에 종료 인덱스를 생략하면 자동으로 맨 마지막 인덱스로 간주한다.
③ ':' 기호 앞뒤에 시작과 종료 인덱스를 둘 다 생략하면 맨 처음~맨 마지막 인덱스로 간주한다.
array1 = np.arange(1, 10)
array4 = array1[:3]
print(array4)
array5 = array1[3:]
print(array5)
array6 = array1[:]
print(array6)
[output]
# [:3] - 처음부터 3-1=2번 인덱스까지
[1 2 3]
# [3:] - 3번 인덱스부터 끝까지
[4 5 6 7 8 9]
# [:] - 처음부터 끝까지
[1 2 3 4 5 6 7 8 9]
- 2차원 슬라이싱은 콤마(,)로 row와 column 인덱스를 지칭한다.
- ex) array1[row 시작 인덱스 : row 종료 인덱스, col 시작 인덱스 : col 종료 인덱스]
array1d = np.arange(1, 10)
array2d = array1d.reshape(3, 3)
print(array2d)
print(array2d[0:2, 0:2])
print(array2d[1:3, 0:3])
print(array2d[1:3, :])
print(array2d[:, :])
print(array2d[:2, 1:])
print(array2d[:2, 0])
[output]
# array2d
[[1 2 3]
[4 5 6]
[7 8 9]]
# array2d[0:2, 0:2]
[[1 2]
[4 5]]
# array2d[1:3, 0:3]
[[4 5 6]
[7 8 9]]
# array2d[1:3, :]
[[4 5 6]
[7 8 9]]
# array2d[:, :]
[[1 2 3]
[4 5 6]
[7 8 9]]
# array2d[:2, 1:]
[[2 3]
[5 6]]
# array2d[:2, 0]
[1 4]
- 혹시 이해가 잘 안가신다면 그림을 참고해주세요!
- 2차원 ndarray에서 뒤에 오는 인덱스를 없애면 1차원 ndarray를 반환한다.
print(array2d[0])
print(array2d[1])
print(f'array2d[0] shape : {array2d[0].shape} array2d[1] shape : {array2d[1].shape}')
[output]
[1 2 3]
[4 5 6]
array2d[0] shape : (3,) array2d[1] shape : (3,)
4. 팬시 인덱싱(Fancy Indexing)
- 팬시 인덱싱은 리스트나 ndarray로 인덱스 집합을 지정하면 해당 위치의 인덱스에 해당하는 ndarray를 반환하는 인덱싱 방식이다.
array1d = np.arange(1, 10)
array2d = array1d.reshape(3, 3)
array3 = array2d[[0, 1], 2]
print(array3.tolist())
array4 = array2d[[0, 1], 0:2]
print(array4.tolist())
array5 = array2d[[0, 1]]
print(array5.tolist())
[output]
[3, 6]
[[1, 2], [4, 5]]
[[1, 2, 3], [4, 5, 6]]
5. 불린 인덱싱(Boolean Indexing)
- 불린 인덱싱은 조건 필터링과 검색을 동시에 할 수 있기 때문에 매우 자주 사용되는 인덱싱 방식이다.
- for loop/if else 문보다 훨씬 간단하게 구현 가능하다.
array1d = np.arange(1, 10)
# [ ] 안에 array1d > 5 Boolean indexing을 적용
array3 = array1d[array1d > 5]
print(array3)
[output]
[6 7 8 9]
- 넘파이 객체에 조건식을 할당했을 때
array1d > 5
[output]
array([False, False, False, False, False, True, True, True, True])
- 5보다 큰 데이터가 있는 위치는 True, 그렇지 않는 경우는 False 값으로 이뤄진 ndarray 객체가 반환되었다.
boolean_indexs = np.array([False, False, False, False, False, True, True, True, True])
array3 = array1d[boolean_indexs]
print('불린 인덱스로 필터링 결과 :', array3)
[output]
불린 인덱스로 필터링 결과 : [6 7 8 9]
- 위와 동일한 불린 ndarray를 만들고 이를 array1d[ ] 내에 인덱스로 입력하면 동일한 데이터 세트가 반환된다.
indexs = np.array([5, 6, 7, 8])
array4 = array1d[indexs]
print('일반 인덱스로 필터링 결과 :', array4)
[output]
일반 인덱스로 필터링 결과 : [6 7 8 9]
- 직접 인덱스 집합을 만들어 대입한 것과 동일하다.
- 불린 인덱싱은 내부적으로 여러 단계를 거쳐서 동작하지만, 코드 자체는 단순히 [ ] 내에 원하는 필터링 조건만 넣으면 해당 조건을 만족하는 ndarray 데이터 세트를 반환하기 때문에 사용자가 내부 로직에 크게 신경 쓰지 않고 쉽게 코딩 가능하다.
* 참고 도서
- 저자
- 권철민
- 출판
- 위키북스
- 출판일
- 2022.04.21